|
1 | 1 | """Views for the B2B API (v0).""" |
2 | 2 |
|
3 | 3 | from django.contrib.contenttypes.models import ContentType |
| 4 | +from django.db.models import Q |
4 | 5 | from django.views.decorators.csrf import csrf_exempt |
5 | 6 | from drf_spectacular.utils import extend_schema |
| 7 | +from mitol.common.utils.datetime import now_in_utc |
6 | 8 | from rest_framework import status, viewsets |
7 | 9 | from rest_framework.permissions import IsAdminUser, IsAuthenticated |
8 | 10 | from rest_framework.response import Response |
9 | 11 | from rest_framework.views import APIView |
10 | 12 | from rest_framework_api_key.permissions import HasAPIKey |
11 | 13 |
|
12 | 14 | from b2b.api import create_b2b_enrollment |
13 | | -from b2b.models import ContractPage, OrganizationPage |
| 15 | +from b2b.models import ( |
| 16 | + ContractPage, |
| 17 | + DiscountContractAttachmentRedemption, |
| 18 | + OrganizationPage, |
| 19 | +) |
14 | 20 | from b2b.serializers.v0 import ( |
15 | 21 | ContractPageSerializer, |
16 | 22 | CreateB2BEnrollmentSerializer, |
17 | 23 | OrganizationPageSerializer, |
18 | 24 | ) |
19 | 25 | from courses.models import CourseRun |
20 | | -from ecommerce.models import Product |
| 26 | +from ecommerce.models import Discount, Product |
21 | 27 | from main.constants import USER_MSG_TYPE_B2B_ENROLL_SUCCESS |
22 | 28 |
|
23 | 29 |
|
@@ -74,3 +80,70 @@ def post(self, request, readable_id: str, format=None): # noqa: A002, ARG002 |
74 | 80 | if response["result"] == USER_MSG_TYPE_B2B_ENROLL_SUCCESS |
75 | 81 | else status.HTTP_406_NOT_ACCEPTABLE, |
76 | 82 | ) |
| 83 | + |
| 84 | + |
| 85 | +class AttachContractApi(APIView): |
| 86 | + """View for attaching a user to a B2B contract.""" |
| 87 | + |
| 88 | + permission_classes = [IsAuthenticated] |
| 89 | + |
| 90 | + @extend_schema( |
| 91 | + request=None, |
| 92 | + responses=ContractPageSerializer(many=True), |
| 93 | + ) |
| 94 | + @csrf_exempt |
| 95 | + def post(self, request, enrollment_code: str, format=None): # noqa: A002, ARG002 |
| 96 | + """ |
| 97 | + Use the provided enrollment code to attach the user to a B2B contract. |
| 98 | +
|
| 99 | + This will not create an order, nor will it enroll the user. It will |
| 100 | + attach the user to the contract and log that the code was used for this |
| 101 | + purpose (but will _not_ invalidate the code, since we're not actually |
| 102 | + using it at this point). |
| 103 | +
|
| 104 | + This will respect the activation and expiration dates (of both the contract |
| 105 | + and the discount), and will make sure there's sufficient available seats |
| 106 | + in the contract. |
| 107 | +
|
| 108 | + If the user is already in the contract, then we skip it. |
| 109 | +
|
| 110 | + Returns: |
| 111 | + - list of ContractPageSerializer - the contracts for the user |
| 112 | + """ |
| 113 | + |
| 114 | + now = now_in_utc() |
| 115 | + try: |
| 116 | + code = ( |
| 117 | + Discount.objects.filter( |
| 118 | + Q(activation_date__isnull=True) | Q(activation_date__lte=now) |
| 119 | + ) |
| 120 | + .filter(Q(expiration_date__isnull=True) | Q(expiration_date__gte=now)) |
| 121 | + .get(discount_code=enrollment_code) |
| 122 | + ) |
| 123 | + except Discount.DoesNotExist: |
| 124 | + return Response( |
| 125 | + ContractPageSerializer(request.user.b2b_contracts.all(), many=True).data |
| 126 | + ) |
| 127 | + |
| 128 | + contract_ids = list(code.b2b_contracts().values_list("id", flat=True)) |
| 129 | + contracts = ( |
| 130 | + ContractPage.objects.filter(pk__in=contract_ids) |
| 131 | + .exclude(pk__in=request.user.b2b_contracts.all()) |
| 132 | + .exclude(Q(contract_end__lt=now) | Q(contract_start__gt=now)) |
| 133 | + .all() |
| 134 | + ) |
| 135 | + |
| 136 | + for contract in contracts: |
| 137 | + if contract.is_full(): |
| 138 | + continue |
| 139 | + |
| 140 | + request.user.b2b_contracts.add(contract) |
| 141 | + DiscountContractAttachmentRedemption.objects.create( |
| 142 | + user=request.user, discount=code, contract=contract |
| 143 | + ) |
| 144 | + |
| 145 | + request.user.save() |
| 146 | + |
| 147 | + return Response( |
| 148 | + ContractPageSerializer(request.user.b2b_contracts.all(), many=True).data |
| 149 | + ) |
0 commit comments