From 25a47b61f33ff650ef23510419bdecac100b980c Mon Sep 17 00:00:00 2001 From: Michael Volo Date: Tue, 22 Oct 2024 09:53:17 -0600 Subject: [PATCH] Fix renewal form endpoint (#1585) * fix adoption query for renewal form * add some useful fields * remove old adoption methods and crons * remove limit used for testing * fix a test and a view * do not get records for users that are current adopters * base year cutoff june 30 * variable name cleanup * remove limit... again * fix base_year calculation * don't truncate table, manually running job --- openstax/settings/base.py | 2 +- salesforce/admin.py | 21 +++++- .../management/commands/update_adopters.py | 31 --------- .../commands/update_opportunities.py | 54 ++++++++++----- ...nityrecord_fall_student_number_and_more.py | 65 +++++++++++++++++++ salesforce/models.py | 11 +++- salesforce/tests.py | 4 +- salesforce/views.py | 17 +---- 8 files changed, 132 insertions(+), 73 deletions(-) delete mode 100644 salesforce/management/commands/update_adopters.py create mode 100644 salesforce/migrations/0112_remove_adoptionopportunityrecord_fall_student_number_and_more.py diff --git a/openstax/settings/base.py b/openstax/settings/base.py index 3486f583d..cb633b69f 100644 --- a/openstax/settings/base.py +++ b/openstax/settings/base.py @@ -259,7 +259,7 @@ ('0 2 * * *', 'django.core.management.call_command', ['delete_resource_downloads']), ('0 6 * * *', 'django.core.management.call_command', ['update_resource_downloads']), ('0 0 8 * *', 'django.core.management.call_command', ['update_schools_and_mapbox']), - ('0 9 * * *', 'django.core.management.call_command', ['update_opportunities']), + # ('0 0 1 * *', 'django.core.management.call_command', ['update_opportunities']), ('0 10 * * *', 'django.core.management.call_command', ['update_partners']), ] diff --git a/salesforce/admin.py b/salesforce/admin.py index 3746bdc78..c47fcf80d 100644 --- a/salesforce/admin.py +++ b/salesforce/admin.py @@ -22,10 +22,25 @@ class SchoolAdmin(admin.ModelAdmin): def has_add_permission(self, request): return False + class AdoptionOpportunityRecordAdmin(admin.ModelAdmin): - list_display = ['account_uuid', 'book_name', 'fall_student_number', 'spring_student_number', 'summer_student_number'] - list_filter = ('book_name', 'created') - search_fields = ['account_uuid', ] + list_display = ['account_uuid', 'book_name', 'students', 'savings'] + list_filter = ('book_name', 'created', 'opportunity_stage') + search_fields = ['account_uuid', 'opportunity_id'] + readonly_fields = [ + 'opportunity_id', + 'opportunity_stage', + 'account_uuid', + 'adoption_type', + 'base_year', + 'confirmation_date', + 'confirmation_type', + 'how_using', + 'savings', + 'students', + 'book_name', + 'created' + ] def has_add_permission(self, request): return False diff --git a/salesforce/management/commands/update_adopters.py b/salesforce/management/commands/update_adopters.py deleted file mode 100644 index 7b30ae010..000000000 --- a/salesforce/management/commands/update_adopters.py +++ /dev/null @@ -1,31 +0,0 @@ -from django.core.management.base import BaseCommand -from salesforce.models import Adopter -from salesforce.salesforce import Salesforce - - -class Command(BaseCommand): - help = "update adopters from salesforce.com" - - def handle(self, *args, **options): - with Salesforce() as sf: - command = "SELECT Id, Name, Description, Website FROM Account "\ - "WHERE All_Time_Students2__c > 0 and Id != '001U0000011KxWa'" - response = sf.query_all(command) - sf_adopters = response['records'] - - if sf_adopters: - Adopter.objects.all().delete() - - for sf_adopter in sf_adopters: - - adopter, created = Adopter.objects.update_or_create( - sales_id=sf_adopter['Id'], - defaults={'name': sf_adopter['Name'], - 'description': sf_adopter['Description'], - 'website': sf_adopter['Website'], - }, - ) - - adopter.save() - response = self.style.SUCCESS("Successfully updated adopters") - self.stdout.write(response) diff --git a/salesforce/management/commands/update_opportunities.py b/salesforce/management/commands/update_opportunities.py index 905bad3a3..5b92b2b1d 100644 --- a/salesforce/management/commands/update_opportunities.py +++ b/salesforce/management/commands/update_opportunities.py @@ -3,6 +3,7 @@ from salesforce.models import AdoptionOpportunityRecord from salesforce.salesforce import Salesforce + class Command(BaseCommand): help = "update book adoptions from salesforce.com for getting adoptions by account uuid" @@ -10,31 +11,48 @@ def handle(self, *args, **options): with Salesforce() as sf: now = datetime.datetime.now() - year = now.year - if now.month < 7: # Salesforce needs the school base year, this is how they calculate it - year = year - 1 - + base_year = now.year + if now.month < 7: # if it's before July, the base year is the previous year (4/1/2024 = base_year 2023) + base_year -= 1 + # TODO: I don't think this is needed - updating the records should be fine, and keeps something on the form + # TODO: for the user. Eventually, this should update CMS DB with updated data if they fill out the form # truncate the table - AdoptionOpportunityRecord.objects.all().delete() + # AdoptionOpportunityRecord.objects.all().delete() # then we will get any new records - command = "SELECT Id, Contact__r.Accounts_UUID__c, Book__r.Name, Base_Year__c, IsWon from Opportunity WHERE Contact__r.Accounts_UUID__c != null AND Base_Year__c = {} AND IsWon = True".format(year) - - response = sf.query_all(command) + query = ("SELECT Id, " + "Adoption_Type__c, " + "Base_Year__c, " + "Confirmation_Date__c, " + "Confirmation_Type__c, " + "How_Using__c, " + "Savings__c, " + "Students__c, " + "Opportunity__r.Book__r.Name, " + "Opportunity__r.StageName, " + "Opportunity__r.Contact__r.Accounts_UUID__c " + "FROM Adoption__c WHERE " + "Base_Year__c = {} AND Opportunity__r.Contact__r.Accounts_UUID__c != null " + "AND Confirmation_Type__c = 'OpenStax Confirmed Adoption' " + "AND Opportunity__r.Contact__r.Adoption_Status__c != 'Current Adopter'").format(base_year) + + response = sf.query(query) records = response['records'] - num_created = 0 for record in records: opportunity, created = AdoptionOpportunityRecord.objects.update_or_create( opportunity_id=record['Id'], - defaults = {'account_uuid': record['Contact__r']['Accounts_UUID__c'], - 'book_name': record['Book__r']['Name'], - } - ) + defaults={'account_uuid': record['Opportunity__r']['Contact__r']['Accounts_UUID__c'], + 'opportunity_stage': record['Opportunity__r']['StageName'], + 'adoption_type': record['Adoption_Type__c'], + 'base_year': record['Base_Year__c'], + 'confirmation_date': record['Confirmation_Date__c'], + 'confirmation_type': record['Confirmation_Type__c'], + 'how_using': record['How_Using__c'], + 'savings': record['Savings__c'], + 'students': record['Students__c'], + 'book_name': record['Opportunity__r']['Book__r']['Name'], + } + ) opportunity.save() - if created: - num_created = num_created + 1 - - response = self.style.SUCCESS("Successfully updated opportunity records. {} were newly created.".format(num_created)) - self.stdout.write(response) diff --git a/salesforce/migrations/0112_remove_adoptionopportunityrecord_fall_student_number_and_more.py b/salesforce/migrations/0112_remove_adoptionopportunityrecord_fall_student_number_and_more.py new file mode 100644 index 000000000..e0c130455 --- /dev/null +++ b/salesforce/migrations/0112_remove_adoptionopportunityrecord_fall_student_number_and_more.py @@ -0,0 +1,65 @@ +# Generated by Django 5.1.1 on 2024-10-17 15:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("salesforce", "0111_alter_partner_partner_sf_account_id_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="adoptionopportunityrecord", + name="fall_student_number", + ), + migrations.RemoveField( + model_name="adoptionopportunityrecord", + name="spring_student_number", + ), + migrations.RemoveField( + model_name="adoptionopportunityrecord", + name="summer_student_number", + ), + migrations.AddField( + model_name="adoptionopportunityrecord", + name="adoption_type", + field=models.CharField(max_length=255, null=True), + ), + migrations.AddField( + model_name="adoptionopportunityrecord", + name="base_year", + field=models.IntegerField(null=True), + ), + migrations.AddField( + model_name="adoptionopportunityrecord", + name="confirmation_date", + field=models.DateField(null=True), + ), + migrations.AddField( + model_name="adoptionopportunityrecord", + name="confirmation_type", + field=models.CharField(max_length=255, null=True), + ), + migrations.AddField( + model_name="adoptionopportunityrecord", + name="how_using", + field=models.CharField(max_length=255, null=True), + ), + migrations.AddField( + model_name="adoptionopportunityrecord", + name="opportunity_stage", + field=models.CharField(max_length=255, null=True), + ), + migrations.AddField( + model_name="adoptionopportunityrecord", + name="savings", + field=models.DecimalField(decimal_places=2, max_digits=10, null=True), + ), + migrations.AddField( + model_name="adoptionopportunityrecord", + name="students", + field=models.IntegerField(null=True), + ), + ] diff --git a/salesforce/models.py b/salesforce/models.py index 3e7d4a214..6ae2c85a5 100644 --- a/salesforce/models.py +++ b/salesforce/models.py @@ -19,12 +19,17 @@ def __str__(self): class AdoptionOpportunityRecord(models.Model): opportunity_id = models.CharField(max_length=255, unique=True) + opportunity_stage = models.CharField(max_length=255, null=True) account_uuid = models.UUIDField(null=True) + adoption_type = models.CharField(max_length=255, null=True) + base_year = models.IntegerField(null=True) + confirmation_date = models.DateField(null=True) + confirmation_type = models.CharField(max_length=255, null=True) + how_using = models.CharField(max_length=255, null=True) + savings = models.DecimalField(null=True, decimal_places=2, max_digits=10) + students = models.IntegerField(null=True) book_name = models.CharField(max_length=255) created = models.DateTimeField(auto_now_add=True) - 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/tests.py b/salesforce/tests.py index 3a37c29fc..fad95d9aa 100644 --- a/salesforce/tests.py +++ b/salesforce/tests.py @@ -112,9 +112,7 @@ def setUp(self): 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) + students=123) self.opportunity.save() def test_query_opportunity_by_account_uuid(self): diff --git a/salesforce/views.py b/salesforce/views.py index 8012009ef..a06a6cef7 100644 --- a/salesforce/views.py +++ b/salesforce/views.py @@ -53,23 +53,12 @@ class AdoptionOpportunityRecordViewSet(viewsets.ViewSet): def list(self, request): account_uuid = request.GET.get('account_uuid', False) # a user can have many adoption records - one for each book + # 10/2024 - added new data that can be used on the form, will need coordination with the FE form + # see https://github.com/openstax/openstax-cms/pull/1585 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))}) + book_list.append({"name": record.book_name , "students": str(record.students)}) data = {"Books": book_list} return JsonResponse(data) - - -def get_adoption_status(request): - account = request.GET.get('id', False) - - if account: - with Salesforce() as sf: - q = sf.query("SELECT Adoption_Status__c FROM Contact WHERE Accounts_ID__c = '{}'".format(account)) - - return JsonResponse(q) - else: - raise Http404('Must supply account id for adoption.')