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

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

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

from decimal import Decimal 

from django.contrib.auth.models import User 

from django.db import models, transaction 

from django.db.models.aggregates import Sum 

from django.utils.translation import ugettext_lazy as _ 

from django.core.urlresolvers import reverse 

 

from shop.models.cartmodel import CartItem 

from shop.models.clientmodel import Client 

from shop.models.productmodel import Product 

from shop.util.fields import CurrencyField 

 

class OrderManager(models.Manager): 

 

    @transaction.commit_on_success 

    def create_from_cart(self, cart): 

        """ 

        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. 

         

        """ 

        # Let's create the Order itself: 

        o = Order() 

        o.user = cart.user 

        o.status = Order.STATUS_CODES[0][0] # Processing 

 

        o.order_subtotal = cart.subtotal_price 

        o.order_total = cart.total_price 

 

        user = cart.user 

        client = None 

 

46        if user: 

            try: 

                client = cart.user.client 

            except Client.DoesNotExist: 

                client = None 

 

        if user and client: 

 

            ship_address = cart.user.client.addresses.filter(is_shipping=True)[0] 

            bill_address = cart.user.client.addresses.filter(is_billing=True)[0] 

 

 

            o.shipping_name = "%s %s" % (cart.user.first_name, cart.user.last_name) 

            o.shipping_address = ship_address.address 

            o.shipping_address2 = ship_address.address2 

            o.shipping_city = ship_address.city 

            o.shipping_zip_code = ship_address.zip_code 

            o.shipping_state = ship_address.state 

            o.shipping_country = ship_address.country.name 

 

            o.billing_name = "%s %s" % (cart.user.first_name, cart.user.last_name) 

            o.billing_address = bill_address.address 

            o.billing_address2 = bill_address.address2 

            o.billing_city = bill_address.city 

            o.billing_zip_code = bill_address.zip_code 

            o.billing_state = bill_address.state 

            o.billing_country = bill_address.country.name 

        o.save() 

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

        for label, value in cart.extra_price_fields: 

            eoi = ExtraOrderPriceField() 

            eoi.order = o 

            eoi.label = 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() 

            i = OrderItem() 

            i.order = o 

            i.product_reference = item.product.id 

            i.product_name = item.product.name 

            i.unit_price = item.product.get_price() 

            i.quantity = item.quantity 

            i.line_total = item.line_total 

            i.line_subtotal = item.line_subtotal 

            i.save() 

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

91            for label, value in item.extra_price_fields: 

                eoi = ExtraOrderItemPriceField() 

                eoi.order_item = i 

                eoi.label = label 

                eoi.value = value 

                eoi.save() 

        return o 

 

class Order(models.Model): 

    """ 

    A model representing an Order. 

     

    An order is the "in process" counterpart of the shopping cart, which holds 

    stuff like the shipping and billing addresses (copied from the User profile) 

    when the Order is first created), list of items, and holds stuff like the 

    status, shipping costs, taxes, etc... 

    """ 

 

    PROCESSING = 1 # New order, no shipping/payment backend chosen yet 

    CONFIRMED = 2 # Chosen shipping/payment backend, processing payment 

    COMPLETED = 3 # Successful payment confirmed by payment backend 

    SHIPPED = 4 # successful order shipped to client 

    CANCELLED = 5 # order has been cancelled 

 

    STATUS_CODES = ( 

        (PROCESSING, 'Processing'), 

        (CONFIRMED, 'Confirmed'), 

        (COMPLETED, 'Completed'), 

        (SHIPPED, 'Shipped'), 

        (CANCELLED, 'Cancelled'), 

    ) 

 

    # If the user is null, the order was created with a session 

    user = models.ForeignKey(User, blank=True, null=True) 

 

    status = models.IntegerField(choices=STATUS_CODES, default=PROCESSING) 

 

    order_subtotal = CurrencyField() 

    order_total = CurrencyField() 

 

    payment_method = models.CharField(max_length=255, null=True) 

 

    # Addresses MUST be copied over to the order when it's created, however 

    # the fields must be nullable since sometimes we cannot create the address  

    # fields right away (for instance when the shopper is a guest) 

    shipping_name = models.CharField(max_length=255, null=True) 

    shipping_address = models.CharField(max_length=255, null=True) 

    shipping_address2 = models.CharField(max_length=255, null=True) 

    shipping_city = models.CharField(max_length=255, null=True) 

    shipping_zip_code = models.CharField(max_length=20, null=True) 

    shipping_state = models.CharField(max_length=255, null=True) 

    shipping_country = models.CharField(max_length=255, null=True) 

 

    billing_name = models.CharField(max_length=255, null=True) 

    billing_address = models.CharField(max_length=255, null=True) 

    billing_address2 = models.CharField(max_length=255, null=True) 

    billing_city = models.CharField(max_length=255, null=True) 

    billing_zip_code = models.CharField(max_length=20, null=True) 

    billing_state = models.CharField(max_length=255, null=True) 

    billing_country = models.CharField(max_length=255, null=True) 

 

    created = models.DateTimeField(auto_now_add=True, 

            verbose_name=_('Created')) 

    modified = models.DateTimeField(auto_now=True, 

            verbose_name=_('Updated')) 

 

    objects = OrderManager() 

 

    class Meta: 

        app_label = 'shop' 

 

    def is_payed(self): 

        """Has this order been integrally payed for?""" 

        return self.amount_payed == self.order_total 

 

    def is_completed(self): 

        return self.status == self.COMPLETED 

 

    @property 

    def amount_payed(self): 

        """ 

        The amount payed is the sum of related orderpayments 

        """ 

        sum = OrderPayment.objects.filter(order=self).aggregate(sum=Sum('amount')) 

        result = sum.get('sum') 

        if not result: 

            result = Decimal('0') 

        return result 

 

    @property 

    def shipping_costs(self): 

        sum = Decimal('0.0') 

        cost_list = ExtraOrderPriceField.objects.filter(order=self).filter(is_shipping=True) 

        for cost in cost_list: 

            sum = sum + cost.value 

        return sum 

 

    def __unicode__(self): 

        return _('Order ID: %(id)s') % {'id': self.id} 

 

    def get_absolute_url(self): 

        return reverse('order_detail', kwargs={'pk': self.pk }) 

 

 

class OrderItem(models.Model): 

    """ 

    A line Item for an order. 

    """ 

 

    order = models.ForeignKey(Order, related_name='items') 

 

    product_reference = models.CharField(max_length=255) 

    product_name = models.CharField(max_length=255) 

    unit_price = CurrencyField() 

    quantity = models.IntegerField() 

 

    line_subtotal = CurrencyField() 

    line_total = CurrencyField() 

 

    class Meta: 

        app_label = 'shop' 

 

    @property 

    def product(self): 

        return Product.objects.get(pk=self.product_reference) 

 

class OrderExtraInfo(models.Model): 

    """ 

    A holder for extra textual information to attach to this order. 

    """ 

    order = models.ForeignKey(Order, related_name="extra_info") 

    text = models.TextField() 

 

    class Meta: 

        app_label = 'shop' 

 

class ExtraOrderPriceField(models.Model): 

    """ 

    This will make Cart-provided extra price fields persistent since we want 

    to "snapshot" their statuses at the time when the order was made 

    """ 

    order = models.ForeignKey(Order) 

    label = models.CharField(max_length=255) 

    value = CurrencyField() 

 

    # Does this represent shipping costs? 

    is_shipping = models.BooleanField(default=False, editable=False) 

 

    class Meta: 

        app_label = 'shop' 

 

class ExtraOrderItemPriceField(models.Model): 

    """ 

    This will make Cart-provided extra price fields persistent since we want 

    to "snapshot" their statuses at the time when the order was made 

    """ 

    order_item = models.ForeignKey(OrderItem) 

    label = models.CharField(max_length=255) 

    value = CurrencyField() 

 

    class Meta: 

        app_label = 'shop' 

 

class OrderPayment(models.Model): 

    """  

    A class to hold basic payment information. Backends should define their own  

    more complex payment types should they need to store more informtion 

    """ 

    order = models.ForeignKey(Order) 

    amount = CurrencyField()# How much was payed with this particular transfer 

    transaction_id = models.CharField(max_length=255, help_text="The transaction processor's reference") 

    payment_method= models.CharField(max_length=255, help_text="The payment backend use to process the purchase") 

 

    class Meta: 

        app_label = 'shop'