Coverage for website/sales/models/product.py: 100.00%

35 statements  

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

1from django.core.validators import MinValueValidator 

2from django.db import models 

3from django.utils.translation import gettext_lazy as _ 

4 

5from payments.models import PaymentAmountField 

6 

7 

8class Product(models.Model): 

9 """Products that can be ordered.""" 

10 

11 name = models.CharField( 

12 verbose_name=_("name"), max_length=50, unique=True, null=False, blank=False 

13 ) 

14 age_restricted = models.BooleanField( 

15 verbose_name=_("age restricted"), default=False, null=False, blank=False 

16 ) 

17 

18 def __str__(self): 

19 return self.name 

20 

21 class Meta: 

22 """Meta class.""" 

23 

24 verbose_name = _("product") 

25 verbose_name_plural = _("products") 

26 ordering = ["name"] 

27 

28 

29class ProductList(models.Model): 

30 """A list of products with various prices.""" 

31 

32 class Meta: 

33 verbose_name = _("product list") 

34 verbose_name_plural = _("product lists") 

35 

36 name = models.CharField( 

37 verbose_name=_("name"), max_length=50, unique=True, null=False, blank=False 

38 ) 

39 products = models.ManyToManyField( 

40 Product, verbose_name=_("products"), through="ProductListItem" 

41 ) 

42 

43 def __str__(self): 

44 return self.name 

45 

46 

47class ProductListItem(models.Model): 

48 class Meta: 

49 verbose_name = _("product") 

50 verbose_name_plural = _("products") 

51 ordering = ("-priority",) 

52 

53 product = models.ForeignKey( 

54 Product, 

55 verbose_name=_("product"), 

56 null=False, 

57 blank=False, 

58 on_delete=models.CASCADE, 

59 ) 

60 product_list = models.ForeignKey( 

61 ProductList, 

62 verbose_name=_("product list"), 

63 related_name="product_items", 

64 null=False, 

65 blank=False, 

66 on_delete=models.CASCADE, 

67 ) 

68 price = PaymentAmountField( 

69 verbose_name=_("price"), 

70 allow_zero=True, 

71 validators=[MinValueValidator(0)], 

72 ) 

73 priority = models.IntegerField( 

74 verbose_name=_("priority"), 

75 help_text="Determines the order of the products in the list, highest first.", 

76 default=1, 

77 ) 

78 

79 def __str__(self): 

80 return f"{self.product} ({self.price})" 

81 

82 @property 

83 def product_name(self): 

84 return self.product.name