Skip to content
Open
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
56 changes: 56 additions & 0 deletions CRM/Banking/PluginImpl/Matcher/CreateCampaignContribution.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ public function __construct($config_name) {
// ...default for which is: only activities with campaigns
$config->activity_with_no_campaign_penalty = 1.00;
}
// penalty for disabled campaigns
if (!isset($config->disabled_campaign_penalty)) {
$config->disabled_campaign_penalty = 0.00;
}
// default status is 'in Progress'
if (!isset($config->active_recurring_contribution_status_ids)) {
$config->active_recurring_contribution_status_ids = ['In Progress'];
Expand Down Expand Up @@ -203,6 +207,7 @@ public function match(CRM_Banking_BAO_BankTransaction $btx, CRM_Banking_Matcher_
$activity_confidence = max($contacts_found[$contact_id], 0.0);
$this->logMessage("Found [{$contact_id}] with confidence {$activity_confidence} (including penalty of {$penalty})", 'debug');
$multiple_recurring_contributions_penalty_applied = $this->adjustRatingOfRecurringContributions($contact_id, $activity_confidence, $btx);
$disabled_campaign_penalty_applied = 0.0;

// also: add a penalty for activities without campaign (if configured this way)
if ($activity_with_no_campaign_penalty > 0.0) {
Expand All @@ -212,6 +217,12 @@ public function match(CRM_Banking_BAO_BankTransaction $btx, CRM_Banking_Matcher_
$no_campaign_penalty_applied = $activity_with_no_campaign_penalty;
}
}
if (!empty($activity['campaign_id'])) {
$disabled_campaign_penalty_applied = $this->adjustRatingOfDisabledCampaign(
$activity['campaign_id'],
$activity_confidence
);
}

// apply the general penalty
$activity_confidence = min($activity_confidence - $penalty, 1.0);
Expand All @@ -226,6 +237,7 @@ public function match(CRM_Banking_BAO_BankTransaction $btx, CRM_Banking_Matcher_
$suggestion->setParameter('activity_id', $activity_id);
$suggestion->setParameter('multiple_recurring_contributions_penalty_applied', $multiple_recurring_contributions_penalty_applied);
$suggestion->setParameter('no_campaign_penalty_applied', $no_campaign_penalty_applied);
$suggestion->setParameter('disabled_campaign_penalty_applied', $disabled_campaign_penalty_applied);
$suggestion->setParameter('time_after_activity', strtotime("{$btx->booking_date}") - strtotime($activity['activity_date_time']));
$suggestion->setProbability($activity_confidence);
if ($activity_confidence == 1.0) {
Expand Down Expand Up @@ -367,6 +379,48 @@ public function adjustRatingOfRecurringContributions($contact_id, &$contact_prob
}
}

/**
* This will reduce the probability of a campaign contribution if
* the referenced campaign is disabled.
*
* @param int|string|null $campaign_id
* Campaign ID.
* @param float $contact_probability
* The probability of the contact to be adjusted.
*
* @return float
* Penalty added to the contact's rating.
*/
public function adjustRatingOfDisabledCampaign($campaign_id, &$contact_probability) {
$disabled_campaign_penalty = (float) ($this->_plugin_config->disabled_campaign_penalty ?? 0);
if ($disabled_campaign_penalty <= 0 || empty($campaign_id)) {
return 0.0;
}

static $campaign_is_active = [];
$campaign_id = (int) $campaign_id;

if (!array_key_exists($campaign_id, $campaign_is_active)) {
try {
$campaign = civicrm_api3('Campaign', 'getsingle', ['id' => $campaign_id]);
}
catch (Exception $ex) {
$this->logMessage("Campaign [{$campaign_id}] could not be loaded, disabled-campaign penalty skipped.", 'warn');
return 0.0;
}
$campaign_is_active[$campaign_id] = !empty($campaign['is_active']);
}

if ($campaign_is_active[$campaign_id]) {
$this->logMessage("Campaign [{$campaign_id}] is active, no disabled-campaign penalty applied.", 'debug');
return 0.0;
}

$contact_probability = max(0.0, $contact_probability - $disabled_campaign_penalty);
$this->logMessage("Campaign [{$campaign_id}] is disabled, suggestion will be reduced by a penalty of {$disabled_campaign_penalty}.", 'info');
return $disabled_campaign_penalty;
}

/**
* If the user has modified the input fields provided by the "visualize" html code,
* the new values will be passed here BEFORE execution
Expand All @@ -391,6 +445,7 @@ public function visualize_match(CRM_Banking_Matcher_Suggestion $match, $btx) {
$contact_id = $match->getParameter('contact_id');
$activity_id = $match->getParameter('activity_id');
$no_campaign_penalty_applied = $match->getParameter('no_campaign_penalty_applied');
$disabled_campaign_penalty_applied = $match->getParameter('disabled_campaign_penalty_applied');
$multiple_recurring_contributions_penalty_applied
= $match->getParameter('multiple_recurring_contributions_penalty_applied');
$contribution = $this->get_contribution_data($btx, $match, $contact_id);
Expand Down Expand Up @@ -440,6 +495,7 @@ public function visualize_match(CRM_Banking_Matcher_Suggestion $match, $btx) {

// penalties
$smarty_vars['no_campaign_penalty_applied'] = (int) (100.0 * $no_campaign_penalty_applied);
$smarty_vars['disabled_campaign_penalty_applied'] = (int) (100.0 * $disabled_campaign_penalty_applied);
$smarty_vars['multiple_recurring_contributions_penalty_applied']
= (int) (100.0 * $multiple_recurring_contributions_penalty_applied);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
{ts 1=$no_campaign_penalty_applied}<b>Caution</b>: the activity found has <b>no campaign</b>, so the score of this suggestion has been reduced by %1%.{/ts}
<br/><br/>
{/if}
{if $disabled_campaign_penalty_applied}
<br/>
{ts 1=$disabled_campaign_penalty_applied}<b>Caution</b>: the selected <b>campaign is disabled</b>, so the score of this suggestion has been reduced by %1%.{/ts}
<br/><br/>
{/if}
{ts 1=$activity_link}Based on activity '%1', the following contribution will be created:{/ts}
<br/>
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,60 @@ public function testCampaignMatcherBasicNegative():void {
);
}

/**
* Disabled campaigns can reduce the suggestion score if configured.
*/
public function testCampaignMatcherAppliesDisabledCampaignPenalty(): void {
$activity_type_id_used_in_matcher_config = 4;
try {
civicrm_api3('OptionValue', 'getsingle', [
'option_group_id' => 'activity_type',
'value' => $activity_type_id_used_in_matcher_config,
]);
}
catch (Exception $ex) {
$this->fail("This test requires activity type [{$activity_type_id_used_in_matcher_config}] to exist.");
}

$contact_id = $this->createContact();
$campaign_id = $this->createCampaign([
'is_active' => 0,
]);
$this->createActivity([
'target_id' => $contact_id,
'activity_status_id' => 'Completed',
'campaign_id' => $campaign_id,
'activity_type_id' => $activity_type_id_used_in_matcher_config,
]);

$config = json_decode(
file_get_contents($this->getTestResourcePath('matcher/configuration/CampaignMatcher-01.civibanking')),
TRUE,
flags: JSON_THROW_ON_ERROR
);
$config['config']['campaign_id'] = $campaign_id;
$config['config']['auto_exec'] = 0;
$config['config']['threshold'] = 0.5;
$config['config']['disabled_campaign_penalty'] = 0.25;
$this->configureCiviBankingModuleWithConfig(json_encode($config, JSON_THROW_ON_ERROR));

$transaction_id = $this->createTransaction([
'purpose' => 'This transaction should create a suggestion with a reduced score',
'campaign_id' => $campaign_id,
'contact_id' => $contact_id,
]);

$this->runMatchers([$transaction_id]);

$transaction = $this->getTransactionInstance($transaction_id);
$this->assertNotNull($transaction, 'The test transaction could not be loaded again.');

$suggestions = $transaction->getSuggestionList();
$this->assertCount(1, $suggestions, 'Expected one campaign contribution suggestion.');

$suggestion = reset($suggestions);
$this->assertEqualsWithDelta(0.25, (float) $suggestion->getParameter('disabled_campaign_penalty_applied'), 0.00001);
$this->assertEqualsWithDelta(0.75, (float) $suggestion->getProbability(), 0.00001);
}

}