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

259 statements  

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

1from unittest.mock import MagicMock 

2 

3from django.core.exceptions import ValidationError 

4from django.test import TestCase 

5from django.utils import timezone 

6 

7from freezegun import freeze_time 

8 

9from activemembers.models import Committee, MemberGroupMembership 

10from members.models import Member 

11from payments.exceptions import PaymentError 

12from payments.models import Payment 

13from payments.services import create_payment 

14from sales.models.order import Order, OrderItem 

15from sales.models.product import Product, ProductList 

16from sales.models.shift import Shift 

17from sales.services import is_manager 

18 

19 

20class ProductTest(TestCase): 

21 @classmethod 

22 def setUpTestData(cls): 

23 cls.beer = Product.objects.create(name="beer", age_restricted=True) 

24 cls.wine = Product.objects.create(name="wine", age_restricted=True) 

25 cls.soda = Product.objects.create(name="soda", age_restricted=False) 

26 

27 def test_str(self): 

28 self.assertEqual("beer", str(self.beer)) 

29 self.assertEqual("wine", str(self.wine)) 

30 self.assertEqual("soda", str(self.soda)) 

31 

32 

33class ProductListTest(TestCase): 

34 fixtures = ["members.json", "products.json"] 

35 

36 @classmethod 

37 def setUpTestData(cls): 

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

39 

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

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

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

43 

44 cls.normal = ProductList.objects.get( 

45 name="normal", 

46 ) 

47 cls.free = ProductList.objects.get( 

48 name="free", 

49 ) 

50 

51 def test_str(self): 

52 self.assertEqual("normal", str(self.normal)) 

53 self.assertEqual("free", str(self.free)) 

54 

55 

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

57class OrderTest(TestCase): 

58 fixtures = ["members.json", "bank_accounts.json", "products.json"] 

59 

60 @classmethod 

61 def setUpTestData(cls): 

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

63 

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

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

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

67 

68 cls.normal = ProductList.objects.get( 

69 name="normal", 

70 ) 

71 cls.free = ProductList.objects.get( 

72 name="free", 

73 ) 

74 

75 cls.shift = Shift.objects.create( 

76 start=timezone.now(), 

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

78 product_list=cls.normal, 

79 ) 

80 

81 def test_age_restricted(self): 

82 order = Order.objects.create(shift=self.shift) 

83 self.assertFalse(order.age_restricted) 

84 i1 = OrderItem.objects.create( 

85 order=order, 

86 product=self.shift.product_list.product_items.get(product=self.soda), 

87 amount=1, 

88 ) 

89 self.assertFalse(order.age_restricted) 

90 i2 = OrderItem.objects.create( 

91 order=order, 

92 product=self.shift.product_list.product_items.get(product=self.beer), 

93 amount=1, 

94 ) 

95 self.assertTrue(order.age_restricted) 

96 i2.delete() 

97 self.assertFalse(order.age_restricted) 

98 

99 def test_subtotal(self): 

100 order = Order.objects.create(shift=self.shift) 

101 self.assertEqual(order.subtotal, 0) 

102 i1 = OrderItem.objects.create( 

103 order=order, 

104 product=self.shift.product_list.product_items.get(product=self.soda), 

105 amount=2, 

106 ) 

107 self.assertEqual(order.subtotal, 0) 

108 i2 = OrderItem.objects.create( 

109 order=order, 

110 product=self.shift.product_list.product_items.get(product=self.beer), 

111 amount=2, 

112 ) 

113 self.assertEqual(order.subtotal, 1) 

114 i3 = OrderItem.objects.create( 

115 order=order, 

116 product=self.shift.product_list.product_items.get(product=self.wine), 

117 amount=3, 

118 ) 

119 self.assertEqual(order.subtotal, 2.5) 

120 i2.delete() 

121 self.assertEqual(order.subtotal, 1.5) 

122 i3.total = 4 

123 i3.save() 

124 self.assertEqual(order.subtotal, 4) 

125 

126 def test_total_amount(self): 

127 order = Order.objects.create(shift=self.shift) 

128 self.assertEqual(order.total_amount, 0) 

129 i1 = OrderItem.objects.create( 

130 order=order, 

131 product=self.shift.product_list.product_items.get(product=self.soda), 

132 amount=2, 

133 ) 

134 self.assertEqual(order.total_amount, 0) 

135 i2 = OrderItem.objects.create( 

136 order=order, 

137 product=self.shift.product_list.product_items.get(product=self.beer), 

138 amount=2, 

139 ) 

140 self.assertEqual(order.total_amount, 1) 

141 i3 = OrderItem.objects.create( 

142 order=order, 

143 product=self.shift.product_list.product_items.get(product=self.wine), 

144 amount=3, 

145 ) 

146 self.assertEqual(order.total_amount, 2.5) 

147 i2.delete() 

148 self.assertEqual(order.total_amount, 1.5) 

149 i3.total = 4 

150 i3.save() 

151 self.assertEqual(order.total_amount, 4) 

152 order.discount = 2 

153 order.save() 

154 self.assertEqual(order.total_amount, 2) 

155 

156 def test_num_items(self): 

157 order = Order.objects.create(shift=self.shift) 

158 self.assertEqual(order.num_items, 0) 

159 i1 = OrderItem.objects.create( 

160 order=order, 

161 product=self.shift.product_list.product_items.get(product=self.soda), 

162 amount=2, 

163 ) 

164 self.assertEqual(order.num_items, 2) 

165 i2 = OrderItem.objects.create( 

166 order=order, 

167 product=self.shift.product_list.product_items.get(product=self.beer), 

168 amount=2, 

169 ) 

170 self.assertEqual(order.num_items, 4) 

171 i3 = OrderItem.objects.create( 

172 order=order, 

173 product=self.shift.product_list.product_items.get(product=self.wine), 

174 amount=3, 

175 ) 

176 self.assertEqual(order.num_items, 7) 

177 i2.delete() 

178 self.assertEqual(order.num_items, 5) 

179 

180 def test_create_order_shift_locked(self): 

181 self.shift.locked = True 

182 self.shift.save() 

183 

184 with self.assertRaises(ValueError): 

185 Order.objects.create(shift=self.shift) 

186 

187 def test_create_order_shift_not_started(self): 

188 self.shift.start = self.shift.start + timezone.timedelta(days=2) 

189 self.shift.end = self.shift.end + timezone.timedelta(days=2) 

190 self.shift.save() 

191 

192 with self.assertRaises(ValueError): 

193 Order.objects.create(shift=self.shift) 

194 

195 def test_update_order_shift_locked(self): 

196 order = Order.objects.create(shift=self.shift) 

197 OrderItem.objects.create( 

198 order=order, 

199 product=self.shift.product_list.product_items.get(product=self.beer), 

200 amount=2, 

201 ) 

202 

203 self.shift.locked = True 

204 self.shift.save() 

205 

206 order.discount = 0.5 

207 

208 with self.assertRaises(Order.DoesNotExist): 

209 order.refresh_from_db() 

210 

211 def test_update_order_paid(self): 

212 order = Order.objects.create(shift=self.shift) 

213 i1 = OrderItem.objects.create( 

214 order=order, 

215 product=self.shift.product_list.product_items.get(product=self.beer), 

216 amount=2, 

217 ) 

218 self.assertEqual(order.total_amount, 1) 

219 self.assertEqual(i1.total, 1) 

220 order.payment = create_payment( 

221 order, processed_by=self.member, pay_type=Payment.CASH 

222 ) 

223 order.save() 

224 

225 order.refresh_from_db() 

226 self.assertIsNotNone(order.payment) 

227 

228 order.discount = 0.5 

229 

230 with self.assertRaises(PaymentError): 

231 order.save() 

232 

233 with self.assertRaises(ValueError): 

234 OrderItem.objects.create( 

235 order=order, 

236 product=self.shift.product_list.product_items.get(product=self.wine), 

237 amount=1, 

238 ) 

239 

240 i1.amount = 3 

241 with self.assertRaises(ValueError): 

242 i1.save() 

243 

244 i1.refresh_from_db() 

245 self.assertEqual(i1.amount, 2) 

246 

247 def test_discount_amount(self): 

248 order = Order.objects.create(shift=self.shift) 

249 i1 = OrderItem.objects.create( 

250 order=order, 

251 product=self.shift.product_list.product_items.get(product=self.beer), 

252 amount=2, 

253 ) 

254 order.discount = 2 

255 with self.assertRaises(ValidationError): 

256 order.clean() 

257 

258 order.discount = 1 

259 order.clean() 

260 order.save() 

261 self.assertEqual(order.total_amount, 0) 

262 

263 def test_order_item_total(self): 

264 order = Order.objects.create(shift=self.shift) 

265 i1 = OrderItem.objects.create( 

266 order=order, 

267 product=self.shift.product_list.product_items.get(product=self.beer), 

268 amount=2, 

269 ) 

270 i1.refresh_from_db() 

271 self.assertEqual(i1.total, 1) 

272 

273 i1.total = None 

274 i1.save() 

275 i1.refresh_from_db() 

276 self.assertEqual(i1.total, 1) 

277 

278 i1.total = 2 

279 i1.save() 

280 i1.refresh_from_db() 

281 self.assertEqual(i1.total, 2) 

282 

283 def test_nonexistent_product(self): 

284 order = Order.objects.create(shift=self.shift) 

285 i1 = OrderItem.objects.create( 

286 order=order, 

287 product=self.free.product_items.get(product=self.beer), 

288 amount=2, 

289 ) 

290 with self.assertRaises(ValidationError): 

291 i1.clean() 

292 

293 

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

295class ShiftTest(TestCase): 

296 fixtures = [ 

297 "members.json", 

298 "bank_accounts.json", 

299 "member_groups.json", 

300 "products.json", 

301 ] 

302 

303 @classmethod 

304 def setUpTestData(cls): 

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

306 

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

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

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

310 

311 cls.normal = ProductList.objects.get( 

312 name="normal", 

313 ) 

314 cls.free = ProductList.objects.get( 

315 name="free", 

316 ) 

317 

318 cls.shift = Shift.objects.create( 

319 start=timezone.now(), 

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

321 product_list=cls.normal, 

322 ) 

323 

324 def test_time(self): 

325 self.assertTrue(self.shift.active) 

326 self.shift.start = self.shift.end 

327 with self.assertRaises(ValidationError): 

328 self.shift.clean() 

329 

330 def test_remove_orders_on_locked(self): 

331 order1 = Order.objects.create(shift=self.shift) 

332 order1.save() 

333 

334 order2 = Order.objects.create(shift=self.shift) 

335 OrderItem.objects.create( 

336 order=order2, 

337 product=self.shift.product_list.product_items.get(product=self.soda), 

338 amount=2, 

339 ) 

340 order2.save() 

341 

342 order3 = Order.objects.create(shift=self.shift) 

343 OrderItem.objects.create( 

344 order=order3, 

345 product=self.shift.product_list.product_items.get(product=self.beer), 

346 amount=2, 

347 ) 

348 order3.save() 

349 

350 order4 = Order.objects.create(shift=self.shift) 

351 OrderItem.objects.create( 

352 order=order4, 

353 product=self.shift.product_list.product_items.get(product=self.beer), 

354 amount=2, 

355 ) 

356 order4.save() 

357 order4.payment = create_payment( 

358 order4, processed_by=self.member, pay_type=Payment.CASH 

359 ) 

360 order4.save() 

361 

362 self.shift.locked = True 

363 self.shift.save() 

364 

365 with self.assertRaises(Order.DoesNotExist): 

366 order1.refresh_from_db() 

367 

368 order2.refresh_from_db() 

369 

370 with self.assertRaises(Order.DoesNotExist): 

371 order3.refresh_from_db() 

372 

373 order4.refresh_from_db() 

374 

375 def test_active(self): 

376 self.assertTrue(self.shift.active) 

377 self.shift.start = self.shift.start + timezone.timedelta(minutes=10) 

378 self.assertFalse(self.shift.active) 

379 

380 def test_shift_statistics(self): 

381 self.assertEqual(self.shift.total_revenue, 0) 

382 self.assertEqual(self.shift.total_revenue_paid, 0) 

383 

384 self.assertEqual(self.shift.num_orders, 0) 

385 self.assertEqual(self.shift.num_orders_paid, 0) 

386 

387 self.assertDictEqual(self.shift.product_sales, {}) 

388 

389 o1 = Order.objects.create(shift=self.shift) 

390 OrderItem.objects.create( 

391 order=o1, 

392 product=self.shift.product_list.product_items.get(product=self.beer), 

393 amount=2, 

394 ) 

395 o2 = Order.objects.create(shift=self.shift) 

396 OrderItem.objects.create( 

397 order=o2, 

398 product=self.shift.product_list.product_items.get(product=self.soda), 

399 amount=2, 

400 ) 

401 o3 = Order.objects.create(shift=self.shift) 

402 OrderItem.objects.create( 

403 order=o3, 

404 product=self.shift.product_list.product_items.get(product=self.beer), 

405 amount=2, 

406 ) 

407 OrderItem.objects.create( 

408 order=o3, 

409 product=self.shift.product_list.product_items.get(product=self.wine), 

410 amount=2, 

411 ) 

412 o4 = Order.objects.create(shift=self.shift) 

413 OrderItem.objects.create( 

414 order=o4, 

415 product=self.shift.product_list.product_items.get(product=self.wine), 

416 amount=2, 

417 ) 

418 o5 = Order.objects.create(shift=self.shift) 

419 OrderItem.objects.create( 

420 order=o5, 

421 product=self.shift.product_list.product_items.get(product=self.beer), 

422 amount=2, 

423 ) 

424 OrderItem.objects.create( 

425 order=o5, 

426 product=self.shift.product_list.product_items.get(product=self.wine), 

427 amount=2, 

428 ) 

429 o4.payment = create_payment(o4, processed_by=self.member, pay_type=Payment.CASH) 

430 o4.save() 

431 o5.payment = create_payment(o5, processed_by=self.member, pay_type=Payment.CASH) 

432 o5.save() 

433 

434 # The test was accidentally passing because orders had the same created at, 

435 # resulting in the distinct seeing them as the same order 

436 o2.created_at += timezone.timedelta(minutes=1) 

437 o3.created_at += timezone.timedelta(minutes=2) 

438 o4.created_at += timezone.timedelta(minutes=3) 

439 o5.created_at += timezone.timedelta(minutes=4) 

440 o2.save() 

441 o3.save() 

442 o4.save() 

443 o5.save() 

444 

445 self.assertEqual(self.shift.total_revenue, 6) 

446 self.assertEqual(self.shift.total_revenue_paid, 3) 

447 

448 self.assertEqual(self.shift.num_orders, 5) 

449 self.assertEqual(self.shift.num_orders_paid, 3) 

450 

451 self.assertDictEqual( 

452 self.shift.product_sales, 

453 {self.beer.name: 6, self.soda.name: 2, self.wine.name: 6}, 

454 ) 

455 

456 def test_is_manager(self): 

457 # @todo Move this test to test_services 

458 self.member.is_superuser = False 

459 self.assertFalse(is_manager(self.member, self.shift)) 

460 

461 cie = Committee.objects.get(pk=1) 

462 MemberGroupMembership.objects.create(group=cie, member=self.member) 

463 self.shift.managers.add(cie) 

464 self.assertTrue(is_manager(self.member, self.shift)) 

465 

466 self.shift.managers.remove(cie) 

467 self.assertFalse(is_manager(self.member, self.shift)) 

468 

469 self.member.is_superuser = True 

470 self.assertTrue(is_manager(self.member, self.shift)) 

471 self.member.is_superuser = False 

472 self.member.has_perm = MagicMock() 

473 self.member.has_perm.return_value = True 

474 self.assertTrue(is_manager(self.member, self.shift))