diff --git a/salesforce/admin.py b/salesforce/admin.py index fede48b24..8f87e6aa7 100644 --- a/salesforce/admin.py +++ b/salesforce/admin.py @@ -24,9 +24,9 @@ def has_add_permission(self, request): return False class AdoptionOpportunityRecordAdmin(admin.ModelAdmin): - list_display = ['email', 'book_name', 'school', 'yearly_students', 'verified'] - list_filter = ('book_name', 'school', 'verified', 'last_update') - search_fields = ['email', 'account_id'] + list_display = ['account_uuid', 'book_name', 'fall_student_number', 'spring_student_number', 'summer_student_number'] + list_filter = ('book_name', 'created') + search_fields = ['account_uuid', ] def has_add_permission(self, request): return False diff --git a/salesforce/management/commands/update_opportunities.py b/salesforce/management/commands/update_opportunities.py index f776b3be6..369657ea2 100644 --- a/salesforce/management/commands/update_opportunities.py +++ b/salesforce/management/commands/update_opportunities.py @@ -4,7 +4,7 @@ from salesforce.salesforce import Salesforce class Command(BaseCommand): - help = "update book adoptions from salesforce.com for getting adoptions by account id" + help = "update book adoptions from salesforce.com for getting adoptions by account uuid" def handle(self, *args, **options): with Salesforce() as sf: @@ -14,55 +14,30 @@ def handle(self, *args, **options): if now.month < 7: # Salesforce needs the school base year, this is how they calculate it year = year - 1 - #first, we need to upload any records that have been updated - adoptions_num_updated = 0 - - adoptions = AdoptionOpportunityRecord.objects.filter(verified=True) - data = [] - for adoption in adoptions: - data_dict_item = { - 'Id': adoption.opportunity_id, - 'CloseDate': adoption.last_update.strftime('%Y-%m-%d'), - 'Type': 'Renewal - Verified' - } - data.append(data_dict_item) - results = sf.bulk.Opportunity.update(data) - for result in results: - if result['success']: # we don't need to store these anymore, they are in SF now with a new opportunity type (so we don't get them in the next step) - adoptions_num_updated = adoptions_num_updated + 1 - adoptions.get(opportunity_id=result['id']).delete() + # truncate the table + AdoptionOpportunityRecord.objects.all().delete() # then we will get any new records - command = "SELECT Id, Accounts_UUID__c, Book_Text__c, Contact_Email__c, School_Name__c, Yearly_Students__c, Students__c, Type, Base_Year__c, IsWon from Opportunity WHERE OS_Accounts_ID__c != null AND Type = 'Renewal' AND Base_Year__c = {} AND IsWon = True".format(year) + command = "SELECT Id, Accounts_UUID__c, Book_Text__c, Base_Year__c, IsWon, Fall_Students__c, Spring_Students__c, Summer_Students__c from Opportunity WHERE Accounts_UUID__c != null AND Base_Year__c = {} AND IsWon = True".format(year) response = sf.query_all(command) records = response['records'] num_created = 0 - num_updated = 0 for record in records: - try: - opportunity = AdoptionOpportunityRecord.objects.get(opportunity_id=record['Id']) - opportunity.account_uuid = record['Accounts_UUID__c'] - opportunity.book_name = record['Book_Text__c'] - opportunity.email = record['Contact_Email__c'] - opportunity.school = record['School_Name__c'] - opportunity.yearly_students = record['Yearly_Students__c'] - num_updated = num_updated + 1 - except AdoptionOpportunityRecord.DoesNotExist: - opportunity = AdoptionOpportunityRecord.objects.create( - opportunity_id=record['Id'], - account_uuid=record['Accounts_UUID__c'], - book_name= record['Book_Text__c'], - email= record['Contact_Email__c'], - school= record['School_Name__c'], - yearly_students= record['Yearly_Students__c'] + opportunity, created = AdoptionOpportunityRecord.objects.update_or_create( + opportunity_id=record['Id'], + defaults = {'account_uuid': record['Accounts_UUID__c'], + 'book_name': record['Book_Text__c'], + 'fall_student_number': record['Fall_Students__c'], + 'spring_student_number': record['Spring_Students__c'], + 'summer_student_number': record['Summer_Students__c'], + } ) - num_created = num_created + 1 - - opportunity.save() + if created: + num_created = num_created + 1 - response = self.style.SUCCESS("Successfully updated opportunity records. {} were newly created and {} were updated. {} were synced with Salesforce".format(num_created, num_updated, adoptions_num_updated)) + response = self.style.SUCCESS("Successfully updated opportunity records. {} were newly created.".format(num_created)) self.stdout.write(response) diff --git a/salesforce/migrations/0102_auto_20220512_0814.py b/salesforce/migrations/0102_auto_20220512_0814.py new file mode 100644 index 000000000..78133f657 --- /dev/null +++ b/salesforce/migrations/0102_auto_20220512_0814.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.5 on 2022-05-12 13:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('salesforce', '0101_merge_20220506_1353'), + ] + + operations = [ + migrations.AddField( + model_name='adoptionopportunityrecord', + name='fall_student_number', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='adoptionopportunityrecord', + name='spring_student_number', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='adoptionopportunityrecord', + name='summer_student_number', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/salesforce/migrations/0103_auto_20220516_1045.py b/salesforce/migrations/0103_auto_20220516_1045.py new file mode 100644 index 000000000..5c26716bc --- /dev/null +++ b/salesforce/migrations/0103_auto_20220516_1045.py @@ -0,0 +1,41 @@ +# Generated by Django 3.2.5 on 2022-05-16 15:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('salesforce', '0102_auto_20220512_0814'), + ] + + operations = [ + migrations.RemoveField( + model_name='adoptionopportunityrecord', + name='account_id', + ), + migrations.RemoveField( + model_name='adoptionopportunityrecord', + name='confirmed_yearly_students', + ), + migrations.RemoveField( + model_name='adoptionopportunityrecord', + name='email', + ), + migrations.RemoveField( + model_name='adoptionopportunityrecord', + name='last_update', + ), + migrations.RemoveField( + model_name='adoptionopportunityrecord', + name='school', + ), + migrations.RemoveField( + model_name='adoptionopportunityrecord', + name='verified', + ), + migrations.RemoveField( + model_name='adoptionopportunityrecord', + name='yearly_students', + ), + ] diff --git a/salesforce/models.py b/salesforce/models.py index e48113ff6..c02f32134 100644 --- a/salesforce/models.py +++ b/salesforce/models.py @@ -23,16 +23,12 @@ def __str__(self): class AdoptionOpportunityRecord(models.Model): opportunity_id = models.CharField(max_length=255, unique=True) - account_id = models.CharField(max_length=255, null=True, blank=True) # TODO: for deletion after switching to UUID account_uuid = models.UUIDField(null=True) book_name = models.CharField(max_length=255) - email = models.EmailField(null=True, blank=True) - school = models.CharField(max_length=255) - yearly_students = models.CharField(max_length=255) - confirmed_yearly_students = models.IntegerField(null=True, blank=True,) created = models.DateTimeField(auto_now_add=True) - last_update = models.DateTimeField(auto_now=True) - verified = models.BooleanField(default=False) + fall_student_number = models.IntegerField(null=True, blank=True,) + spring_student_number = models.IntegerField(null=True, blank=True,) + summer_student_number = models.IntegerField(null=True, blank=True,) def __str__(self): return self.opportunity_id diff --git a/salesforce/serializers.py b/salesforce/serializers.py index 8841c632a..7b3475ae8 100644 --- a/salesforce/serializers.py +++ b/salesforce/serializers.py @@ -41,24 +41,14 @@ class Meta: model = AdoptionOpportunityRecord fields = ('id', 'opportunity_id', - 'account_id', + 'account_uuid', 'book_name', - 'email', - 'school', - 'yearly_students', - 'confirmed_yearly_students', - 'verified', - 'created', - 'last_update') + 'created') read_only_fields = ('id', 'opportunity_id', - 'account_id', + 'account_uuid', 'book_name', - 'email', - 'school', - 'yearly_students', 'created', - 'last_update', ) class PartnerSerializer(serializers.ModelSerializer): diff --git a/salesforce/tests.py b/salesforce/tests.py index b16e04149..38af1d545 100644 --- a/salesforce/tests.py +++ b/salesforce/tests.py @@ -3,7 +3,7 @@ from django.conf import settings from django.core.management import call_command -from django.test import LiveServerTestCase, TestCase +from django.test import LiveServerTestCase, TestCase, Client from six import StringIO from django.core.exceptions import ValidationError @@ -165,3 +165,20 @@ def test_mapbox_setting_creation(self): setting = self.create_mapbox_setting() self.assertTrue(isinstance(setting, MapBoxDataset)) self.assertEqual(setting.__str__(), setting.name) + + +class AdoptionOpportunityTest(TestCase): + def setUp(self): + self.client = Client() + self.opportunity = AdoptionOpportunityRecord(opportunity_id='0066f000015SSy5AAG', + book_name='US History', + account_uuid='f826f1b1-ead5-4594-82b3-df9a2753cb43', + fall_student_number=123, + spring_student_number=75, + summer_student_number=None) + self.opportunity.save() + + def test_query_opportunity_by_account_uuid(self): + response = self.client.get('/apps/cms/api/salesforce/renewal?account_uuid=f826f1b1-ead5-4594-82b3-df9a2753cb43') + self.assertIn(b'"students": "123"', response.content) + diff --git a/salesforce/urls.py b/salesforce/urls.py index 6edfaee03..8a4ee63a3 100644 --- a/salesforce/urls.py +++ b/salesforce/urls.py @@ -12,6 +12,6 @@ urlpatterns = [ url(r'', include(router.urls)), url(r'^adoption-status/', views.get_adoption_status), - url(r'renewal/(?P\d+)/', views.AdoptionOpportunityRecordViewSet.as_view({'get': 'list', 'post': 'post'})), + url(r'^renewal/', views.AdoptionOpportunityRecordViewSet.as_view({'get': 'list'})), url(r'reviews/', views.PartnerReviewViewSet.as_view({'get': 'list', 'post': 'post', 'patch': 'patch', 'delete': 'delete'})), ] diff --git a/salesforce/views.py b/salesforce/views.py index 775c2e2fc..b20a26c42 100644 --- a/salesforce/views.py +++ b/salesforce/views.py @@ -123,40 +123,17 @@ def list(self, request, *args, **kwargs): class AdoptionOpportunityRecordViewSet(viewsets.ViewSet): @action(methods=['get'], detail=True) - def list(self, request, account_id): + def list(self, request): + account_uuid = request.GET.get('account_uuid', False) # a user can have many adoption records - one for each book - queryset = AdoptionOpportunityRecord.objects.filter(account_id=account_id, verified=False) - serializer = AdoptionOpportunityRecordSerializer(queryset, many=True) - return Response(serializer.data) - - @action(methods=['post'], detail=True) - def post(self, request, account_id, format=None): - # this takes the adoption record as a post and looks it up, since a user can adopt more than one book - records = AdoptionOpportunityRecord.objects.filter(account_id=account_id) - if not records: - return JsonResponse({'error': 'No records associated with that ID.'}) - - # adoption id is included in the post request - id = self.request.data.get('id', None) - if id: - try: - record = AdoptionOpportunityRecord.objects.get(id=id) - except AdoptionOpportunityRecord.DoesNotExist: - return JsonResponse({'error': 'Invalid adoption id.'}) - - confirmed_yearly_students = self.request.data.get('confirmed_yearly_students', 0) - data = {"verified": True, "confirmed_yearly_students": confirmed_yearly_students} - - serializer = AdoptionOpportunityRecordSerializer(record, data=data, partial=True) - - if serializer.is_valid(): - serializer.save() - else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - return JsonResponse(data) - else: - return JsonResponse({'error': 'Must include adoption ID to update'}) + queryset = AdoptionOpportunityRecord.objects.filter(account_uuid=account_uuid) + book_list = [] + for record in queryset: + student_nums = [record.fall_student_number or 0, record.spring_student_number or 0, record.summer_student_number or 0] + book_list.append({"name": record.book_name , "students": str(max(student_nums))}) + data = {"Books": book_list} + + return JsonResponse(data) def get_adoption_status(request):