Coverage for website/sales/tests/test_api.py: 100.00%

332 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2025-08-14 10:31 +0000

1from unittest import mock 

2 

3from django.contrib.auth.models import Permission 

4from django.contrib.contenttypes.models import ContentType 

5from django.test import TestCase 

6from django.urls import reverse 

7from django.utils import timezone 

8 

9from freezegun import freeze_time 

10from rest_framework.test import APIClient 

11 

12from activemembers.models import Committee, MemberGroupMembership 

13from members.models import Member 

14from payments.models import Payment 

15from payments.services import create_payment 

16from sales import payables 

17from sales.models.order import Order, OrderItem 

18from sales.models.product import Product, ProductList 

19from sales.models.shift import SelfOrderPeriod, Shift 

20 

21 

22@freeze_time("2021-01-01") 

23class OrderAPITest(TestCase): 

24 fixtures = [ 

25 "members.json", 

26 "bank_accounts.json", 

27 "member_groups.json", 

28 "products.json", 

29 ] 

30 

31 @classmethod 

32 def setUpTestData(cls): 

33 """Create the following test data: 

34 

35 o0: an empty order 

36 o1: an unpaid order of 2 beer 

37 o2: an order of 2 soda that doesn't need a payment 

38 o3: an unpaid order with 2 beer and 2 wine 

39 o4: a paid order with 2 wine 

40 o4: a paid order with 2 beer and 2 wine 

41 """ 

42 payables.register() 

43 

44 cls.member = Member.objects.filter(last_name="Wiggers").first() 

45 

46 cls.beer = Product.objects.get(name="beer") 

47 cls.wine = Product.objects.get(name="wine") 

48 cls.soda = Product.objects.get(name="soda") 

49 

50 cls.normal = ProductList.objects.get( 

51 name="normal", 

52 ) 

53 cls.free = ProductList.objects.get( 

54 name="free", 

55 ) 

56 

57 cls.shift = Shift.objects.create( 

58 start=timezone.now(), 

59 end=timezone.now() + timezone.timedelta(hours=1), 

60 product_list=cls.normal, 

61 ) 

62 

63 cls.o0 = Order.objects.create(shift=cls.shift) 

64 cls.o1 = Order.objects.create(shift=cls.shift) 

65 OrderItem.objects.create( 

66 order=cls.o1, 

67 product=cls.shift.product_list.product_items.get(product=cls.beer), 

68 amount=2, 

69 ) 

70 cls.o2 = Order.objects.create(shift=cls.shift) 

71 OrderItem.objects.create( 

72 order=cls.o2, 

73 product=cls.shift.product_list.product_items.get(product=cls.soda), 

74 amount=2, 

75 ) 

76 cls.o3 = Order.objects.create(shift=cls.shift) 

77 OrderItem.objects.create( 

78 order=cls.o3, 

79 product=cls.shift.product_list.product_items.get(product=cls.beer), 

80 amount=2, 

81 ) 

82 OrderItem.objects.create( 

83 order=cls.o3, 

84 product=cls.shift.product_list.product_items.get(product=cls.wine), 

85 amount=2, 

86 ) 

87 cls.o4 = Order.objects.create(shift=cls.shift) 

88 OrderItem.objects.create( 

89 order=cls.o4, 

90 product=cls.shift.product_list.product_items.get(product=cls.wine), 

91 amount=2, 

92 ) 

93 cls.o5 = Order.objects.create(shift=cls.shift) 

94 OrderItem.objects.create( 

95 order=cls.o5, 

96 product=cls.shift.product_list.product_items.get(product=cls.beer), 

97 amount=2, 

98 ) 

99 OrderItem.objects.create( 

100 order=cls.o5, 

101 product=cls.shift.product_list.product_items.get(product=cls.wine), 

102 amount=2, 

103 ) 

104 cls.o4.payment = create_payment( 

105 cls.o4, processed_by=cls.member, pay_type=Payment.CASH 

106 ) 

107 cls.o4.save() 

108 cls.o5.payment = create_payment( 

109 cls.o5, processed_by=cls.member, pay_type=Payment.CASH 

110 ) 

111 cls.o5.save() 

112 

113 cls.cie = Committee.objects.get(pk=1) 

114 MemberGroupMembership.objects.create(group=cls.cie, member=cls.member) 

115 content_type = ContentType.objects.get_for_model(Order) 

116 permission1 = Permission.objects.get( 

117 content_type=content_type, codename="add_order" 

118 ) 

119 permission2 = Permission.objects.get( 

120 content_type=content_type, codename="change_order" 

121 ) 

122 permission3 = Permission.objects.get( 

123 content_type=content_type, codename="delete_order" 

124 ) 

125 permission4 = Permission.objects.get( 

126 content_type=content_type, codename="view_order" 

127 ) 

128 permission5 = Permission.objects.get( 

129 content_type=ContentType.objects.get_for_model(Shift), codename="view_shift" 

130 ) 

131 cls.cie.permissions.add(permission1) 

132 cls.cie.permissions.add(permission2) 

133 cls.cie.permissions.add(permission3) 

134 cls.cie.permissions.add(permission4) 

135 cls.cie.permissions.add(permission5) 

136 

137 def setUp(self): 

138 self.client = APIClient() 

139 self.client.force_login(self.member) 

140 

141 def test_detail_not_logged_in(self): 

142 self.client.logout() 

143 response = self.client.get( 

144 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o0.pk}) 

145 ) 

146 self.assertEqual(403, response.status_code) 

147 

148 def test_detail_not_authorized__get(self): 

149 response = self.client.get( 

150 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}) 

151 ) 

152 self.assertEqual(200, response.status_code) 

153 

154 self.member.is_superuser = False 

155 self.member.save() 

156 

157 response = self.client.get( 

158 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}) 

159 ) 

160 self.assertEqual(404, response.status_code) 

161 

162 self.shift.managers.add(self.cie) 

163 self.shift.save() 

164 

165 response = self.client.get( 

166 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}) 

167 ) 

168 self.assertEqual(200, response.status_code) 

169 

170 def test_detail_not_authorized__patch(self): 

171 data = {"discount": "0.5"} 

172 

173 response = self.client.patch( 

174 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}), data 

175 ) 

176 self.assertEqual(200, response.status_code) 

177 

178 self.member.is_superuser = False 

179 self.member.save() 

180 

181 response = self.client.patch( 

182 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}), data 

183 ) 

184 self.assertEqual(404, response.status_code) 

185 

186 self.shift.managers.add(self.cie) 

187 self.shift.save() 

188 

189 response = self.client.patch( 

190 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}), data 

191 ) 

192 self.assertEqual(200, response.status_code) 

193 

194 def test_detail_not_authorized__put(self): 

195 data = {"discount": "0.5"} 

196 

197 response = self.client.put( 

198 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}), data 

199 ) 

200 self.assertEqual(200, response.status_code) 

201 

202 self.member.is_superuser = False 

203 self.member.save() 

204 

205 response = self.client.put( 

206 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}), data 

207 ) 

208 self.assertEqual(404, response.status_code) 

209 

210 self.shift.managers.add(self.cie) 

211 self.shift.save() 

212 

213 response = self.client.put( 

214 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}), data 

215 ) 

216 self.assertEqual(200, response.status_code) 

217 

218 def test_detail_not_authorized__delete(self): 

219 response = self.client.delete( 

220 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o1.pk}) 

221 ) 

222 self.assertEqual(204, response.status_code) 

223 

224 self.member.is_superuser = False 

225 self.member.save() 

226 

227 response = self.client.delete( 

228 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o2.pk}) 

229 ) 

230 self.assertEqual(404, response.status_code) 

231 

232 self.shift.managers.add(self.cie) 

233 self.shift.save() 

234 

235 response3 = self.client.delete( 

236 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": self.o2.pk}) 

237 ) 

238 self.assertEqual(204, response3.status_code) 

239 

240 def test_list_not_logged_in(self): 

241 self.client.logout() 

242 response = self.client.get( 

243 reverse("api:v2:admin:sales:shift-orders", kwargs={"pk": self.shift.pk}) 

244 ) 

245 self.assertEqual(403, response.status_code) 

246 

247 def test_list_not_authorized__get(self): 

248 response = self.client.get( 

249 reverse("api:v2:admin:sales:shift-orders", kwargs={"pk": self.shift.pk}) 

250 ) 

251 self.assertEqual(200, response.status_code) 

252 

253 self.member.is_superuser = False 

254 self.member.save() 

255 

256 response = self.client.get( 

257 reverse("api:v2:admin:sales:shift-orders", kwargs={"pk": self.shift.pk}) 

258 ) 

259 self.assertEqual(403, response.status_code) 

260 

261 self.shift.managers.add(self.cie) 

262 self.shift.save() 

263 

264 response = self.client.get( 

265 reverse("api:v2:admin:sales:shift-orders", kwargs={"pk": self.shift.pk}) 

266 ) 

267 self.assertEqual(200, response.status_code) 

268 

269 def test_list_not_authorized__post(self): 

270 data = {"order_items": [{"product": "beer", "amount": 4}]} 

271 

272 response = self.client.post( 

273 reverse("api:v2:admin:sales:shift-orders", kwargs={"pk": self.shift.pk}), 

274 data, 

275 ) 

276 self.assertEqual(201, response.status_code) 

277 

278 self.member.is_superuser = False 

279 self.member.save() 

280 

281 response = self.client.post( 

282 reverse("api:v2:admin:sales:shift-orders", kwargs={"pk": self.shift.pk}), 

283 data, 

284 ) 

285 self.assertEqual(403, response.status_code) 

286 

287 self.shift.managers.add(self.cie) 

288 self.shift.save() 

289 

290 response = self.client.post( 

291 reverse("api:v2:admin:sales:shift-orders", kwargs={"pk": self.shift.pk}), 

292 data, 

293 ) 

294 self.assertEqual(201, response.status_code) 

295 

296 def test_create_order(self): 

297 self.maxDiff = None 

298 with self.subTest("Create new order with single item"): 

299 data = {"order_items": [{"product": "beer", "amount": 4}]} 

300 response = self.client.post( 

301 reverse( 

302 "api:v2:admin:sales:shift-orders", kwargs={"pk": self.shift.pk} 

303 ), 

304 data, 

305 format="json", 

306 ) 

307 self.assertEqual(201, response.status_code) 

308 pk = response.data["pk"] 

309 expected_response = { 

310 "pk": pk, 

311 "shift": self.shift.pk, 

312 "created_at": "2021-01-01T01:00:00+01:00", 

313 "order_items": [{"product": "beer", "amount": 4, "total": "2.00"}], 

314 "order_description": "4x beer", 

315 "age_restricted": True, 

316 "subtotal": "2.00", 

317 "discount": None, 

318 "total_amount": "2.00", 

319 "num_items": 4, 

320 "payment": None, 

321 "payer": None, 

322 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

323 } 

324 self.assertJSONEqual(response.content, expected_response) 

325 

326 with self.subTest("Add product item"): 

327 data = { 

328 "order_items": [ 

329 {"product": "beer", "amount": 5}, 

330 {"product": "soda", "amount": 2}, 

331 ] 

332 } 

333 response = self.client.put( 

334 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": pk}), 

335 data, 

336 format="json", 

337 ) 

338 self.assertEqual(200, response.status_code) 

339 expected_response = { 

340 "pk": pk, 

341 "shift": self.shift.pk, 

342 "created_at": "2021-01-01T01:00:00+01:00", 

343 "order_items": [ 

344 {"product": "beer", "amount": 5, "total": "2.50"}, 

345 {"product": "soda", "amount": 2, "total": "0.00"}, 

346 ], 

347 "order_description": "5x beer, 2x soda", 

348 "age_restricted": True, 

349 "subtotal": "2.50", 

350 "discount": None, 

351 "total_amount": "2.50", 

352 "num_items": 7, 

353 "payment": None, 

354 "payer": None, 

355 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

356 } 

357 self.assertJSONEqual(response.content, expected_response) 

358 

359 with self.subTest("Delete and add product item"): 

360 data = { 

361 "order_items": [ 

362 {"product": "wine", "amount": 1}, 

363 {"product": "soda", "amount": 2}, 

364 ] 

365 } 

366 response = self.client.put( 

367 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": pk}), 

368 data, 

369 format="json", 

370 ) 

371 self.assertEqual(200, response.status_code) 

372 expected_response = { 

373 "pk": pk, 

374 "shift": self.shift.pk, 

375 "created_at": "2021-01-01T01:00:00+01:00", 

376 "order_items": [ 

377 {"product": "wine", "amount": 1, "total": "0.50"}, 

378 {"product": "soda", "amount": 2, "total": "0.00"}, 

379 ], 

380 "order_description": "1x wine, 2x soda", 

381 "age_restricted": True, 

382 "subtotal": "0.50", 

383 "discount": None, 

384 "total_amount": "0.50", 

385 "num_items": 3, 

386 "payment": None, 

387 "payer": None, 

388 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

389 } 

390 self.assertJSONEqual(response.content, expected_response) 

391 

392 with self.subTest("Write discount"): 

393 data = {"discount": 0.2} 

394 response = self.client.patch( 

395 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": pk}), 

396 data, 

397 format="json", 

398 ) 

399 self.assertEqual(200, response.status_code) 

400 expected_response = { 

401 "pk": pk, 

402 "shift": self.shift.pk, 

403 "created_at": "2021-01-01T01:00:00+01:00", 

404 "order_items": [ 

405 {"product": "wine", "amount": 1, "total": "0.50"}, 

406 {"product": "soda", "amount": 2, "total": "0.00"}, 

407 ], 

408 "order_description": "1x wine, 2x soda", 

409 "age_restricted": True, 

410 "subtotal": "0.50", 

411 "discount": "0.20", 

412 "total_amount": "0.30", 

413 "num_items": 3, 

414 "payment": None, 

415 "payer": None, 

416 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

417 } 

418 self.assertJSONEqual(response.content, expected_response) 

419 

420 with self.subTest("Reset discount"): 

421 data = {"discount": 0} 

422 response = self.client.patch( 

423 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": pk}), 

424 data, 

425 format="json", 

426 ) 

427 self.assertEqual(200, response.status_code) 

428 expected_response = { 

429 "pk": pk, 

430 "shift": self.shift.pk, 

431 "created_at": "2021-01-01T01:00:00+01:00", 

432 "order_items": [ 

433 {"product": "wine", "amount": 1, "total": "0.50"}, 

434 {"product": "soda", "amount": 2, "total": "0.00"}, 

435 ], 

436 "order_description": "1x wine, 2x soda", 

437 "age_restricted": True, 

438 "subtotal": "0.50", 

439 "discount": "0.00", 

440 "total_amount": "0.50", 

441 "num_items": 3, 

442 "payment": None, 

443 "payer": None, 

444 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

445 } 

446 self.assertJSONEqual(response.content, expected_response) 

447 

448 with self.subTest("Override total field"): 

449 data = { 

450 "order_items": [ 

451 {"product": "wine", "amount": 1, "total": "1.30"}, 

452 {"product": "soda", "amount": 2, "total": "2.00"}, 

453 ] 

454 } 

455 response = self.client.patch( 

456 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": pk}), 

457 data, 

458 format="json", 

459 ) 

460 self.assertEqual(200, response.status_code) 

461 expected_response = { 

462 "pk": pk, 

463 "shift": self.shift.pk, 

464 "created_at": "2021-01-01T01:00:00+01:00", 

465 "order_items": [ 

466 {"product": "wine", "amount": 1, "total": "1.30"}, 

467 {"product": "soda", "amount": 2, "total": "2.00"}, 

468 ], 

469 "order_description": "1x wine, 2x soda", 

470 "age_restricted": True, 

471 "subtotal": "3.30", 

472 "discount": "0.00", 

473 "total_amount": "3.30", 

474 "num_items": 3, 

475 "payment": None, 

476 "payer": None, 

477 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

478 } 

479 self.assertJSONEqual(response.content, expected_response) 

480 

481 with self.subTest("Reset overridden total fields"): 

482 data = { 

483 "order_items": [ 

484 {"product": "wine", "amount": 1}, 

485 {"product": "soda", "amount": 2}, 

486 ] 

487 } 

488 response = self.client.patch( 

489 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": pk}), 

490 data, 

491 format="json", 

492 ) 

493 self.assertEqual(200, response.status_code) 

494 expected_response = { 

495 "pk": pk, 

496 "shift": self.shift.pk, 

497 "created_at": "2021-01-01T01:00:00+01:00", 

498 "order_items": [ 

499 {"product": "wine", "amount": 1, "total": "0.50"}, 

500 {"product": "soda", "amount": 2, "total": "0.00"}, 

501 ], 

502 "order_description": "1x wine, 2x soda", 

503 "age_restricted": True, 

504 "subtotal": "0.50", 

505 "discount": "0.00", 

506 "total_amount": "0.50", 

507 "num_items": 3, 

508 "payment": None, 

509 "payer": None, 

510 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

511 } 

512 self.assertJSONEqual(response.content, expected_response) 

513 

514 with self.subTest("Write discount without custom price permissions"): 

515 self.member.is_superuser = False 

516 self.member.save() 

517 self.shift.managers.add(self.cie) 

518 self.shift.save() 

519 

520 data = {"discount": 0.2} 

521 response = self.client.patch( 

522 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": pk}), 

523 data, 

524 format="json", 

525 ) 

526 self.assertEqual(200, response.status_code) 

527 expected_response = { 

528 "pk": pk, 

529 "shift": self.shift.pk, 

530 "created_at": "2021-01-01T01:00:00+01:00", 

531 "order_items": [ 

532 {"product": "wine", "amount": 1, "total": "0.50"}, 

533 {"product": "soda", "amount": 2, "total": "0.00"}, 

534 ], 

535 "order_description": "1x wine, 2x soda", 

536 "age_restricted": True, 

537 "subtotal": "0.50", 

538 "discount": "0.00", 

539 "total_amount": "0.50", 

540 "num_items": 3, 

541 "payment": None, 

542 "payer": None, 

543 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

544 } 

545 self.assertJSONEqual(response.content, expected_response) 

546 

547 with self.subTest("Write total field without custom price permissions"): 

548 data = { 

549 "order_items": [ 

550 {"product": "wine", "amount": 1, "total": "1.30"}, 

551 {"product": "soda", "amount": 2, "total": "2.00"}, 

552 ] 

553 } 

554 response = self.client.patch( 

555 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": pk}), 

556 data, 

557 format="json", 

558 ) 

559 self.assertEqual(200, response.status_code) 

560 expected_response = { 

561 "pk": pk, 

562 "shift": self.shift.pk, 

563 "created_at": "2021-01-01T01:00:00+01:00", 

564 "order_items": [ 

565 {"product": "wine", "amount": 1, "total": "0.50"}, 

566 {"product": "soda", "amount": 2, "total": "0.00"}, 

567 ], 

568 "order_description": "1x wine, 2x soda", 

569 "age_restricted": True, 

570 "subtotal": "0.50", 

571 "discount": "0.00", 

572 "total_amount": "0.50", 

573 "num_items": 3, 

574 "payment": None, 

575 "payer": None, 

576 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

577 } 

578 self.assertJSONEqual(response.content, expected_response) 

579 

580 with self.subTest("Delete one product item"): 

581 data = { 

582 "order_items": [ 

583 {"product": "wine", "amount": 1}, 

584 ] 

585 } 

586 response = self.client.patch( 

587 reverse("api:v2:admin:sales:order-detail", kwargs={"pk": pk}), 

588 data, 

589 format="json", 

590 ) 

591 self.assertEqual(200, response.status_code) 

592 expected_response = { 

593 "pk": pk, 

594 "shift": self.shift.pk, 

595 "created_at": "2021-01-01T01:00:00+01:00", 

596 "order_items": [ 

597 {"product": "wine", "amount": 1, "total": "0.50"}, 

598 ], 

599 "order_description": "1x wine", 

600 "age_restricted": True, 

601 "subtotal": "0.50", 

602 "discount": "0.00", 

603 "total_amount": "0.50", 

604 "num_items": 1, 

605 "payment": None, 

606 "payer": None, 

607 "payment_url": f"http://localhost:8000/sales/order/{pk}/pay/", 

608 } 

609 self.assertJSONEqual(response.content, expected_response) 

610 

611 def test_invalid_product(self): 

612 data = {"order_items": [{"product": "invalidproduct", "amount": 4}]} 

613 response = self.client.post( 

614 reverse("api:v2:admin:sales:shift-orders", kwargs={"pk": self.shift.pk}), 

615 data, 

616 format="json", 

617 ) 

618 self.assertEqual(400, response.status_code) 

619 

620 def test_user_self_order(self): 

621 SelfOrderPeriod.objects.create( 

622 shift=self.shift, 

623 start=(timezone.now() - timezone.timedelta(hours=1)), 

624 end=timezone.now() + timezone.timedelta(hours=1), 

625 ) 

626 data = {"order_items": [{"product": "beer", "amount": 4}]} 

627 response = self.client.post( 

628 reverse("api:v2:sales:user-order-list", kwargs={"pk": self.shift.pk}), 

629 data, 

630 format="json", 

631 ) 

632 self.assertEqual(201, response.status_code) 

633 order = Order.objects.get(pk=response.data["pk"]) 

634 self.assertEqual(order.payer, self.member) 

635 

636 def test_claim_order(self): 

637 with self.subTest("Claim a normal order"): 

638 response = self.client.patch( 

639 reverse("api:v2:sales:order-claim", kwargs={"pk": self.o3.pk}) 

640 ) 

641 self.assertEqual(200, response.status_code) 

642 self.assertEqual( 

643 Order.objects.get(pk=response.data["pk"]).payer, self.member 

644 ) 

645 

646 self.o3.payer = None 

647 self.o3.save() 

648 

649 with self.subTest("Claim an order that is already yours"): 

650 self.o3.payer = self.member 

651 self.o3.save() 

652 

653 response = self.client.patch( 

654 reverse("api:v2:sales:order-claim", kwargs={"pk": self.o3.pk}) 

655 ) 

656 self.assertEqual(200, response.status_code) 

657 self.assertEqual( 

658 Order.objects.get(pk=response.data["pk"]).payer, self.member 

659 ) 

660 

661 self.o3.payer = None 

662 self.o3.save() 

663 

664 with self.subTest("Claim an order that is not yours"): 

665 member = Member.objects.create( 

666 username="test1", 

667 first_name="Test1", 

668 last_name="Example", 

669 email="test1@example.org", 

670 is_staff=False, 

671 is_superuser=False, 

672 ) 

673 self.o3.payer = member 

674 self.o3.save() 

675 

676 response = self.client.patch( 

677 reverse("api:v2:sales:order-claim", kwargs={"pk": self.o3.pk}) 

678 ) 

679 self.assertEqual(403, response.status_code) 

680 self.o3.refresh_from_db() 

681 self.assertEqual(self.o3.payer, member) 

682 

683 self.o3.payer = None 

684 self.o3.save() 

685 

686 with self.subTest("Claim a paid order"): 

687 response = self.client.patch( 

688 reverse("api:v2:sales:order-claim", kwargs={"pk": self.o4.pk}) 

689 ) 

690 self.assertEqual(403, response.status_code) 

691 

692 self.o3.payer = None 

693 self.o3.save() 

694 

695 with self.subTest("Claim an age-restricted order as a minor"): 

696 with mock.patch("sales.services.is_adult") as is_adult: 

697 is_adult.return_value = False 

698 response = self.client.patch( 

699 reverse("api:v2:sales:order-claim", kwargs={"pk": self.o3.pk}) 

700 ) 

701 self.assertEqual(403, response.status_code) 

702 self.o3.refresh_from_db() 

703 self.assertEqual(self.o3.payer, self.member) 

704 

705 

706class ShiftAPITest(TestCase): 

707 fixtures = [ 

708 "members.json", 

709 "bank_accounts.json", 

710 "member_groups.json", 

711 "products.json", 

712 ] 

713 

714 @classmethod 

715 def setUpTestData(cls): 

716 """Create the following test data: 

717 

718 o0: an empty order 

719 o1: an unpaid order of 2 beer 

720 o2: an order of 2 soda that doesn't need a payment 

721 o3: an unpaid order with 2 beer and 2 wine 

722 o4: a paid order with 2 wine 

723 o4: a paid order with 2 beer and 2 wine 

724 """ 

725 cls.member = Member.objects.filter(last_name="Wiggers").first() 

726 

727 cls.beer = Product.objects.get(name="beer") 

728 cls.wine = Product.objects.get(name="wine") 

729 cls.soda = Product.objects.get(name="soda") 

730 

731 cls.normal = ProductList.objects.get( 

732 name="normal", 

733 ) 

734 cls.free = ProductList.objects.get( 

735 name="free", 

736 ) 

737 

738 cls.shift = Shift.objects.create( 

739 start=timezone.now(), 

740 end=timezone.now() + timezone.timedelta(hours=1), 

741 product_list=cls.normal, 

742 ) 

743 

744 cls.shift2 = Shift.objects.create( 

745 start=timezone.now(), 

746 end=timezone.now() + timezone.timedelta(hours=1), 

747 product_list=cls.free, 

748 ) 

749 

750 cls.o0 = Order.objects.create(shift=cls.shift) 

751 cls.o1 = Order.objects.create(shift=cls.shift) 

752 OrderItem.objects.create( 

753 order=cls.o1, 

754 product=cls.shift.product_list.product_items.get(product=cls.beer), 

755 amount=2, 

756 ) 

757 cls.o2 = Order.objects.create(shift=cls.shift) 

758 OrderItem.objects.create( 

759 order=cls.o2, 

760 product=cls.shift.product_list.product_items.get(product=cls.soda), 

761 amount=2, 

762 ) 

763 cls.o3 = Order.objects.create(shift=cls.shift) 

764 OrderItem.objects.create( 

765 order=cls.o3, 

766 product=cls.shift.product_list.product_items.get(product=cls.beer), 

767 amount=2, 

768 ) 

769 OrderItem.objects.create( 

770 order=cls.o3, 

771 product=cls.shift.product_list.product_items.get(product=cls.wine), 

772 amount=2, 

773 ) 

774 cls.o4 = Order.objects.create(shift=cls.shift) 

775 OrderItem.objects.create( 

776 order=cls.o4, 

777 product=cls.shift.product_list.product_items.get(product=cls.wine), 

778 amount=2, 

779 ) 

780 cls.o5 = Order.objects.create(shift=cls.shift) 

781 OrderItem.objects.create( 

782 order=cls.o5, 

783 product=cls.shift.product_list.product_items.get(product=cls.beer), 

784 amount=2, 

785 ) 

786 OrderItem.objects.create( 

787 order=cls.o5, 

788 product=cls.shift.product_list.product_items.get(product=cls.wine), 

789 amount=2, 

790 ) 

791 cls.o4.payment = create_payment( 

792 cls.o4, processed_by=cls.member, pay_type=Payment.CASH 

793 ) 

794 cls.o4.save() 

795 cls.o5.payment = create_payment( 

796 cls.o5, processed_by=cls.member, pay_type=Payment.CASH 

797 ) 

798 cls.o5.save() 

799 

800 cls.cie = Committee.objects.get(pk=1) 

801 MemberGroupMembership.objects.create(group=cls.cie, member=cls.member) 

802 content_type = ContentType.objects.get_for_model(Order) 

803 permission1 = Permission.objects.get( 

804 content_type=content_type, codename="add_order" 

805 ) 

806 permission2 = Permission.objects.get( 

807 content_type=content_type, codename="change_order" 

808 ) 

809 permission3 = Permission.objects.get( 

810 content_type=content_type, codename="delete_order" 

811 ) 

812 permission4 = Permission.objects.get( 

813 content_type=content_type, codename="view_order" 

814 ) 

815 permission5 = Permission.objects.get( 

816 content_type=ContentType.objects.get_for_model(Shift), codename="view_shift" 

817 ) 

818 cls.cie.permissions.add(permission1) 

819 cls.cie.permissions.add(permission2) 

820 cls.cie.permissions.add(permission3) 

821 cls.cie.permissions.add(permission4) 

822 cls.cie.permissions.add(permission5) 

823 

824 def setUp(self): 

825 self.client = APIClient() 

826 self.client.force_login(self.member) 

827 

828 def test_detail_not_logged_in(self): 

829 self.client.logout() 

830 response = self.client.get( 

831 reverse("api:v2:admin:sales:shift-detail", kwargs={"pk": self.shift.pk}) 

832 ) 

833 self.assertEqual(403, response.status_code) 

834 

835 def test_detail_not_authorized__get(self): 

836 response = self.client.get( 

837 reverse("api:v2:admin:sales:shift-detail", kwargs={"pk": self.shift.pk}) 

838 ) 

839 self.assertEqual(200, response.status_code) 

840 

841 self.member.is_superuser = False 

842 self.member.save() 

843 

844 response = self.client.get( 

845 reverse("api:v2:admin:sales:shift-detail", kwargs={"pk": self.shift.pk}) 

846 ) 

847 self.assertEqual(403, response.status_code) 

848 

849 self.shift.managers.add(self.cie) 

850 self.shift.save() 

851 

852 response = self.client.get( 

853 reverse("api:v2:admin:sales:shift-detail", kwargs={"pk": self.shift.pk}) 

854 ) 

855 self.assertEqual(200, response.status_code) 

856 

857 def test_list_not_logged_in(self): 

858 self.client.logout() 

859 response = self.client.get(reverse("api:v2:admin:sales:shift-list")) 

860 self.assertEqual(403, response.status_code) 

861 

862 def test_list_not_authorized__get(self): 

863 response = self.client.get(reverse("api:v2:admin:sales:shift-list")) 

864 self.assertEqual(200, response.status_code) 

865 self.assertEqual(2, response.data["count"]) 

866 

867 self.member.is_superuser = False 

868 self.member.save() 

869 

870 response = self.client.get(reverse("api:v2:admin:sales:shift-list")) 

871 self.assertEqual(200, response.status_code) 

872 self.assertEqual(0, response.data["count"]) 

873 

874 self.shift.managers.add(self.cie) 

875 self.shift.save() 

876 

877 response = self.client.get(reverse("api:v2:admin:sales:shift-list")) 

878 self.assertEqual(200, response.status_code) 

879 self.assertEqual(1, response.data["count"])