Repository: mozmeao/basket

Issue:
Accept `campaign_id` in donations
On the new stack, donations will be passing a `campaign_id` key/value. See PR [here](https://github.com/mozilla/donate-wagtail/pull/486/files)

This should get passed through to Salesforce and hooked up as a campaign like petition signatures.


Discussion:
@pmac If you can't get to this early this week (and I totally understand if that's the case), please just let me know if it's indeed [this function](https://github.com/mozmeao/basket/blob/master/basket/news/tasks.py#L1018) that needs some work and I'll see if someone on my team can figure it out based on the petition code.
Are you saying that we should be adding a "Campaign Member" object to the user like we do for a signature, or that the donation Opportunity object should get a CampaignID field to record the ID? Or is this a question for the Salesforce folks?
I’ll yield to @stephaniemcv
I had a convo with @stephaniemcv last night and it seems like we need Basket to handle connecting CampaignIDs to Opportunities.

As she said,
>The Primary Campaign Source field does exist on the Opportunity
I'm sorry, I still don't know what you want me to do. Can you give me SFDC object type and field name that this new data should go into?
Yes, apologies for the delay.
SFDC Object = Opportunity
Field Name = Primary Campaign Source
API Field Name = CampaignId
Field Data Type = Lookup or reference
Length = 15
(This is a similar field data type to Donation_Contact__c)
So, to summarize:

* The `CampaignId` field exists in SFDC on the Opportunity object.
* Basket should take what it receives in the `campaign_id` field and pass it into the `CampaignId` field for new donations.

Do I have that right? And if so do you know if the field exists on Opportunity in both ADVO and prod already, or is there some change coordination to do with the SFDC folks?
Sounds right to me, assuming `Primary Campaign Source` is what `CampaignId` maps to. @stephaniemcv?
<img width="434" alt="image" src="https://user-images.githubusercontent.com/1682681/70937515-69844e00-2012-11ea-9f19-ff992a89181b.png">

That is correct on both fronts. 

- The field exists in both ADVO and prod. 

- Primary Campaign Source is the internal field label for SFDC, CampaignId is the API field name. 

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -1129,6 +1129,10 @@ def process_donation(data):
         'PMT_Transaction_ID__c': data['transaction_id'],
         'Payment_Type__c': 'Recurring' if data['recurring'] else 'One-Time',
     }
+    # https://github.com/mozmeao/basket/issues/364
+    if 'campaign_id' in data:
+        donation['CampaignId'] = data['campaign_id']
+
     # this is a unix timestamp in ms since epoc
     timestamp = data.get('created')
     if timestamp:


Test patch:
diff --git a/basket/news/tests/test_tasks.py b/basket/news/tests/test_tasks.py
--- a/basket/news/tests/test_tasks.py
+++ b/basket/news/tests/test_tasks.py
@@ -686,6 +686,7 @@ class ProcessDonationTests(TestCase):
         'recurring': True,
         'service': 'paypal',
         'transaction_id': 'NLEKFRBED3BQ614797468093.25',
+        'campaign_id': 'were-you-listening-to-the-dudes-story',
     }
 
     def test_one_name(self, sfdc_mock, gud_mock):
@@ -803,6 +804,7 @@ def test_donation_data(self, sfdc_mock, gud_mock):
             'Net_Amount__c': data['net_amount'],
             'Conversion_Amount__c': data['conversion_amount'],
             'Last_4_Digits__c': data['last_4'],
+            'CampaignId': data['campaign_id'],
         })
 
     def test_donation_data_optional_null(self, sfdc_mock, gud_mock):
@@ -837,6 +839,7 @@ def test_donation_data_optional_null(self, sfdc_mock, gud_mock):
             'Net_Amount__c': data['net_amount'],
             'Conversion_Amount__c': data['conversion_amount'],
             'Last_4_Digits__c': data['last_4'],
+            'CampaignId': data['campaign_id'],
         })
 
     def test_donation_silent_failure_on_dupe(self, sfdc_mock, gud_mock):