diff --git a/commcare_connect/opportunity/models.py b/commcare_connect/opportunity/models.py index 6214d3fc..89ab28e7 100644 --- a/commcare_connect/opportunity/models.py +++ b/commcare_connect/opportunity/models.py @@ -115,6 +115,9 @@ def claimed_budget(self): opp_access = OpportunityAccess.objects.filter(opportunity=self) opportunity_claim = OpportunityClaim.objects.filter(opportunity_access__in=opp_access) claim_limits = OpportunityClaimLimit.objects.filter(opportunity_claim__in=opportunity_claim) + org_pay = 0 + if self.managed: + org_pay = self.managedopportunity.org_pay_per_visit payment_unit_counts = claim_limits.values("payment_unit").annotate( visits_count=Sum("max_visits"), amount=F("payment_unit__amount") @@ -123,7 +126,7 @@ def claimed_budget(self): for count in payment_unit_counts: visits_count = count["visits_count"] amount = count["amount"] - claimed += visits_count * amount + claimed += visits_count * (amount + org_pay) return claimed diff --git a/commcare_connect/opportunity/tests/test_api_views.py b/commcare_connect/opportunity/tests/test_api_views.py index 0471ebbd..021fc2c8 100644 --- a/commcare_connect/opportunity/tests/test_api_views.py +++ b/commcare_connect/opportunity/tests/test_api_views.py @@ -29,7 +29,7 @@ UserVisitFactory, ) from commcare_connect.users.models import User -from commcare_connect.users.tests.factories import ConnectIdUserLinkFactory +from commcare_connect.users.tests.factories import ConnectIdUserLinkFactory, MobileUserFactory def _setup_opportunity_and_access(mobile_user: User, total_budget, end_date, budget_per_visit=10): @@ -56,11 +56,37 @@ def test_claim_endpoint_success(mobile_user: User, api_client: APIClient): assert claim.exists() -def test_claim_endpoint_budget_exhausted(mobile_user: User, api_client: APIClient): - opportunity, opportunity_access = _setup_opportunity_and_access( - mobile_user, total_budget=0, end_date=datetime.date.today() + datetime.timedelta(days=100) +@pytest.mark.django_db +@pytest.mark.parametrize("opportunity", [{}, {"opp_options": {"managed": True}}], indirect=["opportunity"]) +def test_claim_endpoint_budget_exhausted(opportunity: Opportunity, api_client: APIClient): + PaymentUnitFactory(opportunity=opportunity, amount=10, max_total=100) + opportunity.total_budget = 10 * 100 + if opportunity.managed: + opportunity.total_budget += 100 * opportunity.managedopportunity.org_pay_per_visit + opportunity.end_date = datetime.date.today() + datetime.timedelta(days=100) + opportunity.save() + + mobile_user_1 = MobileUserFactory() + opportunity_access_1 = OpportunityAccessFactory(opportunity=opportunity, user=mobile_user_1) + ConnectIdUserLinkFactory( + user=mobile_user_1, + commcare_username="test_1@ccc-test.commcarehq.org", + domain=opportunity.deliver_app.cc_domain, ) - api_client.force_authenticate(mobile_user) + api_client.force_authenticate(mobile_user_1) + response = api_client.post(f"/api/opportunity/{opportunity.id}/claim") + assert response.status_code == 201 + claim = OpportunityClaim.objects.filter(opportunity_access=opportunity_access_1) + assert claim.exists() + + mobile_user_2 = MobileUserFactory() + OpportunityAccessFactory(opportunity=opportunity, user=mobile_user_2) + ConnectIdUserLinkFactory( + user=mobile_user_2, + commcare_username="test_2@ccc-test.commcarehq.org", + domain=opportunity.deliver_app.cc_domain, + ) + api_client.force_authenticate(mobile_user_2) response = api_client.post(f"/api/opportunity/{opportunity.id}/claim") assert response.status_code == 400 assert response.data == "Opportunity cannot be claimed. (Budget Exhausted)" diff --git a/commcare_connect/opportunity/tests/test_models.py b/commcare_connect/opportunity/tests/test_models.py index c5a3b494..526b83d2 100644 --- a/commcare_connect/opportunity/tests/test_models.py +++ b/commcare_connect/opportunity/tests/test_models.py @@ -27,18 +27,19 @@ def test_learn_progress(opportunity: Opportunity): @pytest.mark.django_db +@pytest.mark.parametrize("opportunity", [{}, {"opp_options": {"managed": True}}], indirect=True) def test_opportunity_stats(opportunity: Opportunity, user: User): - payment_unit_sub = PaymentUnitFactory( + payment_unit_sub = PaymentUnitFactory.create( opportunity=opportunity, max_total=100, max_daily=10, amount=5, parent_payment_unit=None ) - payment_unit1 = PaymentUnitFactory( + payment_unit1 = PaymentUnitFactory.create( opportunity=opportunity, max_total=100, max_daily=10, amount=3, parent_payment_unit=payment_unit_sub, ) - payment_unit2 = PaymentUnitFactory( + payment_unit2 = PaymentUnitFactory.create( opportunity=opportunity, max_total=100, max_daily=10, amount=5, parent_payment_unit=None ) assert set(list(opportunity.paymentunit_set.values_list("id", flat=True))) == { @@ -46,10 +47,17 @@ def test_opportunity_stats(opportunity: Opportunity, user: User): payment_unit2.id, payment_unit_sub.id, } + payment_units = [payment_unit_sub, payment_unit1, payment_unit2] + budget_per_user = sum(pu.max_total * pu.amount for pu in payment_units) + org_pay = 0 + if opportunity.managed: + org_pay = opportunity.managedopportunity.org_pay_per_visit + budget_per_user += sum(pu.max_total * org_pay for pu in payment_units) + opportunity.total_budget = budget_per_user * 3 payment_units = [payment_unit1, payment_unit2, payment_unit_sub] assert opportunity.budget_per_user == sum([p.amount * p.max_total for p in payment_units]) - assert opportunity.number_of_users == opportunity.total_budget / opportunity.budget_per_user + assert opportunity.number_of_users == 3 assert opportunity.allotted_visits == sum([pu.max_total for pu in payment_units]) * opportunity.number_of_users assert opportunity.max_visits_per_user_new == sum([pu.max_total for pu in payment_units]) assert opportunity.daily_max_visits_per_user_new == sum([pu.max_daily for pu in payment_units]) @@ -61,7 +69,10 @@ def test_opportunity_stats(opportunity: Opportunity, user: User): ocl1 = OpportunityClaimLimitFactory(opportunity_claim=claim, payment_unit=payment_unit1) ocl2 = OpportunityClaimLimitFactory(opportunity_claim=claim, payment_unit=payment_unit2) - opportunity.claimed_budget == (ocl1.max_visits * payment_unit1.amount) + (ocl2.max_visits * payment_unit2.amount) + assert opportunity.claimed_budget == (ocl1.max_visits * (payment_unit1.amount + org_pay)) + ( + ocl2.max_visits * (payment_unit2.amount + org_pay) + ) + assert opportunity.remaining_budget == opportunity.total_budget - opportunity.claimed_budget @pytest.mark.django_db