Merge pull request #129 from levivm/feature/new_digitalglarus
Feature/new digitalglarus
This commit is contained in:
		
				commit
				
					
						68b149cebd
					
				
			
		
					 24 changed files with 735 additions and 148 deletions
				
			
		|  | @ -57,7 +57,7 @@ class MembershipOrderForm(forms.ModelForm): | |||
| 
 | ||||
| 
 | ||||
| class BookingBillingForm(BillingAddressForm): | ||||
|     token = forms.CharField(widget=forms.HiddenInput()) | ||||
|     token = forms.CharField(widget=forms.HiddenInput(), required=False) | ||||
|     start_date = forms.DateField(widget=forms.HiddenInput()) | ||||
|     end_date = forms.DateField(widget=forms.HiddenInput()) | ||||
|     price = forms.FloatField(widget=forms.HiddenInput()) | ||||
|  |  | |||
|  | @ -97,70 +97,3 @@ class Command(BaseCommand): | |||
|                     print(e) | ||||
|                     print("-------------------------") | ||||
|                     continue | ||||
|         # for donator_status in donators: | ||||
|         #     donator = donator_status.user.stripecustomer | ||||
|         #     try: | ||||
|         #         Donation.objects.get(created_at__month=current_month, | ||||
|         #                              created_at__year=current_year, | ||||
|         #                              donator=donator) | ||||
|         #     except Donation.DoesNotExist: | ||||
|         #         try: | ||||
|         #             # Get donator last donation amount | ||||
|         #             last_donation = Donation.objects.filter(donator=donator).last() | ||||
|         #             donation_amount = last_donation.donation | ||||
| 
 | ||||
|         #             # Make stripe charge to a customer | ||||
|         #             stripe_utils = StripeUtils() | ||||
|         #             stripe_utils.CURRENCY = self.CURRENCY | ||||
|         #             charge_response = stripe_utils.make_charge(amount=donation_amount, | ||||
|         #                                                        customer=donator.stripe_id) | ||||
|         #             charge = charge_response.get('response_object') | ||||
| 
 | ||||
|         #             # Check if the payment was approved | ||||
|         #             if not charge: | ||||
|         #                 # There is an error trying to creating the stripe charge | ||||
|         #                 context = { | ||||
|         #                     'paymentError': charge_response.get('error'), | ||||
|         #                 } | ||||
|         #                 print("--------- STRIPE PAYMENT ERROR ---------") | ||||
|         #                 print(context) | ||||
|         #                 print("-------------------------") | ||||
|         #                 continue | ||||
|         #             # Create a donation | ||||
|         #             charge = charge_response.get('response_object') | ||||
|         #             donation_data = { | ||||
|         #                 'cc_brand': charge.source.brand, | ||||
|         #                 'stripe_charge_id': charge.id, | ||||
|         #                 'last4': charge.source.last4, | ||||
|         #                 'billing_address': last_donation.billing_address.id, | ||||
|         #                 'donator': donator.id, | ||||
|         #                 'donation': donation_amount | ||||
|         #             } | ||||
|         #             donation_form = DonationForm(donation_data) | ||||
|         #             if donation_form.is_valid(): | ||||
|         #                 donation = donation_form.save() | ||||
| 
 | ||||
|         #                 context = { | ||||
|         #                     'donation': donation, | ||||
|         #                     'base_url': "{0}://{1}".format('https', 'dynamicweb.ungleich.ch') | ||||
| 
 | ||||
|         #                 } | ||||
|         #                 email_data = { | ||||
|         #                     'subject': 'Your donation have been charged', | ||||
|         #                     'to': donation.donator.user.email, | ||||
|         #                     'context': context, | ||||
|         #                     'template_name': 'donation_charge', | ||||
|         #                     'template_path': 'nosystemd/emails/' | ||||
|         #                 } | ||||
|         #                 email = BaseEmail(**email_data) | ||||
|         #                 email.send() | ||||
| 
 | ||||
|         #                 print("--------- PAYMENT DONATION SUCCESSFULL ---------") | ||||
|         #                 print("Donator: %s" % donation.donator.user.email) | ||||
|         #                 print("Amount: %s %s" % (donation.donation, self.CURRENCY)) | ||||
|         #                 print("-----------------------------------------------") | ||||
|         #         except Exception as e: | ||||
|         #             print("--------- ERROR ---------") | ||||
|         #             print(e) | ||||
|         #             print("-------------------------") | ||||
|         #             continue | ||||
|  |  | |||
							
								
								
									
										35
									
								
								digitalglarus/migrations/0020_auto_20161013_0253.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								digitalglarus/migrations/0020_auto_20161013_0253.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2016-10-13 02:53 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('digitalglarus', '0019_auto_20160929_0324'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='bookingorder', | ||||
|             name='cc_brand', | ||||
|             field=models.CharField(blank=True, max_length=10), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='bookingorder', | ||||
|             name='last4', | ||||
|             field=models.CharField(blank=True, max_length=4), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='membershiporder', | ||||
|             name='cc_brand', | ||||
|             field=models.CharField(blank=True, max_length=10), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='membershiporder', | ||||
|             name='last4', | ||||
|             field=models.CharField(blank=True, max_length=4), | ||||
|         ), | ||||
|     ] | ||||
|  | @ -32,8 +32,8 @@ class Ordereable(models.Model): | |||
|     billing_address = models.ForeignKey(BillingAddress) | ||||
|     created_at = models.DateTimeField(auto_now_add=True) | ||||
|     approved = models.BooleanField(default=False) | ||||
|     last4 = models.CharField(max_length=4) | ||||
|     cc_brand = models.CharField(max_length=10) | ||||
|     last4 = models.CharField(max_length=4, blank=True) | ||||
|     cc_brand = models.CharField(max_length=10, blank=True) | ||||
|     stripe_charge_id = models.CharField(max_length=100, null=True) | ||||
| 
 | ||||
|     class Meta: | ||||
|  | @ -43,6 +43,8 @@ class Ordereable(models.Model): | |||
|     def create(cls, data): | ||||
|         stripe_charge = data.pop('stripe_charge', None) | ||||
|         instance = cls.objects.create(**data) | ||||
|         if not stripe_charge: | ||||
|             return instance | ||||
|         instance.stripe_charge_id = stripe_charge.id | ||||
|         instance.last4 = stripe_charge.source.last4 | ||||
|         instance.cc_brand = stripe_charge.source.brand | ||||
|  |  | |||
|  | @ -71,12 +71,12 @@ class Membership(models.Model): | |||
|     @classmethod | ||||
|     def is_digitalglarus_active_member(cls, user): | ||||
|         past_month = (datetime.today() - relativedelta(months=1)).month | ||||
|         has_booking_current_month = Q(membershiporder__customer__user=user, | ||||
|         has_order_current_month = Q(membershiporder__customer__user=user, | ||||
|                                     membershiporder__created_at__month=datetime.today().month) | ||||
|         has_booking_past_month = Q(membershiporder__customer__user=user, | ||||
|         has_order_past_month = Q(membershiporder__customer__user=user, | ||||
|                                  membershiporder__created_at__month=past_month) | ||||
|         active_membership = Q(active=True) | ||||
|         return cls.objects.filter(has_booking_past_month | has_booking_current_month).\ | ||||
|         return cls.objects.filter(has_order_past_month | has_order_current_month).\ | ||||
|             filter(active_membership).exists() | ||||
| 
 | ||||
|     def deactivate(self): | ||||
|  | @ -147,7 +147,7 @@ class Booking(models.Model): | |||
|     def get_ramaining_free_days(cls, user, start_date, end_date): | ||||
| 
 | ||||
|         TWO_DAYS = 2 | ||||
| 
 | ||||
|         ONE_DAY = 1 | ||||
|         start_month = start_date.month | ||||
|         end_month = end_date.month | ||||
|         months = abs(start_month - (end_month + 12) if end_month < start_month | ||||
|  | @ -156,6 +156,10 @@ class Booking(models.Model): | |||
|         current_month_bookings = cls.objects.filter(bookingorder__customer__user=user, | ||||
|                                                     start_date__month=current_date.month) | ||||
|         free_days_this_month = TWO_DAYS - sum(map(lambda x: x.free_days, current_month_bookings)) | ||||
| 
 | ||||
|         if start_date == end_date and free_days_this_month == TWO_DAYS: | ||||
|             free_days_this_month = ONE_DAY | ||||
| 
 | ||||
|         total_free_days = months * TWO_DAYS + free_days_this_month | ||||
|         return total_free_days | ||||
| 
 | ||||
|  | @ -190,7 +194,7 @@ class Booking(models.Model): | |||
|         # Calculating membership required months price for booking | ||||
|         required_membership_months = 0 | ||||
|         membership_booking_price = 0.0 | ||||
|         if BookingOrder.user_has_not_bookings(user): | ||||
|         # if not BookingOrder.user_has_not_bookings(user): | ||||
|         today = datetime.today().date() | ||||
|         membership_price = MembershipType.objects.get(name=MembershipType.STANDARD).price | ||||
|         required_membership_months = cls.membership_required_booking_months(today, end_date) | ||||
|  | @ -210,11 +214,15 @@ class BookingOrder(Ordereable, models.Model): | |||
|     membership_required_months = models.IntegerField(default=0) | ||||
|     membership_required_months_price = models.FloatField(default=0) | ||||
| 
 | ||||
| 
 | ||||
|     @classmethod | ||||
|     def user_has_not_bookings(cls, user): | ||||
|         return cls.objects.filter(customer__user=user).exists() | ||||
| 
 | ||||
|     def get_booking_cc_data(self): | ||||
|         return { | ||||
|             'last4': self.last4, | ||||
|             'cc_brand': self.cc_brand, | ||||
|         } | ||||
| 
 | ||||
|     def booking_days(self): | ||||
|         return (self.booking.end_date - self.booking.start_date).days + 1 | ||||
|  |  | |||
|  | @ -241,6 +241,8 @@ fieldset[disabled] .btn-xl.active { | |||
| 
 | ||||
| .navbar-default .navbar-collapse { | ||||
|     border-color: rgba(255,255,255,.02); | ||||
|     padding-right: 100px; | ||||
|     text-align: right; | ||||
| } | ||||
| 
 | ||||
| .navbar-default .navbar-toggle { | ||||
|  | @ -259,7 +261,7 @@ fieldset[disabled] .btn-xl.active { | |||
| 
 | ||||
| .navbar-default .nav li a { | ||||
|     text-transform: uppercase; | ||||
|     font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif; | ||||
|     font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; | ||||
|     font-weight: 400; | ||||
|     letter-spacing: 1px; | ||||
|     color: #fff; | ||||
|  |  | |||
|  | @ -197,7 +197,7 @@ | |||
| } | ||||
| 
 | ||||
| .glyphicon-ok { | ||||
| 	font-size: 42px; | ||||
| 	font-size: 28px; | ||||
|     display: block; | ||||
|     text-align: center; | ||||
|     margin-bottom: 20px; | ||||
|  | @ -364,7 +364,7 @@ | |||
| } | ||||
| 
 | ||||
| .form-control { | ||||
|     color: #999; | ||||
|     color: #000; | ||||
| 	border-radius: 0px; | ||||
| 	box-shadow: none; | ||||
| } | ||||
|  |  | |||
|  | @ -24,12 +24,21 @@ $( document ).ready(function() { | |||
|          }  | ||||
|     }); | ||||
| 
 | ||||
|     var submit_form_btn = $('#payment_button'); | ||||
|     submit_form_btn.on('click', submit_payment); | ||||
| 
 | ||||
|     function submit_payment(e){  | ||||
|       $('#billing-form').submit(); | ||||
|       // $form.submit();
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     var $form = $('#payment-form'); | ||||
|     $form.submit(payWithStripe); | ||||
| 
 | ||||
|     /* If you're using Stripe for payments */ | ||||
|     function payWithStripe(e) { | ||||
|         console.log("submiting"); | ||||
|         e.preventDefault(); | ||||
| 
 | ||||
|         /* Visual feedback */ | ||||
|  |  | |||
|  | @ -39,11 +39,15 @@ | |||
|               </tbody> | ||||
|             </table> | ||||
|                          | ||||
|             <h2 class="order-head">Billing Adress<btn class="btn btn-primary btn-grey btn-edit">Edit</btn></h2> | ||||
|             <h2 class="history-name">Nico Schottelius<br> | ||||
|                                     In der Au 7 8762 Schwanden<br> | ||||
|                                     Switzerland | ||||
|             <h2 class="order-head">Billing Adress<a class="btn btn-primary btn-grey btn-edit" href="{% url 'digitalglarus:user_billing_address' %}">Edit</a></h2> | ||||
|             <h2 class="history-name"> | ||||
|               {{request.user.name}} | ||||
|             </h2> | ||||
|             <h2 class="history-name"> | ||||
|               {{billing_address.street_address}},{{billing_address.postal_code}}<br> | ||||
|               {{billing_address.city}}, {{billing_address.country}}. | ||||
|             </h2> | ||||
| 
 | ||||
|                                          | ||||
|         </div> | ||||
|           </div>                 | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ | |||
|                <h2 class="section-heading payment-head">Booking</h2> | ||||
|              <!--  <h2 class="membership-amount">35CHF</h2> --> | ||||
|                  <hr class="greyline-long"> | ||||
|                   <h2 class="billing-head">Billing Adress</h2> | ||||
|                    | ||||
|                   <h2 class="membership-lead"> | ||||
|                       Your Digital Glarus Membership enables  | ||||
|                       you to use our coworking space and it includes  | ||||
|  | @ -37,7 +37,8 @@ | |||
|                       15CHF per day. More than 17 days a month it  | ||||
|                       will charge only 290CHF/month. | ||||
|                   </h2> | ||||
| 
 | ||||
|                   {% if is_free %} | ||||
|                       <h2 class="billing-head">Billing Adress</h2> | ||||
|                       <div class="signup-form form-group row"> | ||||
|                         <form role="form" id="billing-form" method="post" action="{% url 'digitalglarus:booking_payment' %}" novalidate> | ||||
|                         {% for field in form %} | ||||
|  | @ -48,18 +49,43 @@ | |||
|                         <br> | ||||
|                         </form> | ||||
|                       </div> | ||||
| 
 | ||||
|                      <hr class="greyline-long"> | ||||
|                      <br/> | ||||
|                      <h2 class="billing-head">Your Booking is TOTALLY FREE</h2> | ||||
|                      <br/><br/> | ||||
|                   {% else %} | ||||
|                   <h2 class="billing-head">Billing Adress</h2> | ||||
|                     <div class="signup-form form-group row"> | ||||
|                       <form role="form" id="billing-form" method="post" action="{% url 'digitalglarus:booking_payment' %}" novalidate> | ||||
|                       {% for field in form %} | ||||
|                         {% csrf_token %} | ||||
|                         {% bootstrap_field field show_label=False type='fields'%} | ||||
|                       {% endfor %} | ||||
|                       {% bootstrap_form_errors form type='non_fields'%} | ||||
|                       <br> | ||||
|                       </form> | ||||
|                     </div> | ||||
|                     {% if credit_card_data %} | ||||
|                       <form role="form" id="billing-form" method="post" action="{% url 'digitalglarus:booking_payment' %}" novalidate> | ||||
|                         <h2 class="billing-head">Credit Card</h2> | ||||
|                         <h2 class="membership-lead">Last 4: {{credit_card_data.last4}}</h2> | ||||
|                         <h2 class="membership-lead">Type: {{credit_card_data.cc_brand}}</h2> | ||||
|                         <input type="hidden" name="credit_card_needed" value="false"/> | ||||
|                       </form>                 | ||||
|                     {% else %} | ||||
|                     <h2 class="billing-head">Credit Card (Last used)</h2> | ||||
|                     <div class="signup-form form-group row"> | ||||
|                             <form role="form" id="payment-form" novalidate> | ||||
|                                 <div class="row"> | ||||
|                                     <div class="col-xs-9 col-md-12"> | ||||
|                                     <div class="col-xs-12 col-md-12"> | ||||
|                                         <div class="form-group"> | ||||
|                                                 <input type="text" class="form-control" name="cardName" placeholder="Name on card" required autofocus data-stripe="name" /> | ||||
|                                         </div>                             | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="row"> | ||||
|                                     <div class="col-xs-9 col-md-12"> | ||||
|                                     <div class="col-xs-12 col-md-12"> | ||||
|                                         <div class="form-group"> | ||||
|                                                 <input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required data-stripe="number" /> | ||||
|                                         </div>                             | ||||
|  | @ -86,11 +112,6 @@ | |||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="row"> | ||||
|                                     <div class="col-xs-12"> | ||||
|                                         <button class="btn btn-primary btn-lg btn-blck " type="submit">Book</button> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="row" style="display:none;"> | ||||
|                                     <div class="col-xs-12"> | ||||
|                                         <p class="payment-errors"></p> | ||||
|  | @ -109,6 +130,8 @@ | |||
|                             </form> | ||||
|                         <br> | ||||
|                     </div> | ||||
|                     {% endif %} | ||||
|                   {% endif %} | ||||
|           </div>                 | ||||
|       </div>       | ||||
|        <div class="col-xs-12 col-sm-4 col-lg-4 wow fadeInDown">  | ||||
|  | @ -143,9 +166,11 @@ | |||
|                     <span class="custom-control-description">I accept the Digital Glarus <a href=#>Terms and Conditions</a>, <a href=#>Community Guidelines</a> and <a href=#>Privacy Policy</a></span> | ||||
|                     </label> | ||||
|                     <div class="button-box"> | ||||
|                         <div class="row"> | ||||
|                           <div class="col-xs-12"> | ||||
|                               <button id="payment_button" class="btn btn-primary btn-lg btn-blck " type="submit">Book</button> | ||||
|                           </div> | ||||
|                         </div>  | ||||
|                           | ||||
|                          <div class="button-box"> | ||||
|                     </div> | ||||
|                   </div> | ||||
|                 </div> | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ | |||
|                           <a class="btn btn-primary btn-blue" href={% url 'digitalglarus:booking' %}>Go to Booking</a> | ||||
|                           </div> | ||||
|                           <div class="notice-box text-left"> | ||||
|                             <p class="order-bottom-text">Your membership will be automatically renewed each month. For deactivating go to<a href=#>my page</a></p> | ||||
|                             <p class="order-bottom-text">Your membership will be automatically renewed each month. For deactivating go to<a href="{% url 'digitalglarus:membership_deactivate' %}">deactivate page</a></p> | ||||
|                          </div> | ||||
|                           </div> | ||||
|          | ||||
|  |  | |||
|  | @ -22,7 +22,28 @@ | |||
| 
 | ||||
|                           <form method="POST" action=""> | ||||
|                             {% csrf_token %}  | ||||
|                             <button type="submit" class="btn btn-primary btn-blue">Cancel my Membership</button> | ||||
|                             <button type="button" class="btn btn-primary btn-blue" data-toggle="modal" data-target="#cancel-subscription-modal">Cancel my Membership</button> | ||||
| 
 | ||||
| 
 | ||||
|                             <div class="modal fade bs-example-modal-sm" id="cancel-subscription-modal" tabindex="-1" role="dialog"> | ||||
|                               <div class="modal-dialog" role="document"> | ||||
|                                 <div class="modal-content"> | ||||
|                                   <div class="modal-header"> | ||||
|                                     <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> | ||||
|                                     <h4 class="modal-title">Cancel Subscription</h4> | ||||
|                                   </div> | ||||
|                                   <div class="modal-body"> | ||||
|                                     <p>Do you want to cancel your subscription?</p> | ||||
|                                   </div> | ||||
|                                   <div class="modal-footer"> | ||||
|                                     <button type="button" class="btn btn-default" data-dismiss="modal">No</button> | ||||
|                                     <button type="submit" class="btn btn-primary">Yes</button> | ||||
|                                   </div> | ||||
|                                 </div><!-- /.modal-content --> | ||||
|                               </div><!-- /.modal-dialog --> | ||||
|                             </div><!-- /.modal --> | ||||
| 
 | ||||
| 
 | ||||
|                           </form> | ||||
| 
 | ||||
|                         </div> | ||||
|  | @ -33,6 +54,9 @@ | |||
|                          </div> | ||||
|                       </div> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|          | ||||
|       </div>        | ||||
|      <div class="col-xs-12 col-sm-3 col-lg-4 text-center wow fadeInDown"> </div> | ||||
|       </div> | ||||
|  |  | |||
|  | @ -60,6 +60,7 @@ | |||
|                | ||||
|               <h2 class="col-xs-6 payment-total">Membership month {{order.created_at|date:"F"}}</h2> | ||||
|               <h2 class="order-sum">{{order.amount|floatformat}}CHF</h2> | ||||
|               <br/> | ||||
|               <hr class="greyline"> | ||||
|               <h2 class="col-xs-6 payment-total">Total</h2> | ||||
|               <h2 class="order-result">{{order.amount|floatformat}}CHF</h2> | ||||
|  |  | |||
|  | @ -39,10 +39,13 @@ | |||
|               </tbody> | ||||
|             </table> | ||||
|              | ||||
|             <h2 class="order-head">Billing Adress<btn class="btn btn-primary btn-grey btn-edit">Edit</btn></h2> | ||||
|             <h2 class="history-name">Nico Schottelius<br> | ||||
|                                     In der Au 7 8762 Schwanden<br> | ||||
|                                     Switzerland | ||||
|             <h2 class="order-head">Billing Adress<a class="btn btn-primary btn-grey btn-edit" href="{% url 'digitalglarus:user_billing_address' %}">Edit</a></h2> | ||||
|             <h2 class="history-name"> | ||||
|               {{request.user.name}} | ||||
|             </h2> | ||||
|             <h2 class="history-name"> | ||||
|               {{billing_address.street_address}},{{billing_address.postal_code}}<br> | ||||
|               {{billing_address.city}}, {{billing_address.country}}. | ||||
|             </h2> | ||||
| 
 | ||||
|             <hr class="greyline-long"> | ||||
|  | @ -50,14 +53,15 @@ | |||
|             <h2 class="history-name"> | ||||
|               Dates: {{membership_start_date|date}} - {{membership_end_date|date}}<br> | ||||
|             </h2> | ||||
|             <h2 class="history-name"> | ||||
|             <div class="edit-button"> | ||||
| 
 | ||||
|               <a class="btn btn-primary btn-grey btn-edit print" href="{% url 'digitalglarus:membership_deactivate' %}">Deactivate</a> | ||||
|             </h2> | ||||
|               <a class="btn btn-primary btn-grey btn-deactivate print" href="{% url 'digitalglarus:membership_deactivate' %}">Deactivate</a> | ||||
|             </div> | ||||
|             <hr class="greyline-long"> | ||||
|             <div class="row"> | ||||
|               <div class="col-md-10"> | ||||
|                 <span>You will be charged on the first of the month until you  | ||||
|                 cancel your subscription. Previous charges won't be refunded.</span> | ||||
|               <div class="col-md-12 notice-box"> | ||||
|                 <p class="order-bottom-text">You will be charged on the first of the month until you  | ||||
|                 cancel your subscription. Previous charges won't be refunded.</p> | ||||
|               </div> | ||||
|             </div> | ||||
|                                          | ||||
|  | @ -66,18 +70,37 @@ | |||
|       | ||||
|            | ||||
|        <div class="col-xs-12 col-sm-4 col-lg-4 wow fadeInDown">  | ||||
| 
 | ||||
|             <div class="order-summary"> | ||||
| 
 | ||||
|                 {% if messages %} | ||||
|                     <div class="order-box"> | ||||
|                     <h2 class="thankyou">Message</h2> | ||||
|                     <hr class="greyline"> | ||||
|                     {% for message in messages %} | ||||
|                         <h2 class="signup-lead text-center">{{ message }}</h2> | ||||
|                     {% endfor %} | ||||
|                     </div> | ||||
|                 {% else %} | ||||
| 
 | ||||
|                  <h2 class="thankyou">Thank You!</h2> | ||||
|                  <div class="order-box"> | ||||
|                  <span class="glyphicon glyphicon-heart icon-up"></span> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|                   <h2 class="signup-lead text-center">Digital Glarus lives with your love!<br> | ||||
|                                                   Our coworking space is here because of your love and support.</h2> | ||||
|                 | ||||
|                   <hr class="greyline"> | ||||
|                    | ||||
|                   <p class="order-bottom-text text-center">This box is here just to thank you</p> | ||||
|                  </div> | ||||
| 
 | ||||
|                 {% endif %} | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|                    | ||||
|                  </div> | ||||
|             </div> | ||||
|        </div> | ||||
| </section> | ||||
|  |  | |||
|  | @ -56,14 +56,14 @@ | |||
|                     <div class="signup-form form-group row"> | ||||
|                             <form role="form" id="payment-form" novalidate> | ||||
|                                 <div class="row"> | ||||
|                                     <div class="col-xs-9 col-md-12"> | ||||
|                                     <div class="col-xs-12 col-md-12"> | ||||
|                                         <div class="form-group"> | ||||
|                                                 <input type="text" class="form-control" name="cardName" placeholder="Name on card" required autofocus data-stripe="name" /> | ||||
|                                         </div>                             | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="row"> | ||||
|                                     <div class="col-xs-9 col-md-12"> | ||||
|                                     <div class="col-xs-12 col-md-12"> | ||||
|                                         <div class="form-group"> | ||||
|                                                 <input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required data-stripe="number" /> | ||||
|                                         </div>                             | ||||
|  | @ -92,7 +92,7 @@ | |||
|                                 </div> | ||||
|                                 <div class="row"> | ||||
|                                     <div class="col-xs-12"> | ||||
|                                         <button class="btn btn-primary btn-lg btn-blck " type="submit">Purchase membership</button> | ||||
|                                         <button class="btn btn-primary btn-md btn-blck " type="submit">Purchase membership</button> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="row" style="display:none;"> | ||||
|  |  | |||
|  | @ -0,0 +1,83 @@ | |||
| {% extends "new_base_glarus.html" %} | ||||
| {% load staticfiles bootstrap3 i18n %} | ||||
| {% block content %} | ||||
| 
 | ||||
| <style type="text/css"> | ||||
|    | ||||
|   .nopadding { | ||||
|      padding: 0 !important; | ||||
|      margin: 0 !important; | ||||
|   } | ||||
| 
 | ||||
|   .form-control#id_country{ | ||||
|     -webkit-appearance: none; | ||||
|     -moz-appearance: none; | ||||
|     background-position: right 50%; | ||||
|     background-repeat: no-repeat; | ||||
|     background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAMCAYAAABSgIzaAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDZFNDEwNjlGNzFEMTFFMkJEQ0VDRTM1N0RCMzMyMkIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDZFNDEwNkFGNzFEMTFFMkJEQ0VDRTM1N0RCMzMyMkIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0NkU0MTA2N0Y3MUQxMUUyQkRDRUNFMzU3REIzMzIyQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0NkU0MTA2OEY3MUQxMUUyQkRDRUNFMzU3REIzMzIyQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuGsgwQAAAA5SURBVHjaYvz//z8DOYCJgUxAf42MQIzTk0D/M+KzkRGPoQSdykiKJrBGpOhgJFYTWNEIiEeAAAMAzNENEOH+do8AAAAASUVORK5CYII=); | ||||
|     padding: .5em; | ||||
|     padding-right: 1.5em | ||||
|   } | ||||
| </style> | ||||
| 
 | ||||
|   <section id="price"> | ||||
|     <div class="signup-container"> | ||||
|       <div class="col-xs-12 col-sm-6 col-lg-8 text-center wow fadeInDown">  | ||||
|         <div class="payment-box"> | ||||
|                <h2 class="section-heading payment-head">Billing Address Info</h2> | ||||
|              <!--  <h2 class="membership-amount">35CHF</h2> --> | ||||
|                  <hr class="greyline-long"> | ||||
|                   <h2 class="billing-head">Billing Adress</h2> | ||||
|                     <div class="signup-form form-group row"> | ||||
|                       <form role="form" id="billing-address-form" method="post" action="{% url 'digitalglarus:user_billing_address' %}" novalidate> | ||||
|                       {% for field in form %} | ||||
|                         {% csrf_token %} | ||||
|                         {% bootstrap_field field show_label=False type='fields'%} | ||||
|                       {% endfor %} | ||||
|                       {% bootstrap_form_errors form type='non_fields'%} | ||||
|                       <br> | ||||
|                       <div class="row"> | ||||
|                         <div class="col-xs-12"> | ||||
|                             <button id="payment_button" class="btn btn-primary btn-lg btn-blck " type="submit">Save</button> | ||||
|                         </div> | ||||
|                       </div>  | ||||
|                       </form> | ||||
|                     </div> | ||||
|           </div>                 | ||||
|       </div>       | ||||
|     </div>  | ||||
|     </div> | ||||
|       </div> | ||||
|     </div>    | ||||
|   </section>  | ||||
|    | ||||
|    | ||||
|    | ||||
|   <section id="contact"> | ||||
|     <div class="fill"> | ||||
|      <div class="row" class="wow fadeInDown"> | ||||
|       <div class="col-lg-12 text-center wow fadeInDown"> | ||||
|           <div class="col-md-4 map-title"> | ||||
|             Digital Glarus<br> | ||||
|             <span class="map-caption">In der Au 7 Schwanden 8762 Switzerland | ||||
|             <br>info@digitalglarus.ch | ||||
|             <br> | ||||
|             (044) 534-66-22 | ||||
|             <p> </p> | ||||
|             </span> | ||||
|           </div> | ||||
|            <p> </p> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </section> | ||||
| 
 | ||||
| <!-- stripe key data --> | ||||
| {% if stripe_key %} | ||||
| <script type="text/javascript">  | ||||
|      (function () {window.stripeKey = "{{stripe_key}}";})(); | ||||
| </script> | ||||
| 
 | ||||
| {%endif%} | ||||
| 
 | ||||
| {% endblock %} | ||||
|  | @ -107,7 +107,7 @@ | |||
|           <a href="#page-top"></a> | ||||
|         </li> | ||||
|         <li> | ||||
|           <a class="page-scroll" href="#portfolio">booking & price</a> | ||||
|           <a class="page-scroll" href="{% url 'digitalglarus:booking' %}">booking & price</a> | ||||
|         </li> | ||||
|         <li> | ||||
|           <a class="page-scroll" href="{% url 'digitalglarus:history' %}">history</a> | ||||
|  |  | |||
|  | @ -1,11 +1,25 @@ | |||
| import json | ||||
| from model_mommy import mommy | ||||
| from unittest import mock | ||||
| 
 | ||||
| from django.test import TestCase | ||||
| from django.conf import settings | ||||
| from django.core.urlresolvers import reverse | ||||
| from django.core.urlresolvers import resolve | ||||
| from cms.test_utils.testcases import CMSTestCase | ||||
| from django.contrib.auth.tokens import default_token_generator | ||||
| from django.utils.http import urlsafe_base64_encode | ||||
| from django.utils.encoding import force_bytes | ||||
| from cms.api import create_page | ||||
| 
 | ||||
| from membership.models import CustomUser, StripeCustomer | ||||
| from utils.tests import BaseTestCase | ||||
| 
 | ||||
| 
 | ||||
| from .views import LoginView, SignupView, PasswordResetView, PasswordResetConfirmView,\ | ||||
|     MembershipPricingView, MembershipPaymentView | ||||
| from .models import MembershipType, MembershipOrder | ||||
| 
 | ||||
| 
 | ||||
| class ContactViewTest(TestCase): | ||||
|     def setUp(self): | ||||
|  | @ -38,14 +52,243 @@ class ViewsTest(CMSTestCase): | |||
|         self.assertEqual(res2.status_code, 200) | ||||
| 
 | ||||
| 
 | ||||
| class CalendarApiTestCase(TestCase): | ||||
|     def test_api_response(self): | ||||
|         calendar_api_url_1 = reverse('digitalglarus:calendar_api_1', kwargs={'month': '3', 'year': '2016'}) | ||||
|         res1 = self.client.get(calendar_api_url_1) | ||||
|         pd = json.loads(res1.content.decode('utf-8')) | ||||
|         self.assertEqual(pd['month'], '3') | ||||
|         self.assertEqual(pd['year'], '2016') | ||||
| class MembershipPricingViewTest(BaseTestCase): | ||||
| 
 | ||||
|         # TODO:check post | ||||
|         # calendar_api_url = reverse('digitalglarus:calendar_api') | ||||
|         # res = self.client.get(calendar_api_url) | ||||
|     def setUp(self): | ||||
|         super(MembershipPricingViewTest, self).setUp() | ||||
| 
 | ||||
|         self.membership_type = mommy.make(MembershipType) | ||||
|         self.url = reverse('digitalglarus:membership_pricing') | ||||
|         self.view = MembershipPricingView | ||||
|         self.expected_template = 'digitalglarus/membership_pricing.html' | ||||
| 
 | ||||
|     def test_url_resolve_to_view_correctly(self): | ||||
|         found = resolve(self.url) | ||||
|         self.assertEqual(found.func.__name__, self.view.__name__) | ||||
| 
 | ||||
|     def test_get(self): | ||||
|         # Anonymous user should get data | ||||
|         response = self.client.get(self.url) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertEqual(response.context['membership_type'], self.membership_type) | ||||
|         self.assertTemplateUsed(response, self.expected_template) | ||||
| 
 | ||||
| 
 | ||||
| class MembershipPaymentViewTest(BaseTestCase): | ||||
| 
 | ||||
|     def setUp(self): | ||||
|         super(MembershipPaymentViewTest, self).setUp() | ||||
| 
 | ||||
|         self.membership_type = mommy.make(MembershipType) | ||||
|         self.url = reverse('digitalglarus:membership_payment') | ||||
|         self.view = MembershipPaymentView | ||||
|         self.expected_template = 'digitalglarus/membership_payment.html' | ||||
| 
 | ||||
|         # post data | ||||
|         self.billing_address = { | ||||
|             'street_address': 'street name', | ||||
|             'city': 'MyCity', | ||||
|             'postal_code': '32123123123123', | ||||
|             'country': 'VE', | ||||
|             'token': 'a23kfmslwxhkwis', | ||||
|             'membership_type': self.membership_type.id | ||||
|         } | ||||
| 
 | ||||
|     def test_url_resolve_to_view_correctly(self): | ||||
|         found = resolve(self.url) | ||||
|         self.assertEqual(found.func.__name__, self.view.__name__) | ||||
| 
 | ||||
|     def test_get(self): | ||||
| 
 | ||||
|         # Anonymous user should get redirect to login | ||||
|         response = self.client.get(self.url) | ||||
|         expected_url = "%s?next=%s" % (reverse('digitalglarus:signup'), | ||||
|                                        reverse('digitalglarus:membership_payment')) | ||||
|         self.assertRedirects(response, expected_url=expected_url, | ||||
|                              status_code=302, target_status_code=200) | ||||
| 
 | ||||
|         # Logged user should get the page | ||||
|         response = self.customer_client.get(self.url, follow=True) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertEqual(response.context['stripe_key'], | ||||
|                          settings.STRIPE_API_PUBLIC_KEY) | ||||
|         self.assertEqual(response.context['membership_type'], | ||||
|                          self.membership_type) | ||||
| 
 | ||||
|     @mock.patch('utils.stripe_utils.StripeUtils.create_customer') | ||||
|     def test_post(self, stripe_mocked_call): | ||||
| 
 | ||||
|         # Anonymous user should get redirect to login | ||||
|         # response = self.client.post(self.url) | ||||
|         # expected_url = "%s?next=%s" % (reverse('digitalglarus:signup'), | ||||
|         #                                reverse('digitalglarus:membership_payment')) | ||||
|         # self.assertRedirects(response, expected_url=expected_url, | ||||
|         #                      status_code=302, target_status_code=200) | ||||
| 
 | ||||
|         # Customer user should be able to pay | ||||
|         stripe_mocked_call.return_value = { | ||||
|             'paid': True, | ||||
|             'response_object': self.stripe_mocked_customer, | ||||
|             'error': None | ||||
|         } | ||||
|         response = self.customer_client.post(self.url, self.billing_address) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertTrue(StripeCustomer.objects.filter(user__email=self.customer.email).exists()) | ||||
|         stripe_customer = StripeCustomer.objects.get(user__email=self.customer.email) | ||||
|         self.assertEqual(stripe_customer.user, self.customer) | ||||
|         self.assertTrue(MembershipOrder.objects.filter(customer=stripe_customer).exists()) | ||||
|         membership_order = MembershipOrder.objects.filter(customer=stripe_customer).first() | ||||
|         session_data = { | ||||
|             'membership_price': membership_order.membership.type.first_month_price, | ||||
|             'membership_dates': membership_order.membership.type.first_month_formated_range | ||||
|         } | ||||
|         self.assertEqual(session_data.get('membership_price'), | ||||
|                          self.session_data.get('membership_price')) | ||||
|         self.assertEqual(session_data.get('membership_dates'), | ||||
|                          self.session_data.get('membership_dates')) | ||||
| 
 | ||||
|         # self.assertTrue(HostingOrder.objects.filter(customer=stripe_customer).exists()) | ||||
|         # hosting_order = HostingOrder.objects.filter(customer=stripe_customer)[0] | ||||
|         # vm_plan = { | ||||
|             # 'cores': hosting_order.vm_plan.cores, | ||||
|         #     'memory': hosting_order.vm_plan.memory, | ||||
|         #     'disk_size': hosting_order.vm_plan.disk_size, | ||||
|         #     'price': hosting_order.vm_plan.price, | ||||
|         #     'hosting_company': hosting_order.vm_plan.vm_type.hosting_company, | ||||
|         #     'configuration': hosting_order.vm_plan.configuration | ||||
|         # } | ||||
|         # self.assertEqual(vm_plan, self.session_data.get('vm_specs')) | ||||
| 
 | ||||
| 
 | ||||
| class LoginViewTest(TestCase): | ||||
| 
 | ||||
|     def setUp(self): | ||||
|         self.url = reverse('digitalglarus:login') | ||||
|         self.view = LoginView | ||||
|         self.expected_template = 'digitalglarus/login.html' | ||||
|         self.user = mommy.make('membership.CustomUser') | ||||
|         self.password = 'fake_password' | ||||
|         self.user.set_password(self.password) | ||||
|         self.user.save() | ||||
| 
 | ||||
|     def test_url_resolve_to_view_correctly(self): | ||||
|         found = resolve(self.url) | ||||
|         self.assertEqual(found.func.__name__, self.view.__name__) | ||||
| 
 | ||||
|     def test_get(self): | ||||
|         response = self.client.get(self.url) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertTemplateUsed(response, self.expected_template) | ||||
| 
 | ||||
|     def test_anonymous_user_can_login(self): | ||||
|         data = { | ||||
|             'email': self.user.email, | ||||
|             'password': self.password | ||||
|         } | ||||
|         response = self.client.post(self.url, data=data, follow=True) | ||||
|         self.assertEqual(response.context['user'], self.user) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
| 
 | ||||
| 
 | ||||
| class SignupViewTest(TestCase): | ||||
| 
 | ||||
|     def setUp(self): | ||||
|         self.url = reverse('digitalglarus:signup') | ||||
|         self.expected_template = 'digitalglarus/signup.html' | ||||
|         self.view = SignupView | ||||
|         self.signup_data = { | ||||
|             'name': 'ungleich', | ||||
|             'email': 'test@ungleich.com', | ||||
|             'password': 'fake_password', | ||||
|             'confirm_password': 'fake_password', | ||||
|         } | ||||
| 
 | ||||
|     def test_url_resolve_to_view_correctly(self): | ||||
|         found = resolve(self.url) | ||||
|         self.assertEqual(found.func.__name__, self.view.__name__) | ||||
| 
 | ||||
|     def test_get(self): | ||||
|         response = self.client.get(self.url) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertTemplateUsed(response, self.expected_template) | ||||
| 
 | ||||
|     def test_anonymous_user_can_signup(self): | ||||
|         response = self.client.post(self.url, data=self.signup_data, follow=True) | ||||
|         self.user = CustomUser.objects.get(email=self.signup_data.get('email')) | ||||
|         self.assertEqual(response.context['user'], self.user) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
| 
 | ||||
| 
 | ||||
| class PasswordResetViewTest(BaseTestCase): | ||||
| 
 | ||||
|     def setUp(self): | ||||
|         super(PasswordResetViewTest, self).setUp() | ||||
| 
 | ||||
|         self.url = reverse('digitalglarus:reset_password') | ||||
|         self.view = PasswordResetView | ||||
|         self.expected_template = 'digitalglarus/reset_password.html' | ||||
|         self.user = mommy.make('membership.CustomUser') | ||||
|         self.password = 'fake_password' | ||||
|         self.user.set_password(self.password) | ||||
|         self.user.save() | ||||
| 
 | ||||
|         self.post_data = { | ||||
|             'email': self.user.email | ||||
|         } | ||||
| 
 | ||||
|     def test_url_resolve_to_view_correctly(self): | ||||
|         found = resolve(self.url) | ||||
|         self.assertEqual(found.func.__name__, self.view.__name__) | ||||
| 
 | ||||
|     def test_get(self): | ||||
|         response = self.client.get(self.url) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertTemplateUsed(response, self.expected_template) | ||||
| 
 | ||||
|     def test_post(self): | ||||
|         response = self.client.post(self.url, data=self.post_data, follow=True) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
| 
 | ||||
|     def test_test_generate_email_context(self): | ||||
|         context = self.setup_view(self.view()).\ | ||||
|             test_generate_email_context(self.user) | ||||
|         self.assertEqual(context.get('user'), self.user) | ||||
|         self.assertEqual(context.get('site_name'), 'ungleich') | ||||
|         self.assertEqual(len(context.get('token')), 24) | ||||
| 
 | ||||
| 
 | ||||
| class PasswordResetConfirmViewTest(BaseTestCase): | ||||
| 
 | ||||
|     def setUp(self): | ||||
|         super(PasswordResetConfirmViewTest, self).setUp() | ||||
| 
 | ||||
|         self.view = PasswordResetConfirmView | ||||
|         self.expected_template = 'digitalglarus/confirm_reset_password.html' | ||||
|         self.user = mommy.make('membership.CustomUser') | ||||
|         self.password = 'fake_password' | ||||
|         self.user.set_password(self.password) | ||||
|         self.user.save() | ||||
| 
 | ||||
|         self.token = default_token_generator.make_token(self.user) | ||||
|         self.uid = urlsafe_base64_encode(force_bytes(self.user.pk)) | ||||
|         self.url = reverse('digitalglarus:reset_password_confirm', | ||||
|                            kwargs={'token': self.token, 'uidb64': self.uid}) | ||||
| 
 | ||||
|         self.post_data = { | ||||
|             'new_password1': 'new_password', | ||||
|             'new_password2': 'new_password' | ||||
|         } | ||||
| 
 | ||||
|     def test_url_resolve_to_view_correctly(self): | ||||
|         found = resolve(self.url) | ||||
|         self.assertEqual(found.func.__name__, self.view.__name__) | ||||
| 
 | ||||
|     def test_get(self): | ||||
|         response = self.client.get(self.url) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertTemplateUsed(response, self.expected_template) | ||||
| 
 | ||||
|     def test_post(self): | ||||
|         response = self.client.post(self.url, data=self.post_data, follow=True) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertTrue(not response.context['form'].errors) | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ from .views import ContactView, IndexView, AboutView, HistoryView, LoginView, Si | |||
|     PasswordResetView, PasswordResetConfirmView, MembershipPaymentView, MembershipActivatedView,\ | ||||
|     MembershipPricingView, BookingSelectDatesView, BookingPaymentView, OrdersBookingDetailView,\ | ||||
|     BookingOrdersListView, MembershipOrdersListView, OrdersMembershipDetailView, \ | ||||
|     MembershipDeactivateView, MembershipDeactivateSuccessView | ||||
|     MembershipDeactivateView, MembershipDeactivateSuccessView, UserBillingAddressView | ||||
| # from membership.views import LoginRegistrationView | ||||
| 
 | ||||
| urlpatterns = [ | ||||
|  | @ -20,6 +20,7 @@ urlpatterns = [ | |||
|     url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', | ||||
|         PasswordResetConfirmView.as_view(), name='reset_password_confirm'), | ||||
|     url(_(r'history/?$'), HistoryView.as_view(), name='history'), | ||||
|     url(_(r'users/billing_address/?$'), UserBillingAddressView.as_view(), name='user_billing_address'), | ||||
|     url(_(r'booking/?$'), BookingSelectDatesView.as_view(), name='booking'), | ||||
|     url(_(r'booking/payment/?$'), BookingPaymentView.as_view(), name='booking_payment'), | ||||
|     url(_(r'booking/orders/(?P<pk>\d+)/?$'), OrdersBookingDetailView.as_view(), | ||||
|  |  | |||
|  | @ -27,8 +27,9 @@ from membership.models import Calendar as CalendarModel, StripeCustomer | |||
| 
 | ||||
| from utils.views import LoginViewMixin, SignupViewMixin, \ | ||||
|     PasswordResetViewMixin, PasswordResetConfirmViewMixin | ||||
| from utils.forms import PasswordResetRequestForm | ||||
| from utils.forms import PasswordResetRequestForm, UserBillingAddressForm | ||||
| from utils.stripe_utils import StripeUtils | ||||
| from utils.models import UserBillingAddress | ||||
| 
 | ||||
| 
 | ||||
| from .forms import LoginForm, SignupForm, MembershipBillingForm, BookingDateForm,\ | ||||
|  | @ -120,6 +121,7 @@ class BookingSelectDatesView(LoginRequiredMixin, MembershipRequiredMixin, FormVi | |||
|             'free_days': free_days, | ||||
|             'start_date': start_date.strftime('%m/%d/%Y'), | ||||
|             'end_date': end_date.strftime('%m/%d/%Y'), | ||||
|             'is_free': final_price == 0 | ||||
|         }) | ||||
|         return super(BookingSelectDatesView, self).form_valid(form) | ||||
| 
 | ||||
|  | @ -132,7 +134,7 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView): | |||
|     booking_needed_fields = ['original_price', 'final_price', 'booking_days', 'free_days', | ||||
|                              'start_date', 'end_date', 'membership_required_months_price', | ||||
|                              'membership_required_months', 'booking_price_per_day', | ||||
|                              'total_discount'] | ||||
|                              'total_discount', 'is_free'] | ||||
| 
 | ||||
|     def dispatch(self, request, *args, **kwargs): | ||||
|         from_booking = all(field in request.session.keys() | ||||
|  | @ -146,12 +148,17 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView): | |||
|         return reverse('digitalglarus:booking_orders_detail', kwargs={'pk': order_id}) | ||||
| 
 | ||||
|     def get_form_kwargs(self): | ||||
|         current_billing_address = self.request.user.billing_addresses.first() | ||||
|         form_kwargs = super(BookingPaymentView, self).get_form_kwargs() | ||||
|         form_kwargs.update({ | ||||
|             'initial': { | ||||
|                 'start_date': self.request.session.get('start_date'), | ||||
|                 'end_date': self.request.session.get('end_date'), | ||||
|                 'price': self.request.session.get('final_price'), | ||||
|                 'street_address': current_billing_address.street_address, | ||||
|                 'city': current_billing_address.city, | ||||
|                 'postal_code': current_billing_address.postal_code, | ||||
|                 'country': current_billing_address.country, | ||||
|             } | ||||
|         }) | ||||
|         return form_kwargs | ||||
|  | @ -161,11 +168,15 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView): | |||
| 
 | ||||
|         booking_data = {key: self.request.session.get(key) | ||||
|                         for key in self.booking_needed_fields} | ||||
|         user = self.request.user | ||||
|         last_booking_order = BookingOrder.objects.filter(customer__user=user).last() | ||||
|         # booking_price_per_day = BookingPrice.objects.get().price_per_day | ||||
|         # total_discount = booking_price_per_day * booking_data.get('free_days') | ||||
|         booking_data.update({ | ||||
|             # 'booking_price_per_day': booking_price_per_day, | ||||
|             # 'total_discount': total_discount, | ||||
|             # 'current_billing_address': self.request.user.billing_addresses.first().to_dict(), | ||||
|             'credit_card_data': last_booking_order.get_booking_cc_data() if last_booking_order | ||||
|                                 else None, | ||||
|             'stripe_key': settings.STRIPE_API_PUBLIC_KEY | ||||
|         }) | ||||
|         context.update(booking_data) | ||||
|  | @ -177,11 +188,12 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView): | |||
|         token = data.get('token') | ||||
|         start_date = data.get('start_date') | ||||
|         end_date = data.get('end_date') | ||||
| 
 | ||||
|         is_free = context.get('is_free') | ||||
|         normal_price, final_price, free_days, membership_required_months,\ | ||||
|             membership_required_months_price = Booking.\ | ||||
|             booking_price(self.request.user, start_date, end_date) | ||||
| 
 | ||||
|         # if not credit_card_needed: | ||||
|         # Get or create stripe customer | ||||
|         customer = StripeCustomer.get_or_create(email=self.request.user.email, | ||||
|                                                 token=token) | ||||
|  | @ -189,6 +201,44 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView): | |||
|             form.add_error("__all__", "Invalid credit card") | ||||
|             return self.render_to_response(self.get_context_data(form=form)) | ||||
| 
 | ||||
|         if is_free: | ||||
|             billing_address = form.save() | ||||
| 
 | ||||
|             # Create Billing Address for User if he does not have one | ||||
|             if not customer.user.billing_addresses.count(): | ||||
|                 data.update({ | ||||
|                     'user': customer.user.id | ||||
|                 }) | ||||
|                 billing_address_user_form = UserBillingAddressForm(data) | ||||
|                 billing_address_user_form.is_valid() | ||||
|                 billing_address_user_form.save() | ||||
| 
 | ||||
|             # Create membership plan | ||||
|             booking_data = { | ||||
|                 'start_date': start_date, | ||||
|                 'end_date': end_date, | ||||
|                 'start_date': start_date, | ||||
|                 'free_days': free_days, | ||||
|                 'price': normal_price, | ||||
|                 'final_price': final_price, | ||||
|             } | ||||
|             booking = Booking.create(booking_data) | ||||
| 
 | ||||
|             # Create membership order | ||||
|             order_data = { | ||||
|                 'booking': booking, | ||||
|                 'customer': customer, | ||||
|                 'billing_address': billing_address, | ||||
|                 'amount': final_price, | ||||
|                 'original_price': normal_price, | ||||
|                 'special_month_price': BookingPrice.objects.last().special_month_price, | ||||
|                 'membership_required_months': membership_required_months, | ||||
|                 'membership_required_months_price': membership_required_months_price, | ||||
|             } | ||||
|             order = BookingOrder.create(order_data) | ||||
| 
 | ||||
|             return HttpResponseRedirect(self.get_success_url(order.id)) | ||||
| 
 | ||||
|         # Make stripe charge to a customer | ||||
|         stripe_utils = StripeUtils() | ||||
|         charge_response = stripe_utils.make_charge(amount=final_price, | ||||
|  | @ -205,9 +255,18 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView): | |||
| 
 | ||||
|         charge = charge_response.get('response_object') | ||||
| 
 | ||||
|         # Create Billing Address | ||||
|         # Create Billing Address for Membership Order | ||||
|         billing_address = form.save() | ||||
| 
 | ||||
|         # Create Billing Address for User if he does not have one | ||||
|         if not customer.user.billing_addresses.count(): | ||||
|             data.update({ | ||||
|                 'user': customer.user.id | ||||
|             }) | ||||
|             billing_address_user_form = UserBillingAddressForm(data) | ||||
|             billing_address_user_form.is_valid() | ||||
|             billing_address_user_form.save() | ||||
| 
 | ||||
|         # Create membership plan | ||||
|         booking_data = { | ||||
|             'start_date': start_date, | ||||
|  | @ -319,6 +378,7 @@ class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView): | |||
|                 'stripe_charge': charge, | ||||
|                 'amount': membership_type.first_month_price | ||||
|             } | ||||
| 
 | ||||
|             membership_order = MembershipOrder.create(order_data) | ||||
| 
 | ||||
|             request.session.update({ | ||||
|  | @ -369,6 +429,7 @@ class MembershipActivatedView(TemplateView): | |||
| class MembershipDeactivateView(LoginRequiredMixin, UpdateView): | ||||
|     template_name = "digitalglarus/membership_deactivated.html" | ||||
|     model = Membership | ||||
|     success_message = "Your membership has been deactivated :(" | ||||
|     success_url = reverse_lazy('digitalglarus:membership_orders_list') | ||||
|     login_url = reverse_lazy('digitalglarus:login') | ||||
|     fields = '__all__' | ||||
|  | @ -384,9 +445,38 @@ class MembershipDeactivateView(LoginRequiredMixin, UpdateView): | |||
|     def post(self, *args, **kwargs): | ||||
|         membership = self.get_object() | ||||
|         membership.deactivate() | ||||
| 
 | ||||
|         messages.add_message(self.request, messages.SUCCESS, self.success_message) | ||||
| 
 | ||||
|         return HttpResponseRedirect(self.success_url) | ||||
| 
 | ||||
| 
 | ||||
| class UserBillingAddressView(LoginRequiredMixin, UpdateView): | ||||
|     model = UserBillingAddress | ||||
|     form_class = UserBillingAddressForm | ||||
|     template_name =  "digitalglarus/user_billing_address.html" | ||||
|     success_url = reverse_lazy('digitalglarus:user_billing_address') | ||||
| 
 | ||||
|     def get_form_kwargs(self): | ||||
|         current_billing_address = self.request.user.billing_addresses.first() | ||||
|         form_kwargs = super(UserBillingAddressView, self).get_form_kwargs() | ||||
|         form_kwargs.update({ | ||||
|             'initial': { | ||||
|                 'street_address': current_billing_address.street_address, | ||||
|                 'city': current_billing_address.city, | ||||
|                 'postal_code': current_billing_address.postal_code, | ||||
|                 'country': current_billing_address.country, | ||||
|             } | ||||
|         }) | ||||
|         return form_kwargs | ||||
| 
 | ||||
|     def get_object(self): | ||||
|         current_billing_address = self.request.user.billing_addresses.filter(current=True).last() | ||||
|         if not current_billing_address: | ||||
|             raise AttributeError("Billing Address does not exists") | ||||
|         return current_billing_address | ||||
| 
 | ||||
| 
 | ||||
| class MembershipDeactivateSuccessView(LoginRequiredMixin, TemplateView): | ||||
|     template_name = "digitalglarus/membership_deactivated_success.html" | ||||
| 
 | ||||
|  | @ -401,9 +491,11 @@ class MembershipOrdersListView(LoginRequiredMixin, ListView): | |||
|     def get_context_data(self, **kwargs): | ||||
|         context = super(MembershipOrdersListView, self).get_context_data(**kwargs) | ||||
|         start_date, end_date = MembershipOrder.current_membership(self.request.user) | ||||
|         current_billing_address = self.request.user.billing_addresses.filter(current=True).last() | ||||
|         context.update({ | ||||
|             'membership_start_date': start_date, | ||||
|             'membership_end_date': end_date, | ||||
|             'billing_address': current_billing_address | ||||
|         }) | ||||
|         return context | ||||
| 
 | ||||
|  | @ -477,6 +569,14 @@ class BookingOrdersListView(LoginRequiredMixin, ListView): | |||
|     model = BookingOrder | ||||
|     paginate_by = 10 | ||||
| 
 | ||||
|     def get_context_data(self, **kwargs): | ||||
|         context = super(BookingOrdersListView, self).get_context_data(**kwargs) | ||||
|         current_billing_address = self.request.user.billing_addresses.filter(current=True).last() | ||||
|         context.update({ | ||||
|             'billing_address': current_billing_address | ||||
|         }) | ||||
|         return context | ||||
| 
 | ||||
|     def get_queryset(self): | ||||
|         queryset = super(BookingOrdersListView, self).get_queryset() | ||||
|         queryset = queryset.filter(customer__user=self.request.user) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| from django import forms | ||||
| from .models import ContactMessage, BillingAddress | ||||
| from .models import ContactMessage, BillingAddress, UserBillingAddress | ||||
| from django.template.loader import render_to_string | ||||
| from django.core.mail import EmailMultiAlternatives | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
|  | @ -109,6 +109,19 @@ class BillingAddressForm(forms.ModelForm): | |||
|         } | ||||
| 
 | ||||
| 
 | ||||
| class UserBillingAddressForm(forms.ModelForm): | ||||
|     user = forms.ModelChoiceField(queryset=CustomUser.objects.all(), | ||||
|                                              widget=forms.HiddenInput()) | ||||
|     class Meta: | ||||
|         model = UserBillingAddress | ||||
|         fields = ['street_address', 'city', 'postal_code', 'country', 'user'] | ||||
|         labels = { | ||||
|             'street_address': _('Street Address'), | ||||
|             'city': _('City'), | ||||
|             'postal_code': _('Postal Code'), | ||||
|             'Country': _('Country'), | ||||
|         } | ||||
| 
 | ||||
| class ContactUsForm(forms.ModelForm): | ||||
|     error_css_class = 'autofocus' | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										34
									
								
								utils/migrations/0003_userbillingaddress.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								utils/migrations/0003_userbillingaddress.py
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										22
									
								
								utils/migrations/0004_auto_20161013_0253.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								utils/migrations/0004_auto_20161013_0253.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2016-10-13 02:53 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.conf import settings | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('utils', '0003_userbillingaddress'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='userbillingaddress', | ||||
|             name='user', | ||||
|             field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='billing_addresses', to=settings.AUTH_USER_MODEL), | ||||
|         ), | ||||
|     ] | ||||
|  | @ -1,20 +1,45 @@ | |||
| from django.db import models | ||||
| from django.core import serializers | ||||
| from django.forms.models import model_to_dict | ||||
| 
 | ||||
| from membership.models import CustomUser | ||||
| 
 | ||||
| from .fields import CountryField | ||||
| 
 | ||||
| 
 | ||||
| # Create your models here. | ||||
| 
 | ||||
| 
 | ||||
| class BillingAddress(models.Model): | ||||
| class BaseBillingAddress(models.Model): | ||||
|     street_address = models.CharField(max_length=100) | ||||
|     city = models.CharField(max_length=50) | ||||
|     postal_code = models.CharField(max_length=50) | ||||
|     country = CountryField() | ||||
| 
 | ||||
|     class Meta: | ||||
|         abstract = True | ||||
| 
 | ||||
| class BillingAddress(BaseBillingAddress): | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return self.street_address | ||||
| 
 | ||||
| 
 | ||||
| class UserBillingAddress(BaseBillingAddress): | ||||
|     user = models.ForeignKey(CustomUser, related_name='billing_addresses') | ||||
|     current = models.BooleanField(default=True) | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return self.street_address | ||||
| 
 | ||||
|     def to_dict(self): | ||||
|         return { | ||||
|             'Street Address': self.street_address, | ||||
|             'City': self.city, | ||||
|             'Postal Code': self.postal_code, | ||||
|             'Country': self.country, | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| class ContactMessage(models.Model): | ||||
|     name = models.CharField(max_length=200) | ||||
|     email = models.EmailField() | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue