Skip to content

Commit

Permalink
Merge pull request #224 from KerkhoffTechnologies/3694-add-contract-e…
Browse files Browse the repository at this point in the history
…xclusion-set-sync-and-constraints-for-work-types-on-tickets-and-tasks

[ISSUE-3694] add contract exclusion set sync and constraints for work types on tickets and tasks
  • Loading branch information
kti-sam authored Nov 1, 2024
2 parents adc81e2 + 885c7ad commit fc95b59
Show file tree
Hide file tree
Showing 13 changed files with 501 additions and 6 deletions.
2 changes: 1 addition & 1 deletion djautotask/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
VERSION = (1, 6, 8, 'final')
VERSION = (1, 6, 9, 'final')

# pragma: no cover
if VERSION[-1] != "final":
Expand Down
18 changes: 17 additions & 1 deletion djautotask/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,8 @@ class ResourceServiceDeskRoleAdmin(admin.ModelAdmin):

@admin.register(models.Contract)
class ContractAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'number', 'account', 'status_name')
list_display = ('id', 'name', 'number', 'account', 'status_name',
'contract_exclusion_set_id')
search_fields = ('id', 'name', 'number')
list_filter = ('status',)

Expand Down Expand Up @@ -402,3 +403,18 @@ class ProjectNoteTypeAdmin(admin.ModelAdmin):
@admin.register(models.CompanyAlert)
class CompanyAlertsAdmin(admin.ModelAdmin):
list_display = ('id', 'alert_text', 'alert_type', 'account')


@admin.register(models.ContractExclusionSet)
class ContractExclusionSetAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'is_active')


@admin.register(models.ContractExclusionSetExcludedWorkType)
class ContractExcludedWorkTypeAdmin(admin.ModelAdmin):
list_display = ('id', 'contract_exclusion_set', 'excluded_work_type')


@admin.register(models.ContractExclusionSetExcludedRole)
class ContractExcludedRoleAdmin(admin.ModelAdmin):
list_display = ('id', 'contract_exclusion_set', 'excluded_role')
12 changes: 12 additions & 0 deletions djautotask/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,18 @@ class ContractsAPIClient(AutotaskAPIClient):
API = 'Contracts'


class ContractExclusionSetAPIClient(AutotaskAPIClient):
API = 'ContractExclusionSets'


class ContractsExcludedWorkTypeAPIClient(AutotaskAPIClient):
API = 'ContractExclusionSetExcludedWorkTypes'


class ContractsExcludedRoleAPIClient(AutotaskAPIClient):
API = 'ContractExclusionSetExcludedRoles'


class AccountPhysicalLocationsAPIClient(AutotaskAPIClient):
API = 'CompanyLocations'

Expand Down
15 changes: 15 additions & 0 deletions djautotask/management/commands/atsync.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,21 @@ def __init__(self, *args, **kwargs):
('task_predecessor', sync.TaskPredecessorSynchronizer,
_('Task Predecessor')),
('contact', sync.ContactSynchronizer, _('Contact')),
(
'contract_exclusion_set',
sync.ContractExclusionSetSynchronizer,
_('Contract Exclusion Set')
),
(
'contract_excluded_work_type',
sync.ContractExcludedWorkTypeSynchronizer,
_('Contract Excluded Work Type')
),
(
'contract_excluded_role',
sync.ContractExcludedRoleSynchronizer,
_('Contract Excluded Role')
),
)
self.synchronizer_map = OrderedDict()
for name, synchronizer, obj_name in synchronizers:
Expand Down
90 changes: 90 additions & 0 deletions djautotask/migrations/0118_contractexclusionset_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Generated by Django 4.2.16 on 2024-10-30 23:14

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('djautotask', '0117_alter_tasknote_task_alter_ticketnote_ticket'),
]

operations = [
migrations.CreateModel(
name='ContractExclusionSet',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.TextField(blank=True, null=True)),
('is_active', models.BooleanField(default=True)),
('name', models.CharField(max_length=255)),
],
),
migrations.AddField(
model_name='contract',
name='contract_exclusion_set_id',
field=models.IntegerField(blank=True, null=True),
),
migrations.CreateModel(
name='ContractExclusionSetExcludedWorkType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('contract_exclusion_set', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djautotask.contractexclusionset')),
('excluded_work_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djautotask.billingcode')),
],
),
migrations.CreateModel(
name='ContractExclusionSetExcludedRole',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('contract_exclusion_set', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djautotask.contractexclusionset')),
('excluded_role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djautotask.role')),
],
),
migrations.AddField(
model_name='contractexclusionset',
name='excluded_roles',
field=models.ManyToManyField(related_name='excluded_role_sets', through='djautotask.ContractExclusionSetExcludedRole', to='djautotask.role'),
),
migrations.AddField(
model_name='contractexclusionset',
name='excluded_work_types',
field=models.ManyToManyField(related_name='excluded_work_type_sets', through='djautotask.ContractExclusionSetExcludedWorkType', to='djautotask.billingcode'),
),
migrations.CreateModel(
name='ContractExcludedRoleTracker',
fields=[
],
options={
'db_table': 'djautotask_contractexclusionsetrole',
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('djautotask.contractexclusionsetexcludedrole',),
),
migrations.CreateModel(
name='ContractExcludedWorkTypeTracker',
fields=[
],
options={
'db_table': 'djautotask_contractexclusionsetworktype',
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('djautotask.contractexclusionsetexcludedworktype',),
),
migrations.CreateModel(
name='ContractExclusionSetTracker',
fields=[
],
options={
'db_table': 'djautotask_contractexclusionset',
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('djautotask.contractexclusionset',),
),
]
14 changes: 14 additions & 0 deletions djautotask/migrations/0120_merge_20241101_1316.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 4.2.16 on 2024-11-01 13:16

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('djautotask', '0118_contractexclusionset_and_more'),
('djautotask', '0119_merge_20241024_1521'),
]

operations = [
]
64 changes: 63 additions & 1 deletion djautotask/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,10 +948,12 @@ class Contract(models.Model):
number = models.CharField(blank=True, null=True, max_length=50)
status = models.CharField(
max_length=20, blank=True, null=True, choices=STATUS_CHOICES)

account = models.ForeignKey(
'Account', blank=True, null=True, on_delete=models.SET_NULL
)
contract_exclusion_set_id = models.IntegerField(
blank=True, null=True
)

class Meta:
ordering = ('name',)
Expand All @@ -960,6 +962,42 @@ def __str__(self):
return self.name


class ContractExclusionSet(models.Model):
description = models.TextField(blank=True, null=True)
is_active = models.BooleanField(default=True)
name = models.CharField(max_length=255)
excluded_work_types = models.ManyToManyField(
'BillingCode', through='ContractExclusionSetExcludedWorkType',
related_name='excluded_work_type_sets'
)
excluded_roles = models.ManyToManyField(
'Role', through='ContractExclusionSetExcludedRole',
related_name='excluded_role_sets'
)

def __str__(self):
return self.name


class ContractExclusionSetExcludedWorkType(models.Model):
contract_exclusion_set = models.ForeignKey('ContractExclusionSet',
on_delete=models.CASCADE)
excluded_work_type = models.ForeignKey('BillingCode',
on_delete=models.CASCADE)

def __str__(self):
return str(self.id) or ''


class ContractExclusionSetExcludedRole(models.Model):
contract_exclusion_set = models.ForeignKey('ContractExclusionSet',
on_delete=models.CASCADE)
excluded_role = models.ForeignKey('Role', on_delete=models.CASCADE)

def __str__(self):
return str(self.id) or ''


class ServiceCall(TimeStampedModel):
description = models.TextField(blank=True, null=True, max_length=2000)
duration = models.DecimalField(
Expand Down Expand Up @@ -1463,6 +1501,30 @@ class Meta:
db_table = 'djautotask_contract'


class ContractExclusionSetTracker(ContractExclusionSet):
tracker = FieldTracker()

class Meta:
proxy = True
db_table = 'djautotask_contractexclusionset'


class ContractExcludedWorkTypeTracker(ContractExclusionSetExcludedWorkType):
tracker = FieldTracker()

class Meta:
proxy = True
db_table = 'djautotask_contractexclusionsetworktype'


class ContractExcludedRoleTracker(ContractExclusionSetExcludedRole):
tracker = FieldTracker()

class Meta:
proxy = True
db_table = 'djautotask_contractexclusionsetrole'


class ServiceCallTracker(ServiceCall):
tracker = FieldTracker()

Expand Down
72 changes: 72 additions & 0 deletions djautotask/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -1812,6 +1812,8 @@ def _assign_field_data(self, instance, object_data):
instance.name = object_data.get('contractName')
instance.number = object_data.get('contractNumber')
instance.status = str(object_data.get('status'))
instance.contract_exclusion_set_id = \
object_data.get('contractExclusionSetID')

self.set_relations(instance, object_data)

Expand Down Expand Up @@ -2003,6 +2005,76 @@ def active_ids(self):
return active_ids


class ContractExclusionSetSynchronizer(Synchronizer):
client_class = api.ContractExclusionSetAPIClient
model_class = models.ContractExclusionSetTracker
last_updated_field = None

def _assign_field_data(self, instance, object_data):
instance.id = object_data['id']
instance.name = object_data['name']
instance.is_active = object_data['isActive']
instance.description = object_data['description']

return instance


class ContractExcludedWorkTypeSynchronizer(BatchQueryMixin, Synchronizer):
client_class = api.ContractsExcludedWorkTypeAPIClient
model_class = models.ContractExcludedWorkTypeTracker
condition_field_name = 'contractExclusionSetID'
last_updated_field = None

related_meta = {
'contractExclusionSetID':
(models.ContractExclusionSet, 'contract_exclusion_set'),
'excludedWorkTypeID': (models.BillingCode, 'excluded_work_type')
}

def _assign_field_data(self, instance, object_data):
instance.id = object_data['id']
self.set_relations(instance, object_data)

return instance

@property
def active_ids(self):
active_ids = models.ContractExclusionSet.objects.exclude(
is_active=False
).values_list(
'id', flat=True
)
return active_ids


class ContractExcludedRoleSynchronizer(BatchQueryMixin, Synchronizer):
client_class = api.ContractsExcludedRoleAPIClient
model_class = models.ContractExcludedRoleTracker
condition_field_name = 'contractExclusionSetID'
last_updated_field = None

related_meta = {
'contractExclusionSetID':
(models.ContractExclusionSet, 'contract_exclusion_set'),
'excludedRoleID': (models.Role, 'excluded_role')
}

def _assign_field_data(self, instance, object_data):
instance.id = object_data['id']
self.set_relations(instance, object_data)

return instance

@property
def active_ids(self):
active_ids = models.ContractExclusionSet.objects.exclude(
is_active=False
).values_list(
'id', flat=True
)
return active_ids


class NoteTypeSynchronizer(PicklistSynchronizer):
# Ticket note types are including task note types, and there are other
# note types currently not used. e.g. project note types
Expand Down
25 changes: 25 additions & 0 deletions djautotask/tests/fixture_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,31 @@ def init_contracts():
return synchronizer.sync()


def init_contract_exclusion_sets():
models.ContractExclusionSet.objects.all().delete()
mocks.service_api_get_contract_exclusion_sets_call(
fixtures.API_CONTRACT_EXCLUSION_SET)
synchronizer = sync.ContractExclusionSetSynchronizer()
return synchronizer.sync()


def init_contract_exclusion_roles():
models.ContractExclusionSetExcludedRole.objects.all() \
.delete()
mocks.service_api_get_contract_excluded_roles_call(
fixtures.API_CONTRACT_EXCLUSION_ROLE)
synchronizer = sync.ContractExcludedRoleSynchronizer()
return synchronizer.sync()


def init_contract_exclusion_work_types():
models.ContractExclusionSetExcludedWorkType.objects.all().delete()
mocks.service_api_get_contract_excluded_work_types_call(
fixtures.API_CONTRACT_EXCLUSION_WORK_TYPE)
synchronizer = sync.ContractExcludedWorkTypeSynchronizer()
return synchronizer.sync()


def init_service_calls():
models.ServiceCall.objects.all().delete()
mocks.service_api_get_service_calls_call(fixtures.API_SERVICE_CALL)
Expand Down
Loading

0 comments on commit fc95b59

Please sign in to comment.