Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

# -*- coding: utf-8 -*- 

from django.contrib.auth.models import AnonymousUser 

from django.db import models, transaction 

from django.db.models.aggregates import Count 

from polymorphic.manager import PolymorphicManager 

 

from shop.order_signals import processing 

 

 

#============================================================================== 

# Product 

#============================================================================== 

 

class ProductStatisticsManager(PolymorphicManager): 

    """ 

    A Manager for all the non-object manipulation needs, mostly statistics and 

    other "data-mining" toys. 

    """ 

 

    def top_selling_products(self, quantity): 

        """ 

        This method "mines" the previously passed orders, and gets a list of 

        products (of a size equal to the quantity parameter), ordered by how 

        many times they have been purchased. 

        """ 

        # Importing here is fugly, but it saves us from circular imports... 

        from shop.models.ordermodel import OrderItem 

        # Get an aggregate of product references and their respective counts 

        top_products_data = OrderItem.objects.values( 

                'product').annotate( 

                    product_count=Count('product') 

                ).order_by('product_count' 

            )[:quantity] 

 

        # The top_products_data result should be in the form: 

        # [{'product_reference': '<product_id>', 'product_count': <count>}, ..] 

 

        top_products_list = []  # The actual list of products 

        for values in top_products_data: 

            prod = values.get('product') 

            # We could eventually return the count easily here, if needed. 

            top_products_list.append(prod) 

 

        return top_products_list 

 

 

class ProductManager(PolymorphicManager): 

    """ 

    A more classic manager for Product filtering and manipulation. 

    """ 

    def active(self): 

        return self.filter(active=True) 

 

 

#============================================================================== 

# Order 

#============================================================================== 

 

class OrderManager(models.Manager): 

 

    def get_latest_for_user(self, user): 

        """ 

        Returns the last Order (from a time perspective) a given user has 

        placed. 

        """ 

        if user and not isinstance(user, AnonymousUser): 

            return self.filter(user=user).order_by('-modified')[0] 

        else: 

            return None 

 

    def get_unconfirmed_for_cart(self, cart): 

        return self.filter(cart_pk=cart.pk, status__lt=self.model.CONFIRMED) 

 

    def remove_old_orders(self, cart): 

        """ 

        Removes all old unconfirmed order objects. 

        """ 

        old_orders = self.get_unconfirmed_for_cart(cart) 

        old_orders.delete() 

 

    def create_order_object(self, cart, state): 

        """ 

        Create an empty order object and fill it with the given cart data. 

        """ 

        order = self.model() 

        order.cart_pk = cart.pk 

        order.user = cart.user 

        order.status = self.model.PROCESSING  # Processing 

        order.order_subtotal = cart.subtotal_price 

        order.order_total = cart.total_price 

        return order 

 

    @transaction.commit_on_success 

    def create_from_cart(self, cart, state=None): 

        """ 

        This creates a new Order object (and all the rest) from a passed Cart 

        object. 

 

        Specifically, it creates an Order with corresponding OrderItems and 

        eventually corresponding ExtraPriceFields 

 

        This will only actually commit the transaction once the function exits 

        to minimize useless database access. 

 

        The `state` parameter is further passed to process_cart_item, 

        process_cart, and post_process_cart, so it can be used as a way to 

        store per-request arbitrary information. 

 

        Emits the ``processing`` signal. 

        """ 

        # must be imported here! 

        from shop.models.ordermodel import ( 

            ExtraOrderItemPriceField, 

            ExtraOrderPriceField, 

            OrderItem, 

        ) 

        from shop.models.cartmodel import CartItem 

 

        # First, let's remove old orders 

        self.remove_old_orders(cart) 

 

        # Create an empty order object 

        order = self.create_order_object(cart, state) 

        order.save() 

 

        # Let's serialize all the extra price arguments in DB 

        for label, value in cart.extra_price_fields: 

            eoi = ExtraOrderPriceField() 

            eoi.order = order 

            eoi.label = unicode(label) 

            eoi.value = value 

            eoi.save() 

 

        # There, now move on to the order items. 

        cart_items = CartItem.objects.filter(cart=cart) 

        for item in cart_items: 

            item.update(state) 

            order_item = OrderItem() 

            order_item.order = order 

            order_item.product_reference = item.product.get_product_reference() 

            order_item.product_name = item.product.get_name() 

            order_item.product = item.product 

            order_item.unit_price = item.product.get_price() 

            order_item.quantity = item.quantity 

            order_item.line_total = item.line_total 

            order_item.line_subtotal = item.line_subtotal 

            order_item.save() 

            # For each order item, we save the extra_price_fields to DB 

150            for label, value in item.extra_price_fields: 

                eoi = ExtraOrderItemPriceField() 

                eoi.order_item = order_item 

                # Force unicode, in case it has àö... 

                eoi.label = unicode(label) 

                eoi.value = value 

                eoi.save() 

 

        processing.send(self.model, order=order, cart=cart) 

        return order