Commits

Alex Ulianytskyi  committed c103540

Реализовано пошаговое бронирование квартиры (без каптчи) и отправка сообщения клиенту.

  • Participants
  • Parent commits 0b1b77a

Comments (0)

Files changed (22)

 from catalogue.admin import BonusTypeAdmin
 from catalogue.admin import Building
 from catalogue.admin import BuildingAdmin
+from catalogue.admin import CityAdmin
+from catalogue.admin import CountryAdmin
 from catalogue.admin import CurrencyAdmin
 from catalogue.admin import ReservationPeriodAdmin
 from catalogue.admin import Street
 from catalogue.admin import StreetAdmin
 from catalogue.models import Bonus
 from catalogue.models import BonusType
+from catalogue.models import City
+from catalogue.models import Country
 from catalogue.models import Currency
 from catalogue.models import ReservationPeriod
 from django.conf.urls.defaults import *
 site.register(Banner, BannerAdmin)
 site.register(BannerLogEntry, BannerLogEntryAdmin)
 site.register(ReservationPeriod, ReservationPeriodAdmin)
-site.register(Currency, CurrencyAdmin)
+site.register(Currency, CurrencyAdmin)
+site.register(Country, CountryAdmin)
+site.register(City, CityAdmin)

File catalogue/admin.py

 # coding: utf-8
-import multilingual
 from catalogue.forms import ApartmentForm
 from catalogue.forms import BonusForm
 from catalogue.forms import BonusTypeForm
 from catalogue.models import *
 from django.conf.urls.defaults import *
 from django.contrib import admin
+import multilingual
 
 class BonusInline(admin.StackedInline):
     model = Bonus
     list_display = ('__unicode__', 'number', 'street')
 
 class StreetAdmin(multilingual.ModelAdmin):
-    list_display = ('__unicode__',)
+    list_display = ('__unicode__', )
 
 class ApartmentImageAdmin(admin.ModelAdmin):
     pass
     list_display = ('apartment', 'start', 'end')
 
 class CurrencyAdmin(admin.ModelAdmin):
-    list_display = ('code', 'exchange_rate', 'symbol')
+    list_display = ('code', 'exchange_rate', 'symbol')
+
+class CountryAdmin(multilingual.ModelAdmin):
+    list_display = ('name', 'code')
+
+class CityAdmin(multilingual.ModelAdmin):
+    list_display = ('name', 'country')

File catalogue/forms.py

 # -*- coding: utf-8 -*-
+from annoying.functions import get_object_or_None
 from catalogue import models
 from catalogue.utils import build_bonus_field
 from django import forms
 
 class JqueyUiDatepicker(forms.DateInput):
     class Media:
-        js = (settings.MEDIA_URL + "js/jqueryui-calendar.js",)
+        js = (settings.MEDIA_URL + "js/jqueryui-calendar.js", )
 
     def __init__(self, attrs={}):
         super(JqueyUiDatepicker, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
     if start > end:
         raise forms.ValidationError(form.error_fields['order'])
     apartment_id = cleaned_data.get('apartment', None)
-    try:
-        apartment = models.Apartment.objects.get(id=apartment_id)
-    except models.Aprtment.ObjectDoesNotExist:
+    apartment = get_object_or_None(models.Apartment, id=apartment_id)
+    if apartment is None:
         raise forms.ValidationError(_('The apartment not found'))
     if not apartment.is_period_free(start, end):
         raise forms.ValidationError(form.error_fields['occupied'])
     end = forms.DateField(label=_('departure date'), widget=JqueyUiDatepicker, input_formats=['%d.%m.%Y'])
     first_name = forms.CharField(label=_('first name'), max_length=50)
     last_name = forms.CharField(label=_('last name'), max_length=50)
+    country = forms.ModelChoiceField(label=_('country'), queryset=models.Country.objects.all())
+    city = forms.ModelChoiceField(label=_('city'), queryset=models.City.objects.none())
     phone_number = forms.RegexField(label=_('phone number'), regex=PHONE_REGEX, initial='+0 (000) 000-00-00')
     email = forms.EmailField(label=_('email'))
     is_agree = forms.BooleanField(label=_('I am agree'))
-    captcha = ReCaptchaField()
+    def add_captcha(self):
+        self.fields['captcha'] = ReCaptchaField()
+    def update_city_queryset(self):
+        self.fields['city'].queryset = models.City.objects.filter(country=self.data.get('country', None))
+    def is_valid(self):
+        self.update_city_queryset()
+        return super(ReservationForm, self).is_valid()
     def has_changed(self):
         if not self.quiet:
             return super(ReservationForm, self).has_changed()
     def clean_is_agree(self):
         is_agree = self.cleaned_data.get('is_agree', False)
         if not is_agree:
-            raise forms.ValidationError(form.error_fields['not_agree'])
-        return is_agree
+            raise forms.ValidationError(self.error_fields['not_agree'])
+        return is_agree

File catalogue/migrations/0016_add_country_and_city.py

+
+from south.db import db
+from django.db import models
+from catalogue.models import *
+
+class Migration:
+    
+    def forwards(self, orm):
+        
+        # Adding model 'City'
+        db.create_table('catalogue_city', (
+            ('id', orm['catalogue.city:id']),
+            ('country', orm['catalogue.city:country']),
+        ))
+        db.send_create_signal('catalogue', ['City'])
+        
+        # Adding model 'Country'
+        db.create_table('catalogue_country', (
+            ('id', orm['catalogue.country:id']),
+            ('code', orm['catalogue.country:code']),
+        ))
+        db.send_create_signal('catalogue', ['Country'])
+        
+        # Adding model 'CountryTranslation'
+        db.create_table('catalogue_country_translation', (
+            ('id', orm['catalogue.countrytranslation:id']),
+            ('name', orm['catalogue.countrytranslation:name']),
+            ('language_id', orm['catalogue.countrytranslation:language_id']),
+            ('master', orm['catalogue.countrytranslation:master']),
+        ))
+        db.send_create_signal('catalogue', ['CountryTranslation'])
+        
+        # Adding model 'CityTranslation'
+        db.create_table('catalogue_city_translation', (
+            ('id', orm['catalogue.citytranslation:id']),
+            ('name', orm['catalogue.citytranslation:name']),
+            ('language_id', orm['catalogue.citytranslation:language_id']),
+            ('master', orm['catalogue.citytranslation:master']),
+        ))
+        db.send_create_signal('catalogue', ['CityTranslation'])
+        
+        # Creating unique_together for [language_id, master] on CountryTranslation.
+        db.create_unique('catalogue_country_translation', ['language_id', 'master_id'])
+        
+        # Creating unique_together for [language_id, master] on CityTranslation.
+        db.create_unique('catalogue_city_translation', ['language_id', 'master_id'])
+        
+    
+    
+    def backwards(self, orm):
+        
+        # Deleting unique_together for [language_id, master] on CityTranslation.
+        db.delete_unique('catalogue_city_translation', ['language_id', 'master_id'])
+        
+        # Deleting unique_together for [language_id, master] on CountryTranslation.
+        db.delete_unique('catalogue_country_translation', ['language_id', 'master_id'])
+        
+        # Deleting model 'City'
+        db.delete_table('catalogue_city')
+        
+        # Deleting model 'Country'
+        db.delete_table('catalogue_country')
+        
+        # Deleting model 'CountryTranslation'
+        db.delete_table('catalogue_country_translation')
+        
+        # Deleting model 'CityTranslation'
+        db.delete_table('catalogue_city_translation')
+        
+    
+    
+    models = {
+        'catalogue.apartment': {
+            'Meta': {'unique_together': "(('number', 'building'),)"},
+            'building': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Building']"}),
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '3', 'unique': 'True', 'null': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'entrance': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'floor': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'floors': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image': ('ImageWithThumbField', [], {'storage': 'image_strorage_obj', 'max_length': '255', 'blank': 'True', 'null': 'True'}),
+            'layout': ('django.db.models.fields.CharField', [], {'default': "'s'", 'max_length': '1'}),
+            'level': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}),
+            'map_link': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True'}),
+            'number': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'price_euro': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2', 'blank': 'True'}),
+            'price_rur': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2', 'blank': 'True'}),
+            'price_uah': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            'price_usd': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2', 'blank': 'True'}),
+            'rooms': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'updateted_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+        },
+        'catalogue.apartmentimage': {
+            'apartment': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Apartment']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image': ('ImageWithThumbField', [], {'storage': 'image_strorage_obj', 'max_length': '255', 'blank': 'True'})
+        },
+        'catalogue.apartmenttranslation': {
+            'Meta': {'unique_together': "(('language_id', 'master'),)", 'db_table': "'catalogue_apartment_translation'"},
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'master': ('multilingual.fields.TranslationForeignKey', [], {'related_name': "'translations'", 'to': "orm['catalogue.Apartment']"})
+        },
+        'catalogue.bonus': {
+            'Meta': {'unique_together': "(('apartment', 'bonus_type'),)"},
+            'apartment': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Apartment']"}),
+            'bonus_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BonusType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.bonustype': {
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'options': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.building': {
+            'Meta': {'unique_together': "(('number', 'street'),)"},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.CharField', [], {'max_length': '8'}),
+            'street': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Street']"})
+        },
+        'catalogue.city': {
+            'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Country']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.citytranslation': {
+            'Meta': {'unique_together': "(('language_id', 'master'),)", 'db_table': "'catalogue_city_translation'"},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'master': ('multilingual.fields.TranslationForeignKey', [], {'related_name': "'translations'", 'to': "orm['catalogue.City']"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'catalogue.country': {
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.countrytranslation': {
+            'Meta': {'unique_together': "(('language_id', 'master'),)", 'db_table': "'catalogue_country_translation'"},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'master': ('multilingual.fields.TranslationForeignKey', [], {'related_name': "'translations'", 'to': "orm['catalogue.Country']"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'catalogue.currency': {
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4', 'db_index': 'True'}),
+            'exchange_rate': ('django.db.models.fields.FloatField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'symbol': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'})
+        },
+        'catalogue.reservationperiod': {
+            'Meta': {'unique_together': "(('apartment', 'start', 'end'),)"},
+            'apartment': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Apartment']"}),
+            'end': ('django.db.models.fields.DateField', [], {'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'start': ('django.db.models.fields.DateField', [], {'db_index': 'True'})
+        },
+        'catalogue.street': {
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.streettranslation': {
+            'Meta': {'unique_together': "(('language_id', 'master'),)", 'db_table': "'catalogue_street_translation'"},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'master': ('multilingual.fields.TranslationForeignKey', [], {'related_name': "'translations'", 'to': "orm['catalogue.Street']"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+    
+    complete_apps = ['catalogue']

File catalogue/models.py

 import os.path
 
-import multilingual
 from django.conf import settings
 from django.db import models
 from django.db.models.sql.constants import LOOKUP_SEP
 from django_utils.daterange import DateRange
 from django_utils.functions import normalize_filename
 from django_utils.models import ImageWithThumbField
+import multilingual
 
 image_strorage_obj = image_storage.ImageStorage(settings.IMAGE_STORAGE_ROOT, settings.IMAGE_STORAGE_URL)
 
                                 verbose_name=_('image'))
     apartment = models.ForeignKey(Apartment, verbose_name=_('apartment'))
     class Meta:
-        ordering = ('apartment', 'id',)
+        ordering = ('apartment', 'id', )
         verbose_name = _('apartment image')
         verbose_name_plural = _('apartment images')
     def __unicode__(self):
     options = models.CharField(_('options'), max_length=255, null=True, blank=True)
     icon = models.ImageField(_('icon'), upload_to=lambda i, fn: fn, null=True, blank=True)
     class Meta:
-        ordering = ('name', )
+        ordering = ('name',)
         verbose_name = _('bonus type')
         verbose_name_plural = _('bonus types')
     def __unicode__(self):
     value = models.CharField(_('value'), max_length=255, null=True, blank=True)
     class Meta:
         unique_together = ('apartment', 'bonus_type')
-        ordering = ('apartment', 'bonus_type', )
+        ordering = ('apartment', 'bonus_type',)
         verbose_name = _('bonus')
         verbose_name_plural = _('bonuses')
 
     class Meta:
         verbose_name = _('currency')
         verbose_name_plural = _('currencies')
-        ordering = ('exchange_rate', )
+        ordering = ('exchange_rate',)
     def __unicode__(self):
         return self.code
     @models.permalink
     def get_absolute_url(self):
-        return ('catalogue:set_currency', (), {'currency_code': self.code})
+        return ('catalogue:set_currency', (), {'currency_code': self.code})
+
+class Country(models.Model):
+    code = models.CharField(_('code'), max_length=2, unique=True)
+    class Translation(multilingual.Translation):
+        name = models.CharField(_('name'), max_length=255)
+        ordering = ('exchange_rate',)
+    class Meta:
+        verbose_name = _('country')
+        verbose_name_plural = _('countries')
+        ordering = ('code',)
+    def __unicode__(self):
+        return self.name
+    def save(self, * args, ** kwargs):
+        self.code = self.code.upper()
+        super(Country, self).save(*args, ** kwargs)
+
+class City(models.Model):
+    country = models.ForeignKey(Country, verbose_name=_('country'))
+    class Translation(multilingual.Translation):
+        name = models.CharField(_('name'), max_length=255)
+    class Meta:
+        verbose_name = _('city')
+        verbose_name_plural = _('cities')
+        ordering = ('country',)
+    def __unicode__(self):
+        return self.name

File catalogue/urls.py

    url(r'^rooms/(?P<rooms>\d+|vip)/$', 'rooms', name='rooms'),
    url(r'^find_by_id/$', 'find_by_id', name='find_by_id'),
    url(r'^reserve/$', 'reserve', name='reserve'),
-   url(r'^validate_reservation_form/$', 'validate_reservation_form', name='validate_reservation_form'),
 )

File catalogue/views.py

 # -*- coding: utf-8 -*-
 from annoying.decorators import render_to
+from annoying.functions import get_config
 from annoying.functions import get_object_or_None
 from catalogue import forms
 from catalogue import models
 from catalogue import utils
 from catalogue.utils import get_currency_code
 from catalogue.utils import set_currency_code
+from django.core.mail import EmailMultiAlternatives
 from django.http import HttpResponseRedirect
 from django.http import QueryDict
 from django.shortcuts import get_object_or_404
 from django.shortcuts import redirect
+from django.template import RequestContext
+from django.template.loader import render_to_string
+from django.utils.encoding import force_unicode
+from django.utils.translation import ugettext_lazy as _
 from django.views.generic.list_detail import object_list
 from django_utils.functions import compact_dict
 
         apartment = get_object_or_404(models.Apartment, code=code)
     search_form = forms.SearchForm({'currency': currency})
     find_by_id_form = forms.FindByIdForm()
-    reservation_form = forms.ReservationForm({'apartment': apartment.id}, empty_permitted=True)
+    reservation_state = request.session.get('reservation_state', {})
+    apartment_state = reservation_state.get(force_unicode(apartment.id), {})
+    form_data = apartment_state.get('form_data', QueryDict('', True)).copy()
+    form_data.update({u'apartment': apartment.id})
+    reservation_state[force_unicode(apartment.id)] = apartment_state
+    request.session['reservation_state'] = reservation_state
+    request.session.modified = True
+    reservation_form = forms.ReservationForm(form_data, empty_permitted=True)
     reservation_form.quiet = True
     return {
         'apartment': apartment,
         'search_form': search_form,
         'find_by_id_form': find_by_id_form,
         'reservation_form': reservation_form,
+        'reservation_form_valid': reservation_form.is_valid(),
         }
 
 def search(request):
                        extra_context=context
                        )
 
-@render_to('catalogue/find_by_id.html')    
+@render_to('catalogue/find_by_id.html')
 def find_by_id(request):
     currency = get_currency_code(request)
     if request.method == 'POST':
         'find_by_id_query': find_by_id_form.data.get('id', ''),
         }
 
-@render_to('catalogue/reserve.html')
+@render_to('partials/reservation_form.html')
 def reserve(request):
+    context = {}
+    email_sent = False
     currency = get_currency_code(request)
     search_form = forms.SearchForm({'currency': currency})
     find_by_id_form = forms.FindByIdForm()
-    context = {
-        'search_form': search_form,
-        'find_by_id_form': find_by_id_form,
-    }
-    return context
+    context.update({'currency': currency})
+    context.update({'search_form': search_form})
+    context.update({'find_by_id_form': find_by_id_form})
+    reservation_state = request.session.get('reservation_state', {})
+    if request.method == 'POST':
+        apartment_id = request.POST.get(u'apartment', None)
+        apartment = get_object_or_None(models.Apartment, id=apartment_id)
+        context.update({'apartment': apartment})
+        if apartment is not None:
+            action = None
+            if u'continue' in request.POST:
+                action = 'continue'
+            if u'back' in request.POST:
+                action = 'back'
+            if u'reserve' in request.POST:
+                action = 'reserve'
+            if u'reset' in request.POST:
+                action = 'reset'
+            apartment_id = force_unicode(apartment_id)
+            apartment_state = reservation_state.get(apartment_id, {})
+            form_data = apartment_state.get(u'form_data', QueryDict('', True))
+            if action in ('back', 'reserve'):
+                reservation_form = forms.ReservationForm(form_data)
+            else:
+                reservation_form = forms.ReservationForm(request.POST)
+            reservation_form_valid = reservation_form.is_valid()
+            context.update({'reservation_form': reservation_form})
+            context.update({'reservation_form_valid': reservation_form_valid})
+            apartment_state[u'form_data'] = reservation_form.data
+            if reservation_form_valid:
+                if action in ('continue', 'reserve'):
+                    reservation_info = [{'key': field.label, 'value': reservation_form.cleaned_data.get(field.name, "")}
+                    for field in reservation_form.visible_fields()]
+                    context.update({'reservation_info': reservation_info})
+                if action == 'continue':
+                    context.update({'TEMPLATE': 'partials/reservation_info.html'})
+                elif action == 'reserve':
+                    first_name = reservation_form.cleaned_data.get('first_name', None)
+                    last_name = reservation_form.cleaned_data.get('last_name', None)
+                    email = reservation_form.cleaned_data.get('email', None)
+                    context.update({'email': email})
+                    email_prefix = get_config('EMAIL_SUBJECT_PREFIX', 'Botanic Apartments] ')
+                    from_email = get_config('DEFAULT_FROM_EMAIL', 'Botanic Apartments site <noreplay@botanicapartments.1gb.ua>')
+                    client_body_html = render_to_string('catalogue/mail/reservation_for_client.html', RequestContext(request, context))
+                    client_body_text = render_to_string('catalogue/mail/reservation_for_client.txt', RequestContext(request, context))
+                    recipient_list = [u'"%s %s" <%s>' % (first_name, last_name, email)]
+                    subject = _('%(prefix)sApartment reservation - %(apartment)s') % {'prefix': email_prefix, 'apartment': apartment}
+                    client_msg = EmailMultiAlternatives(subject, client_body_text, from_email, recipient_list)
+                    client_msg.attach_alternative(client_body_html, "text/html")
+                    try:
+                        email_sent = client_msg.send()
+                        context.update({'TEMPLATE': 'partials/reservation_complete.html'})
+                    except Exception, exception:
+                        context.update({'exception': exception})
+                        context.update({'TEMPLATE': 'partials/reservation_error.html'})
 
-@render_to()
-def validate_reservation_form(request):
-    currency = get_currency_code(request)
-    context = {}
-    if request.method == 'POST':
-        reservation_form = forms.ReservationForm(request.POST)
-        context.update({'reservation_form_valid': reservation_form.is_valid()})
+                reservation_state[apartment_id] = apartment_state
+            if action == 'reserve' and email_sent or action == 'reset':
+                if apartment_id in reservation_state:
+                    del reservation_state[apartment_id]
     else:
         reservation_form = forms.ReservationForm()
-    context.update({'reservation_form': reservation_form})
-    context.update({'TEMPLATE': 'partials/reservation_form.html'})
-    context.update({'currency': currency})
+    request.session['reservation_state'] = reservation_state
+    request.session.modified = True
     return context
 
 def set_currency(request, currency_code):

File db/development.sqlite

Binary file modified.

File locale/en/LC_MESSAGES/django.mo

Binary file modified.

File locale/en/LC_MESSAGES/django.po

 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-01-17 17:05+0200\n"
+"POT-Creation-Date: 2010-02-13 21:13+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 msgid "file"
 msgstr ""
 
-#: banners/forms.py:40 catalogue/models.py:194
+#: banners/forms.py:40 catalogue/models.py:205
 msgid "start"
 msgstr ""
 
-#: banners/forms.py:41 catalogue/models.py:195
+#: banners/forms.py:41 catalogue/models.py:206
 msgid "end"
 msgstr ""
 
 msgid "link"
 msgstr ""
 
-#: banners/models.py:38
+#: banners/models.py:38 catalogue/models.py:246 catalogue/models.py:260
 msgid "code"
 msgstr ""
 
 msgid "banner log entries"
 msgstr ""
 
-#: catalogue/forms.py:40
+#: catalogue/forms.py:41 catalogue/models.py:250
 msgid "currency"
 msgstr ""
 
-#: catalogue/forms.py:41
+#: catalogue/forms.py:42
 msgid "price"
 msgstr ""
 
-#: catalogue/forms.py:42 catalogue/models.py:115
+#: catalogue/forms.py:43 catalogue/models.py:115
 msgid "rooms"
 msgstr ""
 
-#: catalogue/forms.py:43 catalogue/models.py:125
+#: catalogue/forms.py:44 catalogue/models.py:125
 msgid "layout"
 msgstr ""
 
-#: catalogue/forms.py:44 catalogue/models.py:20 catalogue/models.py:27
+#: catalogue/forms.py:45 catalogue/models.py:20 catalogue/models.py:27
 #: templates/wizard/building_index.html:13
 #: templates/wizard/building_select.html:8 templates/wizard/street_edit.html:9
 msgid "street"
 msgstr ""
 
-#: catalogue/forms.py:45 catalogue/models.py:123
+#: catalogue/forms.py:46 catalogue/models.py:123
 msgid "level"
 msgstr ""
 
-#: catalogue/forms.py:63
+#: catalogue/forms.py:64
 msgid "ID"
 msgstr ""
 
-#: catalogue/forms.py:90
+#: catalogue/forms.py:91
 msgid "Number of choices must be greater then one"
 msgstr ""
 
 msgid "Start later then end"
 msgstr ""
 
-#: catalogue/forms.py:113 catalogue/forms.py:122
+#: catalogue/forms.py:113 catalogue/forms.py:123
 msgid ""
 "The apartment occupied during this period, please select a different dates."
 msgstr ""
 
-#: catalogue/forms.py:121
+#: catalogue/forms.py:122
 msgid "Check-in date later then departure date"
 msgstr ""
 
-#: catalogue/forms.py:124 catalogue/models.py:131 catalogue/models.py:184
-#: catalogue/models.py:193 catalogue/models.py:225
+#: catalogue/forms.py:124
+msgid "You must agree with public agreement"
+msgstr ""
+
+#: catalogue/forms.py:126 catalogue/models.py:131 catalogue/models.py:195
+#: catalogue/models.py:204 catalogue/models.py:236
 #: templates/wizard/apartment_edit.html:9
 msgid "apartment"
 msgstr ""
 
-#: catalogue/forms.py:125
+#: catalogue/forms.py:127
 msgid "check-in date"
 msgstr ""
 
-#: catalogue/forms.py:126
+#: catalogue/forms.py:128
 msgid "departure date"
 msgstr ""
 
-#: catalogue/forms.py:127
+#: catalogue/forms.py:129
 msgid "first name"
 msgstr ""
 
-#: catalogue/forms.py:128
+#: catalogue/forms.py:130
 msgid "last name"
 msgstr ""
 
-#: catalogue/forms.py:129
+#: catalogue/forms.py:131 catalogue/models.py:265 catalogue/models.py:275
+msgid "country"
+msgstr ""
+
+#: catalogue/forms.py:132 catalogue/models.py:279
+msgid "city"
+msgstr ""
+
+#: catalogue/forms.py:133
 msgid "phone number"
 msgstr ""
 
-#: catalogue/forms.py:130
+#: catalogue/forms.py:134
 msgid "email"
 msgstr ""
 
-#: catalogue/forms.py:131
+#: catalogue/forms.py:135
 msgid "I am agree"
 msgstr ""
 
-#: catalogue/models.py:18 catalogue/models.py:213 posts/models.py:6
+#: catalogue/models.py:18 catalogue/models.py:224 catalogue/models.py:262
+#: catalogue/models.py:277 posts/models.py:6
 #: templates/wizard/street_select.html:9
 msgid "name"
 msgstr ""
 msgid "map link"
 msgstr ""
 
-#: catalogue/models.py:105 catalogue/models.py:183
+#: catalogue/models.py:105 catalogue/models.py:194
 msgid "image"
 msgstr ""
 
 msgid "description"
 msgstr ""
 
-#: catalogue/models.py:140
+#: catalogue/models.py:151
 msgid "num."
 msgstr ""
 
-#: catalogue/models.py:187 templates/catalogue/apartment.html:53
+#: catalogue/models.py:198 templates/catalogue/apartment.html:68
 msgid "apartment image"
 msgstr ""
 
-#: catalogue/models.py:188
+#: catalogue/models.py:199
 msgid "apartment images"
 msgstr ""
 
-#: catalogue/models.py:199
+#: catalogue/models.py:210
 msgid "reservation period"
 msgstr ""
 
-#: catalogue/models.py:200
+#: catalogue/models.py:211
 msgid "reservation periods"
 msgstr ""
 
-#: catalogue/models.py:205
+#: catalogue/models.py:216
 msgid "boolean"
 msgstr ""
 
-#: catalogue/models.py:206
+#: catalogue/models.py:217
 msgid "integer"
 msgstr ""
 
-#: catalogue/models.py:207
+#: catalogue/models.py:218
 msgid "float"
 msgstr ""
 
-#: catalogue/models.py:208
+#: catalogue/models.py:219
 msgid "choices"
 msgstr ""
 
-#: catalogue/models.py:214
+#: catalogue/models.py:225
 msgid "field"
 msgstr ""
 
-#: catalogue/models.py:215
+#: catalogue/models.py:226
 msgid "options"
 msgstr ""
 
-#: catalogue/models.py:216
+#: catalogue/models.py:227
 msgid "icon"
 msgstr ""
 
-#: catalogue/models.py:219 catalogue/models.py:226
+#: catalogue/models.py:230 catalogue/models.py:237
 msgid "bonus type"
 msgstr ""
 
-#: catalogue/models.py:220
+#: catalogue/models.py:231
 msgid "bonus types"
 msgstr ""
 
-#: catalogue/models.py:227
+#: catalogue/models.py:238
 msgid "value"
 msgstr ""
 
-#: catalogue/models.py:231
+#: catalogue/models.py:242
 msgid "bonus"
 msgstr ""
 
-#: catalogue/models.py:232
+#: catalogue/models.py:243
 msgid "bonuses"
 msgstr ""
 
+#: catalogue/models.py:247
+msgid "exchange rate"
+msgstr ""
+
+#: catalogue/models.py:248
+msgid "symbol"
+msgstr ""
+
+#: catalogue/models.py:251
+msgid "currencies"
+msgstr ""
+
+#: catalogue/models.py:266
+msgid "countries"
+msgstr ""
+
+#: catalogue/models.py:280
+msgid "cities"
+msgstr ""
+
+#: catalogue/views.py:197
+#, python-format
+msgid "%(prefix)sApartment reservation - %(apartment)s"
+msgstr ""
+
 #: posts/models.py:9 posts/models.py:39
 msgid "post type"
 msgstr ""
 msgid "Language"
 msgstr ""
 
-#: templates/catalogue/apartment.html:31
+#: templates/catalogue/apartment.html:46
 msgid "permalink"
 msgstr ""
 
-#: templates/catalogue/apartment.html:45
+#: templates/catalogue/apartment.html:60
 msgid "view on map"
 msgstr ""
 
-#: templates/catalogue/apartment.html:58
+#: templates/catalogue/apartment.html:73
 msgid "apartment image thumbnail"
 msgstr ""
 
-#: templates/catalogue/apartment.html:63
-#: templates/partials/reservation_form.html:3
+#: templates/catalogue/apartment.html:78
+#: templates/partials/reservation_info.html:14
 msgid "reserve"
 msgstr ""
 
 msgid "Query is not valid"
 msgstr ""
 
+#: templates/catalogue/mail/reservation_for_client.html:2
+#: templates/partials/reservation_complete.html:2
+#, python-format
+msgid ""
+"\n"
+"You reserved the apartment <strong>%(apartment)s</strong>. The instructions "
+"sent to email <strong>%(email)s</strong>.<br/>\n"
+"Details:\n"
+msgstr ""
+
 #: templates/partials/apartment.html:6 templates/partials/building.html:6
 #: templates/partials/street.html:6 templates/wizard/apartment_select.html:5
 #: templates/wizard/building_select.html:5
 msgid "next"
 msgstr ""
 
+#: templates/partials/reservation_error.html:9
+#: templates/partials/reservation_info.html:13
+msgid "back to form"
+msgstr ""
+
+#: templates/partials/reservation_form.html:23
+msgid "continue"
+msgstr ""
+
 #: templates/partials/search_form.html:4
 msgid "search"
 msgstr ""

File locale/ru/LC_MESSAGES/django.mo

Binary file modified.

File locale/ru/LC_MESSAGES/django.po

 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-01-17 17:05+0200\n"
-"PO-Revision-Date: 2010-01-17 17:07+0200\n"
-"Last-Translator: Александр Ульяницкий <asux@i.ua>\n"
+"POT-Creation-Date: 2010-02-13 21:13+0200\n"
+"PO-Revision-Date: 2010-02-12 15:52+0200\n"
+"Last-Translator: \n"
 "Language-Team: Russian <kde-russian@lists.kde.ru>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Generator: Lokalize 1.0\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
-"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%"
+"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
 
 #: banners/admin.py:19
 msgid "Internal"
 msgid "file"
 msgstr "файл"
 
-#: banners/forms.py:40 catalogue/models.py:194
+#: banners/forms.py:40 catalogue/models.py:205
 msgid "start"
 msgstr "начало"
 
-#: banners/forms.py:41 catalogue/models.py:195
+#: banners/forms.py:41 catalogue/models.py:206
 msgid "end"
 msgstr "конец"
 
 msgid "link"
 msgstr "ссылка"
 
-#: banners/models.py:38
+#: banners/models.py:38 catalogue/models.py:246 catalogue/models.py:260
 msgid "code"
 msgstr "код"
 
 msgid "banner log entries"
 msgstr "записи журнала баннера"
 
-#: catalogue/forms.py:40
+#: catalogue/forms.py:41 catalogue/models.py:250
 msgid "currency"
 msgstr "валюта"
 
-#: catalogue/forms.py:41
+#: catalogue/forms.py:42
 msgid "price"
 msgstr "цена"
 
-#: catalogue/forms.py:42 catalogue/models.py:115
+#: catalogue/forms.py:43 catalogue/models.py:115
 msgid "rooms"
 msgstr "комнат"
 
-#: catalogue/forms.py:43 catalogue/models.py:125
+#: catalogue/forms.py:44 catalogue/models.py:125
 msgid "layout"
 msgstr "планировка"
 
-#: catalogue/forms.py:44 catalogue/models.py:20 catalogue/models.py:27
+#: catalogue/forms.py:45 catalogue/models.py:20 catalogue/models.py:27
 #: templates/wizard/building_index.html:13
 #: templates/wizard/building_select.html:8 templates/wizard/street_edit.html:9
 msgid "street"
 msgstr "улица"
 
-#: catalogue/forms.py:45 catalogue/models.py:123
+#: catalogue/forms.py:46 catalogue/models.py:123
 msgid "level"
 msgstr "уровень"
 
-#: catalogue/forms.py:63
+#: catalogue/forms.py:64
 msgid "ID"
 msgstr "ID"
 
-#: catalogue/forms.py:90
+#: catalogue/forms.py:91
 msgid "Number of choices must be greater then one"
 msgstr "Количество вариантов должно быть более одного"
 
 msgid "Start later then end"
 msgstr "Начало позже чем конец"
 
-#: catalogue/forms.py:113 catalogue/forms.py:122
+#: catalogue/forms.py:113 catalogue/forms.py:123
 msgid ""
 "The apartment occupied during this period, please select a different dates."
 msgstr "Квартира занята за этот период, пожалуйста выберите другие даты."
 
-#: catalogue/forms.py:121
+#: catalogue/forms.py:122
 msgid "Check-in date later then departure date"
 msgstr "Дата заселения позже чем выселения"
 
-#: catalogue/forms.py:124 catalogue/models.py:131 catalogue/models.py:184
-#: catalogue/models.py:193 catalogue/models.py:225
+#: catalogue/forms.py:124
+msgid "You must agree with public agreement"
+msgstr "Вы должны согласится с публичным договором"
+
+#: catalogue/forms.py:126 catalogue/models.py:131 catalogue/models.py:195
+#: catalogue/models.py:204 catalogue/models.py:236
 #: templates/wizard/apartment_edit.html:9
 msgid "apartment"
 msgstr "квартира"
 
-#: catalogue/forms.py:125
+#: catalogue/forms.py:127
 msgid "check-in date"
 msgstr "дата заселения"
 
-#: catalogue/forms.py:126
+#: catalogue/forms.py:128
 msgid "departure date"
 msgstr "дата выселения"
 
-#: catalogue/forms.py:127
+#: catalogue/forms.py:129
 msgid "first name"
 msgstr "имя"
 
-#: catalogue/forms.py:128
+#: catalogue/forms.py:130
 msgid "last name"
 msgstr "фамилия"
 
-#: catalogue/forms.py:129
+#: catalogue/forms.py:131 catalogue/models.py:265 catalogue/models.py:275
+msgid "country"
+msgstr "страна"
+
+#: catalogue/forms.py:132 catalogue/models.py:279
+msgid "city"
+msgstr "город"
+
+#: catalogue/forms.py:133
 msgid "phone number"
 msgstr "телефонный номер"
 
-#: catalogue/forms.py:130
+#: catalogue/forms.py:134
 msgid "email"
 msgstr "емейл"
 
-#: catalogue/forms.py:131
+#: catalogue/forms.py:135
 msgid "I am agree"
 msgstr "Я согласен"
 
-#: catalogue/models.py:18 catalogue/models.py:213 posts/models.py:6
+#: catalogue/models.py:18 catalogue/models.py:224 catalogue/models.py:262
+#: catalogue/models.py:277 posts/models.py:6
 #: templates/wizard/street_select.html:9
 msgid "name"
 msgstr "имя"
 msgid "map link"
 msgstr "ссылка на карту"
 
-#: catalogue/models.py:105 catalogue/models.py:183
+#: catalogue/models.py:105 catalogue/models.py:194
 msgid "image"
 msgstr "изображение"
 
 msgid "description"
 msgstr "описание"
 
-#: catalogue/models.py:140
+#: catalogue/models.py:151
 msgid "num."
 msgstr "кв."
 
-#: catalogue/models.py:187 templates/catalogue/apartment.html:53
+#: catalogue/models.py:198 templates/catalogue/apartment.html:68
 msgid "apartment image"
 msgstr "изображение квартиры"
 
-#: catalogue/models.py:188
+#: catalogue/models.py:199
 msgid "apartment images"
 msgstr "изображения квартир"
 
-#: catalogue/models.py:199
+#: catalogue/models.py:210
 msgid "reservation period"
 msgstr "период брони"
 
-#: catalogue/models.py:200
+#: catalogue/models.py:211
 msgid "reservation periods"
 msgstr "периоды брони"
 
-#: catalogue/models.py:205
+#: catalogue/models.py:216
 msgid "boolean"
 msgstr "да/нет"
 
-#: catalogue/models.py:206
+#: catalogue/models.py:217
 msgid "integer"
 msgstr "целое число"
 
-#: catalogue/models.py:207
+#: catalogue/models.py:218
 msgid "float"
 msgstr "число с плавающей точкой"
 
-#: catalogue/models.py:208
+#: catalogue/models.py:219
 msgid "choices"
 msgstr "варианты"
 
-#: catalogue/models.py:214
+#: catalogue/models.py:225
 msgid "field"
 msgstr "поле"
 
-#: catalogue/models.py:215
+#: catalogue/models.py:226
 msgid "options"
 msgstr "опции"
 
-#: catalogue/models.py:216
+#: catalogue/models.py:227
 msgid "icon"
 msgstr "иконка"
 
-#: catalogue/models.py:219 catalogue/models.py:226
+#: catalogue/models.py:230 catalogue/models.py:237
 msgid "bonus type"
 msgstr "тип бонуса"
 
-#: catalogue/models.py:220
+#: catalogue/models.py:231
 msgid "bonus types"
 msgstr "типы бонусов"
 
-#: catalogue/models.py:227
+#: catalogue/models.py:238
 msgid "value"
 msgstr "значение"
 
-#: catalogue/models.py:231
+#: catalogue/models.py:242
 msgid "bonus"
 msgstr "бонус"
 
-#: catalogue/models.py:232
+#: catalogue/models.py:243
 msgid "bonuses"
 msgstr "бонусы"
 
+#: catalogue/models.py:247
+msgid "exchange rate"
+msgstr "курс валюты"
+
+#: catalogue/models.py:248
+msgid "symbol"
+msgstr "символ"
+
+#: catalogue/models.py:251
+msgid "currencies"
+msgstr "валюты"
+
+#: catalogue/models.py:266
+msgid "countries"
+msgstr "страны"
+
+#: catalogue/models.py:280
+msgid "cities"
+msgstr "города"
+
+#: catalogue/views.py:197
+#, python-format
+msgid "%(prefix)sApartment reservation - %(apartment)s"
+msgstr ""
+
 #: posts/models.py:9 posts/models.py:39
 msgid "post type"
 msgstr "тип поста"
 msgid "Language"
 msgstr "Язык"
 
-#: templates/catalogue/apartment.html:31
+#: templates/catalogue/apartment.html:46
 msgid "permalink"
 msgstr "постоянная ссылка"
 
-#: templates/catalogue/apartment.html:45
+#: templates/catalogue/apartment.html:60
 msgid "view on map"
 msgstr "посмотреть на карте"
 
-#: templates/catalogue/apartment.html:58
+#: templates/catalogue/apartment.html:73
 msgid "apartment image thumbnail"
 msgstr "эскиз изображения квартиры"
 
-#: templates/catalogue/apartment.html:63
-#: templates/partials/reservation_form.html:3
+#: templates/catalogue/apartment.html:78
+#: templates/partials/reservation_info.html:14
 msgid "reserve"
 msgstr "зарезервировать"
 
 msgid "Query is not valid"
 msgstr "Не верный запрос"
 
+#: templates/catalogue/mail/reservation_for_client.html:2
+#: templates/partials/reservation_complete.html:2
+#, python-format
+msgid ""
+"\n"
+"You reserved the apartment <strong>%(apartment)s</strong>. The instructions "
+"sent to email <strong>%(email)s</strong>.<br/>\n"
+"Details:\n"
+msgstr ""
+
 #: templates/partials/apartment.html:6 templates/partials/building.html:6
 #: templates/partials/street.html:6 templates/wizard/apartment_select.html:5
 #: templates/wizard/building_select.html:5
 msgid "next"
 msgstr "следующий"
 
+#: templates/partials/reservation_error.html:9
+#: templates/partials/reservation_info.html:13
+msgid "back to form"
+msgstr ""
+
+#: templates/partials/reservation_form.html:23
+#, fuzzy
+msgid "continue"
+msgstr "страны"
+
 #: templates/partials/search_form.html:4
 msgid "search"
 msgstr "поиск"
 #~ msgid "Select/edit/create"
 #~ msgstr "Выбрать/редактировать/создать"
 
-#~ msgid "city"
-#~ msgstr "город"
-
 #~ msgid "rents"
 #~ msgstr "аренды"

File media/css/common.css

 #image-orig img {
     border: solid 4px blue !important;
 }
-.errorlist, .warninglist {
+/*.errorlist, .warninglist {
     margin: 2px;
     padding: 2px;
-}
-.errorlist {
+}*/
+/*.errorlist {
     background-color: #ffcc99;
     color: red;
     border-right-style: solid;
     border-left-color: red;
     border-bottom-color: red;
     border-top-color: red;
-}
+}*/
 .warninglist {
 
     background-color: #ffff99;
 }
 #reserve {
     display:  none;
+}
+.reservation-form {
+    text-align: left;
+    vertical-align: middle;
+    font-size: 11pt;
+    font-family: sans-serif;
+}
+.reservation-form-item td {
+    /*height: 3ex;*/
+    padding: 4px;
+    border: none;
+}
+.errors {
+    padding: 4px;
 }
 DATABASE_USER = ''             # Not used with sqlite3.
 DATABASE_PASSWORD = ''         # Not used with sqlite3.
 DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
-DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
+DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
+
+#EMAIL_HOST = 'localhost'
+#EMAIL_PORT = 1025
+#EMAIL_HOST_USER = None
+#EMAIL_HOST_PASSWORD = None

File settings_base.py

 DEBUG_TOOLBAR_CONFIG = {
     'INTERCEPT_REDIRECTS': False
 }
-RECAPTCHA_PUBLIC_KEY = '6LfJkAoAAAAAAOVYmUxy5NF39RFx9YvpiIumVIFP'
+RECAPTCHA_PUBLIC_KEY = '6LfJkAoAAAAAAOVYmUxy5NF39RFx9YvpiIumVIFP'
+DEFAULT_FROM_EMAIL = 'Botanic Apartments site <noreplay@botanicapartments.1gb.ua>'
+EMAIL_HOST = 'smtp-2.1gb.ua'
+EMAIL_HOST_USER = 'u26848'
+EMAIL_HOST_PASSWORD = '0851b349'
+#EMAIL_PORT = 25
+EMAIL_USE_TLS = False
+EMAIL_SUBJECT_PREFIX = '[Botanic Apartments] '

File templates/catalogue/apartment.html

 <script src="{{ MEDIA_URL }}js/jquery.form.js" type="text/javascript"></script>
 <script type="text/javascript">
     jQuery(function(){
+        AJAX_FORM = true;
         jQuery("#apartment_images").tabs();
         jQuery('#do_reserve').click(function(){
             jQuery('#reserve').slideToggle();
         });
-        reservationForm = jQuery('#reservation_form');
+        var reservationForm = jQuery('#reservation_form');
         reservationForm.find('input').live('change', function(event){
             reservationForm.submit();
         });
-        var ajaxOptions = {
+        reservationForm.find('select').live('change', function(event){
+            reservationForm.submit();
+        });
+        var reservationFormAjaxOptions = {
             target: '#reservation_form_body',
-            url: '{% url catalogue:validate_reservation_form %}',
             success: function(response) {
                 initDatepickers();
             }
         }
-        reservationForm.ajaxForm(ajaxOptions);
+        //reservationForm.submit(function(){
+        //    if(AJAX_FORM){
+        //       reservationForm.ajaxSubmit(reservationFormAjaxOptions);
+        //        return false;
+        //    }
+        //    else {
+        //       return true;
+        //    }
+        //});
+        if(AJAX_FORM){
+            reservationForm.ajaxForm(reservationFormAjaxOptions);
+        }
     });
 </script>
 {% endblock %}

File templates/catalogue/mail/reservation_for_client.html

+{% load i18n %}
+<p>{% blocktrans %}
+You reserved the apartment <strong>{{ apartment }}</strong>. The instructions sent to email <strong>{{ email }}</strong>.<br/>
+Details:
+{% endblocktrans %}
+</p>
+<div id="reservation_info">
+    <table>
+        {% for item in reservation_info %}
+        <tr><td>{{ item.key }}:</td><td>{{ item.value }}</td></tr>
+        {% endfor %}
+    </table>
+ </div>

File templates/catalogue/mail/reservation_for_client.txt

+{% load i18n %}
+{% blocktrans %}
+You reserved the apartment {{ apartment }}. The instructions sent to email {{ email }}.
+Details:
+{% endblocktrans %}
+{% for item in reservation_info %}
+{{ item.key }}: {{ item.value }}
+{% endfor %}

File templates/partials/reservation_complete.html

+{% load i18n %}
+<p>{% blocktrans %}
+You reserved the apartment <strong>{{ apartment }}</strong>. The instructions sent to email <strong>{{ email }}</strong>.<br/>
+Details:
+{% endblocktrans %}
+</p>
+<div id="reservation_info">
+    <table>
+        {% for item in reservation_info %}
+        <tr><td>{{ item.key }}:</td><td>{{ item.value }}</td></tr>
+        {% endfor %}
+    </table>
+ </div>
+<div>
+    {% for field in reservation_form.hidden_fields %}
+    {{ field }}
+    {% endfor %}
+</div>
+<p><input type="submit" name="reset" value="to start" /></p>

File templates/partials/reservation_error.html

+{% load i18n %}<p class="ui-state-error ui-corner-all errors">
+    {{ exception }}
+</p>
+<div>
+    {% for field in reservation_form.hidden_fields %}
+    {{ field }}
+    {% endfor %}
+</div>
+<input type="submit" name="back" value="{% trans 'back to form'%}"/>

File templates/partials/reservation_form.html

-{% load i18n %}{{ reservation_form.as_p }}
+{% load i18n %}
+<table class="reservation-form">
+    {% for field in reservation_form.visible_fields %}
+    <tr class="reservation-form-item">
+        <td>
+            {{ field.label_tag }}:
+        </td>
+        <td>{{ field }}</td>
+        <td>
+            {% if field.errors %}
+            <div class="ui-state-error ui-corner-all errors">{{ field.errors }}</div>
+            {% endif %}
+        </td>
+    </tr>
+    {% endfor %}
+</table>
+<div>
+    {% for field in reservation_form.hidden_fields %}
+    {{ field }}
+    {% endfor %}
+</div>
 {% if reservation_form_valid %}
-<p><input type="submit" value="{% trans 'reserve' %}" /></p>
+<p><input type="submit" name="continue" value="{% trans 'continue' %}"/></p>
 {% endif %}

File templates/partials/reservation_info.html

+{% load i18n %}<div id="reservation_info">
+    <table>
+        {% for item in reservation_info %}
+        <tr><td>{{ item.key }}:</td><td>{{ item.value }}</td></tr>
+        {% endfor %}
+    </table>
+ </div>
+<div>
+    {% for field in reservation_form.hidden_fields %}
+    {{ field }}
+    {% endfor %}
+</div>
+<input type="submit" name="back" value="{% trans 'back to form'%}"/>
+<input type="submit" name="reserve" value="{% trans 'reserve'%}"/>