diff --git a/port/admin.py b/port/admin.py index 469cd5b..452653c 100644 --- a/port/admin.py +++ b/port/admin.py @@ -7,6 +7,8 @@ admin.site.register(Person) admin.site.register(Company) admin.site.register(Employee) admin.site.register(Boat) +admin.site.register(BoatInsurance) +admin.site.register(Insurance) admin.site.register(Port) admin.site.register(Dock) admin.site.register(Plug) @@ -15,4 +17,5 @@ admin.site.register(Payment) admin.site.register(Service) admin.site.register(Bill) admin.site.register(Stay) +admin.site.register(SailsOn) admin.site.register(Mooring) diff --git a/port/forms.py b/port/forms.py index ed0a685..b362b3d 100644 --- a/port/forms.py +++ b/port/forms.py @@ -7,6 +7,17 @@ from phonenumber_field.formfields import PhoneNumberField from .models import * +class AddressForm(ModelForm): + class Meta: + model = Address + + fields = [ + 'address', + 'zip_code', + 'city', + 'country' + ] + class PersonForm(ModelForm): class Meta: model = Person @@ -26,18 +37,56 @@ class PersonForm(ModelForm): phone = PhoneNumberField(label='Phone Number') """ -class AddressForm(ModelForm): +class CompanyForm(ModelForm): class Meta: - model = Address + model = Company + + fields = [ + 'name', + 'email', + 'phone', + 'registration_num'] + +class InsuranceForm(CompanyForm): + class Meta: + model = Insurance fields = [ + 'name'] + + +class BoatForm(ModelForm): + class Meta: + model = Boat + + fields = ['name', + 'registration_num', + 'length', + 'beam', + 'water_draught', + 'tonnage', + 'water_tank', + 'model', + 'heating', + 'passenger_capacity', + 'company', + 'picture'] + +class PortForm(ModelForm): + class Meta: + model = Port + + fields = [ + 'name', 'address', - 'zip_code', - 'city', - 'country' - ] - + 'company'] + """ + name = CharField(label='Name', max_length=50) + address = CharField(label='Address', max_length=50) + company = CharField(label='Company', max_length=50) + """ + class DockForm(ModelForm): class Meta: model = Dock @@ -71,25 +120,10 @@ class DockForm(ModelForm): DockFormSet = formset_factory(DockForm) -class PortForm(ModelForm): - class Meta: - model = Port - - fields = [ - 'name', - 'address', - 'company'] - - """ - name = CharField(label='Name', max_length=50) - address = CharField(label='Address', max_length=50) - company = CharField(label='Company', max_length=50) - """ - class PlugForm(ModelForm): class Meta: model = Plug - + fields = [ 'name', 'amperage', @@ -125,45 +159,28 @@ class TapForm(ModelForm): TapFormSet = formset_factory(TapForm) -class BoatForm(ModelForm): - class Meta: - model = Boat - fields = ['name', - 'registration_num', - 'length', - 'beam', - 'water_draught', - 'tonnage', - 'water_tank', - 'model', - 'heating', - 'passenger_capacity', - 'picture'] - + + class StayForm(ModelForm): - class Meta: model = Stay + fields = ['arrival', 'departure', 'coming_from', 'going_to', 'no_mooring'] widgets = { - #Use localization and bootstrap 3 - 'datetime': DateTimeWidget( - attrs={'id':"yourdatetimeid"}, - usel10n = True) } class MooringForm(ModelForm): - class Meta: model = Mooring + fields = ['date', 'dock', 'tap', 'plug'] - + MooringFormSet = formset_factory(MooringForm) diff --git a/port/models.py b/port/models.py index 76f5967..0431921 100644 --- a/port/models.py +++ b/port/models.py @@ -1,237 +1,331 @@ -from django.db import models -from django.db.models import Model, CharField, EmailField, \ - DateTimeField, DecimalField, ForeignKey, ManyToManyField, \ - IntegerField, ImageField, BooleanField, \ - CASCADE, SET_NULL, PROTECT -from django_countries.fields import CountryField -from phonenumber_field.modelfields import PhoneNumberField -from django.core.exceptions import ValidationError -from django.utils.translation import gettext_lazy as _ - -def validate_positive(value): - if value < 0: - raise ValidationError( - _('%(value)s is not a positive number'), - params={'value': value}, - ) - -class Address(Model): - address = CharField(max_length=200) - zip_code = CharField(max_length=10) - city = CharField(max_length=200) - country = CountryField() - - def __str__(self): - return '{}, {} - {}, {}'.format(self.address, self.zip_code, - self.city, self.country) - -class Person(Model): - name = CharField(max_length=50,blank=True,null=True) - surname = CharField(max_length=50,blank=True,null=True) - nationality = CountryField(blank=True,null=True) - email = EmailField(max_length=254,blank=True,null=True) - phone = PhoneNumberField(blank=True,null=True) - - # Foreign keys - address = ForeignKey(Address,on_delete=SET_NULL,blank=True,null=True) - def __str__(self): - return '{} {}'.format(self.name, self.surname) - - -class Company(Model): - name = CharField(max_length=50) - email = EmailField(max_length=200,blank=True,null=True) - phone = PhoneNumberField(blank=True,null=True) - registration_num = CharField(max_length=50,blank=True,null=True) - - # Foreign keys - address = ForeignKey(Address,on_delete=SET_NULL,blank=True,null=True) - def __str__(self): - return '{}'.format(self.name) - -class Insurance(Company): - pass - -class Boat(Model): - name = CharField(max_length=50) - registration_num = CharField('Registration number', max_length=20,blank=True,null=True) - length = DecimalField('Length', max_digits=7, decimal_places=2, blank=True,null=True, validators=[validate_positive]) - beam = DecimalField(max_digits=7, decimal_places=2, blank=True,null=True, validators=[validate_positive]) - water_draught = DecimalField(max_digits=7, decimal_places=2, blank=True,null=True, validators=[validate_positive]) - tonnage = DecimalField(max_digits=7, decimal_places=2, blank=True,null=True, validators=[validate_positive]) - water_tank = DecimalField('Water tank capacity', max_digits=7, decimal_places=2, - blank=True,null=True, validators=[validate_positive]) - model = CharField(max_length=50,blank=True,null=True) - heating = CharField(max_length=50,blank=True,null=True) - passenger_capacity = IntegerField(blank=True,null=True) - picture = ImageField(upload_to='uploads/', height_field=None, - width_field=None, max_length=100, blank=True,null=True) - - # Foreign keys - company = ForeignKey(Company,on_delete=SET_NULL,blank=True,null=True,related_name='+') - - boat_insurance = ManyToManyField( - Insurance, - through='BoatInsurance', - through_fields=('boat', 'insurance') - ) - persons = ManyToManyField( - Person, - through='SailsOn', - through_fields=('boat', 'person') - ) - def __str__(self): - return '{}'.format(self.name) - -class SailsOn(Model): - boat = ForeignKey(Boat,on_delete=PROTECT) - person = ForeignKey(Person,on_delete=PROTECT) - - is_captain = BooleanField() - is_crew = BooleanField() - is_owner = BooleanField() - is_guest = BooleanField() - is_pet = BooleanField() - -class BoatInsurance(Model): - contract = IntegerField(blank=True,null=True) - date = DateTimeField(auto_now_add=True) - - # Foreign keys - insurance = ForeignKey(Insurance,on_delete=PROTECT) - boat = ForeignKey(Boat,on_delete=PROTECT) - -class Port(Model): - name = CharField(max_length=50) - - # Foreign keys - address = ForeignKey(Address,on_delete=PROTECT) - company = ForeignKey(Company,on_delete=PROTECT) - employees = ManyToManyField( - Person, - through='Employee', - through_fields=('port', 'person') - ) - def __str__(self): - return '{}'.format(self.name) - - -class Employee(Model): - position = CharField('Job', max_length=20, blank=True,null=True) - - # Foreign keys - port = ForeignKey(Port,on_delete=PROTECT) - person = ForeignKey(Person,on_delete=PROTECT) - def __str__(self): - return '{}, {} at {}'.format(str(self.person), self.position, - str(self.port)) - -class Dock(Model): - name = CharField(max_length=10) - length = DecimalField(max_digits=7, decimal_places=2, blank=True,null=True, validators=[validate_positive]) - width = DecimalField(max_digits=7, decimal_places=2, blank=True,null=True, validators=[validate_positive]) - depth_min = DecimalField(max_digits=7, decimal_places=2, blank=True,null=True, validators=[validate_positive]) - depth_max = DecimalField(max_digits=7, decimal_places=2, blank=True,null=True, validators=[validate_positive]) - - # Foreign keys - port = ForeignKey(Port,on_delete=PROTECT) - def __str__(self): - return '{} : {}'.format(str(self.port), self.num) - -class Plug(Model): - name = CharField(max_length=10) - amperage = DecimalField(max_digits=7, decimal_places=2, validators=[validate_positive]) - voltage = DecimalField(max_digits=7, decimal_places=2, validators=[validate_positive]) - - # Foreign keys - port = ForeignKey(Port,on_delete=PROTECT) - def __str__(self): - return '{} : {}'.format(str(self.port), self.num) - -class Tap(Model): - name = CharField(max_length=10) - - # Foreign keys - port = ForeignKey(Port,on_delete=PROTECT) - def __str__(self): - return '{} : {}'.format(str(self.port), self.num) - -class Payment(Model): - num = CharField(max_length=40) - date = DateTimeField() - amount = DecimalField(max_digits=7, decimal_places=2) - pay_type = CharField( - max_length=3, - choices=[ - ('CSH', 'Cash'), - ('CHK', 'Check'), - ('TSF', 'Transfer'), - ('CRD', 'Credit Card') - ], - default='CSH' - ) - def __str__(self): - return '{} at {}'.format(self.num, str(self.date)) - -class Service(Model): - name = CharField(max_length=50) - def __str__(self): - return '{}'.format(self.name) - -class Bill(Model): - num = IntegerField() - date = DateTimeField() - total = DecimalField(max_digits=7, decimal_places=2) - - # Foreign keys - payments = ManyToManyField( - Payment, - through='BillPayment', - through_fields=('bill', 'payment') - ) - def __str__(self): - return '{} at {}'.format(self.num, str(self.date)) - -class BillLine(Model): - quantity = DecimalField(max_digits=7, decimal_places=2) - value = DecimalField(max_digits=7, decimal_places=2, default=0.) - - # Foreign keyks - service = ForeignKey(Service,on_delete=PROTECT) - bill = ForeignKey(Bill,on_delete=PROTECT) - def __str__(self): - return '{}x {} - {}'.format(self.quantity, str(self.service), - str(self.value)) - -class BillPayment(Model): - amount = DecimalField(max_digits=7, decimal_places=2) - - # Foreign keys - bill = ForeignKey(Bill,on_delete=PROTECT) - payment = ForeignKey(Payment,on_delete=PROTECT) - -class Stay(Model): - arrival = DateTimeField() - departure = DateTimeField(blank=True,null=True) - coming_from = CharField(max_length=200,blank=True,null=True) - going_to = CharField(max_length=200,blank=True,null=True) - no_mooring = BooleanField('Set moorings later',default=False,blank=True) - - # Foreign keys - boat = ForeignKey(Boat,on_delete=PROTECT) - bill = ForeignKey(Bill,on_delete=PROTECT,blank=True,null=True) - def __str__(self): - return '{} - {} : {}'.format(self.arrival, self.departure, str(self.boat)) - -class Mooring(Model): - date = DateTimeField() - - # Foreign keys - stay = ForeignKey(Stay,on_delete=CASCADE) - dock = ForeignKey(Dock,on_delete=PROTECT) - tap = ForeignKey(Tap,on_delete=PROTECT,blank=True,null=True) - plug = ForeignKey(Plug,on_delete=PROTECT,blank=True,null=True) - - # Methods - def __str__(self): - return '{} - {} at {}'.format(str(self.date), str(self.stay.boat), str(self.dock)) +from django.db import models +from django.db.models import Model, CharField, EmailField, \ + DateTimeField, DecimalField, ForeignKey, ManyToManyField, \ + IntegerField, ImageField, BooleanField, \ + CASCADE, SET_NULL, PROTECT +from django_countries.fields import CountryField +from phonenumber_field.modelfields import PhoneNumberField +from django.core.exceptions import ValidationError +from django.utils.translation import gettext_lazy as _ + +def validate_positive(value): + if value < 0: + raise ValidationError( + _('%(value)s is not a positive number'), + params={'value': value}, + ) + +class Address(Model): + address = CharField(max_length=200) + zip_code = CharField(max_length=10) + city = CharField(max_length=200) + country = CountryField() + + # Methods + def __str__(self): + return '{}, {} - {}, {}'.format(self.address, self.zip_code, + self.city, self.country) + +class Person(Model): + name = CharField(max_length=50,blank=True,null=True) + surname = CharField(max_length=50,blank=True,null=True) + nationality = CountryField(blank=True,null=True) + email = EmailField(max_length=254,blank=True,null=True) + phone = PhoneNumberField(blank=True,null=True) + + # Foreign keys + address = ForeignKey(Address,on_delete=SET_NULL,blank=True,null=True) + + # Methods + def __str__(self): + return '{} {}'.format(self.name, self.surname) + + +class Company(Model): + name = CharField(max_length=50,blank=True) + email = EmailField(max_length=200,blank=True,null=True) + phone = PhoneNumberField(blank=True,null=True) + registration_num = CharField(max_length=50,blank=True,null=True) + + # Foreign keys + address = ForeignKey(Address,on_delete=SET_NULL,blank=True,null=True) + + # Methods + def __str__(self): + return '{}'.format(self.name) + +class Insurance(Company): + pass + +class Boat(Model): + name = CharField(max_length=50) + registration_num = CharField('Registration number', + max_length=20, + blank=True, + null=True) + + length = DecimalField('Length', + max_digits=7, + decimal_places=2, + blank=True,null=True, + validators=[validate_positive]) + + beam = DecimalField(max_digits=7, + decimal_places=2, + blank=True, + null=True, + validators=[validate_positive]) + + water_draught = DecimalField(max_digits=7, + decimal_places=2, + blank=True, + null=True, + validators=[validate_positive]) + + tonnage = DecimalField(max_digits=7, + decimal_places=2, + blank=True, + null=True, + validators=[validate_positive]) + + water_tank = DecimalField('Water tank capacity', + max_digits=7, + decimal_places=2, + blank=True, + null=True, + validators=[validate_positive]) + + model = CharField(max_length=50,blank=True,null=True) + + heating = CharField(max_length=50,blank=True,null=True) + + passenger_capacity = IntegerField(blank=True,null=True) + + picture = ImageField(upload_to='uploads/', + height_field=None, + width_field=None, + max_length=100, + blank=True, + null=True) + + # Foreign keys + company = ForeignKey(Company, + on_delete=SET_NULL, + blank=True, + null=True, + related_name='+') + + boat_insurance = ManyToManyField( + Insurance, + through='BoatInsurance', + through_fields=('boat', 'insurance'), + blank=True + ) + + persons = ManyToManyField( + Person, + through='SailsOn', + through_fields=('boat', 'person') + ) + + # Methods + def __str__(self): + return '{}'.format(self.name) + +class SailsOn(Model): + boat = ForeignKey(Boat,on_delete=PROTECT) + person = ForeignKey(Person,on_delete=PROTECT) + + is_captain = BooleanField() + is_crew = BooleanField() + is_owner = BooleanField() + is_guest = BooleanField() + is_pet = BooleanField() + +class BoatInsurance(Model): + contract = IntegerField(blank=True,null=True) + date = DateTimeField(auto_now_add=True) + + # Foreign keys + insurance = ForeignKey(Insurance,on_delete=PROTECT) + boat = ForeignKey(Boat,on_delete=PROTECT) + +class Port(Model): + name = CharField(max_length=50) + + # Foreign keys + address = ForeignKey(Address,on_delete=PROTECT) + company = ForeignKey(Company,on_delete=PROTECT) + employees = ManyToManyField( + Person, + through='Employee', + through_fields=('port', 'person')) + + # Methods + def __str__(self): + return '{}'.format(self.name) + + +class Employee(Model): + position = CharField('Job', max_length=20, blank=True,null=True) + + # Foreign keys + port = ForeignKey(Port,on_delete=PROTECT) + person = ForeignKey(Person,on_delete=PROTECT) + + # Methods + def __str__(self): + return '{}, {} at {}'.format(str(self.person), self.position, + str(self.port)) + +class Dock(Model): + name = CharField(max_length=10) + + length = DecimalField(max_digits=7, + decimal_places=2, + blank=True, + null=True, + validators=[validate_positive]) + + width = DecimalField(max_digits=7, + decimal_places=2, + blank=True, + null=True, + validators=[validate_positive]) + + depth_min = DecimalField(max_digits=7, + decimal_places=2, + blank=True, + null=True, + validators=[validate_positive]) + + depth_max = DecimalField(max_digits=7, + decimal_places=2, + blank=True, + null=True, + validators=[validate_positive]) + + # Foreign keys + port = ForeignKey(Port,on_delete=PROTECT) + + # Methods + def __str__(self): + return '{} : {}'.format(str(self.port), self.num) + +class Plug(Model): + name = CharField(max_length=10) + amperage = DecimalField(max_digits=7, decimal_places=2, + validators=[validate_positive]) + voltage = DecimalField(max_digits=7, decimal_places=2, + validators=[validate_positive]) + + # Foreign keys + port = ForeignKey(Port,on_delete=PROTECT) + + # Methods + def __str__(self): + return '{} : {}'.format(str(self.port), self.num) + +class Tap(Model): + name = CharField(max_length=10) + + # Foreign keys + port = ForeignKey(Port,on_delete=PROTECT) + + # Methods + def __str__(self): + return '{} : {}'.format(str(self.port), self.num) + +class Payment(Model): + num = CharField(max_length=40) + date = DateTimeField() + amount = DecimalField(max_digits=7, decimal_places=2) + pay_type = CharField( + max_length=3, + choices=[ + ('CSH', 'Cash'), + ('CHK', 'Check'), + ('TSF', 'Transfer'), + ('CRD', 'Credit Card') + ], + default='CSH' + ) + + # Methods + def __str__(self): + return '{} at {}'.format(self.num, str(self.date)) + +class Service(Model): + name = CharField(max_length=50) + + # Methods + def __str__(self): + return '{}'.format(self.name) + +class Bill(Model): + num = IntegerField() + date = DateTimeField() + total = DecimalField(max_digits=7, decimal_places=2) + + # Foreign keys + payments = ManyToManyField( + Payment, + through='BillPayment', + through_fields=('bill', 'payment') + ) + + # Methods + def __str__(self): + return '{} at {}'.format(self.num, str(self.date)) + +class BillLine(Model): + quantity = DecimalField(max_digits=7, decimal_places=2) + value = DecimalField(max_digits=7, decimal_places=2, default=0.) + + # Foreign keyks + service = ForeignKey(Service,on_delete=PROTECT) + bill = ForeignKey(Bill,on_delete=PROTECT) + + # Methods + def __str__(self): + return '{}x {} - {}'.format(self.quantity, str(self.service), + str(self.value)) + +class BillPayment(Model): + amount = DecimalField(max_digits=7, decimal_places=2) + + # Foreign keys + bill = ForeignKey(Bill,on_delete=PROTECT) + payment = ForeignKey(Payment,on_delete=PROTECT) + +class Stay(Model): + arrival = DateTimeField() + departure = DateTimeField(blank=True,null=True) + coming_from = CharField(max_length=200,blank=True,null=True) + going_to = CharField(max_length=200,blank=True,null=True) + no_mooring = BooleanField('Set moorings later',default=False,blank=True) + + # Foreign keys + boat = ForeignKey(Boat,on_delete=PROTECT) + bill = ForeignKey(Bill,on_delete=PROTECT,blank=True,null=True) + + # Methods + def __str__(self): + return '{} - {} : {}'.format(self.arrival, + self.departure, + str(self.boat)) + +class Mooring(Model): + date = DateTimeField() + + # Foreign keys + stay = ForeignKey(Stay,on_delete=CASCADE) + dock = ForeignKey(Dock,on_delete=PROTECT) + tap = ForeignKey(Tap,on_delete=PROTECT,blank=True,null=True) + plug = ForeignKey(Plug,on_delete=PROTECT,blank=True,null=True) + + # Methods + def __str__(self): + return '{} - {} at {}'.format(str(self.date), + str(self.stay.boat), + str(self.dock)) diff --git a/port/templates/boat/form.html b/port/templates/boat/form.html index 87e2889..f14fccf 100644 --- a/port/templates/boat/form.html +++ b/port/templates/boat/form.html @@ -6,9 +6,18 @@ Boat {{ boat_form }} +
+ Insurance + {{ insurance_form }} +
+
+ Company + {{ company_form }} +
Person {{ person_form }} + {{ address_form }}
Stay diff --git a/port/views/boat.py b/port/views/boat.py index ee9106f..56f836d 100644 --- a/port/views/boat.py +++ b/port/views/boat.py @@ -1,5 +1,6 @@ from django.shortcuts import render from django.http import HttpResponse +from django.core.exceptions import ValidationError from pprint import pprint @@ -12,38 +13,113 @@ def index(request): def list_boats(request): return render(request, 'boat/list.html', - {'boats': boat.objects.all}) + {'boats': Boat.objects.all}) def form(request): - boat_form = BoatForm() - person_form = PersonForm() - stay_form = StayForm() - mooring_forms = MooringFormSet() - - return render(request, 'boat/form.html', + boat_form = BoatForm(prefix='boa') + company_form = CompanyForm(prefix='com') + insurance_form = InsuranceForm(prefix='ins') + person_form = PersonForm(prefix='per') + address_form = AddressForm(prefix='add') + stay_form = StayForm(prefix='sta') + mooring_forms = MooringFormSet(prefix='moo') + + return render(request, 'boat/form.html', {'boat_form': boat_form, + 'company_form': company_form, + 'insurance_form': insurance_form, 'person_form': person_form, + 'address_form': address_form, 'stay_form': stay_form, 'mooring_forms': mooring_forms}) def add_boat(request): - if request.method == 'POST': - boat_form = BoatForm(request.POST) - if not boat_form.is_valid(): - return form(request) - - person_form = PersonForm(request.POST) - if not person_form.is_valid(): - return form(request) - - stay_form = StayForm(request.POST) - if not stay_form.is_valid(): - return form(request) - - if stay_form.no_mooring is not True: - mooring_forms = MooringFormSet(request.POST) - mooring_forms_data = mooring_forms.save(commit=False) - for mooring_data in mooring_forms_data: - pass + if request.method != 'POST': + return form(request.POST) - return form(request) + try: + new_boat_form = BoatForm(request.POST, prefix='boa') + new_boat = new_boat_form.save(commit=False) + pprint(new_boat) + + # TODO : Handle case where insurance is already existing + # if not new_boat.boat_insurance: + # When the boat's insurance is not specified, it means we need to + # create a new insurance + # new_insurance = InsuranceForm(request.POST).save(commit=False) + #else: + # new_insurance = None + + new_insurance = InsuranceForm(request.POST, prefix='ins').save(commit=False) + + if not new_boat.company: + # When the boat's company is not specified, it means we need to + # create a new company + try: + new_company = CompanyForm(request.POST, prefix='com').save(commit=False) + except: + return form(request) + else: + new_company = None + + new_person = PersonForm(request.POST, prefix='per').save(commit=False) + new_address = AddressForm(request.POST, prefix='add').save(commit=False) + new_stay = StayForm(request.POST, prefix='sta').save(commit=False) + if new_stay.no_mooring is not True: + new_moorings = MooringFormSet(request.POST, prefix='moo').save(commit=False) + for mooring in new_moorings: + # Check if mooring is between the date of stay + if new_stay.arrival > mooring.date: + mooring = False + elif new_stay.departure is not None and\ + new_stay.departure < mooring.date: + + mooring = False + + # Check if dock is available + else: + # Set the moorings later + new_moorings = [] + + if new_company is not None: + new_boat.company = new_company.save() + + new_boat.save() + + if new_insurance is not None: + new_insurance.save() + + # Set insurance for boat since current date + BoatInsurance.objects.create( + insurance = new_insurance, + boat = new_boat + ) + + + new_person.address = new_address.save() + new_person.save() + + # Say that person is sailing on boat + SailsOn.objects.create( + boat = new_boat, + person = new_person, + is_captain = False, + is_crew = False, + is_owner = False, + is_guest = False, + is_pet = True) + + new_stay.boat = new_boat + new_stay.save() + + for mooring in new_moorings: + morring.stay = new_stay + mooring.save() + + new_boat_form.save_m2m() + + except ValidationError as err: + pprint(err) + return form(request) + + return list_boats(request)