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

CIWEMB-487: Prevent deletion membership lines linked to one-off payments #504

Merged
merged 1 commit into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,12 @@ private function getOneOffPaymentLineItemParams() {
*/
private function createOneOffPayment($createdLineItemData) {
$totalIncTax = $this->submittedValues['amount_inc_tax'] ?? 0;
$priceSetId = $this->getPriceFieldParentSet($createdLineItemData['line_item']['price_field_id']);
$lineItem = [
$priceSetId => [
$createdLineItemData['line_item']['price_field_id'] => $createdLineItemData['line_item'],
],
];

$contribution = civicrm_api3('Contribution', 'create', [
'financial_type_id' => $createdLineItemData['line_item']['financial_type_id'],
Expand All @@ -254,16 +260,19 @@ private function createOneOffPayment($createdLineItemData) {
'source' => 'Manage Instalments form - One off payment',
'contribution_recur_id' => $this->recurringContribution['id'],
'is_pay_later' => TRUE,
]);

civicrm_api3('MembershipPayment', 'create', [
'membership_id' => $createdLineItemData['membership']['id'],
'contribution_id' => $contribution['id'],
'line_item' => $lineItem,
]);

return array_shift($contribution['values']);
}

private function getPriceFieldParentSet($priceFieldId) {
return civicrm_api3('PriceField', 'getvalue', [
'return' => 'price_set_id',
'id' => $priceFieldId,
]);
}

/**
* Sends the confirmation email if such option is selected.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ public function postProcess() {
$this->lineItemEndDate = $this->getElementValue('end_date');
}

if ($this->isLineItemLinkedToOneOffPayment()) {
CRM_Core_Session::setStatus(
'This line item cannot be deleted as it is the last line item linked to a one-off contribution.',
"Error Removing {$this->recurringLineItemData['label']}",
'error'
);

return;
}

$tx = new CRM_Core_Transaction();
try {
if ($this->isLineItemAMembership()) {
Expand Down Expand Up @@ -137,6 +147,26 @@ public function postProcess() {
}
}

private function isLineItemLinkedToOneOffPayment() {
$pendingStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
$lineToDeleteFullAmount = $this->recurringLineItemData['line_total'] + $this->recurringLineItemData['tax_amount'];
$query = "SELECT count(li.contribution_id) as lines_count FROM civicrm_contribution c
INNER JOIN civicrm_line_item li ON c.id = li.contribution_id
WHERE c.contribution_status_id = {$pendingStatusId}
AND c.contribution_recur_id = {$this->recurringContributionID}
AND c.total_amount = {$lineToDeleteFullAmount}
AND c.source = 'Manage Instalments form - One off payment'
GROUP BY li.contribution_id
HAVING lines_count = 1";
$lineItemsCount = CRM_Core_DAO::singleValueQuery($query);

if ($lineItemsCount == 1) {
return TRUE;
}

return FALSE;
}

/**
* Updates the amount of the recurring contribution checking list of line
* items associated to it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public function calculate() {
$taxAmount = $this->instalmentTaxAmountCalculator->calculateByMembershipType($membershipType, $membershipAmount);

$skipProRataUntilSetting = $settings[SettingField::ANNUAL_PRORATA_SKIP_ELEMENT] ?? NULL;
if (!empty($skipProRataUntilSetting) && $this->isWithinMembershipTypeProRataSkipPeriod($skipProRataUntilSetting)) {
if (!empty($skipProRataUntilSetting) && !empty($skipProRataUntilSetting['M']) && $this->isWithinMembershipTypeProRataSkipPeriod($skipProRataUntilSetting)) {
$amount = $membershipAmount;
}
else {
Expand Down Expand Up @@ -124,16 +124,16 @@ private function calculateProRataAmounts($membershipType, $proRataScheme, $nonPr
$this->proRatedUnit = self::BY_MONTHS;
$duration = self::TWELVE_MONTHS;
$this->proRatedNumber = $membershipTypeDurationCalculator->calculateMonthsBasedOnDates($this->startDate, $this->endDate, $this->joinDate);
if ($this->isDurationWithInOneYearPeriod($duration, $this->proRatedNumber)) {
if ($this->isDurationWithInOneYearPeriod($duration, $this->proRatedNumber) && !empty($this->endDate)) {
$this->reCalculateEndDate();
$this->proRatedNumber = $membershipTypeDurationCalculator->calculateMonthsBasedOnDates($this->startDate, $this->endDate, $this->joinDate);
}
}
else {
$this->proRatedUnit = self::BY_DAYS;
$duration = $membershipTypeDurationCalculator->calculateOriginalInDays();
$duration = $membershipTypeDurationCalculator->calculateOriginalInDays();
$this->proRatedNumber = $membershipTypeDurationCalculator->calculateDaysBasedOnDates($this->startDate, $this->endDate, $this->joinDate);
if ($this->isDurationWithInOneYearPeriod($duration, $this->proRatedNumber)) {
if ($this->isDurationWithInOneYearPeriod($duration, $this->proRatedNumber) && !empty($this->endDate)) {
$this->reCalculateEndDate();
$this->proRatedNumber = $membershipTypeDurationCalculator->calculateDaysBasedOnDates($this->startDate, $this->endDate, $this->joinDate);
}
Expand Down
14 changes: 7 additions & 7 deletions tests/phpunit/api/v3/PaymentSchedule/GetPaymentScheduleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ public function testGetMonthlyFixedMembershipTypeCalculatedByDays() {
$endDate = new DateTime(date('2021-09-30'));
$interval = $endDate->diff($startDate);
$durationInDays = (int) $interval->format("%a") + 1;
//Calculate expected amount by days.
//Membership fee is 120
//Membership roll over day is 30 Sep
//No of days between start date 01 Jan 2021 to membership rollover day is 274 days
//No of months between start date 01 Jan to membership rollover day is 9 months
//2021 has 365 days
$expectedAmount = round(((120 / 365) * $durationInDays) / 9, 2);

$membershipTypeObj = CRM_Member_BAO_MembershipType::findById($membershipType['id']);
$membershipTypeDates = new CRM_MembershipExtras_Service_MembershipTypeDatesCalculator();
$durationCalculator = new CRM_MembershipExtras_Service_MembershipTypeDurationCalculator($membershipTypeObj, $membershipTypeDates);
$membershipDuration = $durationCalculator->calculateOriginalInDays();

$expectedAmount = round(((120 / $membershipDuration) * $durationInDays) / 9, 2);
$expectedTaxAmount = 0;
$instalments = $this->getMembershipTypeSchedule($membershipType['id'], 'monthly', $formattedStartDate);
$expectedInstalmentDate = new DateTime($this->getMembershipStartDate($membershipType['id'], $formattedStartDate));
Expand Down
Loading