diff --git a/djconnectwise/__init__.py b/djconnectwise/__init__.py index 4903e64..28dedfa 100644 --- a/djconnectwise/__init__.py +++ b/djconnectwise/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -VERSION = (1, 5, 9, 'final') +VERSION = (1, 6, 0, 'final') # pragma: no cover if VERSION[-1] != "final": diff --git a/djconnectwise/admin.py b/djconnectwise/admin.py index 9f9f994..4beeb1d 100644 --- a/djconnectwise/admin.py +++ b/djconnectwise/admin.py @@ -107,10 +107,16 @@ class CompanyStatusAdmin(admin.ModelAdmin): @admin.register(models.CompanyType) class CompanyTypeAdmin(admin.ModelAdmin): - list_display = ('id', 'name', 'vendor_flag') + list_display = ('id', 'name', 'vendor_flag', 'service_alert_flag') search_fields = ['name'] +@admin.register(models.ContactType) +class ContactTypeAdmin(admin.ModelAdmin): + list_display = ('id', 'description', 'default_flag', 'service_alert_flag') + search_fields = ['description'] + + @admin.register(models.Contact) class ContactAdmin(admin.ModelAdmin): list_display = ('id', 'full_name') diff --git a/djconnectwise/api.py b/djconnectwise/api.py index a3b5796..c44b2da 100644 --- a/djconnectwise/api.py +++ b/djconnectwise/api.py @@ -499,6 +499,7 @@ class CompanyAPIClient(ConnectWiseAPIClient): ENDPOINT_CONTACTS = 'contacts' ENDPOINT_COMPANY_STATUSES = '{}/statuses'.format(ENDPOINT_COMPANIES) ENDPOINT_COMPANY_TYPES = '{}/types'.format(ENDPOINT_COMPANIES) + ENDPOINT_CONTACT_TYPES = '{}/types'.format(ENDPOINT_CONTACTS) ENDPOINT_COMPANY_COMMUNICATION_TYPES = 'communicationTypes' ENDPOINT_CONTACT_COMMUNICATIONs = 'communications' ENDPOINT_COMPANY_NOTE_TYPES = 'noteTypes' @@ -550,6 +551,11 @@ def get_contacts(self, *args, **kwargs): return self.fetch_resource(self.ENDPOINT_CONTACTS, should_page=True, *args, **kwargs) + def get_contact_types(self, *args, **kwargs): + return self.fetch_resource(self.ENDPOINT_CONTACT_TYPES, + should_page=True, + *args, **kwargs) + def get_contact_communications(self, contact_id, *args, **kwargs): endpoint_url = '{}/{}/communications'.format(self.ENDPOINT_CONTACTS, contact_id) diff --git a/djconnectwise/management/commands/cwsync.py b/djconnectwise/management/commands/cwsync.py index f5bf881..73be7e9 100644 --- a/djconnectwise/management/commands/cwsync.py +++ b/djconnectwise/management/commands/cwsync.py @@ -55,6 +55,7 @@ def __init__(self, *args, **kwargs): ('communication_type', sync.CommunicationTypeSynchronizer, _('Communication Type')), ('contact', sync.ContactSynchronizer, _('Contact')), + ('contact_type', sync.ContactTypeSynchronizer, _('Contact Type')), ('contact_communication', sync.ContactCommunicationSynchronizer, _('Contact Communication')), diff --git a/djconnectwise/migrations/0183_companytype_default_flag_and_more.py b/djconnectwise/migrations/0183_companytype_default_flag_and_more.py new file mode 100644 index 0000000..019c1b1 --- /dev/null +++ b/djconnectwise/migrations/0183_companytype_default_flag_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.11 on 2024-06-05 16:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('djconnectwise', '0182_alter_companysite_inactive'), + ] + + operations = [ + migrations.AddField( + model_name='companytype', + name='default_flag', + field=models.BooleanField(blank=True, null=True), + ), + migrations.AddField( + model_name='companytype', + name='service_alert_flag', + field=models.BooleanField(blank=True, null=True), + ), + migrations.AddField( + model_name='companytype', + name='service_alert_message', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/djconnectwise/migrations/0184_contacttype_contacttypetracker.py b/djconnectwise/migrations/0184_contacttype_contacttypetracker.py new file mode 100644 index 0000000..4895cf3 --- /dev/null +++ b/djconnectwise/migrations/0184_contacttype_contacttypetracker.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2.11 on 2024-06-05 16:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('djconnectwise', '0183_companytype_default_flag_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='ContactType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('description', models.CharField(max_length=250)), + ('default_flag', models.BooleanField()), + ('service_alert_flag', models.BooleanField(blank=True, null=True)), + ('service_alert_message', models.TextField(blank=True, null=True)), + ], + options={ + 'ordering': ('description',), + }, + ), + migrations.CreateModel( + name='ContactTypeTracker', + fields=[ + ], + options={ + 'db_table': 'djconnectwise_contacttype', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('djconnectwise.contacttype',), + ), + ] diff --git a/djconnectwise/migrations/0185_contact_type.py b/djconnectwise/migrations/0185_contact_type.py new file mode 100644 index 0000000..d26b49f --- /dev/null +++ b/djconnectwise/migrations/0185_contact_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.11 on 2024-06-05 20:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('djconnectwise', '0184_contacttype_contacttypetracker'), + ] + + operations = [ + migrations.AddField( + model_name='contact', + name='type', + field=models.ManyToManyField(to='djconnectwise.contacttype'), + ), + ] diff --git a/djconnectwise/models.py b/djconnectwise/models.py index d177fe4..e5fe612 100644 --- a/djconnectwise/models.py +++ b/djconnectwise/models.py @@ -426,6 +426,11 @@ def __str__(self): class CompanyType(models.Model): name = models.CharField(max_length=50) vendor_flag = models.BooleanField() + default_flag = models.BooleanField(blank=True, null=True) + service_alert_flag = models.BooleanField(blank=True, null=True) + service_alert_message = models.TextField( + blank=True, null=True + ) class Meta: ordering = ('name', ) @@ -434,6 +439,22 @@ def __str__(self): return self.name +class ContactType(models.Model): + description = models.CharField(max_length=250) + default_flag = models.BooleanField() + service_alert_flag = models.BooleanField( + blank=True, null=True) + service_alert_message = models.TextField( + blank=True, null=True + ) + + class Meta: + ordering = ('description', ) + + def __str__(self): + return self.description + + class CompanyNoteType(models.Model): name = models.CharField(max_length=50) identifier = models.CharField(max_length=50) @@ -509,6 +530,7 @@ def __str__(self): title = models.CharField(blank=True, null=True, max_length=200) company = models.ForeignKey( 'Company', null=True, on_delete=models.CASCADE) + type = models.ManyToManyField('ContactType') class Meta: ordering = ('first_name', 'last_name') @@ -2375,6 +2397,14 @@ class Meta: db_table = 'djconnectwise_companytype' +class ContactTypeTracker(ContactType): + tracker = FieldTracker() + + class Meta: + proxy = True + db_table = 'djconnectwise_contacttype' + + class CompanyNoteTypeTracker(CompanyNoteType): tracker = FieldTracker() diff --git a/djconnectwise/sync.py b/djconnectwise/sync.py index b15ea7a..82fb7f1 100644 --- a/djconnectwise/sync.py +++ b/djconnectwise/sync.py @@ -1227,11 +1227,29 @@ def _assign_field_data(self, instance, json_data): instance.id = json_data.get('id') instance.name = json_data.get('name') instance.vendor_flag = json_data.get('vendorFlag') + instance.default_flag = json_data.get('defaultFlag') + instance.service_alert_flag = json_data.get('service_alert_flag') + instance.service_alert_message = json_data.get('service_alert_message') def get_page(self, *args, **kwargs): return self.client.get_company_types(*args, **kwargs) +class ContactTypeSynchronizer(Synchronizer): + client_class = api.CompanyAPIClient + model_class = models.ContactTypeTracker + + def _assign_field_data(self, instance, json_data): + instance.id = json_data.get('id') + instance.description = json_data.get('description') + instance.default_flag = json_data.get('defaultFlag') + instance.service_alert_flag = json_data.get('service_alert_flag') + instance.service_alert_message = json_data.get('service_alert_message') + + def get_page(self, *args, **kwargs): + return self.client.get_contact_types(*args, **kwargs) + + class CompanyNoteTypesSynchronizer(Synchronizer): client_class = api.CompanyAPIClient model_class = models.CompanyNoteTypeTracker diff --git a/djconnectwise/tests/fixture_utils.py b/djconnectwise/tests/fixture_utils.py index cbfb0db..97cd913 100644 --- a/djconnectwise/tests/fixture_utils.py +++ b/djconnectwise/tests/fixture_utils.py @@ -198,6 +198,12 @@ def init_company_site(): return synchronizer.sync() +def init_contact_type(): + mocks.company_api_get_contact_type_call(fixtures.API_CONTACT_TYPE_LIST) + synchronizer = sync.ContactTypeSynchronizer() + return synchronizer.sync() + + def init_company_team_role(): mocks.company_api_get_company_team_role_call( [fixtures.API_COMPANY_TEAM_ROLE]) diff --git a/djconnectwise/tests/fixtures.py b/djconnectwise/tests/fixtures.py index 8d33b6a..db36f7c 100644 --- a/djconnectwise/tests/fixtures.py +++ b/djconnectwise/tests/fixtures.py @@ -479,6 +479,16 @@ "inactive": False, }] +API_CONTACT_TYPE_LIST = [ + { + "id": 1, + "description": "Test Contact Type", + "defaultFlag": True, + "serviceAlertFlag": True, + "serviceAlertMessage": "", +}] + + API_SERVICE_NOTE_LIST = [ { 'id': 3, diff --git a/djconnectwise/tests/mocks.py b/djconnectwise/tests/mocks.py index 8162080..40d1e1d 100644 --- a/djconnectwise/tests/mocks.py +++ b/djconnectwise/tests/mocks.py @@ -73,6 +73,11 @@ def company_api_get_company_site_call(return_value, raised=None): return create_mock_call(method_name, return_value, side_effect=raised) +def company_api_get_contact_type_call(return_value, raised=None): + method_name = 'djconnectwise.api.CompanyAPIClient.get_contact_types' + return create_mock_call(method_name, return_value, side_effect=raised) + + def company_api_get_company_team_role_call(return_value, raised=None): method_name = 'djconnectwise.api.CompanyAPIClient.get_company_team_role' return create_mock_call(method_name, return_value, side_effect=raised) diff --git a/djconnectwise/tests/test_commands.py b/djconnectwise/tests/test_commands.py index 39cbdf5..5129744 100644 --- a/djconnectwise/tests/test_commands.py +++ b/djconnectwise/tests/test_commands.py @@ -200,6 +200,18 @@ def setUp(self): fixture_utils.init_company_site() +class TestSyncContactTypeCommand(AbstractBaseSyncTest, TestCase): + args = ( + mocks.company_api_get_contact_type_call, + fixtures.API_CONTACT_TYPE_LIST, + 'contact_type' + ) + + def setUp(self): + fixture_utils.init_companies() + fixture_utils.init_contact_type() + + class TestSyncCompanyTeamRoleCommand(AbstractBaseSyncTest, TestCase): args = ( mocks.company_api_get_company_team_role_call, @@ -813,6 +825,7 @@ def setUp(self): TestSyncTimeEntriesCommand, TestSyncContactCommunicationsCommand, TestSyncContactsCommand, + TestSyncContactTypeCommand, TestSyncCommunicationTypesCommand, TestSyncCompaniesCommand, TestSyncCompanyTypesCommand, @@ -942,6 +955,7 @@ def test_full_sync(self): 'service_note': models.ServiceNote, 'opportunity_note': models.OpportunityNote, 'contact': models.Contact, + 'contact_type': models.ContactType, 'communication_type': models.CommunicationType, 'contact_communication': models.ContactCommunication, 'company': models.Company, @@ -1003,7 +1017,7 @@ def test_full_sync(self): 'holiday', 'contact', 'contact_communication', - 'company_site' + 'company_site', ): # Assert that there were objects to get deleted, then change # to zero to verify the output formats correctly.