Skip to content

Commit

Permalink
Merge pull request #445 from dimagi/pkv/claim-limit-remaining-budget-fix
Browse files Browse the repository at this point in the history
Claim Limit Remaining Budget Fix
  • Loading branch information
pxwxnvermx authored Dec 17, 2024
2 parents afea4f6 + d2138ec commit 8b5f339
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 11 deletions.
5 changes: 4 additions & 1 deletion commcare_connect/opportunity/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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

Expand Down
36 changes: 31 additions & 5 deletions commcare_connect/opportunity/tests/test_api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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="[email protected]",
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="[email protected]",
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)"
Expand Down
21 changes: 16 additions & 5 deletions commcare_connect/opportunity/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,37 @@ 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))) == {
payment_unit1.id,
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])
Expand All @@ -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
Expand Down

0 comments on commit 8b5f339

Please sign in to comment.