Repository: svthalia/concrexit

Issue:
Moneybird synchronization error: InvalidData("API error 422: {'customer_id': ['is already in use']}")
https://thalia.sentry.io/issues/4111947935/events/ac2f50ee6eea4c988e439135c8d797c4/?project=1463433


Discussion:

Resolving patch:
diff --git a/website/moneybirdsynchronization/models.py b/website/moneybirdsynchronization/models.py
--- a/website/moneybirdsynchronization/models.py
+++ b/website/moneybirdsynchronization/models.py
@@ -225,11 +225,10 @@ def to_moneybird(self):
                 member=self.payable.payment_payer
             )
             if moneybird_contact.moneybird_id is None:
-                response = moneybird.create_contact(moneybird_contact.to_moneybird())
-                moneybird_contact.moneybird_id = response["id"]
-                moneybird_contact.save()
+                # I know this is ugly, but I don't want to totally refactor the app.
+                from moneybirdsynchronization.services import create_or_update_contact
 
-            contact_id = moneybird_contact.moneybird_id
+                moneybird_contact = create_or_update_contact(moneybird_contact.member)
 
         invoice_date = date_for_payable_model(self.payable_object).strftime("%Y-%m-%d")
 
diff --git a/website/moneybirdsynchronization/moneybird.py b/website/moneybirdsynchronization/moneybird.py
--- a/website/moneybirdsynchronization/moneybird.py
+++ b/website/moneybirdsynchronization/moneybird.py
@@ -1,4 +1,4 @@
-from moneybirdsynchronization.administration import HttpsAdministration
+from moneybirdsynchronization.administration import Administration, HttpsAdministration
 from thaliawebsite import settings
 
 
@@ -15,6 +15,12 @@ def update_contact(self, contact_id, contact_data):
     def delete_contact(self, contact_id):
         self._administration.delete(f"contacts/{contact_id}")
 
+    def get_contact_by_customer_id(self, member):
+        try:
+            self._administration.get(f"contacts/customer_id/C-{self.member.pk}")
+        except Administration.NotFound:
+            return None
+
     def create_project(self, project_data):
         return self._administration.post("projects", project_data)
 
diff --git a/website/moneybirdsynchronization/services.py b/website/moneybirdsynchronization/services.py
--- a/website/moneybirdsynchronization/services.py
+++ b/website/moneybirdsynchronization/services.py
@@ -18,17 +18,25 @@ def create_or_update_contact(member):
     moneybird = get_moneybird_api_service()
 
     if moneybird_contact.moneybird_id is None:
-        # Push the contact to Moneybird
-        response = moneybird.create_contact(moneybird_contact.to_moneybird())
-        moneybird_contact.moneybird_id = response["id"]
-        moneybird_contact.save()
+        # Check if the contact already exists in Moneybird somehow.
+        response = moneybird.get_contact_by_customer_id(member)
+        if response is None:
+            # Push the contact to Moneybird.
+            response = moneybird.create_contact(moneybird_contact.to_moneybird())
+            moneybird_contact.moneybird_id = response["id"]
+        else:
+            # Link the existing moneybird contact. Then update the data.
+            moneybird_contact.moneybird_id = response["id"]
+            response = moneybird.update_contact(
+                moneybird_contact.moneybird_id, moneybird_contact.to_moneybird()
+            )
     else:
         # Update the contact data (right now we always do this, but we could use the version to check if it's needed)
         response = moneybird.update_contact(
             moneybird_contact.moneybird_id, moneybird_contact.to_moneybird()
         )
-        moneybird_contact.save()
 
+    moneybird_contact.save()
     return moneybird_contact
 
 


Test patch: