Skip to content

Feat/institution contribution receipt#634

Merged
belwalshubham merged 5 commits intodevelopfrom
feat/institution-contribution-receipt
Dec 17, 2024
Merged

Feat/institution contribution receipt#634
belwalshubham merged 5 commits intodevelopfrom
feat/institution-contribution-receipt

Conversation

@belwalshubham
Copy link
Copy Markdown
Collaborator

@belwalshubham belwalshubham commented Dec 17, 2024

Summary by CodeRabbit

  • New Features

    • Introduced InstitutionCampaignService for handling campaign-related events and logging activities.
    • Added InstitutionMaterialContributionService to process material contributions and send acknowledgment emails.
  • Bug Fixes

    • Enhanced error logging for contribution receipt attachment.
  • Refactor

    • Simplified logic in MaterialContributionService for determining campaign fields and address retrieval.
  • Documentation

    • Updated documentation for the getSubscribedEvents method in MaterialContributionService.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 17, 2024

Walkthrough

This pull request introduces two new service classes, InstitutionCampaignService and InstitutionMaterialContributionService, within the CiviCRM extension for Goonj. These classes extend the AutoSubscriber and are designed to handle specific event processing and notification workflows related to campaigns and material contributions. Additionally, the MaterialContributionService underwent minor refactoring to improve code readability and maintainability.

Changes

File Change Summary
wp-content/civi-extensions/goonjcustom/Civi/InstitutionCampaignService.php New service class for handling campaign-related events, logging activities, and processing campaign information
wp-content/civi-extensions/goonjcustom/Civi/InstitutionMaterialContributionService.php New comprehensive service class for processing material contributions, fetching relevant contacts, and sending acknowledgment emails
wp-content/civi-extensions/goonjcustom/Civi/MaterialContributionService.php Minor refactoring of code logic, including improved field mapping and error logging

Sequence Diagram

sequenceDiagram
    participant Event as CiviCRM Event
    participant Service as InstitutionCampaignService
    participant API as CiviCRM API
    participant Activity as Activity Creation

    Event ->> Service: Trigger post-event hook
    Service ->> API: Retrieve Campaign Details
    API -->> Service: Return Campaign Information
    Service ->> Activity: Create Campaign Organization Activity
    Activity -->> Service: Confirm Activity Creation
Loading

Possibly related PRs

Suggested labels

status : ready for review

Suggested reviewers

  • tarunnjoshi

Poem

🌟 Code flows like a river bright,
Campaigns tracked with digital might,
Services dance, events entwine,
Goonj's system starts to shine! 🚀
Refactored paths, clear and clean,
A software symphony unseen! 💻


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🔭 Outside diff range comments (1)
wp-content/civi-extensions/goonjcustom/Civi/MaterialContributionService.php (1)

Line range hint 147-157: Consider consistent refactoring pattern

The address field determination uses nested ternary operators, similar to what was previously refactored with the campField mapping. Consider applying the same mapping pattern here for consistency and readability.

-    $addressField = ($subtype == 'Collection_Camp')
-    ? 'Collection_Camp_Intent_Details.Location_Area_of_camp'
-    : (($subtype == 'Dropping_Center')
-        ? 'Dropping_Centre.Where_do_you_wish_to_open_dropping_center_Address_'
-        : (($subtype == 'Institution_Collection_Camp')
-            ? 'Institution_Collection_Camp_Intent.Collection_Camp_Address'
-            : (($subtype == 'Institution_Dropping_Center')
-                ? 'Institution_Dropping_Center_Intent.Dropping_Center_Address'
-                : NULL)));
+    $addressFieldMapping = [
+      'Collection_Camp' => 'Collection_Camp_Intent_Details.Location_Area_of_camp',
+      'Dropping_Center' => 'Dropping_Centre.Where_do_you_wish_to_open_dropping_center_Address_',
+      'Institution_Collection_Camp' => 'Institution_Collection_Camp_Intent.Collection_Camp_Address',
+      'Institution_Dropping_Center' => 'Institution_Dropping_Center_Intent.Dropping_Center_Address'
+    ];
+    
+    $addressField = $addressFieldMapping[$subtype] ?? NULL;
🧹 Nitpick comments (7)
wp-content/civi-extensions/goonjcustom/Civi/MaterialContributionService.php (2)

36-37: Documentation needs improvement

The current documentation "Er." is insufficient. Please provide meaningful documentation describing the purpose of getSubscribedEvents() and its role in the event subscription system.

-  /**
-   * Er.
-   */
+  /**
+   * {@inheritdoc}
+   * 
+   * Subscribes to the alterMailParams hook to attach contribution receipts to emails.
+   * 
+   * @return array
+   *   An array of event subscriber configurations.
+   */

Line range hint 183-350: Consider template externalization and configuration improvements

The HTML generation method could benefit from several architectural improvements:

  1. Move the HTML template to a separate file
  2. Extract social media URLs to configuration
  3. Make image paths configurable
  4. Consider using a template engine for better separation of concerns

Would you like me to provide a detailed implementation plan for these improvements?

wp-content/civi-extensions/goonjcustom/Civi/InstitutionCampaignService.php (4)

21-23: Provide meaningful method documentation for getSubscribedEvents.

The method getSubscribedEvents lacks a descriptive doc comment. Adding a meaningful description will enhance code readability and maintainability.


26-28: Provide meaningful method documentation for linkCollectionCampToContact.

The method linkCollectionCampToContact does not have a descriptive doc comment. Including details about its purpose, parameters, and expected behavior will improve code understanding.


73-75: Provide meaningful method documentation for createCollectionCampOrganizeActivity.

The method createCollectionCampOrganizeActivity has minimal documentation. Elaborating on its functionality and parameters will assist in code comprehension.


76-76: Add type hints to method parameters for createCollectionCampOrganizeActivity.

Adding type declarations improves code robustness and readability. Consider specifying parameter types and return type:

-private static function createCollectionCampOrganizeActivity($currentInstitutionId, $campaignTitle, $campaignId) {
+private static function createCollectionCampOrganizeActivity(int $currentInstitutionId, string $campaignTitle, int $campaignId): void {
wp-content/civi-extensions/goonjcustom/Civi/InstitutionMaterialContributionService.php (1)

175-193: Consider using a templating system for generating the email body in generateEmailBody.

Embedding HTML directly within the code reduces readability. Utilizing a templating system or external template files can improve code organization and maintainability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c46818 and af6e3d0.

📒 Files selected for processing (3)
  • wp-content/civi-extensions/goonjcustom/Civi/InstitutionCampaignService.php (1 hunks)
  • wp-content/civi-extensions/goonjcustom/Civi/InstitutionMaterialContributionService.php (1 hunks)
  • wp-content/civi-extensions/goonjcustom/Civi/MaterialContributionService.php (3 hunks)
🔇 Additional comments (1)
wp-content/civi-extensions/goonjcustom/Civi/MaterialContributionService.php (1)

125-135: Well-structured mapping implementation

Good refactoring of the subtype-to-field mapping. The associative array improves readability and maintainability compared to nested ternary operators. The early return pattern is also well applied.

Comment on lines +71 to +72
error_log("activitiesmaterial: " . print_r($activities, TRUE));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove or improve debug logging

The current error logging using print_r could expose sensitive data and should not be in production code. If logging is needed, consider:

  1. Using structured logging with appropriate log levels
  2. Logging only specific fields rather than the entire array
  3. Sanitizing sensitive information
-    error_log("activitiesmaterial: " . print_r($activities, TRUE));
+    if (\Civi::settings()->get('debug_enabled')) {
+      \Civi::log()->debug('Material contribution activities processed', [
+        'activity_count' => $activities->count(),
+        'activity_ids' => array_column((array) $activities, 'id')
+      ]);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
error_log("activitiesmaterial: " . print_r($activities, TRUE));
if (\Civi::settings()->get('debug_enabled')) {
\Civi::log()->debug('Material contribution activities processed', [
'activity_count' => $activities->count(),
'activity_ids' => array_column((array) $activities, 'id')
]);
}

Comment on lines +39 to +41
error_log("objectName " . print_r($objectName, TRUE));
error_log("objectId " . print_r($objectId, TRUE));
error_log("objectRef " . print_r($objectRef, TRUE));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid logging sensitive information using error_log.

Logging variables like $objectRef, $institutionCampaign, $campaignTitle, and $campaignId may expose sensitive data. Use a proper logging mechanism with appropriate log levels and avoid logging sensitive information in production.

Also applies to: 55-55, 63-63, 65-65

Comment on lines +57 to +58
$currentInstitutionCampaign = $institutionCampaign->first();
$currentInstitutionId= $currentInstitutionCampaign['Additional_Details.Institution'];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null check before accessing $currentInstitutionCampaign to prevent errors.

If $institutionCampaign->first() returns null, accessing its properties will cause an error. Ensure $currentInstitutionCampaign is not null before proceeding.

Apply this diff to add the null check:

$currentInstitutionCampaign = $institutionCampaign->first();
+if (!$currentInstitutionCampaign) {
+  return;
+}
$currentInstitutionId = $currentInstitutionCampaign['Additional_Details.Institution'];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$currentInstitutionCampaign = $institutionCampaign->first();
$currentInstitutionId= $currentInstitutionCampaign['Additional_Details.Institution'];
$currentInstitutionCampaign = $institutionCampaign->first();
if (!$currentInstitutionCampaign) {
return;
}
$currentInstitutionId = $currentInstitutionCampaign['Additional_Details.Institution'];


<div style="width: 100%; font-size: 14px;">
<div style="float: left; text-align: left;">
<!-- Material Acknowledgment# {$activity['id']} -->
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix undefined variable $activity in generateContributionReceiptHtml.

The variable $activity is used but not defined within this method. This will cause an undefined variable error. Ensure that $activity is defined or passed as a parameter if needed.

*
*/
private static function prepareEmailParams(string $emailSubject, string $emailBody, array $attachments, string $recipientEmail) {
$from = HelperService::getDefaultFromEmail();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Undefined class HelperService used in prepareEmailParams.

The class HelperService is not imported or defined. This will result in an error when calling HelperService::getDefaultFromEmail(). Ensure HelperService is properly imported or defined.

Comment on lines +78 to +94
private static function getBranchWisePOCs($campaignId) {
$contacts = [];
$campaign = Campaign::get(TRUE)
->addSelect('Additional_Details.Branch_Wise_POCs')
->addWhere('id', '=', $campaignId)
->execute()
->first();

$branchWisePOCs = $campaign['Additional_Details.Branch_Wise_POCs'] ?? [];
foreach ($branchWisePOCs as $contactId) {
$contact = self::getContactDetails($contactId);
if ($contact) {
$contacts[$contact['email']] = $contact;
}
}
return $contacts;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor to eliminate duplicate code in contact-fetching methods.

The methods getBranchWisePOCs, getCampaignInstitutionPOC, and getInstitutionRelationships contain similar logic for fetching contacts. Refactoring to extract common functionality will reduce code duplication and enhance maintainability.

Also applies to: 99-114, 119-134

Comment on lines +220 to +347
private static function generateContributionReceiptHtml($email, $contactPhone, $description, $contactName, $deliveredBy, $deliveredByContact, $activityDate) {

$baseDir = plugin_dir_path(__FILE__) . '../../../themes/goonj-crm/';

$paths = [
'logo' => $baseDir . 'images/goonj-logo.png',
'qrCode' => $baseDir . 'images/qr-code.png',
'callIcon' => $baseDir . 'Icon/call.png',
'domainIcon' => $baseDir . 'Icon/domain.png',
'emailIcon' => $baseDir . 'Icon/email.png',
'facebookIcon' => $baseDir . 'Icon/facebook.webp',
'instagramIcon' => $baseDir . 'Icon/instagram.png',
'twitterIcon' => $baseDir . 'Icon/twitter.webp',
'youtubeIcon' => $baseDir . 'Icon/youtube.webp',
];

$imageData = array_map(fn ($path) => base64_encode(file_get_contents($path)), $paths);

$html = <<<HTML
<html>
<body style="font-family: Arial, sans-serif;">
<div style="text-align: center; margin-bottom: 16px;">
<img src="data:image/png;base64,{$imageData['logo']}" alt="Goonj Logo" style="width: 95px; height: 80px;">
</div>

<div style="width: 100%; font-size: 14px;">
<div style="float: left; text-align: left;">
<!-- Material Acknowledgment# {$activity['id']} -->
</div>
<div style="float: right; text-align: right;">
Goonj, C-544, Pocket C, Sarita Vihar, Delhi, India
</div>
</div>
<br><br>
<div style="font-weight: bold; font-style: italic; margin-top: 6px; margin-bottom: 6px;">
"We appreciate your contribution of pre-used/new material. Goonj makes sure that the material reaches people with dignity and care."
</div>
<table border="1" cellpadding="10" cellspacing="0" style="width: 100%; border-collapse: collapse;">
<style>
.table-header {
text-align: left;
font-weight: bold;
}
</style>
<!-- Table rows for each item -->
<tr>
<td class="table-header">Description of Material *</td>
<td style="text-align: center;">{$description}</td>
</tr>
<tr>
<td class="table-header">Received On</td>
<td style="text-align: center;">{$activityDate}</td>
</tr>
<tr>
<td class="table-header">From</td>
<td style="text-align: center;">{$contactName}</td>
</tr>
<tr>
<td class="table-header">Email</td>
<td style="text-align: center;">{$email}</td>
</tr>
<tr>
<td class="table-header">Phone</td>
<td style="text-align: center;">{$contactPhone}</td>
</tr>
<tr>
<td class="table-header">Delivered by (Name & contact no.)</td>
<td style="text-align: center;">
{$deliveredBy}<br>
{$deliveredByContact}
</td>
</tr>

</table>
<div style="text-align: right; font-size: 14px;">
Team Goonj
</div>
<div style="width: 100%; margin-top: 16px;">
<div style="float: left; width: 60%; font-size: 14px;">
<p>Join us, by encouraging your friends, relatives, colleagues, and neighbours to join the journey as all of us have a lot to give.</p>
<p style="margin-top: 8px;">
<strong>With Material Money Matters</strong> Your monetary contribution is needed too for sorting, packing, transportation to implementation. (Financial contributions are tax-exempted u/s 80G of IT Act)
</p>
<p style="margin-top: 10px; font-size: 12px; float: left">* Received material has 'No Commercial Value' for Goonj.</p>
</div>
<div style="float: right; width: 40%; text-align: right; font-size: 12px; font-style: italic;">
<p>To contribute, please scan the code.</p>
<img src="data:image/png;base64,{$imageData['qrCode']}" alt="QR Code" style="width: 80px; height: 70px; margin-top: 2px"></div>
</div>
<div style="clear: both; margin-top: 20px;"></div>
<div style="width: 100%; margin-top: 15px; background-color: #f2f2f2; padding: 16px; font-weight: 300; color: #000000">
<div style="font-size: 14px; margin-bottom: 20px;">
<div style="position: relative; height: 24px;">
<div style="font-size: 14px; float: left; color:">
Goonj, C-544, 1st Floor, C-Pocket, Sarita Vihar,<br>
New Delhi-110076
</div>
<div style="font-size: 14px; float: right;">
<img src="data:image/png;base64,{$imageData['callIcon']}" alt="Phone" style="width: 16px; height: 16px; margin-right: 5px;">
011-26972351/41401216
</div>
</div>
</div>

<div style="text-align: center; width: 100%; font-size: 14px; margin-bottom: 20px;">
<div style="font-size: 14px;">
<img src="data:image/png;base64,{$imageData['emailIcon']}" alt="Email" style="width: 16px; height: 16px; display: inline;">
<span style="display: inline; margin-left: 0;">mail@goonj.org</span>
<img src="data:image/png;base64,{$imageData['domainIcon']}" alt="Website" style="width: 16px; height: 16px; margin-right: 5px;">
<span style="display: inline; margin-left: 0;">www.goonj.org</span>
</div>
</div>

<!-- Social Media Icons -->
<div style="text-align: center; width: 100%; margin-top: 28px;">
<a href="https://www.facebook.com/goonj.org" target="_blank"><img src="data:image/webp;base64,{$imageData['facebookIcon']}" alt="Facebook" style="width: 24px; height: 24px; margin-right: 10px;"></a>
<a href="https://www.instagram.com/goonj/" target="_blank"><img src="data:image/webp;base64,{$imageData['instagramIcon']}" alt="Instagram" style="width: 24px; height: 24px; margin-right: 10px;"></a>
<a href="https://x.com/goonj" target="_blank"><img src="data:image/webp;base64,{$imageData['twitterIcon']}" alt="Twitter" style="width: 24px; height: 24px; margin-right: 10px;"></a>
<a href="https://www.youtube.com/channel/UCCq8iYlmjT7rrgPI1VHzIHg" target="_blank"><img src="data:image/webp;base64,{$imageData['youtubeIcon']}" alt="YouTube" style="width: 24px; height: 24px; margin-right: 10px;"></a>
</div>
</div>
<p style="margin-bottom: 2px; text-align: center; font-size: 12px;">* This is a computer generated receipt, signature is not required.</p>
</body>
</html>
HTML;

return $html;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor generateContributionReceiptHtml to adhere to the single responsibility principle.

The method is lengthy and handles multiple responsibilities, making it hard to read and maintain. Consider breaking it down into smaller methods or using a templating system to improve clarity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant