From 42f075107148cc08272e4449d17f8f7cc621f3a1 Mon Sep 17 00:00:00 2001
From: Aidan Saunders <aidan.saunders@squiffle.uk>
Date: Fri, 24 Jan 2025 18:08:15 +0000
Subject: [PATCH] Add DRYRUN mode to import script

---
 cli/import-from-gc.php | 177 ++++++++++++++++++++++++++---------------
 1 file changed, 112 insertions(+), 65 deletions(-)

diff --git a/cli/import-from-gc.php b/cli/import-from-gc.php
index 99ee236..3e03bc4 100644
--- a/cli/import-from-gc.php
+++ b/cli/import-from-gc.php
@@ -81,6 +81,9 @@ define('GC_CONFIRM_BEFORE_CREATING_RECUR',  FALSE);
 // or try further matching?
 define('GC_DUPE_EMAIL_SKIP', TRUE);
 
+// Dry run - don't make any changes
+define('GC_DRYRUN', FALSE);
+
 // Import Code begins
 // ==================
 
@@ -153,12 +156,16 @@ class GCImport
   public $logFile = NULL;
   public $lastSave = NULL;
   public $dupeEmailSkip = TRUE;
+
+  /** $var bool **/
+  public $dryRun = FALSE;
+
   /**
    * @param String $financialTypeName
    * @param null|String $importSince (date)
    */
   public function __construct($financialTypeName, $priceFieldID, $importSince = NULL, $confirmCreateRecur=TRUE, $logDir=NULL,
-    $dupeEmailSkip=TRUE) {
+    $dupeEmailSkip=TRUE, $dryRun=FALSE) {
     civicrm_initialize();
 
     if ($logDir !== NULL) {
@@ -215,6 +222,8 @@ class GCImport
     $this->gcAPI = $this->processor->getGoCardlessApi();
 
     $this->dupeEmailSkip = $dupeEmailSkip;
+
+    $this->dryRun = $dryRun;
   }
 
   /**
@@ -326,10 +335,15 @@ class GCImport
       // if it is still live; Cancelled if it was alive but is now expired/cancelled.
       $expectedStatus = $this->getMapSubscriptionStatusToContribRecurStatus($subscription);
       if ($expectedStatus != $this->contribRecurStatusInProgress) {
-        civicrm_api3('ContributionRecur', 'create', [
-          'id' => $contribRecurID,
-          'contribution_status_id' => $expectedStatus
-        ]);
+        if (!$this->dryRun) {
+          civicrm_api3('ContributionRecur', 'create', [
+            'id' => $contribRecurID,
+            'contribution_status_id' => $expectedStatus
+          ]);
+        }
+        else {
+          print "...(DRYRUN) would have updated Recur $contribRecurID to status $expectedStatus\n";
+        }
       }
 
       if (($expectedStatus != $this->contribRecurStatusCancelled) and empty($payments)) {
@@ -350,10 +364,15 @@ class GCImport
       $expectedStatus = $this->getMapSubscriptionStatusToContribRecurStatus($subscription);
       if ($expectedStatus != $recur['values'][0]['contribution_status_id']) {
         print "...! Recur Status was {$recur['values'][0]['contribution_status_id']} expected $expectedStatus. Updated.\n";
-        civicrm_api3('ContributionRecur', 'create', [
-          'id' => $contribRecurID,
-          'contribution_status_id' => $expectedStatus
-        ]);
+        if (!$this->dryRun) {
+          civicrm_api3('ContributionRecur', 'create', [
+            'id' => $contribRecurID,
+            'contribution_status_id' => $expectedStatus
+          ]);
+        }
+        else {
+          print "...(DRYRUN) would have updated Recur $contribRecurID to status $expectedStatus\n";
+        }
         $this->log['subscriptionsStatusUpdated']++;
         $this->log['subscriptions'][$subscription->id]['statusChange'] =
           ['old' => $recur['values'][0]['contribution_status_id'], 'new' => $expectedStatus];
@@ -427,33 +446,39 @@ class GCImport
 
     if (!$contactID) {
       print "...Email not found in CiviCRM, creating contact now.\n";
+      if (!$this->dryRun) {
+        // If first/last are all uppercase or all lowercase, attempt to improve case.
+        $caseify = function ($_) { return ((mb_strtolower($_) == $_) or (mb_strtoupper($_) == $_)) ? ucwords(mb_strtolower($_)) : $_; };
+        $first_name = $caseify($customer->given_name);
+        $last_name = $caseify($customer->family_name);
+
+        $contact = civicrm_api3('Contact', 'create', [
+          'contact_type'           => 'Individual',
+          'first_name'             => $first_name,
+          'last_name'              => $last_name,
+          'email'                  => $customer->email,
+          'source'                 => 'GoCardless import ' . date('Y-m-d H:i:s'),
+        ]);
+        print "...+ Created contact $first_name $last_name id: $contact[id]\n";
+        $this->log['contactsAdded'][$contact['id']] = TRUE;
+        $this->log['subscriptions'][$subscription->id]['newContactID'] = $contact['id'];
+        // Create address.
+        $address = civicrm_api3('Address', 'create', [
+            'contact_id'             => $contact['id'],
+            'location_type_id'       => 'Main',
+            'street_address'         => $customer->address_line1,
+            'supplemental_address_1' => $customer->address_line2,
+            'city'                   => $customer->city,
+            'postal_code'            => $customer->postal_code,
+            'country_id'             => $customer->country_code,
+        ]);
+        $contactID = (int) $contact['id'];
 
-      // If first/last are all uppercase or all lowercase, attempt to improve case.
-      $caseify = function ($_) { return ((mb_strtolower($_) == $_) or (mb_strtoupper($_) == $_)) ? ucwords(mb_strtolower($_)) : $_; };
-      $first_name = $caseify($customer->given_name);
-      $last_name = $caseify($customer->family_name);
-
-      $contact = civicrm_api3('Contact', 'create', [
-        'contact_type'           => 'Individual',
-        'first_name'             => $first_name,
-        'last_name'              => $last_name,
-        'email'                  => $customer->email,
-        'source'                 => 'GoCardless import ' . date('Y-m-d H:i:s'),
-      ]);
-      print "...+ Created contact $first_name $last_name id: $contact[id]\n";
-      $this->log['contactsAdded'][$contact['id']] = TRUE;
-      $this->log['subscriptions'][$subscription->id]['newContactID'] = $contact['id'];
-      // Create address.
-      $address = civicrm_api3('Address', 'create', [
-          'contact_id'             => $contact['id'],
-          'location_type_id'       => 'Main',
-          'street_address'         => $customer->address_line1,
-          'supplemental_address_1' => $customer->address_line2,
-          'city'                   => $customer->city,
-          'postal_code'            => $customer->postal_code,
-          'country_id'             => $customer->country_code,
-      ]);
-      $contactID = (int) $contact['id'];
+      }
+      else {
+        print "...(DRYRUN) would have created contact $first_name $last_name\n";
+        $contactID = 0;
+      }
     }
 
     return $contactID;
@@ -567,11 +592,17 @@ class GCImport
       'source'                 => 'GoCardless import ' . date('Y-m-d H:i:s'),
     ];
     //print "...Creating with " . json_encode($params, JSON_PRETTY_PRINT) . "\n";
-    $result = civicrm_api3('ContributionRecur', 'create', $params);
-    if ($result['id']) {
-      $recur_id = $result['id'];
-      print "...✔ Created ContributionRecur $recur_id for subscription $subscription->id\n";
-      return $recur_id;
+    if (!$this->dryRun) {
+      $result = civicrm_api3('ContributionRecur', 'create', $params);
+      if ($result['id']) {
+        $recur_id = $result['id'];
+        print "...✔ Created ContributionRecur $recur_id for subscription $subscription->id\n";
+        return $recur_id;
+      }
+    }
+    else {
+      print "...(DRYRUN) would have created ContributionRecur for subscription $subscription->id\n";
+      return 0;
     }
   }
   /**
@@ -607,12 +638,17 @@ class GCImport
       ],
     ];
     //print json_encode($_, JSON_PRETTY_PRINT) . "\n";
-    $result = civicrm_api3('Order', 'create', $_);
-    if (!$result['is_error']) {
-      print "...✔ Created initial payment $result[id]\n";
+    if (!$this->dryRun) {
+      $result = civicrm_api3('Order', 'create', $_);
+      if (!$result['is_error']) {
+        print "...✔ Created initial payment $result[id]\n";
+      }
+      else {
+        throw new \RuntimeException(json_encode($result, JSON_PRETTY_PRINT));
+      }
     }
     else {
-      throw new \RuntimeException(json_encode($result, JSON_PRETTY_PRINT));
+      print "...(DRYRUN) would have created initial payment\n";
     }
   }
   /**
@@ -670,18 +706,23 @@ class GCImport
           'source'                 => 'GoCardless import ' . date('Y-m-d H:i:s'),
         ];
         // print json_encode($payment, JSON_PRETTY_PRINT) . "\n";
-        $orderCreateResult = civicrm_api3('Order', 'create', $payment);
-        if (!$orderCreateResult['is_error']) {
-          $contributionID = $orderCreateResult['id'];
-          print "...+ Created Order for payment $payment[trxn_id], contribution ID: $contributionID on $payment[receive_date]\n";
-          $log['newContribID'] = $contributionID;
-          $log['amount'] = $payment['total_amount'];
-          $log['date'] = $payment['receive_date'];
-          $this->log['paymentsAdded']++;
-          $this->log['paymentsAddedAmount'] += $payment['total_amount'];
+        if (!$this->dryRun) {
+          $orderCreateResult = civicrm_api3('Order', 'create', $payment);
+          if (!$orderCreateResult['is_error']) {
+            $contributionID = $orderCreateResult['id'];
+            print "...+ Created Order for payment $payment[trxn_id], contribution ID: $contributionID on $payment[receive_date]\n";
+            $log['newContribID'] = $contributionID;
+            $log['amount'] = $payment['total_amount'];
+            $log['date'] = $payment['receive_date'];
+            $this->log['paymentsAdded']++;
+            $this->log['paymentsAddedAmount'] += $payment['total_amount'];
+          }
+          else {
+            throw new RuntimeException("Error creating order: " . json_encode($orderCreateResult, JSON_PRETTY_PRINT));
+          }
         }
         else {
-          throw new RuntimeException("Error creating order: " . json_encode($orderCreateResult, JSON_PRETTY_PRINT));
+          print "...(DRYRUN) would have created Order for payment $payment[trxn_id] on $payment[receive_date]\n";
         }
       }
 
@@ -693,18 +734,23 @@ class GCImport
         'trxn_id'                           => $payment['trxn_id'],
         'is_send_contribution_notification' => 0,
       ];
-      $paymentCreateResult = civicrm_api3('Payment', 'create', $paymentCreateParams);
-      if (!$paymentCreateResult['is_error']) {
-        // correct the contribution date.
-        civicrm_api3('Contribution', 'create', [
-          'id' => $contributionID,
-          'receive_date' => $payment['receive_date'],
-        ]);
-
-        print "...+ Created Payment on Order for payment $payment[trxn_id], contribution ID: $contributionID\n";
+      if (!$this->dryRun) {
+        $paymentCreateResult = civicrm_api3('Payment', 'create', $paymentCreateParams);
+        if (!$paymentCreateResult['is_error']) {
+          // correct the contribution date.
+          civicrm_api3('Contribution', 'create', [
+            'id' => $contributionID,
+            'receive_date' => $payment['receive_date'],
+          ]);
+
+          print "...+ Created Payment on Order for payment $payment[trxn_id], contribution ID: $contributionID\n";
+        }
+        else {
+          throw new RuntimeException("Error calling payment.create with " . json_encode($paymentCreateResult, JSON_PRETTY_PRINT));
+        }
       }
       else {
-        throw new RuntimeException("Error calling payment.create with " . json_encode($paymentCreateResult, JSON_PRETTY_PRINT));
+        print "...(DRYRUN) would have created Payment on Order for payment $payment[trxn_id]\n";
       }
     }
     if ($skips>0) {
@@ -747,7 +793,8 @@ try {
     GC_IMPORT_SINCE,
     GC_CONFIRM_BEFORE_CREATING_RECUR,
     GC_PRIVATE_OUTPUT_DIR,
-    GC_DUPE_EMAIL_SKIP
+    GC_DUPE_EMAIL_SKIP,
+    GC_DRYRUN
   );
   $importer->run(GC_SUBSCRIPTIONS_LIMIT);
 }
-- 
GitLab