I have a model that has class methods. In testing the class methods work and alter the model instances according to my needs. The issue is using this class method in the admin. When an application cannot pay a late payment fee is applied creating another transaction altering the balance. The method in models is decorated with a @classmethod decorator:
class Transactions(models.Model):
application = models.ForeignKey(Application,
related_name='application_id', blank=True, null=True)
transaction_type = models.CharField(max_length=56,
choices=TRANSACTION_TYPE, null=True)
transaction_source = models.CharField(max_length=56,
choices=TRANSACTION_SOURCE, null=True)
transaction_method = models.CharField(max_length=56,
choices=TRANSACTION_METHOD, null=True)
amount = models.DecimalField(max_digits=10, decimal_places=2)
created_date = models.DateField()
posted_date = models.DateField()
external_reference = models.CharField(max_length=100, null=True,
verbose_name='External Reference')
def __unicode__(self):
return self.application.forename + " " +
self.application.surname + "(" + str(self.application.id) + ")"
@classmethod
def late_payment_fee(cls, app, desired_action):
"""
This function enacts a late payment fee to an application as a
transaction
:param app: application the fee is going to be applied to
:param desired_action: either True or False, when reversing the
fee the transaction shouldn't be deleted,
just another transaction of the opposite effect in order to
help loans collection with tracking, True will
enact a feee, False will reverse the fee
:return: a transaction which is stored in the database
"""
today = str(date.today())
if desired_action:
trans_type = MISSEDREPAYMENTFEE
amount = float(12)
else:
trans_type = MISSEDREPAYMENTFEEREVERSAL
amount = float(-12)
cls.create_trasaction(app, trans_type, INTERNALBOOKING,
INTERNALBOOKING, amount, today, today, None)
I need to get it so when status is altered, or when a tickbox is checked in the admin for an application it fires the class method. I have Googled overriding models in admin but cannot find anything. Here's the admin:
class ApplicationAdmin(ImportExportModelAdmin):
resource_class = ApplicationResource
search_fields = ['forename', 'surname']
list_filter = ('status', 'underwritingresult', 'test', 'signed',
'mandateapproved', 'dealership', 'brand')
list_select_related = ('instalment',)
list_display = ('id', 'SPV_ID', 'forename', 'surname'......
inlines = [
InstalmentInline,
AddressInline
]
exclude = ['customer', 'accountnumber', 'sortcode']
readonly_fields = ('Account_Number', 'Sort_Code', 'SPV_ID')
def get_readonly_fields(self, request, obj=None):
readonly_fields = []
if obj.locked :
for field in self.model._meta.fields:
readonly_fields.append(field.name)
else:
readonly_fields = ('Account_Number', 'Sort_Code', 'SPV_ID')
return readonly_fields
def Account_Number(self, obj):
return Decrypt(obj.accountnumber)
def Sort_Code(self, obj):
return Decrypt(obj.sortcode)
def SPV_ID(self, obj):
return obj.spv.id
def has_delete_permission(self, request, obj=None):
return False
Many thanks for reading this.
I've now found the solution (sorry if my initial question was not very clear). It turned out that I needed to override the save function in the Application model. However, another issue was raised when doing this. If a user changed something benign like the name of the customer then the class method would fire and create new transactions even though there was no real change. Because of this, we have to override the init and the save function in the model but still actually save. For this model, we are interested to see if the status has changed or a late fee has been applied. In order to do this, we have to store the initial status values by overriding the init method in the model:
__initial_status = None
__initial_fee_status = None
def __init__(self, *args, **kwargs):
"""
This function overrides the __init__ function in order to
save initial_status facilitating comparisons in the save function
:param args: allows the function to accept an arbitrary number of
arguments and/or keyword arguments
:param kwargs: same as above
"""
super(Application, self).__init__(*args, **kwargs)
self.__initial_status = self.status
self.__initial_fee_status = self.feeadded
Now we have them stored we can pass them through our save function. If there is a change we can utilize the class method:
def save(self, *args, **kwargs):
"""
This function overrides the standard save function. The application
is still saved, however, other functions are fired when it is
saved.
:return: creates reversal commission and dealership transactions
if the status is CANCELLED
"""
if self.signed and self.status != self.__initial_status:
if self.status == Application.CANCELLED:
Transactions.create_commision_trasaction(app=self,
reverse=True)
Transactions.create_dealership_trasaction(app=self,
reverse=True)
elif self.status == Application.OK:
Transactions.create_commision_trasaction(app=self,
reverse=False)
Transactions.create_dealership_trasaction(app=self,
reverse=False)
if self.signed and self.feeadded != self.__initial_fee_status:
if self.feeadded:
Transactions.late_payment_fee(app=self, desired_action=True)
elif self.feeadded is False:
Transactions.late_payment_fee(app=self, desired_action=False)
super(Application, self).save(*args, **kwargs)