@@ -156,12 +156,14 @@ class Sponsorship(models.Model):
156
156
REJECTED = "rejected"
157
157
APPROVED = "approved"
158
158
FINALIZED = "finalized"
159
+ CANCELLED = "cancelled"
159
160
160
161
STATUS_CHOICES = [
161
162
(APPLIED , "Applied" ),
162
163
(REJECTED , "Rejected" ),
163
164
(APPROVED , "Approved" ),
164
165
(FINALIZED , "Finalized" ),
166
+ (CANCELLED , "Cancelled" ),
165
167
]
166
168
167
169
objects = SponsorshipQuerySet .as_manager ()
@@ -180,6 +182,7 @@ class Sponsorship(models.Model):
180
182
applied_on = models .DateField (auto_now_add = True )
181
183
approved_on = models .DateField (null = True , blank = True )
182
184
rejected_on = models .DateField (null = True , blank = True )
185
+ cancelled_on = models .DateField (null = True , blank = True )
183
186
finalized_on = models .DateField (null = True , blank = True )
184
187
year = models .PositiveIntegerField (null = True , validators = YEAR_VALIDATORS , db_index = True )
185
188
@@ -218,7 +221,14 @@ def level_name(self, value):
218
221
@cached_property
219
222
def user_customizations (self ):
220
223
benefits = [b .sponsorship_benefit for b in self .benefits .select_related ("sponsorship_benefit" )]
221
- return self .package .get_user_customization (benefits )
224
+ if self .package :
225
+ return self .package .get_user_customization (benefits )
226
+ else :
227
+ # Return default customization structure for sponsorships without packages
228
+ return {
229
+ "added_by_user" : [],
230
+ "removed_by_user" : []
231
+ }
222
232
223
233
def __str__ (self ):
224
234
repr = f"{ self .level_name } - { self .year } - ({ self .get_status_display ()} ) for sponsor { self .sponsor .name } "
@@ -327,8 +337,17 @@ def approve(self, start_date, end_date):
327
337
self .end_date = end_date
328
338
self .approved_on = timezone .now ().date ()
329
339
340
+
341
+ def cancel (self ):
342
+ if self .CANCELLED not in self .next_status :
343
+ msg = f"Can't cancel a { self .get_status_display ()} sponsorship."
344
+ raise InvalidStatusException (msg )
345
+ self .status = self .CANCELLED
346
+ self .locked = True
347
+ self .cancelled_on = timezone .now ().date ()
348
+
330
349
def rollback_to_editing (self ):
331
- accepts_rollback = [self .APPLIED , self .APPROVED , self .REJECTED ]
350
+ accepts_rollback = [self .APPLIED , self .APPROVED , self .REJECTED , self . CANCELLED ]
332
351
if self .status not in accepts_rollback :
333
352
msg = f"Can't rollback to edit a { self .get_status_display ()} sponsorship."
334
353
raise InvalidStatusException (msg )
@@ -345,6 +364,7 @@ def rollback_to_editing(self):
345
364
self .status = self .APPLIED
346
365
self .approved_on = None
347
366
self .rejected_on = None
367
+ self .cancelled_on = None
348
368
349
369
@property
350
370
def unlocked (self ):
@@ -388,10 +408,11 @@ def open_for_editing(self):
388
408
@property
389
409
def next_status (self ):
390
410
states_map = {
391
- self .APPLIED : [self .APPROVED , self .REJECTED ],
392
- self .APPROVED : [self .FINALIZED ],
411
+ self .APPLIED : [self .APPROVED , self .REJECTED , self . CANCELLED ],
412
+ self .APPROVED : [self .FINALIZED , self . CANCELLED ],
393
413
self .REJECTED : [],
394
414
self .FINALIZED : [],
415
+ self .CANCELLED : [],
395
416
}
396
417
return states_map [self .status ]
397
418
0 commit comments