Skip to content

Commit 9a53eeb

Browse files
authored
Access to neighbour courses homework (#2748)
* QoL: order.refund() now by default issues full refund instead of failing * Allowed access to the questions and answers from the neighbour courses
1 parent 440fd38 commit 9a53eeb

File tree

14 files changed

+94
-46
lines changed

14 files changed

+94
-46
lines changed

src/apps/amocrm/tests/services/order/tests_order_pusher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def not_paid_order_with_lead(factory, user, course, amocrm_lead):
5050
@pytest.fixture
5151
def returned_order_with_lead(factory, user, course, amocrm_lead):
5252
order = factory.order(user=user, item=course, is_paid=True, author=user, amocrm_lead=amocrm_lead)
53-
order.refund(order.price)
53+
order.refund()
5454
return order
5555

5656

src/apps/amocrm/tests/services/order/tests_order_returner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ def mock_delete_transaction(mocker):
2222
@pytest.fixture
2323
def unpaid_order(mixer, paid_order_with_lead):
2424
mixer.blend("amocrm.AmoCRMOrderTransaction", order=paid_order_with_lead, amocrm_id=876)
25-
paid_order_with_lead.refund(paid_order_with_lead.price)
25+
paid_order_with_lead.refund()
2626
return paid_order_with_lead
2727

2828

2929
@pytest.fixture
3030
def unpaid_order_not_in_amo(paid_order_without_lead):
31-
paid_order_without_lead.refund(paid_order_without_lead.price)
31+
paid_order_without_lead.refund()
3232
return paid_order_without_lead
3333

3434

src/apps/homework/models/question.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def for_user(self, user: User) -> "QuestionQuerySet":
2929
)
3030

3131
def limit_to_questions_from_purchased_course(self, user: User) -> "QuestionQuerySet":
32-
purchased_lessons = apps.get_model("lms.Lesson").objects.for_user(user).exclude(question=None)
32+
purchased_lessons = apps.get_model("lms.Lesson").objects.for_user_including_neighbour_courses(user).exclude(question=None)
3333

3434
return self.filter(pk__in=purchased_lessons.values("question").distinct())
3535

src/apps/homework/services/answer_creator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def validate_parent_slug(self) -> None:
109109
return
110110

111111
if not is_valid_uuid(self.parent_slug):
112-
raise ValidationError("Question should be a valid uuid")
112+
raise ValidationError("Parent should be a valid uuid")
113113

114114
if not Answer.objects.filter(slug=self.parent_slug).exists():
115115
raise ValidationError("Answer does not exist")

src/apps/homework/tests/homework/api/answers/retrieve/tests_answer_retrieve.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def test_answers_with_parents_have_parent_field(api, answer, another_answer):
129129

130130

131131
def test_200_for_not_purchased_users(api, answer, purchase):
132-
purchase.refund(purchase.price)
132+
purchase.refund()
133133

134134
api.get(
135135
f"/api/v2/homework/answers/{answer.slug}/",
@@ -138,7 +138,7 @@ def test_200_for_not_purchased_users(api, answer, purchase):
138138

139139

140140
def test_ok_for_superusers_even_when_they_did_not_purchase_the_course(api, answer, purchase):
141-
purchase.refund(purchase.price)
141+
purchase.refund()
142142

143143
api.user.update(is_superuser=True)
144144

@@ -149,7 +149,7 @@ def test_ok_for_superusers_even_when_they_did_not_purchase_the_course(api, answe
149149

150150

151151
def test_ok_for_users_with_permission_even_when_they_did_not_purchase_the_course(api, answer, purchase):
152-
purchase.refund(purchase.price)
152+
purchase.refund()
153153

154154
api.user.add_perm("homework.question.see_all_questions")
155155

src/apps/homework/tests/homework/api/answers/tests_answer_create.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,22 @@ def test_404_for_not_purchased_users(api, question):
213213
)
214214

215215

216+
@pytest.mark.usefixtures("_no_purchase")
217+
def test_ok_for_users_that_purchased_another_course_from_the_group(api, question, course, factory):
218+
"""Create a purchase of the item with the same group as the course, and check if user can post an answer"""
219+
another_course = factory.course(group=course.group)
220+
factory.order(item=another_course, user=api.user, is_paid=True)
221+
222+
api.post(
223+
"/api/v2/homework/answers/",
224+
{
225+
"question": question.slug,
226+
"content": JSON,
227+
},
228+
expected_status_code=201,
229+
)
230+
231+
216232
@pytest.mark.usefixtures("_no_purchase")
217233
@pytest.mark.parametrize(
218234
"permission",

src/apps/homework/tests/homework/api/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ def purchase_of_another_course(factory, another_course, api):
6464

6565

6666
@pytest.fixture
67-
def _no_purchase(purchase, _set_current_user):
67+
def _no_purchase(purchase, purchase_of_another_course, _set_current_user):
6868
"""Invalidate the purchase"""
6969
purchase.refund(amount=purchase.price)
70+
purchase_of_another_course.refund(amount=purchase_of_another_course.price)
7071

7172

7273
@pytest.fixture

src/apps/homework/tests/homework/api/questions/tests_question_retrieve.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@
1010
]
1111

1212

13+
@pytest.fixture
14+
def group(mixer, course, another_course):
15+
group = mixer.blend("products.Group")
16+
17+
course.update(group=group)
18+
another_course.update(group=group)
19+
20+
return group
21+
22+
1323
def test_ok(api, question, course):
1424
got = api.get(f"/api/v2/homework/questions/{question.slug}/")
1525

@@ -79,6 +89,22 @@ def test_ok_for_superusers_even_when_they_did_not_purchase_the_course(api, quest
7989
api.get(f"/api/v2/homework/questions/{question.slug}/", expected_status_code=200)
8090

8191

92+
@pytest.mark.usefixtures("group", "purchase_of_another_course", "_set_current_user")
93+
def test_ok_if_user_purchased_same_course_from_the_group(api, purchase, question):
94+
purchase.refund()
95+
96+
api.get(f"/api/v2/homework/questions/{question.slug}/", expected_status_code=200)
97+
98+
99+
@pytest.mark.usefixtures("group", "_set_current_user")
100+
def test_fail_if_user_has_not_purchased_same_course_from_the_group(api, mixer, purchase, purchase_of_another_course, question):
101+
"""Same as above, just to make sure above test works"""
102+
purchase.refund()
103+
purchase_of_another_course.course.update(group=mixer.blend("products.Group")) # another_group
104+
105+
api.get(f"/api/v2/homework/questions/{question.slug}/", expected_status_code=404)
106+
107+
82108
@pytest.mark.usefixtures("_no_purchase")
83109
@pytest.mark.parametrize(
84110
"permission",

src/apps/lms/models/lesson.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ def for_user(self, user: User) -> "LessonQuerySet":
2626
module__course__in=purchased_courses,
2727
)
2828

29+
def for_user_including_neighbour_courses(self, user: User) -> "LessonQuerySet":
30+
"""The same as above, but with all courses belonging to the same product groups that user has puchased"""
31+
purchased_courses = apps.get_model("studying.Study").objects.filter(student=user).values_list("course_id", flat=True)
32+
purchased_groups = apps.get_model("products.Course").objects.filter(pk__in=purchased_courses).values("group_id").distinct()
33+
34+
return self.filter(
35+
module__course__group__in=purchased_groups,
36+
)
37+
2938
def for_admin(self) -> "LessonQuerySet":
3039
return self.select_related(
3140
"module",

src/apps/orders/models/order.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def set_paid(self, silent: bool | None = False) -> None:
134134

135135
OrderPaidSetter(self, silent=silent)()
136136

137-
def refund(self, amount: Decimal) -> None:
137+
def refund(self, amount: Decimal | None = None) -> None:
138138
from apps.orders.services import OrderRefunder
139139

140140
OrderRefunder(self, amount)()

0 commit comments

Comments
 (0)