Update all
@ -10,23 +10,17 @@ class OfferCreateForm(forms.ModelForm):
|
||||
price = forms.DecimalField(min_value=0.01,max_digits=10, decimal_places=2)
|
||||
class Meta:
|
||||
model = Offer
|
||||
fields = ('title','category','price','DMC','Places_to_sleep','cooker','fridge', 'microwave', 'Webasto', 'auto_temp_control', 'water_level_indicator', 'battery_level', 'photovoltaic_panels', 'gearbox', 'tempomat')
|
||||
fields = ('title','category','price','description','DMC','Places_to_sleep','cooker','fridge', 'microwave', 'Webasto', 'auto_temp_control', 'water_level_indicator', 'battery_level', 'photovoltaic_panels', 'gearbox', 'tempomat')
|
||||
|
||||
class OfferImageCreateForm(forms.ModelForm):
|
||||
image = forms.ImageField()
|
||||
image = forms.ImageField(required = True)
|
||||
main_image = forms.BooleanField(initial=False, required=False)
|
||||
class Meta:
|
||||
model = Image_offer
|
||||
fields = ('image','main_image')
|
||||
|
||||
class BaseOfferImageFormSet(BaseFormSet):
|
||||
def clean(self):
|
||||
if any(self.errors):
|
||||
return
|
||||
count = 0
|
||||
for form in self.forms:
|
||||
if form.cleaned_data['main_image']:
|
||||
count += 1
|
||||
if count > 1:
|
||||
raise forms.ValidationError(_("Mozna ustawić tylko jedno zdjecie jako miniaturka."))
|
||||
pass
|
||||
|
||||
|
||||
OfferImageFormSet = formset_factory(OfferImageCreateForm,formset = BaseOfferImageFormSet)
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 3.1.3 on 2021-01-12 17:44
|
||||
# Generated by Django 3.1.3 on 2021-02-06 00:07
|
||||
|
||||
import camper.models
|
||||
from django.conf import settings
|
||||
@ -15,12 +15,6 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AlbumImage',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Offer_Category',
|
||||
fields=[
|
||||
@ -40,6 +34,7 @@ class Migration(migrations.Migration):
|
||||
('status', models.CharField(choices=[('Active', 'Active offer'), ('Inactive', 'Inactive offer'), ('Verification', 'Waiting for verication'), ('Blocked', 'Blocked Offer')], default='Inactive', max_length=32)),
|
||||
('title', models.CharField(default='Tytuł oferty', max_length=50, verbose_name='Title')),
|
||||
('slug', models.SlugField(default='', editable=False, max_length=70, unique=True)),
|
||||
('description', models.TextField(default='', max_length=1000)),
|
||||
('price', models.DecimalField(decimal_places=2, max_digits=10)),
|
||||
('DMC', models.CharField(choices=[('B', 'Do 3.5 tony.'), ('C', 'Między 3.5 a 7.5 tony'), ('C1', 'Powyzej 7.5 tony')], default='B', max_length=32)),
|
||||
('Places_to_sleep', models.IntegerField(default=2)),
|
||||
@ -53,7 +48,6 @@ class Migration(migrations.Migration):
|
||||
('photovoltaic_panels', models.BooleanField(default=False)),
|
||||
('gearbox', models.CharField(choices=[('Manual', 'Skrzynia manualna'), ('Automat', 'Skrzynia automatyczna')], default='Manual', max_length=32)),
|
||||
('tempomat', models.BooleanField(default=False)),
|
||||
('album', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='album', to='camper.albumimage', verbose_name='')),
|
||||
('category', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='camper.offer_category')),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
@ -66,7 +60,7 @@ class Migration(migrations.Migration):
|
||||
name='Image_offer',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('image', models.ImageField(blank=True, null=True, upload_to=camper.models._upload_path)),
|
||||
('image', models.ImageField(upload_to=camper.models._upload_path)),
|
||||
('main_image', models.BooleanField(default=False)),
|
||||
('offer', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='camper.offer', verbose_name='')),
|
||||
],
|
||||
|
@ -1,20 +0,0 @@
|
||||
# Generated by Django 3.1.3 on 2021-01-12 17:48
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camper', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='offer',
|
||||
name='album',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='AlbumImage',
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.1.3 on 2021-01-24 16:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camper', '0002_auto_20210112_1748'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='offer',
|
||||
name='description',
|
||||
field=models.TextField(default='', max_length=1000),
|
||||
),
|
||||
]
|
BIN
vagus/camper/migrations/__pycache__/0004_auto_20210202_1834.cpython-37.pyc
Executable file
BIN
vagus/camper/migrations/__pycache__/0005_auto_20210202_1834.cpython-37.pyc
Executable file
BIN
vagus/camper/migrations/__pycache__/0006_auto_20210204_1352.cpython-37.pyc
Executable file
@ -85,7 +85,7 @@ class Offer(models.Model):
|
||||
return super(Offer, self).save(*args, **kwargs)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('detail_offer_page', kwargs = {'slug': self.slug })
|
||||
return reverse('offer_detail', kwargs = {'slug': self.slug })
|
||||
|
||||
|
||||
def _upload_path(instance,filename):
|
||||
|
@ -7,8 +7,10 @@ from django.conf.urls.static import static
|
||||
|
||||
urlpatterns = [
|
||||
re_path('add-offer/', views.CreateOffer.as_view(), name='create_offer'),
|
||||
#path('offer/<slug:slug>', views.OfferDetailView.as_view(), name = 'offer_detail_view'),
|
||||
path('offer/<slug:slug>', views.OfferDetailView.as_view(), name = 'offer_detail'),
|
||||
path('update-offer/<slug:slug>',views.EditOffer.as_view(), name = 'update-offer'),
|
||||
path('offers/', views.OfferListView.as_view(), name = 'list_offers'),
|
||||
#path('offer/<slug:slug>', views.OfferDetailView.as_view(), name = 'detail_offer_page'),
|
||||
path('', views.Home.as_view(), name='homepage')
|
||||
|
||||
|
||||
|
@ -28,21 +28,12 @@ def check_user_is_owner(request):
|
||||
else:
|
||||
render (request,'upgrade-to-onwer.html')
|
||||
|
||||
#def check_offer_belong_to_user(request):
|
||||
# if request.check_user_is_owner() == True:
|
||||
# if offer.author == request.users.id:
|
||||
# return True
|
||||
# else:
|
||||
# raise ValueError("To chyba nie twoje :)")
|
||||
|
||||
# if check_users_is_owner == True:
|
||||
|
||||
# Create your views here.
|
||||
|
||||
class CreateOffer(CreateView):
|
||||
|
||||
@method_decorator(login_required)
|
||||
def get(self, request,*args,**kwargs):
|
||||
if check_user_is_owner(request) == True or request.user.is_staff == True:
|
||||
form = OfferCreateForm()
|
||||
formset = OfferImageFormSet()
|
||||
context = {
|
||||
@ -50,8 +41,10 @@ class CreateOffer(CreateView):
|
||||
'formset': formset,
|
||||
}
|
||||
#if check_user_is_owner(request) == True or request.Base_user.is_staff == True:
|
||||
|
||||
return render(request,'offer/add-offer.html', context)
|
||||
else:
|
||||
messages.error(request, 'Nie masz uprawnień!')
|
||||
return redirect('/') #Redirect to login / register page for owner
|
||||
|
||||
@method_decorator(login_required)
|
||||
def post(self,request,*args,**kwargs):
|
||||
@ -64,7 +57,6 @@ class CreateOffer(CreateView):
|
||||
offer.created_by = request.user
|
||||
offer.status = 'Verification'
|
||||
offer.save()
|
||||
|
||||
for photo in formset:
|
||||
try:
|
||||
imageoffer = Image_offer(offer = offer,main_image = photo.cleaned_data['main_image'],image = photo.cleaned_data['image'])
|
||||
@ -74,7 +66,6 @@ class CreateOffer(CreateView):
|
||||
offer.save()
|
||||
messages.error(request,'Dodaj zdjęcia aby przesłać ofertę do weryfikacji')
|
||||
return redirect('/') #Redirect to offe-update-page or user's offers list
|
||||
|
||||
messages.success(request,'Oferta utworzona i przesłana do weryfikacji')
|
||||
return redirect('/') #Redirect to offer page or user's offers list
|
||||
else:
|
||||
@ -91,8 +82,6 @@ class CreateOffer(CreateView):
|
||||
|
||||
|
||||
class EditOffer(UpdateView):
|
||||
|
||||
def get(self,request,*args, **kwargs):
|
||||
pass
|
||||
def post(self,request,*args,**kwargs):
|
||||
pass
|
||||
model = Offer
|
||||
template_name = 'offer/update-offer.html'
|
||||
fields = ('title','category','price','description','DMC','Places_to_sleep','cooker','fridge', 'microwave', 'Webasto', 'auto_temp_control', 'water_level_indicator', 'battery_level', 'photovoltaic_panels', 'gearbox', 'tempomat')
|
||||
|
@ -2,28 +2,72 @@ from django.shortcuts import render,redirect, get_object_or_404
|
||||
from django.views.generic import (
|
||||
ListView,
|
||||
DetailView,
|
||||
View
|
||||
View,
|
||||
FormView,
|
||||
CreateView,
|
||||
)
|
||||
from camper.models import Offer, Image_offer
|
||||
from camper.models import Offer, Image_offer,Offer_Category
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.core.paginator import Paginator ,EmptyPage, PageNotAnInteger
|
||||
from reservation.forms import ReservationForm
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.views.generic.edit import FormMixin
|
||||
from reservation.models import Reservation
|
||||
|
||||
|
||||
class Home(View):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return render(request, 'index.html')
|
||||
context = {}
|
||||
offers= Offer.objects.filter(status = 'Active').order_by('-date_added')[:2]
|
||||
context['offers'] = offers
|
||||
return render(request,'home.html', context=context)
|
||||
|
||||
################################################
|
||||
class OfferDetailView(DetailView):
|
||||
template_name = 'offer/offer-detail.html'
|
||||
model = Offer
|
||||
context_object_name = 'offer'
|
||||
template_name = 'offer/offer-detail.html'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super(OfferDetailView,self).get_context_data(*args, **kwargs)
|
||||
context.update({
|
||||
'form': ReservationForm(),
|
||||
})
|
||||
return context
|
||||
|
||||
@method_decorator(login_required)
|
||||
def post(self,request,*args,**kwargs):
|
||||
self.object = self.get_object(self.get_queryset())
|
||||
form = ReservationForm(self.request.POST)
|
||||
if form.is_valid():
|
||||
reservation = form.save(commit = False)
|
||||
reservation.user = request.user
|
||||
reservation.offer = self.object
|
||||
reservation.status = 'Confirmed'
|
||||
reservation.save()
|
||||
messages.success(request,'Twoja rezerwacja została potwierdzona.')
|
||||
return redirect('/')
|
||||
else:
|
||||
context = self.get_context_data(**kwargs)
|
||||
context.update({'form': ReservationForm()})
|
||||
messages.error(request, 'Wprowadzone dane są nieprawidłowe.')
|
||||
return render(request,'offer/offer-detail.html', context)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
################################################
|
||||
|
||||
class OfferListView(ListView):
|
||||
model = Offer
|
||||
template_name = 'offer/offers.html'
|
||||
context_object_name = 'active_offers_list'
|
||||
paginate_by = 4
|
||||
|
||||
allow_empty_first_page = True
|
||||
ordering = ['-date_added']
|
||||
|
||||
def get_queryset(self):
|
||||
|
BIN
vagus/media/ian-usher-Af1OMQpuN14-unsplash.jpg
Executable file
After Width: | Height: | Size: 1.1 MiB |
BIN
vagus/media/uploads/._default.png
Executable file
BIN
vagus/media/uploads/default.png
Executable file
After Width: | Height: | Size: 713 B |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 3.1 MiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 124 KiB |
BIN
vagus/media/uploads/offer-images/id-17/DSCF5544.jpg
Executable file
After Width: | Height: | Size: 287 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 173 KiB |
BIN
vagus/media/uploads/offer-images/id-24/DSCF5544.jpg
Executable file
After Width: | Height: | Size: 287 KiB |
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 3.1 MiB |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 3.1 MiB |
After Width: | Height: | Size: 622 KiB |
After Width: | Height: | Size: 3.1 MiB |
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 251 KiB |
After Width: | Height: | Size: 378 KiB |
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 378 KiB |
After Width: | Height: | Size: 154 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 622 KiB |
After Width: | Height: | Size: 154 KiB |
After Width: | Height: | Size: 154 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 148 KiB |
444938
vagus/mysite.log
Executable file
@ -2,10 +2,23 @@ from django.contrib import admin
|
||||
from .models import Reservation
|
||||
from .forms import ReservationForm
|
||||
|
||||
class ReservationAdmin(admin.ModelAdmin):
|
||||
list_display = ('__str__','offer','status')
|
||||
ordering = ['reservation_start_date']
|
||||
group_by = ['offer','user']
|
||||
|
||||
class CustomReservation(admin.ModelAdmin):
|
||||
|
||||
admin.site.register(Reservation, ReservationAdmin)
|
||||
model = Reservation
|
||||
|
||||
list_display = ('created_date','user','offer','reservation_start_date','reservation_end_date','reservation_cost')
|
||||
fieldsets = (
|
||||
(None, {'fields': ('user','offer','reservation_start_date','reservation_end_date','status','message_to_owner',)}),
|
||||
|
||||
)
|
||||
add_fieldsets = (
|
||||
(None, {
|
||||
'classes': ('wide',),
|
||||
'fields': ('user','offer','reservation_start_date','reservation_end_date','status',)}
|
||||
),
|
||||
)
|
||||
ordering = ('offer','user')
|
||||
search_fields = ('offer','user')
|
||||
|
||||
admin.site.register(Reservation, CustomReservation)
|
@ -3,20 +3,24 @@ from .models import Reservation
|
||||
from camper.models import Offer
|
||||
from users.models import Base_User
|
||||
from django.core.exceptions import ValidationError
|
||||
from bootstrap_datepicker_plus import DatePickerInput
|
||||
|
||||
|
||||
class ReservationForm(forms.ModelForm):
|
||||
reservation_start_date = forms.DateField(required=True)
|
||||
reservation_end_date = forms.DateField(required=True,)
|
||||
reservation_start_date = forms.DateField(
|
||||
widget=DatePickerInput(format='%m/%d/%Y')
|
||||
)
|
||||
reservation_end_date = forms.DateField(
|
||||
widget=DatePickerInput(format='%m/%d/%Y')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Reservation
|
||||
exclude = ('user',)
|
||||
|
||||
|
||||
widget = {
|
||||
'reservation_start_date': forms.DateInput(format =['%d/%m/%y']),
|
||||
'reservation_end_date': forms.DateInput(format =['%d/%m/%y']),
|
||||
|
||||
}
|
||||
exclude = ('user','offer','status','reservation_cost')
|
||||
fields = ['reservation_start_date','reservation_end_date','message_to_owner']
|
||||
|
||||
class ReservationStatus(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Reservation
|
||||
fields = ['status']
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 3.1.3 on 2021-01-14 00:40
|
||||
# Generated by Django 3.1.3 on 2021-02-06 09:49
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
@ -11,8 +11,8 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('camper', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('camper', '0002_auto_20210112_1748'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
@ -20,11 +20,15 @@ class Migration(migrations.Migration):
|
||||
name='Reservation',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_date', models.DateTimeField(auto_now_add=True)),
|
||||
('reservation_start_date', models.DateField(default=django.utils.timezone.now)),
|
||||
('reservation_end_date', models.DateField()),
|
||||
('reservation_cost', models.DecimalField(decimal_places=2, default=0, max_digits=12)),
|
||||
('message_to_owner', models.TextField(blank=True, max_length=500, null=True)),
|
||||
('status', models.CharField(choices=[('Confirmed', 'Rezerwacja potwierdzona'), ('Requsted', 'Oczekuję na potwierdzenie'), ('Cancelled', 'Anulowana'), ('In_progress', 'Trwająca rezerwacja')], max_length=50)),
|
||||
('offer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camper.offer', verbose_name='')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='')),
|
||||
('slug', models.SlugField(default='', editable=False, max_length=255)),
|
||||
('offer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camper.offer', verbose_name='Offer')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
@ -1,33 +0,0 @@
|
||||
# Generated by Django 3.1.3 on 2021-01-15 00:37
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camper', '0002_auto_20210112_1748'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('reservation', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='reservation',
|
||||
name='offer',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camper.offer', verbose_name='Offer'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reservation',
|
||||
name='reservation_start_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reservation',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
),
|
||||
]
|
18
vagus/reservation/migrations/0002_auto_20210206_1048.py
Executable file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.1.3 on 2021-02-06 10:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('reservation', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='reservation',
|
||||
name='id',
|
||||
field=models.AutoField(primary_key=True, serialize=False),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# Generated by Django 3.1.3 on 2021-01-24 14:57
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('reservation', '0002_auto_20210115_0037'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='reservation',
|
||||
name='reservation_start_date',
|
||||
field=models.DateField(default=django.utils.timezone.now),
|
||||
),
|
||||
]
|
BIN
vagus/reservation/migrations/__pycache__/0002_auto_20210206_0041.cpython-37.pyc
Executable file
BIN
vagus/reservation/migrations/__pycache__/0002_auto_20210206_1048.cpython-37.pyc
Executable file
BIN
vagus/reservation/migrations/__pycache__/0003_auto_20210206_0030.cpython-37.pyc
Executable file
BIN
vagus/reservation/migrations/__pycache__/0003_reservation_slug.cpython-37.pyc
Executable file
BIN
vagus/reservation/migrations/__pycache__/0004_auto_20210206_0030.cpython-37.pyc
Executable file
BIN
vagus/reservation/migrations/__pycache__/0004_auto_20210206_0946.cpython-37.pyc
Executable file
@ -6,23 +6,31 @@ from django.core.exceptions import ValidationError
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import NON_FIELD_ERRORS
|
||||
from django.db.models import Q
|
||||
from django import forms
|
||||
from django.contrib import messages
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
STATUS = (
|
||||
('Confirmed',_('Rezerwacja potwierdzona')),
|
||||
('Requsted',_('Oczekuję na potwierdzenie')),
|
||||
('Cancelled',_('Anulowana')),
|
||||
('In_progress',_('Trwająca rezerwacja')),
|
||||
)
|
||||
|
||||
|
||||
|
||||
class Reservation(models.Model):
|
||||
id = models.AutoField(primary_key= True)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE)
|
||||
offer = models.ForeignKey("camper.Offer", verbose_name=_("Offer"), on_delete=models.CASCADE)
|
||||
|
||||
created_date = models.DateTimeField(auto_now_add=True)
|
||||
reservation_start_date = models.DateField(default = timezone.now,)
|
||||
reservation_end_date = models.DateField()
|
||||
reservation_cost = models.DecimalField(max_digits=12,default=0,decimal_places=2)
|
||||
message_to_owner = models.TextField(max_length=500, blank = True, null = True)
|
||||
status = models.CharField(choices = STATUS, max_length = 50)
|
||||
slug = models.SlugField(max_length=255,editable = False, default='', unique = False)
|
||||
|
||||
def __str__(self):
|
||||
return "%s (%s to %s)" % (self.user.email,
|
||||
@ -30,13 +38,38 @@ class Reservation(models.Model):
|
||||
"%Y/%m/%d"),
|
||||
self.reservation_end_date.strftime(
|
||||
"%Y/%m/%d"),)
|
||||
@property
|
||||
def get_total_cost(self):
|
||||
diff_days = self.reservation_end_date - self.reservation_start_date
|
||||
self.reservation_cost = diff_days.days * self.offer.price
|
||||
return self.reservation_cost
|
||||
|
||||
def clean(self):
|
||||
def clean(self,*args,**kwargs):
|
||||
errors = {}
|
||||
today_date = timezone.now().date()
|
||||
if self.created_date != None:
|
||||
if self.reservation_start_date < today_date:
|
||||
errors['reservation_start_date'] = "Rezerwacja nie moze rozpoczynać sie w przeszłości"
|
||||
if self.reservation_end_date < self.reservation_start_date:
|
||||
errors['reservation_end_date'] = "Data zakończenia wynajmu nie moze byc data wczesniejsza od daty rozpoczecia."
|
||||
errors['reservation_end_date'] = "Data zakończenia wynajmu nie moze byc data wczesniejsza / równa od daty rozpoczecia."
|
||||
if self.reservation_end_date == self.reservation_start_date:
|
||||
errors['reservation_end_date'] = "Wynajem nie moze kończyc sie w dniu rozpoczecia wynajmu."
|
||||
if errors:
|
||||
raise forms.ValidationError(errors)
|
||||
|
||||
filter_params = dict(reservation_end_date__lte=self.reservation_end_date, reservation_start_date__gte=self.reservation_start_date, offer = self.offer)
|
||||
if Reservation.objects.filter(**filter_params).exists():
|
||||
def get_absolute_url(self):
|
||||
return reverse("reservation_detail", kwargs={"slug": self.slug})
|
||||
|
||||
|
||||
def save(self,*args,**kwargs):
|
||||
errors = {}
|
||||
filter_params = dict(reservation_end_date__lte=self.reservation_end_date, reservation_start_date__gte=self.reservation_start_date,offer =self.offer)
|
||||
if Reservation.objects.filter(**filter_params):
|
||||
errors[NON_FIELD_ERRORS] = "Kamper w tym okresie jest juz zarezerwowany."
|
||||
raise ValidationError(errors)
|
||||
if errors:
|
||||
raise forms.ValidationError(errors)
|
||||
else:
|
||||
self.slug = slugify(f'{self.offer.title}-{self.id}-/{self.reservation_start_date}-{self.reservation_end_date}')
|
||||
return super(Reservation,self).save(*args,**kwargs)
|
||||
|
||||
|
||||
|
@ -7,28 +7,3 @@ from users.models import Base_User
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
class ReservationCreate(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
testauthor = Base_User.objects.create(email ='test2@vagus.pl', password = '13test45')
|
||||
testoffer = Offer.objects.create(title = 'tytul',created_by = testauthor,price = 23, category = Offer_Category.objects.get(name = 'CamperVan'))
|
||||
|
||||
def test_correct_date(self):
|
||||
today= datetime.date.today()
|
||||
ed = datetime.date(year=2021, month=1, day=20)
|
||||
Reservation.objects.create(
|
||||
user = Base_User.objects.get(email = 'test2@vagus.pl'),
|
||||
offer = Offer.objects.get(id = 16),
|
||||
reservation_start_date = today,
|
||||
reservation_end_date = ed,
|
||||
)
|
||||
|
||||
def test_incorret_date(self):
|
||||
today = datetime.date.today()
|
||||
ed = datetime.date(year = 2020, month=5, day = 15)
|
||||
Reservation.objects.create(
|
||||
user = Base_User.objects.get(email = 'test2@vagus.pl'),
|
||||
offer = Offer.objects.get(id = 16),
|
||||
reservation_start_date = today,
|
||||
reservation_end_date = ed,
|
||||
)
|
@ -3,9 +3,10 @@ from django.urls import path
|
||||
from django.conf.urls.static import static
|
||||
from camper.models import Offer
|
||||
from . import views
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('offer/<slug:slug>', views.DetailOfferPage.as_view(), name = 'detail_offer_page'),
|
||||
path('reservation-detail/<slug:slug>', login_required(views.Reservation_Detail.as_view()), name = 'reservation_detail'),
|
||||
|
||||
]
|
@ -1,22 +1,12 @@
|
||||
from django.shortcuts import render,get_object_or_404
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from .forms import ReservationForm
|
||||
from camper.models import Offer, Image_offer
|
||||
from django.views.generic import (
|
||||
DetailView
|
||||
DetailView,
|
||||
UpdateView
|
||||
)
|
||||
from .models import Reservation
|
||||
from .forms import ReservationStatus
|
||||
|
||||
class DetailOfferPage(DetailView):
|
||||
model = Offer
|
||||
template_name = 'offer/offer-detail.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailOfferPage, self).get_context_data(**kwargs)
|
||||
context['form'] = ReservationForm
|
||||
context['Image_offer'] = Image_offer.objects.filter(offer = self.object)
|
||||
return context
|
||||
|
||||
@method_decorator(login_required)
|
||||
def post(self,request,*args,**kwargs):
|
||||
pass
|
||||
class Reservation_Detail(UpdateView):
|
||||
model = Reservation
|
||||
template_name = 'reservation/reservation-detail.html'
|
||||
form_class = ReservationStatus
|
||||
|
@ -20,8 +20,7 @@ body a:hover {
|
||||
text-decoration: none;}
|
||||
|
||||
.container {
|
||||
width: 1380px;
|
||||
max-width: 100%;
|
||||
max-width: 1380px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@ -118,13 +117,6 @@ body a:hover {
|
||||
font-weight: 600!important;
|
||||
}
|
||||
|
||||
|
||||
.header-nav-bar-avatar {
|
||||
min-width: 36px;
|
||||
max-height: 36px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.header-nav-bar img {
|
||||
width: 36px;
|
||||
border-radius: 50%;
|
||||
@ -157,8 +149,8 @@ body a:hover {
|
||||
|
||||
|
||||
.main-content {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
/* OFFERS BOX */
|
||||
@ -235,7 +227,44 @@ body a:hover {
|
||||
|
||||
.offer-detail-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.offer-detail-content {
|
||||
min-width: 600px;
|
||||
}
|
||||
.offer-detail-status-info {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.offer-detail-title-text {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
.offer-detail-description {
|
||||
margin-top: 25px;
|
||||
}
|
||||
.offer-detail-parameters {
|
||||
margin-top: 25px;
|
||||
}
|
||||
.offer-detail-parameters li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.offer-detail-price {
|
||||
margin:15px;
|
||||
font-size: 1.5em;
|
||||
text-align: right;
|
||||
}
|
||||
.offer-reservation {
|
||||
flex:40%;
|
||||
min-width: 300px;
|
||||
margin-top: 50px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* PROFILE */
|
||||
|
||||
.profile-wrapper {
|
||||
@ -250,7 +279,6 @@ body a:hover {
|
||||
}
|
||||
.profile-wrapper-row-information {
|
||||
flex:20%;
|
||||
background-color: crimson;
|
||||
}
|
||||
.profile-wrapper-row-content {
|
||||
flex:70%;
|
||||
@ -295,3 +323,60 @@ body a:hover {
|
||||
}
|
||||
|
||||
|
||||
/* HOME PAGE */
|
||||
.home-header{
|
||||
background:
|
||||
linear-gradient(
|
||||
rgba(0, 0, 0, 0.5),
|
||||
rgba(0, 0, 0, 0.5)
|
||||
),
|
||||
url(/media/ian-usher-Af1OMQpuN14-unsplash.jpg);
|
||||
background-size: cover;
|
||||
height: 70vh;
|
||||
}
|
||||
|
||||
.home-header-title {
|
||||
position: absolute;
|
||||
font-size: 3em;
|
||||
top: 45%;
|
||||
left: 35%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
.home-header-button {
|
||||
border: 1px solid black;
|
||||
border-radius: 25px;
|
||||
height: 40px;
|
||||
width: 300px
|
||||
}
|
||||
.home-header-button p {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
.home-row{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
.home-grid-offer {
|
||||
flex:2;
|
||||
text-align: center;
|
||||
padding: 50px 25px 50px 25px;
|
||||
}
|
||||
.home-grid-offer a, a:hover {
|
||||
color:white;
|
||||
}
|
||||
.home-grid-offer img {
|
||||
width: 450px;
|
||||
}
|
||||
.home-footer-copyright {
|
||||
width: 100%;
|
||||
background-color: black;
|
||||
color: white;
|
||||
text-align: right;
|
||||
padding: 50px 10px 1px 10px;
|
||||
}
|
||||
.home-footer-copyright p {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="{% static 'css/style.css' %}">
|
||||
<link rel="stylesheet" href = 'style.css'>
|
||||
<link href = "https://fonts.googleapis.com/css?family=Dosis" rel='stylesheet'>
|
||||
<link href = "https://fonts.googleapis.com/css?family=Poppins" rel='stylesheet'>
|
||||
</head>
|
||||
<body>
|
||||
<div class='header-smart-baner'> <!-- TOP HEADER SMART BANER-->
|
||||
<div class='header-smart-baner-text'> Lubisz ciasteczka? Mamy ich naprawdę duzo.</div>
|
||||
</div> <!-- TOP HEADER SMART BANER END-->
|
||||
<div class='header'>
|
||||
<div class='container'>
|
||||
<div class='header-inner'>
|
||||
<div class='header-logo'> <a href='#'> vagus </a> </div>
|
||||
<div class='header-nav'>
|
||||
<a href='#'>KAMPERY </a>
|
||||
<a href='#'>O NAS </a>
|
||||
<a href='#'>FAQ</a>
|
||||
<a href='#'>KONTAKT </a>
|
||||
<div class='header-nav-bar'>
|
||||
<div class='header-nav-bar-container'>
|
||||
<div class='header-nav-bar-menu-mobile'>
|
||||
<div class='header-nav-bar-menu-mobile-icon'></div>
|
||||
<div class='header-nav-bar-menu-mobile-icon'></div>
|
||||
<div class='header-nav-bar-menu-mobile-icon'></div>
|
||||
</div>
|
||||
<div class='header-nav-bar-text'> Witaj, Krzysztof</div>
|
||||
<div class='header-nav-bar-avatar'> <img src='photo.jpg'> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
33
vagus/template/home.html
Executable file
@ -0,0 +1,33 @@
|
||||
{% extends 'index.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class='container'>
|
||||
<div class='home-wrapper'>
|
||||
<section class='home-header'>
|
||||
<div class="home-header-title">
|
||||
<h1>Stress is caused by</h1>
|
||||
<p>not camping enough!</p>
|
||||
<a href = "{{ request.scheme }}://{{ request.META.HTTP_HOST }}/offers"><button class='home-header-button'> <p>Zarezerwuj swój kamper</p> </button> </a>
|
||||
</div>
|
||||
</section>
|
||||
<div class='home-row'>
|
||||
{% for offer in offers%}
|
||||
<div class='home-grid-offer'>
|
||||
<a href ="{{ offer.get_absolute_url}}"> <h4> {{ offer.title}} </h4></a>
|
||||
{% for photo in offer.images.all %}
|
||||
{% if photo.main_image == True%}
|
||||
<img src="{{ photo.image.url }}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class='home-footer-copyright'>
|
||||
<p> Copyright by Krzysztof Bonecki 2021 </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -14,7 +14,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class='header-smart-baner'> <!-- TOP HEADER SMART BANER-->
|
||||
<div class='header-smart-baner-text'> Lubisz ciasteczka? Mamy ich naprawdę duzo.</div>
|
||||
<div class='header-smart-baner-text'> Lorem Ipsum is simply dummy text of the print...</div>
|
||||
</div> <!-- TOP HEADER SMART BANER END-->
|
||||
<div class='header'>
|
||||
<div class='container'>
|
||||
@ -22,12 +22,10 @@
|
||||
<div class='header-logo'> <a href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/'> vagus </a> </div>
|
||||
<div class='header-nav'>
|
||||
<a href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/offers'>KAMPERY </a>
|
||||
|
||||
<a href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/add-offer'>DODAJ OFERTĘ </a>
|
||||
<div class='header-nav-bar'>
|
||||
<div class='header-nav-bar-container'>
|
||||
<div class='header-nav-bar-text'>
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
{% if user.status != 'Moderator' %}
|
||||
<a class='link' href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/profile'> Witaj, {{ user.first_name }} </a>
|
||||
|
@ -38,31 +38,30 @@
|
||||
</div>
|
||||
|
||||
<div class='offer-detail-parameters'>
|
||||
<ul>
|
||||
<li> 'DMC' <b> {{offer.DMC}} </b> </li>
|
||||
<li> 'Places_to_sleep' <b> {{offer.Places_to_sleep}} </b></li>
|
||||
<li> 'cooker' <b> {{offer.cooker}} </b></li>
|
||||
<li> 'fridge' <b> {{offer.fridge}} </b></li>
|
||||
<li> 'microwave' <b> {{offer.microwave}} </b></li>
|
||||
<li> 'Webasto' <b> {{offer.Webasto}} </b></li>
|
||||
<li> 'auto_temp_control' <b> {{offer.auto_temp_control}} </b></li>
|
||||
<li> 'water_level_indicator' <b> {{offer.water_level_indicator}} </b> </li>
|
||||
<li> 'battery_level'<b> {{offer.battery_level}} </b> </li>
|
||||
<li> 'photovoltaic_panels' <b> {{offer.photovoltaic_panels}} </b> </li>
|
||||
<li> 'gearbox <b> {{offer.gearbox}} </b> </li>
|
||||
<li> 'tempomat' <b> {{offer.tempomat}} </b> </li>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item"> 'DMC' <b> {{offer.DMC}} </b> </li>
|
||||
<li class="list-group-item"> 'Places_to_sleep' <b> {{offer.Places_to_sleep}} </b></li>
|
||||
<li class="list-group-item"> 'cooker' <b> {{offer.cooker}} </b></li>
|
||||
<li class="list-group-item"> 'fridge' <b> {{offer.fridge}} </b></li>
|
||||
<li class="list-group-item"> 'microwave' <b> {{offer.microwave}} </b></li>
|
||||
<li class="list-group-item"> 'Webasto' <b> {{offer.Webasto}} </b></li>
|
||||
<li class="list-group-item"> 'auto_temp_control' <b> {{offer.auto_temp_control}} </b></li>
|
||||
<li class="list-group-item"> 'water_level_indicator' <b> {{offer.water_level_indicator}} </b> </li>
|
||||
<li class="list-group-item"> 'battery_level'<b> {{offer.battery_level}} </b> </li>
|
||||
<li class="list-group-item"> 'photovoltaic_panels' <b> {{offer.photovoltaic_panels}} </b> </li>
|
||||
<li class="list-group-item"> 'gearbox <b> {{offer.gearbox}} </b> </li>
|
||||
<li class="list-group-item"> 'tempomat' <b> {{offer.tempomat}} </b> </li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class ='offer-detail-price'> KOSZT: {{offer.price}} PLN / dzień</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='offer-reservation'>
|
||||
<form id="form-container" method="POST" class="reservation-form">
|
||||
|
||||
<h3> Zarezerwuj kamper </h3>
|
||||
{% csrf_token %}
|
||||
{{ form.media }}
|
||||
{{form.as_p}}
|
||||
|
||||
<button style="background-color:black; color:white" class="btn btn-outline-info" type="submit">Wyślij zapytanie</button>
|
||||
</form>
|
||||
<div>
|
||||
|
11
vagus/template/offer/update-offer.html
Executable file
@ -0,0 +1,11 @@
|
||||
{% extends 'index.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
{{formset.as_p}}
|
||||
<input type="submit" value="Update">
|
||||
</form>
|
||||
{% endblock %}
|
30
vagus/template/reservation/reservation-detail.html
Executable file
@ -0,0 +1,30 @@
|
||||
{% extends 'index.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Tytuł oferty</th>
|
||||
<th scope="col">Data rezerwacji</th>
|
||||
<th scope="col">Początek rezerwacji</th>
|
||||
<th scope="col">Koniec rezerwacji</th>
|
||||
<th scope ="col"> Koszt </th>
|
||||
<th scope="col">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ reservation.offer.get_absolute_url }}">{{ reservation.offer.title }}</a>
|
||||
</td>
|
||||
<td >{{reservation.user.email}}</td>
|
||||
<td>{{ reservation.reservation_start_date }}</td>
|
||||
<td>{{ reservation.reservation_end_date }}</td>
|
||||
<td> {{ reservation.get_total_cost}} </td>
|
||||
<td>{{reservation.status}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
@ -48,6 +48,37 @@
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif%}
|
||||
|
||||
{% if reservations %}
|
||||
<h4> Rezerwacje klientów </h4>
|
||||
<table class="table ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Wynajęty kamper</th>
|
||||
<th scope="col">Uzytkownik </th>
|
||||
<th scope="col">Początek rezerwacji</th>
|
||||
<th scope="col">Koniec rezerwacji</th>
|
||||
<th scope="col"> Koszt </th>
|
||||
<th scope="col">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for reservation in reservations %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ reservation.get_absolute_url }}">{{ reservation.offer.title }}</a>
|
||||
</td>
|
||||
<td >{{reservation.user.email}}</td>
|
||||
<td>{{ reservation.reservation_start_date }}</td>
|
||||
<td>{{ reservation.reservation_end_date }}</td>
|
||||
<td> {{ reservation.get_total_cost}} </td>
|
||||
<td> {{reservation.status}} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if user_reservations %}
|
||||
<h4> Moje rezerwacje </h4>
|
||||
<table class="table ">
|
||||
@ -64,7 +95,7 @@
|
||||
{% for reservation in user_reservations %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ reservation.offer.get_absolute_url }}">{{ reservation.offer.title }}</a>
|
||||
<a href="{{ reservation.get_absolute_url }}">{{ reservation.offer.title }}</a>
|
||||
</td>
|
||||
<td title="{{ reservation.created_date}}">{{ reservation.created_date }}</td>
|
||||
<td>{{ reservation.reservation_start_date }}</td>
|
||||
|