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

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

# -*- 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 django.views.generic import RedirectView 

 

from shop.forms import BillingShippingForm 

from shop.models import AddressModel, OrderExtraInfo 

from shop.models import Order 

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 = self.get_billing_form_class() 

 

            # 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 get_extra_info_form(self): 

        """ 

        Initializes and handles the form for order extra info. 

        """ 

        # Try to get the cached version first. 

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

171        if not form: 

            # Create a dynamic Form class for the model 

            form_class = model_forms.modelform_factory(OrderExtraInfo, exclude=['order']) 

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

                form = form_class(self.request.POST) 

            else: 

                form = form_class() 

            setattr(self, '_extra_info_form', form) 

        return form 

 

    def save_extra_info_to_order(self, order, form): 

        if form.cleaned_data.get('text'): 

            extra_info = form.save(commit=False) 

            extra_info.order = order 

            extra_info.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() 

        extra_info_form = self.get_extra_info_form() 

        if shipping_form.is_valid() and billing_form.is_valid() and extra_info_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(): 

                # save selected billing and shipping methods 

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

                    billingshipping_form.cleaned_data['payment_method'] 

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

                    billingshipping_form.cleaned_data['shipping_method'] 

 

                # add extra info to order 

                self.save_extra_info_to_order(order, extra_info_form) 

 

                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() 

        extra_info_form = self.get_extra_info_form() 

        ctx.update({ 

            'shipping_address': shipping_address_form, 

            'billing_address': billing_address_form, 

            'billing_shipping_form': billingshipping_form, 

            'extra_info_form': extra_info_form, 

        }) 

        return ctx 

 

 

class OrderConfirmView(RedirectView): 

    url_name = 'checkout_payment' 

 

    def confirm_order(self): 

        order = get_order_from_request(self.request) 

        order.status = Order.CONFIRMED 

        order.save() 

 

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

        self.confirm_order() 

        return super(OrderConfirmView, self).get(request, *args, **kwargs) 

 

    def get_redirect_url(self, **kwargs): 

        self.url = reverse(self.url_name) 

        return super(OrderConfirmView, self).get_redirect_url(**kwargs) 

 

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) 

 

        # put the latest order in the context only if it is completed 

        order = get_order_from_request(self.request) 

264        if order and order.status == Order.COMPLETED: 

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

 

        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'))