Coverage for products/utils/install_membership.py: 67%

40 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-03-29 14:03 -0600

1import calendar 

2from dateutil.relativedelta import relativedelta 

3from django.db import transaction 

4from django.utils import timezone 

5from memberships.models import Membership, MembershipPeriod 

6from memberships.serializers import MembershipPeriodSerializer 

7from products.models import MembershipProduct, ProductPurchase 

8 

9 

10def install_membership_purchase(purchase: ProductPurchase): 

11 """Install all memberships in a membership purchase""" 

12 owners_relation = ( 

13 purchase.owner_relation 

14 if purchase.owner_relation 

15 else [{"membership": purchase.membership.random_slug, "quantity": purchase.quantity}] 

16 ) 

17 response_data = [] 

18 with transaction.atomic(): 

19 for item in owners_relation: 

20 membership = Membership.objects.get(random_slug=item.get("membership")) 

21 for i in range(item.get("quantity")): 

22 data = install_single_membership_product(membership, purchase.product) 

23 response_data.append(data) 

24 return response_data 

25 

26 

27def install_single_membership_product(membership: Membership, product: MembershipProduct): 

28 """ 

29 Installation Process for membership product 

30 """ 

31 today = timezone.datetime.today() 

32 

33 last_period = MembershipPeriod.objects.filter(membership=membership).first() 

34 new_period_start = ( 

35 max(last_period.period_end + relativedelta(days=1), today.date()) if last_period else today.date() 

36 ) 

37 

38 new_period_end = _get_price_and_end_period_date(product, new_period_start) 

39 

40 period = MembershipPeriod.objects.create( 

41 membership=membership, 

42 product=product, 

43 previous=last_period, 

44 period_start=new_period_start, 

45 period_end=new_period_end, 

46 ) 

47 

48 serializer = MembershipPeriodSerializer(period) 

49 

50 return serializer.data 

51 

52 

53def _get_price_and_end_period_date(product, new_period_start): 

54 if product.renewal_type == MembershipProduct.RenewalTypeChoices.days: 54 ↛ 60line 54 didn't jump to line 60, because the condition on line 54 was never false

55 """If renewal is by 'days' calculate period end date by adding a month/year""" 

56 duration = MembershipProduct.DurationChoices.get_choice(product.duration) 

57 new_period_end = new_period_start + relativedelta(**duration.days) 

58 return new_period_end 

59 

60 if product.renewal_type == MembershipProduct.RenewalTypeChoices.calendar: 

61 """If renewal is by 'calendar' calculate period end by taking last month/year day 

62 and recalculating price by days proportion""" 

63 

64 if product.duration != MembershipProduct.DurationChoices.yearly: 

65 duration = MembershipProduct.DurationChoices.get_choice(product.duration) 

66 

67 new_period_month = (new_period_start + relativedelta(months=duration.days["months"] - 1)).month 

68 

69 month_days = calendar.monthrange(new_period_start.year, new_period_month)[1] 

70 new_period_end = timezone.datetime( 

71 day=month_days, 

72 month=new_period_month, 

73 year=new_period_start.year, 

74 ) 

75 

76 return new_period_end.date() 

77 

78 if product.duration == MembershipProduct.DurationChoices.yearly: 

79 new_period_end = timezone.datetime(day=31, month=12, year=new_period_start.year) 

80 

81 return new_period_end.date()