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

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

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

""" 

This models the checkout process using views. 

""" 

from django.core.urlresolvers import reverse 

from django.forms import models as model_forms 

from django.http import HttpResponseRedirect 

 

from shop.forms import BillingShippingForm 

from shop.models import AddressModel 

from shop.models.ordermodel import Order 

from shop.order_signals import completed 

from shop.util.address import ( 

    assign_address_to_request, 

    get_billing_address_from_request, 

    get_shipping_address_from_request, 

) 

from shop.util.cart import get_or_create_cart 

from shop.util.order import add_order_to_request, get_order_from_request 

from shop.views import ShopTemplateView, ShopView 

from shop.util.login_mixin import LoginMixin 

 

 

class CheckoutSelectionView(LoginMixin, ShopTemplateView): 

    template_name = 'shop/checkout/selection.html' 

 

    def _get_dynamic_form_class_from_factory(self): 

        """ 

        Returns a dynamic ModelForm from the loaded AddressModel 

        """ 

        form_class = model_forms.modelform_factory( 

            AddressModel, exclude=['user_shipping', 'user_billing']) 

        return form_class 

 

    def get_shipping_form_class(self): 

        """ 

        Provided for extensibility. 

        """ 

        return self._get_dynamic_form_class_from_factory() 

 

    def get_billing_form_class(self): 

        """ 

        Provided for extensibility. 

        """ 

        return self._get_dynamic_form_class_from_factory() 

 

    def create_order_object_from_cart(self): 

        """ 

        This will create an Order object form the current cart, and will pass 

        a reference to the Order on either the User object or the session. 

        """ 

        cart = get_or_create_cart(self.request) 

        cart.update() 

        order = Order.objects.create_from_cart(cart) 

        request = self.request 

        add_order_to_request(request, order) 

        return order 

 

    def get_shipping_address_form(self): 

        """ 

        Initializes and handles the form for the shipping address. 

 

        AddressModel is a model of the type defined in 

        ``settings.SHOP_ADDRESS_MODEL``. 

 

        The trick here is that we generate a ModelForm for whatever model was 

        passed to us by the SHOP_ADDRESS_MODEL setting, and us this, prefixed, 

        as the shipping address form. So this can be as complex or as simple as 

        one wants. 

 

        Subclasses of this view can obviously override this method and return 

        any other form instead. 

        """ 

        # Try to get the cached version first. 

        form = getattr(self, '_shipping_form', None) 

        if not form: 

            # Create a dynamic Form class for the model specified as the 

            # address model 

            form_class = self.get_shipping_form_class() 

 

            # Try to get a shipping address instance from the request (user or 

            # session)) 

            shipping_address = get_shipping_address_from_request(self.request) 

            if self.request.method == "POST": 

                form = form_class(self.request.POST, prefix="ship", 

                    instance=shipping_address) 

            else: 

                # We should either have an instance, or None 

                if not shipping_address: 

                    # The user or guest doesn't already have a favorite 

                    # address. Instanciate a blank one, and use this as the 

                    # default value for the form. 

                    shipping_address = AddressModel() 

 

                # Instanciate the form 

                form = form_class(instance=shipping_address, prefix="ship") 

            setattr(self, '_shipping_form', form) 

        return form 

 

    def get_billing_address_form(self): 

        """ 

        Initializes and handles the form for the shipping address. 

        AddressModel is a model of the type defined in 

        ``settings.SHOP_ADDRESS_MODEL``. 

        """ 

        # Try to get the cached version first. 

        form = getattr(self, '_billing_form', None) 

        if not form: 

            # Create a dynamic Form class for the model specified as the 

            # address model 

            form_class = model_forms.modelform_factory( 

                AddressModel, exclude=['user_shipping', 'user_billing']) 

 

            # Try to get a shipping address instance from the request (user or 

            # session)) 

            billing_address = get_billing_address_from_request(self.request) 

            if self.request.method == "POST": 

                form = form_class(self.request.POST, prefix="bill", 

                    instance=billing_address) 

            else: 

                # We should either have an instance, or None 

                if not billing_address: 

                    # The user or guest doesn't already have a favorite 

                    # address. Instansiate a blank one, and use this as the 

                    # default value for the form. 

                    billing_address = AddressModel() 

 

                #Instanciate the form 

                form = form_class(instance=billing_address, prefix="bill") 

            setattr(self, '_billing_form', form) 

        return form 

 

    def get_billing_and_shipping_selection_form(self): 

        """ 

        Get (and cache) the BillingShippingForm instance 

        """ 

        form = getattr(self, '_billingshipping_form', None) 

        if not form: 

            if self.request.method == 'POST': 

                form = BillingShippingForm(self.request.POST) 

            else: 

                form = BillingShippingForm() 

            self._billingshipping_form = form 

        return form 

 

    def save_addresses_to_order(self, order, shipping_address, 

                                billing_address): 

        """ 

        Provided for extensibility. 

 

        Adds both addresses (shipping and billing addresses) to the Order 

        object. 

        """ 

        order.set_shipping_address(shipping_address) 

        order.set_billing_address(billing_address) 

        order.save() 

 

    def post(self, *args, **kwargs): 

        """ Called when view is POSTed """ 

        shipping_form = self.get_shipping_address_form() 

        billing_form = self.get_billing_address_form() 

        if shipping_form.is_valid() and billing_form.is_valid(): 

 

            # Add the address to the order 

            shipping_address = shipping_form.save() 

            billing_address = billing_form.save() 

            order = self.create_order_object_from_cart() 

 

            self.save_addresses_to_order(order, shipping_address, 

                billing_address) 

 

            # The following marks addresses as being default addresses for 

            # shipping and billing. For more options (amazon style), we should 

            # remove this 

            assign_address_to_request(self.request, shipping_address, 

                shipping=True) 

            assign_address_to_request(self.request, billing_address, 

                shipping=False) 

 

            billingshipping_form = \ 

                self.get_billing_and_shipping_selection_form() 

            if billingshipping_form.is_valid(): 

                self.request.session['payment_backend'] = \ 

                    billingshipping_form.cleaned_data['payment_method'] 

                self.request.session['shipping_backend'] = \ 

                    billingshipping_form.cleaned_data['shipping_method'] 

                return HttpResponseRedirect(reverse('checkout_shipping')) 

 

        return self.get(self, *args, **kwargs) 

 

    def get_context_data(self, **kwargs): 

        """ 

        This overrides the context from the normal template view 

        """ 

        ctx = super(CheckoutSelectionView, self).get_context_data(**kwargs) 

 

        shipping_address_form = self.get_shipping_address_form() 

        billing_address_form = self.get_billing_address_form() 

        billingshipping_form = self.get_billing_and_shipping_selection_form() 

        ctx.update({ 

            'shipping_address': shipping_address_form, 

            'billing_address': billing_address_form, 

            'billing_shipping_form': billingshipping_form, 

        }) 

        return ctx 

 

 

class ThankYouView(LoginMixin, ShopTemplateView): 

    template_name = 'shop/checkout/thank_you.html' 

 

    def get_context_data(self, **kwargs): 

        ctx = super(ShopTemplateView, self).get_context_data(**kwargs) 

 

        # Set the order status: 

        order = get_order_from_request(self.request) 

221        if order: 

            order.status = Order.COMPLETED 

            order.save() 

            completed.send(sender=self, order=order) 

        else: 

            order = Order.objects.get_latest_for_user(self.request.user) 

            #TODO: Is this ever the case? 

        ctx.update({'order': order, }) 

 

        # Empty the customers basket, to reflect that the purchase was 

        # completed 

        cart_object = get_or_create_cart(self.request) 

        cart_object.empty() 

 

        return ctx 

 

 

class ShippingBackendRedirectView(LoginMixin, ShopView): 

    def get(self, *args, **kwargs): 

        try: 

            backend_namespace = self.request.session.pop('shipping_backend') 

            return HttpResponseRedirect(reverse(backend_namespace)) 

        except KeyError: 

            return HttpResponseRedirect(reverse('cart')) 

 

 

class PaymentBackendRedirectView(LoginMixin, ShopView): 

    def get(self, *args, **kwargs): 

        try: 

            backend_namespace = self.request.session.pop('payment_backend') 

            return HttpResponseRedirect(reverse(backend_namespace)) 

        except KeyError: 

            return HttpResponseRedirect(reverse('cart'))