Skip to content

Institution Collection Camp#503

Merged
belwalshubham merged 6 commits intodevelopfrom
feat/institution-collection-camp-functionality
Nov 27, 2024
Merged

Institution Collection Camp#503
belwalshubham merged 6 commits intodevelopfrom
feat/institution-collection-camp-functionality

Conversation

@belwalshubham
Copy link
Copy Markdown
Collaborator

@belwalshubham belwalshubham commented Nov 27, 2024

  1. Send an authorized email and an unauthorized email to the institution's point of contact (POC).
  2. Attach the poster along with the authorized email when sending it to the institution's POC.
  3. Send the material contribution receipt to the institution.

Summary by CodeRabbit

  • New Features

    • Introduced a new class for handling institution collection camp tokens, enhancing token functionality within the CiviCRM system.
    • Expanded data retrieval for material contributions, improving the accuracy of contribution information.
    • Enhanced handling of authorization emails by refining the logic for identifying initiators based on relationships.
    • Added new tab configurations for 'vehicleDispatch' and 'materialAuthorization' in the institution collection camp interface.
  • Bug Fixes

    • Improved error handling and logging for various processes related to authorization emails and contribution data.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 27, 2024

Walkthrough

This pull request introduces a new class CRM_Goonjcustom_Token_InstitutionCollectionCamp for handling token evaluation related to institution collection camps in the CiviCRM framework. It includes methods for formatting various token fields and enhances the CollectionBaseService and MaterialContributionService classes to improve the handling of relationships and contribution data. Additionally, the goonjcustom.php file is updated to register the new token handling class, extending the token processing capabilities within the system.

Changes

File Path Change Summary
wp-content/civi-extensions/goonjcustom/CRM/Goonjcustom/Token/InstitutionCollectionCamp.php Added class CRM_Goonjcustom_Token_InstitutionCollectionCamp with methods for token evaluation and formatting.
wp-content/civi-extensions/goonjcustom/Civi/CollectionBaseService.php Modified CollectionBaseService to include a new method getInitiatorId and updated handleAuthorizationEmails methods to improve relationship handling.
wp-content/civi-extensions/goonjcustom/Civi/MaterialContributionService.php Updated attachContributionReceiptToEmail and getContributionCity methods to include new subtypes and improve data handling.
wp-content/civi-extensions/goonjcustom/goonjcustom.php Added new subscriber for CRM_Goonjcustom_Token_InstitutionCollectionCamp to the CiviCRM dispatcher and updated registration methods.
wp-content/civi-extensions/goonjcustom/Civi/InstitutionCollectionCampService.php Updated institutionCollectionCampTabset to include new tab configurations for 'vehicleDispatch' and 'materialAuthorization'.

Possibly related PRs

Suggested labels

status : ready for review

Suggested reviewers

  • tarunnjoshi
  • nishant22029

🎉 In the land of code, where tokens do play,
A new class emerged, brightening the day.
With formatting magic and relationships tight,
CiviCRM's tokens now shine ever bright!
So here's to the changes, both big and small,
Enhancing our system, let's celebrate all! 🎊


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 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: 5

🧹 Outside diff range and nitpick comments (7)
wp-content/civi-extensions/goonjcustom/goonjcustom.php (2)

Line range hint 19-28: Update function documentation to reflect new token handler

The hook documentation should be updated to mention the InstitutionCollectionCamp token handler in the function's docblock.

 /**
  * Implements hook_civicrm_config().
  *
+ * Registers token handlers for:
+ * - Collection Camp
+ * - Dropping Center
+ * - Institution Collection Camp
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/
  */

Line range hint 71-189: Consider refactoring token evaluation for better maintainability

The goonjcustom_evaluate_tokens function has grown quite complex and handles multiple responsibilities. Consider breaking it down into smaller, focused methods:

+ /**
+  * Service class for handling token evaluation
+  */
+ class CRM_Goonjcustom_TokenEvaluationService {
+   /**
+    * Process induction center details
+    */
+   public function getInductionCenterDetails($contactId) {
+     // Extract processing center logic here
+   }
+
+   /**
+    * Process induction activity details
+    */
+   public function getInductionActivityDetails($contactId) {
+     // Extract induction activity logic here
+   }
+
+   /**
+    * Process office details
+    */
+   public function getOfficeDetails($inductionGoonjOffice) {
+     // Extract office details logic here
+   }
+ }

  function goonjcustom_evaluate_tokens(TokenValueEvent $e) {
    $service = new CRM_Goonjcustom_TokenEvaluationService();
    foreach ($e->getRows() as $row) {
      // Delegate to service methods
    }
  }

This would:

  1. Improve testability by isolating different concerns
  2. Make the code more maintainable
  3. Make it easier to add new token processing logic
wp-content/civi-extensions/goonjcustom/CRM/Goonjcustom/Token/InstitutionCollectionCamp.php (3)

3-6: Add meaningful documentation for the class and methods

The docblocks for the class and methods are currently empty or minimal. Providing detailed documentation enhances code readability and maintainability.

Also applies to: 14-16


58-58: Remove debug logging statement

The error_log call at line 58 seems to be a leftover from debugging. It's best practice to remove such statements from production code to avoid unnecessary logging.

Suggested change:

-    error_log("collectionSource: " . print_r($collectionSource, TRUE));

169-171: Ensure proper checking of 'phone.is_primary' field

In the array_filter function at lines 169-171, ensure that the 'phone.is_primary' field exists and is correctly checked to avoid potential notices or incorrect filtering.

Suggested change:

 $primaryVolunteer = array_filter($volunteersArray, function ($volunteer) {
-    return $volunteer['phone.is_primary'];
+    return isset($volunteer['phone.is_primary']) && $volunteer['phone.is_primary'];
 });
wp-content/civi-extensions/goonjcustom/Civi/MaterialContributionService.php (2)

61-61: Optimize addSelect to include only necessary fields

Including '*' in your addSelect call along with specific fields may lead to unnecessary data retrieval and impact performance. Consider selecting only the required fields to improve efficiency.

Apply this diff to refine the addSelect statement:

->addSelect('*',
+->addSelect(
 'contact.display_name',
 'Material_Contribution.Delivered_By',
 'Material_Contribution.Delivered_By_Contact',
 'Material_Contribution.Goonj_Office',
 'Material_Contribution.Collection_Camp.subtype:name',
 'Material_Contribution.Institution_Collection_Camp.subtype:name',
 'Material_Contribution.Dropping_Center.subtype:name')

74-84: Adhere to Single Responsibility Principle

The methods attachContributionReceiptToEmail and getContributionCity handle multiple responsibilities, including data fetching, business logic, and logging. Refactoring these methods to separate concerns will enhance code maintainability.

Consider extracting the subtype determination and field assignments into separate methods:

private static function determineSubtype($contribution) { /* ... */ }

private static function getCampField($subtype) { /* ... */ }

private static function getAddressField($subtype) { /* ... */ }

Also applies to: 119-125, 137-143

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3571982 and 40dd54e.

📒 Files selected for processing (4)
  • wp-content/civi-extensions/goonjcustom/CRM/Goonjcustom/Token/InstitutionCollectionCamp.php (1 hunks)
  • wp-content/civi-extensions/goonjcustom/Civi/CollectionBaseService.php (5 hunks)
  • wp-content/civi-extensions/goonjcustom/Civi/MaterialContributionService.php (4 hunks)
  • wp-content/civi-extensions/goonjcustom/goonjcustom.php (1 hunks)
🔇 Additional comments (3)
wp-content/civi-extensions/goonjcustom/goonjcustom.php (1)

27-27: Verify the existence of InstitutionCollectionCamp token handler

The integration follows the established pattern, but let's ensure the referenced class exists and is properly implemented.

✅ Verification successful

Token handler implementation looks solid and follows best practices

The InstitutionCollectionCamp token handler:

  • Properly extends AbstractTokenSubscriber
  • Follows CiviCRM naming conventions
  • Has well-organized token definitions with translations
  • Implements clean, focused methods for each token type
  • Includes proper error handling and logging
  • Uses appropriate API calls for data retrieval

The implementation maintains single responsibility principle with separate formatting methods for each token type and follows established CiviCRM token handling patterns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the token handler class file exists and follows CiviCRM conventions
fd -e php "InstitutionCollectionCamp.php" 

# Check for potential token name collisions
ast-grep --pattern 'function registerTokens($e) {
  $$$
}'

Length of output: 9465


Script:

#!/bin/bash
# Let's check the content of the token handler class
cat "wp-content/civi-extensions/goonjcustom/CRM/Goonjcustom/Token/InstitutionCollectionCamp.php"

# Let's also check for any similar token handlers to understand the pattern
rg -l "implements TokenSubscriberInterface" --type php

Length of output: 6392

wp-content/civi-extensions/goonjcustom/CRM/Goonjcustom/Token/InstitutionCollectionCamp.php (1)

41-42: ⚠️ Potential issue

Syntax Error: Remove trailing comma in function declaration

The evaluateToken function has a trailing comma after the last parameter at line 41, which is invalid in PHP function declarations. Please remove the comma to fix the syntax error.

Suggested change:

     $field,
-    $prefetch = NULL,
+    $prefetch = NULL
   ) {

Likely invalid or redundant comment.

wp-content/civi-extensions/goonjcustom/Civi/CollectionBaseService.php (1)

351-353: Ensure safe access to 'contact_id_b' in relationships array

Accessing $relationships[0]['contact_id_b'] without confirming the array structure may lead to undefined index errors. Ensure that $relationships is properly indexed and that 'contact_id_b' exists before accessing it. Consider using the first() method or checking the existence explicitly.

Run the following script to verify the structure of the $relationships array:

✅ Verification successful

Accessing array index is safe with current null and empty checks

The current implementation already has proper checks in place before accessing the array index:

if (!empty($relationships) && isset($relationships[0]['contact_id_b'])) {
  return $relationships[0]['contact_id_b'];
}

The code follows best practices by:

  1. Checking if $relationships is not empty using !empty()
  2. Explicitly verifying the existence of the 'contact_id_b' key using isset()

The codebase shows consistent usage of this pattern when dealing with relationship data, particularly in the CiviCRM core and extensions. The Relationship::get() API consistently returns relationships with 'contact_id_b' as a standard field.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that 'contact_id_b' exists in the relationships fetched.

# Test: Fetch relationships and check for 'contact_id_b'.
php -r "
\$relationships = Civi\Api4\Relationship::get(FALSE)
    ->addWhere('contact_id_a', '=', $organizationId)
    ->addWhere('relationship_type_id:name', 'IN', ['Primary Institution POC of', 'Secondary Institution POC of'])
    ->execute();
if (!empty(\$relationships) && isset(\$relationships[0]['contact_id_b'])) {
    echo 'contact_id_b exists.';
} else {
    echo 'contact_id_b not found in relationships.';
}
"

Length of output: 552


Script:

#!/bin/bash
# Let's analyze the codebase to understand the relationship structure and usage
# Search for similar patterns of relationship access
rg -U "contact_id_b.*relationships" -A 3 -B 3

# Also check the schema or relationship type definitions
ast-grep --pattern 'contact_id_b'

# Look for similar array access patterns
rg "\\\$relationships\[.*?\]\[" -A 2 -B 2

Length of output: 9353


Script:

#!/bin/bash
# Let's try a different approach to find relationship-related code
# Search for files that might contain relationship definitions or handling
fd -e php -e xml -e json | xargs rg -l "relationship" 

# Look specifically in the CiviCRM extensions directory
fd -e php . wp-content/civi-extensions/ | xargs rg -l "Relationship::"

# Search for API calls or relationship handling patterns
rg -g "*.php" "(\\\$relationships|Relationship::)" -A 2 -B 2

Length of output: 102264

Comment on lines +137 to +148
$relationships = Relationship::get(FALSE)
->addWhere('contact_id_a', '=', $organizationId)
->addWhere('relationship_type_id:name', '=', 'Primary Institution POC of')
->execute();

// If no relationships found for 'Primary Institution POC of', check for 'Secondary Institution POC of'.
if (empty($relationships)) {
$relationships = Relationship::get(FALSE)
->addWhere('contact_id_a', '=', $organizationId)
->addWhere('relationship_type_id:name', '=', 'Secondary Institution POC of')
->execute();
}
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 duplicated relationship queries and avoid hard-coded strings

The code querying relationships for 'Primary Institution POC of' and 'Secondary Institution POC of' is duplicated. Consider refactoring to eliminate duplication and improve maintainability.

Additionally, hard-coding relationship type names may cause issues if the names change. It's better to use relationship type IDs or define constants.

Suggested refactor:

 $relationships = Relationship::get(FALSE)
   ->addWhere('contact_id_a', '=', $organizationId)
-  ->addWhere('relationship_type_id:name', '=', 'Primary Institution POC of')
-  ->execute();

-// If no relationships found for 'Primary Institution POC of', check for 'Secondary Institution POC of'.
-if (empty($relationships)) {
-  $relationships = Relationship::get(FALSE)
-    ->addWhere('contact_id_a', '=', $organizationId)
-    ->addWhere('relationship_type_id:name', '=', 'Secondary Institution POC of')
-    ->execute();
-}

+  ->addWhere('relationship_type_id:name', 'IN', ['Primary Institution POC of', 'Secondary Institution POC of'])
+  ->execute();

If possible, retrieve the relationship type IDs dynamically or define them as constants to avoid hard-coding.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +74 to +84
$subtype = NULL;
if (!empty($contribution['Material_Contribution.Collection_Camp.subtype:name'])) {
$subtype = $contribution['Material_Contribution.Collection_Camp.subtype:name'];
}
elseif (!empty($contribution['Material_Contribution.Institution_Collection_Camp.subtype:name'])) {
$subtype = $contribution['Material_Contribution.Institution_Collection_Camp.subtype:name'];
}
elseif (!empty($contribution['Material_Contribution.Dropping_Center.subtype:name'])) {
$subtype = $contribution['Material_Contribution.Dropping_Center.subtype:name'];
}

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

Eliminate duplicate code in $subtype assignment

The logic for assigning $subtype repeats similar patterns. Refactoring this code will enhance readability and maintainability.

Consider using a loop or an array to streamline the assignment:

$subtypeFields = [
  'Material_Contribution.Collection_Camp.subtype:name',
  'Material_Contribution.Institution_Collection_Camp.subtype:name',
  'Material_Contribution.Dropping_Center.subtype:name',
];

$subtype = NULL;
foreach ($subtypeFields as $field) {
  if (!empty($contribution[$field])) {
    $subtype = $contribution[$field];
    break;
  }
}

Comment on lines +119 to +125
? 'Material_Contribution.Collection_Camp'
: (($subtype == 'Dropping_Center')
? 'Material_Contribution.Dropping_Center'
: (($subtype == 'Institution_Collection_Camp')
? 'Material_Contribution.Institution_Collection_Camp'
: NULL));
error_log("campField: " . print_r($campField, 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

Fix syntax errors in nested ternary operators

The nested ternary operators in $campField assignment contain syntax errors, making the code unreadable and prone to bugs. Rewriting this logic using if-elseif-else statements will improve clarity and correctness.

Here's the corrected code:

if ($subtype == 'Collection_Camp') {
  $campField = 'Material_Contribution.Collection_Camp';
} elseif ($subtype == 'Dropping_Center') {
  $campField = 'Material_Contribution.Dropping_Center';
} elseif ($subtype == 'Institution_Collection_Camp') {
  $campField = 'Material_Contribution.Institution_Collection_Camp';
} else {
  $campField = NULL;
}
error_log("campField: " . print_r($campField, TRUE));

Comment on lines +137 to +143
? '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'
: NULL));
error_log("addressField: " . print_r($addressField, 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

Correct syntax and improve readability in $addressField assignment

Similar to the previous issue, the nested ternary operators for $addressField have syntax errors and reduce code readability. Refactor using if-elseif-else statements for better comprehension.

Updated code:

if ($subtype == 'Collection_Camp') {
  $addressField = 'Collection_Camp_Intent_Details.Location_Area_of_camp';
} elseif ($subtype == 'Dropping_Center') {
  $addressField = 'Dropping_Centre.Where_do_you_wish_to_open_dropping_center_Address_';
} elseif ($subtype == 'Institution_Collection_Camp') {
  $addressField = 'Institution_Collection_Camp_Intent.Collection_Camp_Address';
} else {
  $addressField = NULL;
}
error_log("addressField: " . print_r($addressField, TRUE));

Comment on lines +329 to +358
/**
*
*/
private static function getInitiatorId(array $collectionCamp) {
$subtypeName = $collectionCamp['subtype:name'];

$organizationId = $collectionCamp['Institution_Collection_Camp_Intent.Organization_Name.id'];

if ($subtypeName === 'Institution_Collection_Camp') {
$relationships = Relationship::get(FALSE)
->addWhere('contact_id_a', '=', $organizationId)
->addWhere('relationship_type_id:name', '=', 'Primary Institution POC of')
->execute();
// If no relationships found for 'Primary Institution POC of', check for 'Secondary Institution POC of'.
if (empty($relationships)) {
$relationships = Relationship::get(FALSE)
->addWhere('contact_id_a', '=', $organizationId)
->addWhere('relationship_type_id:name', '=', 'Secondary Institution POC of')
->execute();
}

// Return contact_id_b as initiator if found.
if (!empty($relationships) && isset($relationships[0]['contact_id_b'])) {
return $relationships[0]['contact_id_b'];
}
}

// If the subtype is not 'Institution_Collection_Camp', or no relationship was found, use the default contact_id.
return $collectionCamp['Collection_Camp_Core_Details.Contact_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.

🛠️ Refactor suggestion

Refactor getInitiatorId to eliminate duplicate relationship queries

The getInitiatorId method contains duplicated code when querying relationships for 'Primary Institution POC of' and 'Secondary Institution POC of'. Refactor the code to combine these queries into a single call to improve maintainability and readability.

Apply this diff to refactor the method:

private static function getInitiatorId(array $collectionCamp) {
    $subtypeName = $collectionCamp['subtype:name'];

    $organizationId = $collectionCamp['Institution_Collection_Camp_Intent.Organization_Name.id'];

    if ($subtypeName === 'Institution_Collection_Camp') {
+       $relationshipTypes = ['Primary Institution POC of', 'Secondary Institution POC of'];
        $relationships = Relationship::get(FALSE)
            ->addWhere('contact_id_a', '=', $organizationId)
-           ->addWhere('relationship_type_id:name', '=', 'Primary Institution POC of')
+           ->addWhere('relationship_type_id:name', 'IN', $relationshipTypes)
            ->execute();
-       // If no relationships found for 'Primary Institution POC of', check for 'Secondary Institution POC of'.
-       if (empty($relationships)) {
-         $relationships = Relationship::get(FALSE)
-           ->addWhere('contact_id_a', '=', $organizationId)
-           ->addWhere('relationship_type_id:name', '=', 'Secondary Institution POC of')
-           ->execute();
-       }

        // Return contact_id_b as initiator if found.
        if (!empty($relationships) && isset($relationships[0]['contact_id_b'])) {
            return $relationships[0]['contact_id_b'];
        }
    }

    // If the subtype is not 'Institution_Collection_Camp', or no relationship was found, use the default contact_id.
    return $collectionCamp['Collection_Camp_Core_Details.Contact_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
/**
*
*/
private static function getInitiatorId(array $collectionCamp) {
$subtypeName = $collectionCamp['subtype:name'];
$organizationId = $collectionCamp['Institution_Collection_Camp_Intent.Organization_Name.id'];
if ($subtypeName === 'Institution_Collection_Camp') {
$relationships = Relationship::get(FALSE)
->addWhere('contact_id_a', '=', $organizationId)
->addWhere('relationship_type_id:name', '=', 'Primary Institution POC of')
->execute();
// If no relationships found for 'Primary Institution POC of', check for 'Secondary Institution POC of'.
if (empty($relationships)) {
$relationships = Relationship::get(FALSE)
->addWhere('contact_id_a', '=', $organizationId)
->addWhere('relationship_type_id:name', '=', 'Secondary Institution POC of')
->execute();
}
// Return contact_id_b as initiator if found.
if (!empty($relationships) && isset($relationships[0]['contact_id_b'])) {
return $relationships[0]['contact_id_b'];
}
}
// If the subtype is not 'Institution_Collection_Camp', or no relationship was found, use the default contact_id.
return $collectionCamp['Collection_Camp_Core_Details.Contact_Id'];
}
/**
*
*/
private static function getInitiatorId(array $collectionCamp) {
$subtypeName = $collectionCamp['subtype:name'];
$organizationId = $collectionCamp['Institution_Collection_Camp_Intent.Organization_Name.id'];
if ($subtypeName === 'Institution_Collection_Camp') {
$relationshipTypes = ['Primary Institution POC of', 'Secondary Institution POC of'];
$relationships = Relationship::get(FALSE)
->addWhere('contact_id_a', '=', $organizationId)
->addWhere('relationship_type_id:name', 'IN', $relationshipTypes)
->execute();
// Return contact_id_b as initiator if found.
if (!empty($relationships) && isset($relationships[0]['contact_id_b'])) {
return $relationships[0]['contact_id_b'];
}
}
// If the subtype is not 'Institution_Collection_Camp', or no relationship was found, use the default contact_id.
return $collectionCamp['Collection_Camp_Core_Details.Contact_Id'];
}

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: 0

🧹 Outside diff range and nitpick comments (3)
wp-content/civi-extensions/goonjcustom/Civi/InstitutionCollectionCampService.php (3)

245-256: Consider extracting tab configurations to a separate configuration file.

The tab configurations are growing, and they all share similar structure. Consider moving these configurations to a separate configuration file to improve maintainability and reduce the risk of configuration errors.

Example structure:

// config/tabs.php
return [
    'vehicleDispatch' => [
        'title' => ts('Dispatch'),
        'module' => 'afsearchInstitutionCampVehicleDispatchData',
        'directive' => 'afsearch-institution-camp-vehicle-dispatch-data',
        'template' => 'CRM/Goonjcustom/Tabs/CollectionCamp.tpl',
    ],
    // ... other configurations
];

247-249: Standardize naming conventions across directives and modules.

The naming conventions between tabs are inconsistent:

  • afsearchInstitutionCampVehicleDispatchData vs afsearchInstitutionCampAcknowledgementDispatch
  • afsearch-institution-camp-vehicle-dispatch-data vs afsearch-institution-camp-acknowledgement-dispatch

Consider standardizing these names for better maintainability.

Also applies to: 253-255


Line range hint 1-300: Consider splitting the class into smaller, focused services.

The InstitutionCollectionCampService class currently handles multiple responsibilities:

  • QR code generation
  • State options management
  • Office details management
  • Tab configuration

Consider splitting these into separate services to improve maintainability and testability:

  • InstitutionCollectionCampQRService
  • InstitutionCollectionCampStateService
  • InstitutionCollectionCampOfficeService
  • InstitutionCollectionCampTabService

This would:

  1. Make the code more maintainable
  2. Make it easier to test individual components
  3. Follow the Single Responsibility Principle
  4. Make it easier to modify individual features without affecting others
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 08fa2ee and 00ff291.

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

249-249: Verify if using the same template for all tabs is intentional.

All tabs are using the same template 'CRM/Goonjcustom/Tabs/CollectionCamp.tpl'. Please verify if this is intentional or if each tab should have its own specialized template.

Also applies to: 255-255

✅ Verification successful

Let me gather more information about how this template is being used.


Let me check the content of the other template file to understand if it's being used and how it differs.


Using the same template across all tabs is intentional and follows best practices

The shared template CollectionCamp.tpl is correctly used across all tabs because:

  1. It serves as a generic wrapper that dynamically loads Angular modules and directives based on the tab configuration
  2. Each tab's unique functionality is encapsulated in its own directive and module, making the template reusable
  3. The template structure is identical for all tabs, following the DRY (Don't Repeat Yourself) principle

The existence of CollectionCampService.tpl appears to be legacy code as it's only referenced in DroppingCenterService.php but not actively used in the tab configurations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the template file exists and look for tab-specific content
cat wp-content/civi-extensions/goonjcustom/templates/CRM/Goonjcustom/Tabs/CollectionCamp.tpl

# Check if there are any other similar templates that might be more appropriate
fd CollectionCamp.tpl wp-content/civi-extensions/goonjcustom/templates/

Length of output: 460


Script:

#!/bin/bash
# Check for the usage of CollectionCamp.tpl in PHP files
rg -l "CollectionCamp.tpl" wp-content/civi-extensions/goonjcustom/

# Check the context of template usage
rg -B 5 -A 5 "CollectionCamp.tpl" wp-content/civi-extensions/goonjcustom/

# Look for other similar templates
fd .tpl wp-content/civi-extensions/goonjcustom/templates/CRM/Goonjcustom/Tabs/

Length of output: 22051


Script:

#!/bin/bash
# Check the content of CollectionCampService.tpl
cat wp-content/civi-extensions/goonjcustom/templates/CRM/Goonjcustom/Tabs/CollectionCampService.tpl

# Check if CollectionCampService.tpl is referenced anywhere
rg -l "CollectionCampService.tpl" wp-content/civi-extensions/goonjcustom/

Length of output: 464

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: 2

🧹 Outside diff range and nitpick comments (1)
wp-content/civi-extensions/goonjcustom/Civi/CollectionBaseService.php (1)

361-361: Improve error logging for queue failures

The error logging should be enhanced to include more debugging information.

Suggested improvement:

- \Civi::log()->debug('Cannot queue authorization email for initiator.', [
+ \Civi::log()->error('Cannot queue authorization email for initiator.', [
    'initiatorId' => $initiatorId,
    'status' => $status,
    'entityId' => $objectRef['id'],
    'error' => $ex->getMessage(),
+   'stack_trace' => $ex->getTraceAsString(),
  ]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 00ff291 and 132b67e.

📒 Files selected for processing (1)
  • wp-content/civi-extensions/goonjcustom/Civi/CollectionBaseService.php (5 hunks)
🔇 Additional comments (2)
wp-content/civi-extensions/goonjcustom/Civi/CollectionBaseService.php (2)

13-13: LGTM!

The Relationship API import is properly placed with other Civi API imports.


70-73: LGTM!

The change improves flexibility by generating filenames based on entity subtype while maintaining consistent naming conventions.

Comment on lines +329 to +355
private static function getInitiatorId(array $collectionCamp) {
$subtypeName = $collectionCamp['subtype:name'];

$organizationId = $collectionCamp['Institution_Collection_Camp_Intent.Organization_Name.id'];

if ($subtypeName === 'Institution_Collection_Camp') {
$relationships = Relationship::get(FALSE)
->addWhere('contact_id_a', '=', $organizationId)
->addWhere('relationship_type_id:name', '=', 'Primary Institution POC of')
->execute();
// If no relationships found for 'Primary Institution POC of', check for 'Secondary Institution POC of'.
if (empty($relationships)) {
$relationships = Relationship::get(FALSE)
->addWhere('contact_id_a', '=', $organizationId)
->addWhere('relationship_type_id:name', '=', 'Secondary Institution POC of')
->execute();
}

// Return contact_id_b as initiator if found.
if (!empty($relationships) && isset($relationships[0]['contact_id_b'])) {
return $relationships[0]['contact_id_b'];
}
}

// If the subtype is not 'Institution_Collection_Camp', or no relationship was found, use the default contact_id.
return $collectionCamp['Collection_Camp_Core_Details.Contact_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.

🛠️ Refactor suggestion

Refactor to improve maintainability and reduce duplication

The method has several issues:

  1. Duplicate API calls for different relationship types
  2. Hardcoded relationship type names
  3. Missing error handling

Suggested improvements:

+ private const PRIMARY_POC_RELATIONSHIP = 'Primary Institution POC of';
+ private const SECONDARY_POC_RELATIONSHIP = 'Secondary Institution POC of';

  private static function getInitiatorId(array $collectionCamp) {
    $subtypeName = $collectionCamp['subtype:name'];
    $organizationId = $collectionCamp['Institution_Collection_Camp_Intent.Organization_Name.id'];

    if ($subtypeName === 'Institution_Collection_Camp') {
+     try {
+       $relationshipTypes = [self::PRIMARY_POC_RELATIONSHIP, self::SECONDARY_POC_RELATIONSHIP];
+       $relationships = Relationship::get(FALSE)
+         ->addWhere('contact_id_a', '=', $organizationId)
+         ->addWhere('relationship_type_id:name', 'IN', $relationshipTypes)
+         ->execute();

+       if (!empty($relationships) && isset($relationships[0]['contact_id_b'])) {
+         return $relationships[0]['contact_id_b'];
+       }
+     } catch (\Exception $e) {
+       \Civi::log()->error('Failed to retrieve relationships', [
+         'error' => $e->getMessage(),
+         'organizationId' => $organizationId
+       ]);
+     }
    }

    return $collectionCamp['Collection_Camp_Core_Details.Contact_Id'];
  }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +284 to +289
->addSelect('Collection_Camp_Core_Details.Status', 'Collection_Camp_Core_Details.Contact_Id', 'Institution_Collection_Camp_Intent.Organization_Name.id', 'subtype:name')
->addWhere('id', '=', $objectId)
->execute()->single();

$initiatorId = self::getInitiatorId($currentCollectionCamp);

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 error handling for the API call

The EckEntity::get call could fail, but there's no try-catch block to handle potential exceptions.

Add error handling:

-    $currentCollectionCamp = EckEntity::get('Collection_Camp', FALSE)
-      ->addSelect('Collection_Camp_Core_Details.Status', 'Collection_Camp_Core_Details.Contact_Id', 'Institution_Collection_Camp_Intent.Organization_Name.id', 'subtype:name')
-      ->addWhere('id', '=', $objectId)
-      ->execute()->single();
-
-    $initiatorId = self::getInitiatorId($currentCollectionCamp);
+    try {
+      $currentCollectionCamp = EckEntity::get('Collection_Camp', FALSE)
+        ->addSelect('Collection_Camp_Core_Details.Status', 'Collection_Camp_Core_Details.Contact_Id', 'Institution_Collection_Camp_Intent.Organization_Name.id', 'subtype:name')
+        ->addWhere('id', '=', $objectId)
+        ->execute()->single();
+
+      $initiatorId = self::getInitiatorId($currentCollectionCamp);
+    } catch (\Exception $e) {
+      \Civi::log()->error('Failed to retrieve collection camp details', [
+        'error' => $e->getMessage(),
+        'objectId' => $objectId
+      ]);
+      return;
+    }
📝 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
->addSelect('Collection_Camp_Core_Details.Status', 'Collection_Camp_Core_Details.Contact_Id', 'Institution_Collection_Camp_Intent.Organization_Name.id', 'subtype:name')
->addWhere('id', '=', $objectId)
->execute()->single();
$initiatorId = self::getInitiatorId($currentCollectionCamp);
try {
$currentCollectionCamp = EckEntity::get('Collection_Camp', FALSE)
->addSelect('Collection_Camp_Core_Details.Status', 'Collection_Camp_Core_Details.Contact_Id', 'Institution_Collection_Camp_Intent.Organization_Name.id', 'subtype:name')
->addWhere('id', '=', $objectId)
->execute()->single();
$initiatorId = self::getInitiatorId($currentCollectionCamp);
} catch (\Exception $e) {
\Civi::log()->error('Failed to retrieve collection camp details', [
'error' => $e->getMessage(),
'objectId' => $objectId
]);
return;
}

*
*/
private function formatDate($start, $end) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

remove this line

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

will do this in new pr

*
*/
private function formatTime($start, $end) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

remove this line

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

will do this in new pr

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.

2 participants