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

331 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2026-06-21 23:59 +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 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 selforder=True, 

62 ) 

63 

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

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

66 OrderItem.objects.create( 

67 order=cls.o1, 

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

69 amount=2, 

70 ) 

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

72 OrderItem.objects.create( 

73 order=cls.o2, 

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

75 amount=2, 

76 ) 

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

78 OrderItem.objects.create( 

79 order=cls.o3, 

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

81 amount=2, 

82 ) 

83 OrderItem.objects.create( 

84 order=cls.o3, 

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

86 amount=2, 

87 ) 

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

89 OrderItem.objects.create( 

90 order=cls.o4, 

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

92 amount=2, 

93 ) 

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

95 OrderItem.objects.create( 

96 order=cls.o5, 

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

98 amount=2, 

99 ) 

100 OrderItem.objects.create( 

101 order=cls.o5, 

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

103 amount=2, 

104 ) 

105 cls.o4.payment = create_payment( 

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

107 ) 

108 cls.o4.save() 

109 cls.o5.payment = create_payment( 

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

111 ) 

112 cls.o5.save() 

113 

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

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

116 content_type = ContentType.objects.get_for_model(Order) 

117 permission1 = Permission.objects.get( 

118 content_type=content_type, codename="add_order" 

119 ) 

120 permission2 = Permission.objects.get( 

121 content_type=content_type, codename="change_order" 

122 ) 

123 permission3 = Permission.objects.get( 

124 content_type=content_type, codename="delete_order" 

125 ) 

126 permission4 = Permission.objects.get( 

127 content_type=content_type, codename="view_order" 

128 ) 

129 permission5 = Permission.objects.get( 

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

131 ) 

132 cls.cie.permissions.add(permission1) 

133 cls.cie.permissions.add(permission2) 

134 cls.cie.permissions.add(permission3) 

135 cls.cie.permissions.add(permission4) 

136 cls.cie.permissions.add(permission5) 

137 

138 def setUp(self): 

139 self.client = APIClient() 

140 self.client.force_login(self.member) 

141 

142 def test_detail_not_logged_in(self): 

143 self.client.logout() 

144 response = self.client.get( 

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

146 ) 

147 self.assertEqual(403, response.status_code) 

148 

149 def test_detail_not_authorized__get(self): 

150 response = self.client.get( 

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

152 ) 

153 self.assertEqual(200, response.status_code) 

154 

155 self.member.is_superuser = False 

156 self.member.save() 

157 

158 response = self.client.get( 

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

160 ) 

161 self.assertEqual(404, response.status_code) 

162 

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

164 self.shift.save() 

165 

166 response = self.client.get( 

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

168 ) 

169 self.assertEqual(200, response.status_code) 

170 

171 def test_detail_not_authorized__patch(self): 

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

173 

174 response = self.client.patch( 

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

176 ) 

177 self.assertEqual(200, response.status_code) 

178 

179 self.member.is_superuser = False 

180 self.member.save() 

181 

182 response = self.client.patch( 

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

184 ) 

185 self.assertEqual(404, response.status_code) 

186 

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

188 self.shift.save() 

189 

190 response = self.client.patch( 

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

192 ) 

193 self.assertEqual(200, response.status_code) 

194 

195 def test_detail_not_authorized__put(self): 

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

197 

198 response = self.client.put( 

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

200 ) 

201 self.assertEqual(200, response.status_code) 

202 

203 self.member.is_superuser = False 

204 self.member.save() 

205 

206 response = self.client.put( 

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

208 ) 

209 self.assertEqual(404, response.status_code) 

210 

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

212 self.shift.save() 

213 

214 response = self.client.put( 

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

216 ) 

217 self.assertEqual(200, response.status_code) 

218 

219 def test_detail_not_authorized__delete(self): 

220 response = self.client.delete( 

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

222 ) 

223 self.assertEqual(204, response.status_code) 

224 

225 self.member.is_superuser = False 

226 self.member.save() 

227 

228 response = self.client.delete( 

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

230 ) 

231 self.assertEqual(404, response.status_code) 

232 

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

234 self.shift.save() 

235 

236 response3 = self.client.delete( 

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

238 ) 

239 self.assertEqual(204, response3.status_code) 

240 

241 def test_list_not_logged_in(self): 

242 self.client.logout() 

243 response = self.client.get( 

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

245 ) 

246 self.assertEqual(403, response.status_code) 

247 

248 def test_list_not_authorized__get(self): 

249 response = self.client.get( 

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

251 ) 

252 self.assertEqual(200, response.status_code) 

253 

254 self.member.is_superuser = False 

255 self.member.save() 

256 

257 response = self.client.get( 

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

259 ) 

260 self.assertEqual(403, response.status_code) 

261 

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

263 self.shift.save() 

264 

265 response = self.client.get( 

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

267 ) 

268 self.assertEqual(200, response.status_code) 

269 

270 def test_list_not_authorized__post(self): 

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

272 

273 response = self.client.post( 

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

275 data, 

276 ) 

277 self.assertEqual(201, response.status_code) 

278 

279 self.member.is_superuser = False 

280 self.member.save() 

281 

282 response = self.client.post( 

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

284 data, 

285 ) 

286 self.assertEqual(403, response.status_code) 

287 

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

289 self.shift.save() 

290 

291 response = self.client.post( 

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

293 data, 

294 ) 

295 self.assertEqual(201, response.status_code) 

296 

297 def test_create_order(self): 

298 self.maxDiff = None 

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

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

301 response = self.client.post( 

302 reverse( 

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

304 ), 

305 data, 

306 format="json", 

307 ) 

308 self.assertEqual(201, response.status_code) 

309 pk = response.data["pk"] 

310 expected_response = { 

311 "pk": pk, 

312 "shift": self.shift.pk, 

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

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

315 "order_description": "4x beer", 

316 "age_restricted": True, 

317 "subtotal": "2.00", 

318 "discount": None, 

319 "total_amount": "2.00", 

320 "num_items": 4, 

321 "payment": None, 

322 "payer": None, 

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

324 } 

325 self.assertJSONEqual(response.content, expected_response) 

326 

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

328 data = { 

329 "order_items": [ 

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

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

332 ] 

333 } 

334 response = self.client.put( 

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

336 data, 

337 format="json", 

338 ) 

339 self.assertEqual(200, response.status_code) 

340 expected_response = { 

341 "pk": pk, 

342 "shift": self.shift.pk, 

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

344 "order_items": [ 

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

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

347 ], 

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

349 "age_restricted": True, 

350 "subtotal": "2.50", 

351 "discount": None, 

352 "total_amount": "2.50", 

353 "num_items": 7, 

354 "payment": None, 

355 "payer": None, 

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

357 } 

358 self.assertJSONEqual(response.content, expected_response) 

359 

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

361 data = { 

362 "order_items": [ 

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

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

365 ] 

366 } 

367 response = self.client.put( 

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

369 data, 

370 format="json", 

371 ) 

372 self.assertEqual(200, response.status_code) 

373 expected_response = { 

374 "pk": pk, 

375 "shift": self.shift.pk, 

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

377 "order_items": [ 

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

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

380 ], 

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

382 "age_restricted": True, 

383 "subtotal": "0.50", 

384 "discount": None, 

385 "total_amount": "0.50", 

386 "num_items": 3, 

387 "payment": None, 

388 "payer": None, 

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

390 } 

391 self.assertJSONEqual(response.content, expected_response) 

392 

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

394 data = {"discount": 0.2} 

395 response = self.client.patch( 

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

397 data, 

398 format="json", 

399 ) 

400 self.assertEqual(200, response.status_code) 

401 expected_response = { 

402 "pk": pk, 

403 "shift": self.shift.pk, 

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

405 "order_items": [ 

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

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

408 ], 

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

410 "age_restricted": True, 

411 "subtotal": "0.50", 

412 "discount": "0.20", 

413 "total_amount": "0.30", 

414 "num_items": 3, 

415 "payment": None, 

416 "payer": None, 

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

418 } 

419 self.assertJSONEqual(response.content, expected_response) 

420 

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

422 data = {"discount": 0} 

423 response = self.client.patch( 

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

425 data, 

426 format="json", 

427 ) 

428 self.assertEqual(200, response.status_code) 

429 expected_response = { 

430 "pk": pk, 

431 "shift": self.shift.pk, 

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

433 "order_items": [ 

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

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

436 ], 

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

438 "age_restricted": True, 

439 "subtotal": "0.50", 

440 "discount": "0.00", 

441 "total_amount": "0.50", 

442 "num_items": 3, 

443 "payment": None, 

444 "payer": None, 

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

446 } 

447 self.assertJSONEqual(response.content, expected_response) 

448 

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

450 data = { 

451 "order_items": [ 

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

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

454 ] 

455 } 

456 response = self.client.patch( 

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

458 data, 

459 format="json", 

460 ) 

461 self.assertEqual(200, response.status_code) 

462 expected_response = { 

463 "pk": pk, 

464 "shift": self.shift.pk, 

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

466 "order_items": [ 

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

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

469 ], 

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

471 "age_restricted": True, 

472 "subtotal": "3.30", 

473 "discount": "0.00", 

474 "total_amount": "3.30", 

475 "num_items": 3, 

476 "payment": None, 

477 "payer": None, 

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

479 } 

480 self.assertJSONEqual(response.content, expected_response) 

481 

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

483 data = { 

484 "order_items": [ 

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

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

487 ] 

488 } 

489 response = self.client.patch( 

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

491 data, 

492 format="json", 

493 ) 

494 self.assertEqual(200, response.status_code) 

495 expected_response = { 

496 "pk": pk, 

497 "shift": self.shift.pk, 

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

499 "order_items": [ 

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

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

502 ], 

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

504 "age_restricted": True, 

505 "subtotal": "0.50", 

506 "discount": "0.00", 

507 "total_amount": "0.50", 

508 "num_items": 3, 

509 "payment": None, 

510 "payer": None, 

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

512 } 

513 self.assertJSONEqual(response.content, expected_response) 

514 

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

516 self.member.is_superuser = False 

517 self.member.save() 

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

519 self.shift.save() 

520 

521 data = {"discount": 0.2} 

522 response = self.client.patch( 

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

524 data, 

525 format="json", 

526 ) 

527 self.assertEqual(200, response.status_code) 

528 expected_response = { 

529 "pk": pk, 

530 "shift": self.shift.pk, 

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

532 "order_items": [ 

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

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

535 ], 

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

537 "age_restricted": True, 

538 "subtotal": "0.50", 

539 "discount": "0.00", 

540 "total_amount": "0.50", 

541 "num_items": 3, 

542 "payment": None, 

543 "payer": None, 

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

545 } 

546 self.assertJSONEqual(response.content, expected_response) 

547 

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

549 data = { 

550 "order_items": [ 

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

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

553 ] 

554 } 

555 response = self.client.patch( 

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

557 data, 

558 format="json", 

559 ) 

560 self.assertEqual(200, response.status_code) 

561 expected_response = { 

562 "pk": pk, 

563 "shift": self.shift.pk, 

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

565 "order_items": [ 

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

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

568 ], 

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

570 "age_restricted": True, 

571 "subtotal": "0.50", 

572 "discount": "0.00", 

573 "total_amount": "0.50", 

574 "num_items": 3, 

575 "payment": None, 

576 "payer": None, 

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

578 } 

579 self.assertJSONEqual(response.content, expected_response) 

580 

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

582 data = { 

583 "order_items": [ 

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

585 ] 

586 } 

587 response = self.client.patch( 

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

589 data, 

590 format="json", 

591 ) 

592 self.assertEqual(200, response.status_code) 

593 expected_response = { 

594 "pk": pk, 

595 "shift": self.shift.pk, 

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

597 "order_items": [ 

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

599 ], 

600 "order_description": "1x wine", 

601 "age_restricted": True, 

602 "subtotal": "0.50", 

603 "discount": "0.00", 

604 "total_amount": "0.50", 

605 "num_items": 1, 

606 "payment": None, 

607 "payer": None, 

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

609 } 

610 self.assertJSONEqual(response.content, expected_response) 

611 

612 def test_invalid_product(self): 

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

614 response = self.client.post( 

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

616 data, 

617 format="json", 

618 ) 

619 self.assertEqual(400, response.status_code) 

620 

621 def test_user_self_order(self): 

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

623 response = self.client.post( 

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

625 data, 

626 format="json", 

627 ) 

628 self.assertEqual(201, response.status_code) 

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

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

631 

632 def test_claim_order(self): 

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

634 response = self.client.patch( 

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

636 ) 

637 self.assertEqual(200, response.status_code) 

638 self.assertEqual( 

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

640 ) 

641 

642 self.o3.payer = None 

643 self.o3.save() 

644 

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

646 self.o3.payer = self.member 

647 self.o3.save() 

648 

649 response = self.client.patch( 

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

651 ) 

652 self.assertEqual(200, response.status_code) 

653 self.assertEqual( 

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

655 ) 

656 

657 self.o3.payer = None 

658 self.o3.save() 

659 

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

661 member = Member.objects.create( 

662 username="test1", 

663 first_name="Test1", 

664 last_name="Example", 

665 email="test1@example.org", 

666 is_staff=False, 

667 is_superuser=False, 

668 ) 

669 self.o3.payer = member 

670 self.o3.save() 

671 

672 response = self.client.patch( 

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

674 ) 

675 self.assertEqual(403, response.status_code) 

676 self.o3.refresh_from_db() 

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

678 

679 self.o3.payer = None 

680 self.o3.save() 

681 

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

683 response = self.client.patch( 

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

685 ) 

686 self.assertEqual(403, response.status_code) 

687 

688 self.o3.payer = None 

689 self.o3.save() 

690 

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

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

693 is_adult.return_value = False 

694 response = self.client.patch( 

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

696 ) 

697 self.assertEqual(403, response.status_code) 

698 self.o3.refresh_from_db() 

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

700 

701 

702class ShiftAPITest(TestCase): 

703 fixtures = [ 

704 "members.json", 

705 "bank_accounts.json", 

706 "member_groups.json", 

707 "products.json", 

708 ] 

709 

710 @classmethod 

711 def setUpTestData(cls): 

712 """Create the following test data: 

713 

714 o0: an empty order 

715 o1: an unpaid order of 2 beer 

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

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

718 o4: a paid order with 2 wine 

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

720 """ 

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

722 

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

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

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

726 

727 cls.normal = ProductList.objects.get( 

728 name="normal", 

729 ) 

730 cls.free = ProductList.objects.get( 

731 name="free", 

732 ) 

733 

734 cls.shift = Shift.objects.create( 

735 start=timezone.now(), 

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

737 product_list=cls.normal, 

738 ) 

739 

740 cls.shift2 = Shift.objects.create( 

741 start=timezone.now(), 

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

743 product_list=cls.free, 

744 ) 

745 

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

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

748 OrderItem.objects.create( 

749 order=cls.o1, 

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

751 amount=2, 

752 ) 

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

754 OrderItem.objects.create( 

755 order=cls.o2, 

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

757 amount=2, 

758 ) 

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

760 OrderItem.objects.create( 

761 order=cls.o3, 

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

763 amount=2, 

764 ) 

765 OrderItem.objects.create( 

766 order=cls.o3, 

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

768 amount=2, 

769 ) 

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

771 OrderItem.objects.create( 

772 order=cls.o4, 

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

774 amount=2, 

775 ) 

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

777 OrderItem.objects.create( 

778 order=cls.o5, 

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

780 amount=2, 

781 ) 

782 OrderItem.objects.create( 

783 order=cls.o5, 

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

785 amount=2, 

786 ) 

787 cls.o4.payment = create_payment( 

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

789 ) 

790 cls.o4.save() 

791 cls.o5.payment = create_payment( 

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

793 ) 

794 cls.o5.save() 

795 

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

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

798 content_type = ContentType.objects.get_for_model(Order) 

799 permission1 = Permission.objects.get( 

800 content_type=content_type, codename="add_order" 

801 ) 

802 permission2 = Permission.objects.get( 

803 content_type=content_type, codename="change_order" 

804 ) 

805 permission3 = Permission.objects.get( 

806 content_type=content_type, codename="delete_order" 

807 ) 

808 permission4 = Permission.objects.get( 

809 content_type=content_type, codename="view_order" 

810 ) 

811 permission5 = Permission.objects.get( 

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

813 ) 

814 cls.cie.permissions.add(permission1) 

815 cls.cie.permissions.add(permission2) 

816 cls.cie.permissions.add(permission3) 

817 cls.cie.permissions.add(permission4) 

818 cls.cie.permissions.add(permission5) 

819 

820 def setUp(self): 

821 self.client = APIClient() 

822 self.client.force_login(self.member) 

823 

824 def test_detail_not_logged_in(self): 

825 self.client.logout() 

826 response = self.client.get( 

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

828 ) 

829 self.assertEqual(403, response.status_code) 

830 

831 def test_detail_not_authorized__get(self): 

832 response = self.client.get( 

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

834 ) 

835 self.assertEqual(200, response.status_code) 

836 

837 self.member.is_superuser = False 

838 self.member.save() 

839 

840 response = self.client.get( 

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

842 ) 

843 self.assertEqual(403, response.status_code) 

844 

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

846 self.shift.save() 

847 

848 response = self.client.get( 

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

850 ) 

851 self.assertEqual(200, response.status_code) 

852 

853 def test_list_not_logged_in(self): 

854 self.client.logout() 

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

856 self.assertEqual(403, response.status_code) 

857 

858 def test_list_not_authorized__get(self): 

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

860 self.assertEqual(200, response.status_code) 

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

862 

863 self.member.is_superuser = False 

864 self.member.save() 

865 

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

867 self.assertEqual(200, response.status_code) 

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

869 

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

871 self.shift.save() 

872 

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

874 self.assertEqual(200, response.status_code) 

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