internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: mozmeao/basket

Issue:
Initial Purchase flag - Subscription Services
Please set Initial_Purchase__c on the Opportunity to TRUE on the code that processes the payload from customer.subscription.created - that will denote if the charge is for a new subscription or a recurring charge. 

Any code that checked for Opportunity with the same subscription ID should be removed from existing code as it will not work with multiple data payloads coming in from SubHub. 


Discussion:

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -818,10 +818,6 @@ def process_subhub_event_subscription_charge(data):
     - last4
     - exp_month
     - exp_year
-
-    We also need to determine if this payment (the combination of both events
-    listed above) is the first or is a recurrence. This is done by checking to
-    see if the subscription_id in data already exists in Salesforce.
     """
 
     statsd.incr('news.tasks.process_subhub_event.subscription_charge')
@@ -885,21 +881,6 @@ def process_subhub_event_subscription_charge(data):
             # we are creating a new opportunity, so put the charge_id in
             transaction_data['PMT_Transaction_ID__c'] = charge_id
 
-            # determine if this is an initial or recurring payment
-            try:
-                # look for existing opportunities with the current subscription_id
-                sfdc.opportunity.get_by_custom_id('PMT_Subscription_ID__C', data['subscription_id'])
-            # if no opportunities are found with the current subscription_id,
-            # we know this is an initial purchase
-            except sfapi.SalesforceResourceNotFound:
-                transaction_data['Initial_Purchase__c'] = True
-            # if one or more records are found, we know this is *not* an
-            # initial purchase
-            except sfapi.SalesforceMoreThanOneRecord:
-                transaction_data['Initial_Purchase__c'] = False
-            else:
-                transaction_data['Initial_Purchase__c'] = False
-
             sfdc.opportunity.create(transaction_data)
         else:
             statsd.incr('news.tasks.process_subhub_event.subscription_charge_update')
@@ -966,6 +947,8 @@ def process_subhub_event_subscription_updated(data):
             'StageName': 'Closed Won',
         })
 
+        transaction_data['Initial_Purchase__c'] = data['event_type'] == 'customer.subscription.created'
+
         try:
             # attempt to update the opportunity identified by the invoice_id
             sfdc.opportunity.update('PMT_Invoice_ID__c/{}'.format(data['invoice_id']), transaction_data)


Test patch:
Repository: mozmeao/basket

Issue:
SFMC Credit Card Expiration trigger testing and setup
2 things related to the cc expiration trigger setup: 

(1) the trigger subscription-services-cc-expired has been created in the Basket Admin interface and when we tried to test it, we did not receive the email 

(2) we need to set up basket to automatically call this job ^ when it receives the credit card expiration event (Customer.Source.Expiring) from SubHub

Brynne is out the week of July 29 in onboarding, so Desiree can assist with testing as necessary. 


Discussion:
What is the exact ID of the trigger in SFMC? Basket is setup to translate these, so it prepends the language to the front of the ID (e.g. "en_subscription_services_cc_expired"). I can make our job not do that, but it would be looking for the language version by default and that might be why the test didn't work.
And if basket is only going to be using this trigger internally then it doesn't need to be in the basket admin.

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -973,14 +973,7 @@ def process_subhub_event_credit_card_expiring(data):
     Event name: customer.source.expiring
     """
     statsd.incr('news.tasks.process_subhub_event.credit_card_expiring')
-
-    # get the user's email address to send to the extension
-    user_data = get_user_data(payee_id=data['customer_id'])
-
-    if user_data:
-        sfmc.add_row('customer-card-expiring', {
-            'EmailAddress': user_data['email'],
-        })
+    sfmc.send_mail(settings.SUBHUB_CC_EXPIRE_TRIGGER, data['email'], data['email'])
 
 
 @et_task
diff --git a/basket/settings.py b/basket/settings.py
--- a/basket/settings.py
+++ b/basket/settings.py
@@ -393,6 +393,7 @@ def get_default_gateway_linux():
 }
 
 SUBHUB_OPP_RECORD_TYPE = config('SUBHUB_OPP_RECORD_TYPE', default='')
+SUBHUB_CC_EXPIRE_TRIGGER = config('SUBHUB_CC_EXPIRE_TRIGGER', default='en_subscription_services_cc_expired')
 
 COMMON_VOICE_NEWSLETTER = config('COMMON_VOICE_NEWSLETTER', default='common-voice')
 


Test patch:
Repository: mozmeao/basket

Issue:
Increase basket's task retry timeout
The request is to have the tasks continue to retry for at least 48 hours.

cc @metadave @andrewmozilla 


Discussion:

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -146,7 +146,7 @@ def et_task(func):
     @celery_app.task(name=full_task_name,
                      bind=True,
                      default_retry_delay=300,  # 5 min
-                     max_retries=8)
+                     max_retries=11)
     @wraps(func)
     def wrapped(self, *args, **kwargs):
         start_time = kwargs.pop('start_time', None)
@@ -184,6 +184,7 @@ def wrapped(self, *args, **kwargs):
                 if not (isinstance(e, RetryTask) or ignore_error_post_retry(e)):
                     sentry_client.captureException(tags={'action': 'retried'})
 
+                # ~68 hr at 11 retries
                 raise self.retry(countdown=2 ** (self.request.retries + 1) * 60)
             except self.MaxRetriesExceededError:
                 statsd.incr(self.name + '.retry_max')


Test patch:
Repository: mozmeao/basket

Issue:
Allow for pass-through of the language parameter for mozilla-foundation newsletter
Currently, signups for the mozilla foundation newsletter are hardcoded to be English only (https://github.com/mozmeao/basket/blob/master/basket/news/tasks.py#L1101), and we'd like to be able to get the user's language preference passed through instead, based on the `lang` property.




Discussion:

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -1098,7 +1098,7 @@ def process_petition_signature(data):
     if data.get('email_subscription', False):
         upsert_user.delay(SUBSCRIBE, {
             'token': user_data['token'],
-            'lang': 'en-US',
+            'lang': data.get('lang', 'en-US'),
             'newsletters': 'mozilla-foundation',
             'source_url': data['source_url'],
         })


Test patch:
Repository: mozmeao/basket

Issue:
Fx Account flow testing in Salesforce - ensure reactivation of an account flows properly
With the config of the deletion of a Firefox Account for subscription services, we will be automatically checking a flag on the Contact record to denote they've deleted their Firefox Account. A few things that may impact Basket: 

(1) the case when someone deletes a Fx Account and does not have an active subscription - how will Salesforce know about that? (right now it's logging in a SFMC data extension, but going forward we will have a checkbox on the Contact record to denote they've deleted their account)

(2) If someone re-creates another Fx Account using the same email address after deleting their Fx Account, we should ensure that the new Fx Account flow checks to make sure the checkbox is NOT equal to true and if it is, set it to false


Discussion:
API name of field to check on Contact is : FxA_Account_Deleted__c
Is that field in ADVO and ready for testing? I can get that working. 

The only info I get from FxA when an account is deleted is an FxA ID, so I can only flip that switch if SFDC already has a record of that FxA ID. Is that okay, or should I create an empty record with just that FxA ID and that flag flipped?
Field is on ADVO and basket-stage has the ability to write to it. 

The question on what to do if an FxA doesn't exist yet is a good one. Let me check with Andrew and we'll get back to you. It will probably be a moot point (hopefully) depending on when the Firefox Accounts get moved over to SFDC from SFMC
Should I add flipping this flag to the handling of the `customer.deleted` event from #340 ?
Yes, please add it to that event as well - flip the "Fx Account Deleted" flag to true when that comes in from SubHub

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -848,6 +848,9 @@ def process_subhub_event_subscription_cancel(data):
         'StageName': SUB_STAGE_NAMES[data['event_type']],
     })
 
+    if data['event_type'] == 'customer.deleted':
+        sfdc.update(user_data, {'fxa_deleted': True})
+
 
 @et_task
 def process_subhub_event_credit_card_expiring(data):


Test patch:
Repository: mozmeao/basket

Issue:
Donation optional field set to null error
I saw an [error in basket prod in sentry](https://sentry.prod.mozaws.net/operations/basket-prod/issues/6624551/?referrer=slack) that was caused by an optional field for a donation existed but was null (or `None`). These fields' values are truncated to ensure that the data will be valid for SFDC, so it threw a `TypeError: 'NoneType' object is not subscriptable`. I'm not sure if this is a result of a change on the donation site side or not, but it's easy enough to fix in basket.


Discussion:

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -1078,7 +1078,7 @@ def process_donation(data):
             donation[dest_name] = data[source_name]
 
     for dest_name, source_name in DONATION_OPTIONAL_FIELDS.items():
-        if source_name in data:
+        if data.get(source_name):
             # truncate at 2000 chars as that's the max for
             # a SFDC text field. We may do more granular
             # truncation per field in future.


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
@@ -625,6 +625,40 @@ def test_donation_data(self, sfdc_mock, gud_mock):
             'Last_4_Digits__c': data['last_4'],
         })
 
+    def test_donation_data_optional_null(self, sfdc_mock, gud_mock):
+        """Having a `None` in an optional field used to throw a TypeError.
+
+        https://github.com/mozmeao/basket/issues/366
+        """
+        data = self.donate_data.copy()
+        data['subscription_id'] = None
+        gud_mock.return_value = {
+            'id': '1234',
+            'first_name': 'Jeffery',
+            'last_name': 'Lebowski',
+        }
+        process_donation(data)
+        sfdc_mock.opportunity.create.assert_called_with({
+            'RecordTypeId': ANY,
+            'Name': 'Foundation Donation',
+            'Donation_Contact__c': '1234',
+            'StageName': 'Closed Won',
+            # calculated from data['created']
+            'CloseDate': '2016-11-21T16:46:49.327000',
+            'Amount': float(data['donation_amount']),
+            'Currency__c': 'USD',
+            'Payment_Source__c': 'paypal',
+            'PMT_Transaction_ID__c': data['transaction_id'],
+            'Payment_Type__c': 'Recurring',
+            'SourceURL__c': data['source_url'],
+            'Project__c': data['project'],
+            'Donation_Locale__c': data['locale'],
+            'Processors_Fee__c': data['transaction_fee'],
+            'Net_Amount__c': data['net_amount'],
+            'Conversion_Amount__c': data['conversion_amount'],
+            'Last_4_Digits__c': data['last_4'],
+        })
+
     def test_donation_silent_failure_on_dupe(self, sfdc_mock, gud_mock):
         data = self.donate_data.copy()
         gud_mock.return_value = {
Repository: mozmeao/basket

Issue:
SubHub Event Payload Updates
The SubHub team will be adding a new field to two payloads that are being sent to Basket

Event types: `customer.subscription.created` and `customer.recurring_charge`

Property being passed: `next_invoice_date` (Needs to be converted from UNIX EPOCH to date (MM/DD/YYYY format))

SFDC Field: `Next_Invoice_Date__c`


Discussion:

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -805,6 +805,7 @@ def process_subhub_event_subscription_charge(data):
         'Invoice_Number__c': data['invoice_number'],
         'Last_4_Digits__c': data['last4'],
         'Name': 'Subscription Services',
+        'Next_Invoice_Date__c': iso_format_unix_timestamp(data['next_invoice_date']),
         'Payment_Source__c': 'Stripe',
         'PMT_Subscription_ID__c': data['subscription_id'],
         'PMT_Transaction_ID__c': data['charge'],


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
@@ -364,6 +364,7 @@ class SubHubEventTests(TestCase):
         'plan_amount': '1000',
         'current_period_end': '1566305505',
         'current_period_start': '1566305502',
+        'next_invoice_date': '1566305605',
         'created': '1566305509',
         'brand': 'visa',
         'last4': '1111',
@@ -519,6 +520,7 @@ def test_subscription_charge_initial(self, sfdc_mock, gud_mock):
             'Invoice_Number__c': data['invoice_number'],
             'Last_4_Digits__c': data['last4'],
             'Name': 'Subscription Services',
+            'Next_Invoice_Date__c': '2019-08-20T12:53:25',
             'Payment_Source__c': 'Stripe',
             'PMT_Subscription_ID__c': data['subscription_id'],
             'PMT_Transaction_ID__c': data['charge'],
@@ -551,6 +553,7 @@ def test_subscription_charge_recurring(self, sfdc_mock, gud_mock):
             'Invoice_Number__c': data['invoice_number'],
             'Last_4_Digits__c': data['last4'],
             'Name': 'Subscription Services',
+            'Next_Invoice_Date__c': '2019-08-20T12:53:25',
             'Payment_Source__c': 'Stripe',
             'PMT_Subscription_ID__c': data['subscription_id'],
             'PMT_Transaction_ID__c': data['charge'],
Repository: mozmeao/basket

Issue:
SubHub > Salesforce integration to modify customer.recurring_charge payload
Update basket to add new fields to customer.recurring_charge payload. Mappings can be found here: https://docs.google.com/spreadsheets/d/1z6aXj_9GUpl5g_hR2k_SmjY5tyhxvnoHGOr8e-cbo18/edit#gid=1328177684

@marty331 is making the changes on Subhub
Brandon P will need to fill in the field for the total amount mapping in SF.


Discussion:
Are these different updates than #374 ?
If not can you indicate here which fields are new and/or updated?
@pmac yes these are different than #374 and I just updated the sheet to show the two new fields which are proration_amount and total_amount.
Is this it? I'd like to avoid a flurry of small updates and just knock them all out at once if possible.

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -791,7 +791,7 @@ def process_subhub_event_subscription_charge(data):
         nickname = nickname[0]
 
     # if a customer re-instates service after a cancellation, the record needs to be updated
-    sfdc.opportunity.upsert(f'PMT_Invoice_ID__c/{data["invoice_id"]}', {
+    oppy_data = {
         'Amount': cents_to_dollars(data['plan_amount']),
         'Billing_Cycle_End__c': iso_format_unix_timestamp(data['current_period_end']),
         'Billing_Cycle_Start__c': iso_format_unix_timestamp(data['current_period_start']),
@@ -812,7 +812,14 @@ def process_subhub_event_subscription_charge(data):
         'RecordTypeId': settings.SUBHUB_OPP_RECORD_TYPE,
         'Service_Plan__c': nickname,
         'StageName': 'Closed Won',
-    })
+    }
+    if 'proration_amount' in data:
+        oppy_data['Proration_Amount__c'] = cents_to_dollars(data['proration_amount'])
+
+    if 'total_amount' in data:
+        oppy_data['Total_Amount__c'] = cents_to_dollars(data['total_amount'])
+
+    sfdc.opportunity.upsert(f'PMT_Invoice_ID__c/{data["invoice_id"]}', oppy_data)
 
 
 @et_task


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
@@ -533,7 +533,11 @@ def test_subscription_charge_recurring(self, sfdc_mock, gud_mock):
         gud_mock.return_value = {'id': '1234'}
 
         data = self.charge_data.copy()
-        data['event_type'] = 'customer.recurring_charge'
+        data.update({
+            'event_type': 'customer.recurring_charge',
+            'proration_amount': 250,
+            'total_amount': 350,
+        })
 
         process_subhub_event_subscription_charge(data)
 
@@ -557,9 +561,11 @@ def test_subscription_charge_recurring(self, sfdc_mock, gud_mock):
             'Payment_Source__c': 'Stripe',
             'PMT_Subscription_ID__c': data['subscription_id'],
             'PMT_Transaction_ID__c': data['charge'],
+            'Proration_Amount__c': 2.5,
             'RecordTypeId': settings.SUBHUB_OPP_RECORD_TYPE,
             'Service_Plan__c': data['nickname'],
             'StageName': 'Closed Won',
+            'Total_Amount__c': 3.5,
         })
Repository: mozmeao/basket

Issue:
Pass through 2 Additional Fields to SalesForce CRM from FxA Form
Hola PMac.

I would like to request that we collect both Geo and Platform when someone submits their interest for the FPN product. We currently were only collecting email. 

The two fields are:

  | "FPN Waitlist Geo" | API Name: FPN_Waitlist_Geo__c |   | Text(120) |

  | "FPN Waitlist Platform" | API Name: FPN_Waitlist_Platform__c |   | Text(120) |

This is ready in Prod. 









Discussion:

Resolving patch:
diff --git a/basket/news/backends/sfdc.py b/basket/news/backends/sfdc.py
--- a/basket/news/backends/sfdc.py
+++ b/basket/news/backends/sfdc.py
@@ -78,6 +78,8 @@ def cast_lower(value):
     'payee_id': 'PMT_Cust_Id__c',
     'fxa_id': 'FxA_Id__c',
     'fxa_deleted': 'FxA_Account_Deleted__c',
+    'fpn_country': 'FPN_Waitlist_Geo__c',
+    'fpn_platform': 'FPN_Waitlist_Platform__c',
 }
 INV_FIELD_MAP = {v: k for k, v in FIELD_MAP.items()}
 FIELD_DEFAULTS = {
@@ -104,6 +106,8 @@ def cast_lower(value):
     'FSA_Grad_Year__c': 4,
     'FSA_Major__c': 100,
     'MailingCity': 100,
+    'FPN_Waitlist_Geo__c': 120,
+    'FPN_Waitlist_Platform__c': 120,
 }
 
 


Test patch:
Repository: mozmeao/basket

Issue:
Capture credit card type in MoFo donations
(MoFo work tracked [here](https://github.com/mozilla/donate-wagtail/issues/620))

We need to capture credit card type in donation opportunity records so that we can include it in emailed receipts to donors. It sounds like we need to

a.) add the `Credit_Card_Type_c` field to the donation opportunity record type so that MoFo users can see the value.
b.) record the type when passed to Basket. I'm not sure if it needs to be a new key in the Basket payload or additional options for payment method, and I don't have an opinion. But whatever key we use should be able to not exist or be an empty string (or null?) like other optional fields already are.


Discussion:
Sounds good. As soon as y'all determine the key for this value I'll get a PR going.
The key that we use to pass in to Basket? I'd assume `card_type` should be fine? @pomax @stephaniemcv any concerns with that? It still gets passed to the same spot in Salesforce regardless of the name.
Yes. I just need to know what it'll be.
Based on the API that Braintree gives us, `card_type` looks right to me. I've got a tentative PR up that uses that as new key for the SQS payload.
Is this for all of the donation event types, or just the initial `donation` type?
As far as I know, only for donations, so that we can add that information to donation receipt emails.
I believe this needs to work for recurring donations too. 
> I believe this needs to work for recurring donations too.

I think recurring donations are represented by an Opportunity object in SFDC for each donation with `Payment_Type__c == 'Recurring'`. So it's still just `donation` events we need to worry about in basket. The other donation event types seem to all deal with updating an Opportunity to indicate it was canceled for some reason.
Pmac is correct. 
Based on [this comment](https://github.com/mozilla/donate-wagtail/issues/620#issuecomment-558872914) the value could be the string `"None"`. What should basket record in that case?
Can Basket record "none"? The field in Salesforce is text 25. I'm assuming this scenario would happen with Braintree processing Paypal as those transactions don't have a card type. Subscription Services wouldn't have designed for this since they're taking only Stripe right now.

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -1042,6 +1042,7 @@ def process_donation_event(data):
     'Donation_Form_URL__c': 'donation_url',
     'Project__c': 'project',
     'PMT_Subscription_ID__c': 'subscription_id',
+    'Credit_Card_Type__c': 'card_type',
 }
 # Add these fields as optional for now as some messages
 # could still come through without them. Mix of string


Test patch:
Repository: mozmeao/basket

Issue:
Proration_Amount__c Decimal Location
The decimal location of `Proration_Amount__c` is off by two marks.
Example: Payload value of 356 resolves to $356.00 in Salesforce; the expected result is $3.56.


Discussion:

Resolving patch:
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -886,6 +886,7 @@ def process_subhub_event_subscription_updated(data):
     sfdc.opportunity.create({
         'Amount': cents_to_dollars(data['plan_amount_new']),
         'Plan_Amount_Old__c': cents_to_dollars(data['plan_amount_old']),
+        'Proration_Amount__c': cents_to_dollars(data['proration_amount']),
         'Billing_Cycle_End__c': iso_format_unix_timestamp(data['current_period_end']),
         'CloseDate': iso_format_unix_timestamp(data.get('close_date', time())),
         'Donation_Contact__c': user_data['id'],
@@ -897,7 +898,6 @@ def process_subhub_event_subscription_updated(data):
         'Payment_Source__c': 'Stripe',
         'PMT_Invoice_ID__c': data['invoice_id'],
         'PMT_Subscription_ID__c': data['subscription_id'],
-        'Proration_Amount__c': data['proration_amount'],
         'RecordTypeId': settings.SUBHUB_OPP_RECORD_TYPE,
         'Service_Plan__c': data['nickname_new'],
         'Nickname_Old__c': data['nickname_old'],


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
@@ -320,7 +320,7 @@ def test_upgrade(self, sfdc_mock, gud_mock):
             'Payment_Source__c': 'Stripe',
             'PMT_Invoice_ID__c': data['invoice_id'],
             'PMT_Subscription_ID__c': data['subscription_id'],
-            'Proration_Amount__c': data['proration_amount'],
+            'Proration_Amount__c': 0.05,
             'RecordTypeId': settings.SUBHUB_OPP_RECORD_TYPE,
             'Service_Plan__c': data['nickname_new'],
             'Nickname_Old__c': data['nickname_old'],
@@ -345,7 +345,7 @@ def test_downgrade(self, sfdc_mock, gud_mock):
             'Payment_Source__c': 'Stripe',
             'PMT_Invoice_ID__c': data['invoice_id'],
             'PMT_Subscription_ID__c': data['subscription_id'],
-            'Proration_Amount__c': data['proration_amount'],
+            'Proration_Amount__c': 0.05,
             'RecordTypeId': settings.SUBHUB_OPP_RECORD_TYPE,
             'Service_Plan__c': data['nickname_new'],
             'Nickname_Old__c': data['nickname_old'],