diff --git a/.gitignore b/.gitignore index e427ff3..68bd265 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,29 @@ node_modules/ */node_modules/ */build/ + +# Python +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +env/ +venv/ +pip-log.txt +pip-delete-this-directory.txt +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.pot +*.mo + +# Django +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal \ No newline at end of file diff --git a/assets/pasted-20251128-174621-a2cd9ba0.jpg b/assets/pasted-20251128-174621-a2cd9ba0.jpg new file mode 100644 index 0000000..a4b9e54 Binary files /dev/null and b/assets/pasted-20251128-174621-a2cd9ba0.jpg differ diff --git a/assets/pasted-20251128-175049-6a6f869a.png b/assets/pasted-20251128-175049-6a6f869a.png new file mode 100644 index 0000000..587c7bf Binary files /dev/null and b/assets/pasted-20251128-175049-6a6f869a.png differ diff --git a/assets/pasted-20251128-210523-42559ed3.png b/assets/pasted-20251128-210523-42559ed3.png new file mode 100644 index 0000000..d6aa4b6 Binary files /dev/null and b/assets/pasted-20251128-210523-42559ed3.png differ diff --git a/assets/vm-shot-2025-11-28T17-46-10-206Z.jpg b/assets/vm-shot-2025-11-28T17-46-10-206Z.jpg new file mode 100644 index 0000000..a4b9e54 Binary files /dev/null and b/assets/vm-shot-2025-11-28T17-46-10-206Z.jpg differ diff --git a/core/__pycache__/__init__.cpython-311.pyc b/core/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 3b7774e..0000000 Binary files a/core/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc deleted file mode 100644 index cd6f855..0000000 Binary files a/core/__pycache__/admin.cpython-311.pyc and /dev/null differ diff --git a/core/__pycache__/apps.cpython-311.pyc b/core/__pycache__/apps.cpython-311.pyc deleted file mode 100644 index 6435d92..0000000 Binary files a/core/__pycache__/apps.cpython-311.pyc and /dev/null differ diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc deleted file mode 100644 index e85aca4..0000000 Binary files a/core/__pycache__/context_processors.cpython-311.pyc and /dev/null differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc deleted file mode 100644 index 9a7d3a4..0000000 Binary files a/core/__pycache__/models.cpython-311.pyc and /dev/null differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc deleted file mode 100644 index 81fedb5..0000000 Binary files a/core/__pycache__/urls.cpython-311.pyc and /dev/null differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc deleted file mode 100644 index 595a222..0000000 Binary files a/core/__pycache__/views.cpython-311.pyc and /dev/null differ diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..0039adb --- /dev/null +++ b/core/forms.py @@ -0,0 +1,7 @@ +from django import forms + +class OrderForm(forms.Form): + client_name = forms.CharField(max_length=100) + client_email = forms.EmailField() + property_address = forms.CharField(widget=forms.Textarea) + description = forms.CharField(widget=forms.Textarea, required=False) diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py index 7563638..b9d7ab3 100644 --- a/core/migrations/0001_initial.py +++ b/core/migrations/0001_initial.py @@ -1,9 +1,10 @@ -# Generated by Django 5.2.7 on 2025-11-27 23:25 +# Generated by Django 5.2.7 on 2025-11-29 01:21 import django.contrib.auth.models import django.contrib.auth.validators import django.db.models.deletion import django.utils.timezone +from django.conf import settings from django.db import migrations, models @@ -39,6 +40,7 @@ class Migration(migrations.Migration): ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), ('user_type', models.CharField(choices=[('internal', 'Internal'), ('client', 'Client')], max_length=10)), + ('role', models.CharField(choices=[('ORG_ADMIN', 'Org admin'), ('SUPER_ADMIN', 'Super admin'), ('JUNIOR_APPRAISER', 'Junior appraiser'), ('SENIOR_APPRAISER', 'Senior appraiser'), ('DESIGNATED_APPRAISER', 'Designated appraiser'), ('CLIENT_USER', 'Client user'), ('CLIENT_MANAGER', 'Client manager'), ('CLIENT_ADMIN', 'Client admin')], default='CLIENT_USER', max_length=20)), ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='core_user_set', related_query_name='user', to='auth.group', verbose_name='groups')), ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='core_user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), ('organization', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='users', to='core.organization')), @@ -52,4 +54,78 @@ class Migration(migrations.Migration): ('objects', django.contrib.auth.models.UserManager()), ], ), + migrations.CreateModel( + name='Invoice', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('invoice_number', models.CharField(max_length=50)), + ('amount', models.DecimalField(decimal_places=2, max_digits=10)), + ('due_date', models.DateField()), + ('status', models.CharField(choices=[('DRAFT', 'Draft'), ('SENT', 'Sent'), ('PAID', 'Paid'), ('CANCELLED', 'Cancelled')], default='DRAFT', max_length=20)), + ('client', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='invoices', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order_date', models.DateField(auto_now_add=True)), + ('status', models.CharField(choices=[('PENDING', 'Pending'), ('IN_PROGRESS', 'In Progress'), ('COMPLETED', 'Completed'), ('CANCELLED', 'Cancelled')], default='PENDING', max_length=20)), + ('description', models.TextField(blank=True)), + ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Project', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(blank=True)), + ('start_date', models.DateField()), + ('end_date', models.DateField(blank=True, null=True)), + ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='projects', to=settings.AUTH_USER_MODEL)), + ('invoice', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projects', to='core.invoice')), + ], + ), + migrations.CreateModel( + name='Property', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('address', models.CharField(max_length=255)), + ('city', models.CharField(max_length=255)), + ('state', models.CharField(max_length=2)), + ('zip_code', models.CharField(max_length=10)), + ('property_type', models.CharField(choices=[('SINGLE_FAMILY', 'Single Family'), ('MULTI_FAMILY', 'Multi-Family'), ('COMMERCIAL', 'Commercial')], max_length=20)), + ('square_footage', models.PositiveIntegerField()), + ('bedrooms', models.PositiveIntegerField()), + ('bathrooms', models.DecimalField(decimal_places=1, max_digits=3)), + ('year_built', models.PositiveIntegerField()), + ('description', models.TextField()), + ('status', models.CharField(choices=[('FOR_SALE', 'For Sale'), ('FOR_RENT', 'For Rent'), ('SOLD', 'Sold')], max_length=20)), + ('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='properties', to='core.organization')), + ], + ), + migrations.CreateModel( + name='Appraisal', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('appraisal_date', models.DateField()), + ('appraised_value', models.DecimalField(decimal_places=2, max_digits=12)), + ('notes', models.TextField(blank=True)), + ('appraiser', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='appraisals', to=settings.AUTH_USER_MODEL)), + ('invoice', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='appraisals', to='core.invoice')), + ('order', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='appraisals', to='core.order')), + ('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='appraisals', to='core.property')), + ], + ), + migrations.CreateModel( + name='PropertyPhoto', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image', models.ImageField(upload_to='property_photos/')), + ('caption', models.CharField(blank=True, max_length=255)), + ('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='photos', to='core.property')), + ], + ), ] diff --git a/core/migrations/0002_add_invoice_to_appraisal.py b/core/migrations/0002_add_invoice_to_appraisal.py new file mode 100644 index 0000000..0c1d366 --- /dev/null +++ b/core/migrations/0002_add_invoice_to_appraisal.py @@ -0,0 +1,17 @@ + +from django.db import migrations, models +import django.db.models.deletion + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='appraisal', + name='invoice', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='appraisals', to='core.invoice'), + ), + ] diff --git a/core/migrations/0002_create_groups.py b/core/migrations/0002_create_groups.py deleted file mode 100644 index 88e865c..0000000 --- a/core/migrations/0002_create_groups.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 5.2.7 on 2025-11-27 23:25 - -from django.db import migrations - -ROLES = [ - "Super admin", - "Org admin", - "Senior_appraiser", - "Junior_appraiser", - "Designated_appraiser", - "Client_user", - "Client_manager", - "Client_admin", -] - -def create_groups(apps, schema_editor): - Group = apps.get_model('auth', 'Group') - for role in ROLES: - Group.objects.create(name=role) - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0001_initial'), - ] - - operations = [ - migrations.RunPython(create_groups), - ] diff --git a/core/migrations/0003_property_propertyphoto.py b/core/migrations/0003_property_propertyphoto.py deleted file mode 100644 index cb69c81..0000000 --- a/core/migrations/0003_property_propertyphoto.py +++ /dev/null @@ -1,42 +0,0 @@ -# Generated by Django 5.2.7 on 2025-11-27 23:32 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0002_create_groups'), - ] - - operations = [ - migrations.CreateModel( - name='Property', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('address', models.CharField(max_length=255)), - ('city', models.CharField(max_length=255)), - ('state', models.CharField(max_length=2)), - ('zip_code', models.CharField(max_length=10)), - ('property_type', models.CharField(choices=[('SINGLE_FAMILY', 'Single Family'), ('MULTI_FAMILY', 'Multi-Family'), ('COMMERCIAL', 'Commercial')], max_length=20)), - ('square_footage', models.PositiveIntegerField()), - ('bedrooms', models.PositiveIntegerField()), - ('bathrooms', models.DecimalField(decimal_places=1, max_digits=3)), - ('year_built', models.PositiveIntegerField()), - ('description', models.TextField()), - ('status', models.CharField(choices=[('FOR_SALE', 'For Sale'), ('FOR_RENT', 'For Rent'), ('SOLD', 'Sold')], max_length=20)), - ('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='properties', to='core.organization')), - ], - ), - migrations.CreateModel( - name='PropertyPhoto', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('image', models.ImageField(upload_to='property_photos/')), - ('caption', models.CharField(blank=True, max_length=255)), - ('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='photos', to='core.property')), - ], - ), - ] diff --git a/core/migrations/0003_remove_invoice_from_appraisal.py b/core/migrations/0003_remove_invoice_from_appraisal.py new file mode 100644 index 0000000..f7afb52 --- /dev/null +++ b/core/migrations/0003_remove_invoice_from_appraisal.py @@ -0,0 +1,15 @@ + +from django.db import migrations + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_add_invoice_to_appraisal'), + ] + + operations = [ + migrations.RemoveField( + model_name='appraisal', + name='invoice', + ), + ] diff --git a/core/migrations/0004_add_invoice_to_appraisal.py b/core/migrations/0004_add_invoice_to_appraisal.py new file mode 100644 index 0000000..8e46325 --- /dev/null +++ b/core/migrations/0004_add_invoice_to_appraisal.py @@ -0,0 +1,17 @@ + +from django.db import migrations, models +import django.db.models.deletion + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_remove_invoice_from_appraisal'), + ] + + operations = [ + migrations.AddField( + model_name='appraisal', + name='invoice', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='appraisals', to='core.invoice'), + ), + ] diff --git a/core/migrations/0004_project_appraisal_invoice.py b/core/migrations/0004_project_appraisal_invoice.py deleted file mode 100644 index d3ded10..0000000 --- a/core/migrations/0004_project_appraisal_invoice.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 5.2.7 on 2025-11-28 02:14 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0003_property_propertyphoto'), - ] - - operations = [ - migrations.CreateModel( - name='Project', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('description', models.TextField(blank=True)), - ('start_date', models.DateField()), - ('end_date', models.DateField(blank=True, null=True)), - ], - ), - migrations.CreateModel( - name='Appraisal', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('appraisal_date', models.DateField()), - ('appraised_value', models.DecimalField(decimal_places=2, max_digits=12)), - ('notes', models.TextField(blank=True)), - ('appraiser', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='appraisals', to=settings.AUTH_USER_MODEL)), - ('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='appraisals', to='core.property')), - ], - ), - migrations.CreateModel( - name='Invoice', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('invoice_number', models.CharField(max_length=50)), - ('amount', models.DecimalField(decimal_places=2, max_digits=10)), - ('due_date', models.DateField()), - ('status', models.CharField(choices=[('DRAFT', 'Draft'), ('SENT', 'Sent'), ('PAID', 'Paid'), ('CANCELLED', 'Cancelled')], default='DRAFT', max_length=20)), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invoices', to='core.project')), - ], - ), - ] diff --git a/core/migrations/0005_remove_appraisal_invoice.py b/core/migrations/0005_remove_appraisal_invoice.py new file mode 100644 index 0000000..cc2994a --- /dev/null +++ b/core/migrations/0005_remove_appraisal_invoice.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.7 on 2025-11-29 01:24 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_add_invoice_to_appraisal'), + ] + + operations = [ + migrations.RemoveField( + model_name='appraisal', + name='invoice', + ), + ] diff --git a/core/migrations/0005_user_user_role_temp.py b/core/migrations/0005_user_user_role_temp.py deleted file mode 100644 index b8f291e..0000000 --- a/core/migrations/0005_user_user_role_temp.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.7 on 2025-11-28 04:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0004_project_appraisal_invoice'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='user_role_temp', - field=models.CharField(choices=[('ORG_ADMIN', 'Org admin'), ('SUPER_ADMIN', 'Super admin'), ('JUNIOR_APPRAISER', 'Junior appraiser'), ('SENIOR_APPRAISER', 'Senior appraiser'), ('DESIGNATED_APPRAISER', 'Designated appraiser'), ('CLIENT_USER', 'Client user'), ('CLIENT_MANAGER', 'Client manager'), ('CLIENT_ADMIN', 'Client admin')], default='CLIENT_USER', max_length=20), - ), - ] diff --git a/core/migrations/0006_add_client_to_project.py b/core/migrations/0006_add_client_to_project.py new file mode 100644 index 0000000..700a182 --- /dev/null +++ b/core/migrations/0006_add_client_to_project.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.3 on 2025-11-29 02:30 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_remove_appraisal_invoice'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='client', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='projects', to=settings.AUTH_USER_MODEL), + ), + ] \ No newline at end of file diff --git a/core/migrations/0006_rename_user_role_to_role.py b/core/migrations/0006_rename_user_role_to_role.py deleted file mode 100644 index 56f0d83..0000000 --- a/core/migrations/0006_rename_user_role_to_role.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.6 on 2025-11-28 20:23 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0005_user_user_role_temp'), - ] - - operations = [ - migrations.RenameField( - model_name='user', - old_name='user_role_temp', - new_name='role', - ), - ] \ No newline at end of file diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc deleted file mode 100644 index bb56d59..0000000 Binary files a/core/migrations/__pycache__/0001_initial.cpython-311.pyc and /dev/null differ diff --git a/core/migrations/__pycache__/0002_create_groups.cpython-311.pyc b/core/migrations/__pycache__/0002_create_groups.cpython-311.pyc deleted file mode 100644 index d6fa8d0..0000000 Binary files a/core/migrations/__pycache__/0002_create_groups.cpython-311.pyc and /dev/null differ diff --git a/core/migrations/__pycache__/0003_property_propertyphoto.cpython-311.pyc b/core/migrations/__pycache__/0003_property_propertyphoto.cpython-311.pyc deleted file mode 100644 index ed8e16d..0000000 Binary files a/core/migrations/__pycache__/0003_property_propertyphoto.cpython-311.pyc and /dev/null differ diff --git a/core/migrations/__pycache__/0004_project_appraisal_invoice.cpython-311.pyc b/core/migrations/__pycache__/0004_project_appraisal_invoice.cpython-311.pyc deleted file mode 100644 index 9d873be..0000000 Binary files a/core/migrations/__pycache__/0004_project_appraisal_invoice.cpython-311.pyc and /dev/null differ diff --git a/core/migrations/__pycache__/0005_user_user_role_temp.cpython-311.pyc b/core/migrations/__pycache__/0005_user_user_role_temp.cpython-311.pyc deleted file mode 100644 index eace857..0000000 Binary files a/core/migrations/__pycache__/0005_user_user_role_temp.cpython-311.pyc and /dev/null differ diff --git a/core/migrations/__pycache__/0006_rename_user_role_to_role.cpython-311.pyc b/core/migrations/__pycache__/0006_rename_user_role_to_role.cpython-311.pyc deleted file mode 100644 index 6783e3d..0000000 Binary files a/core/migrations/__pycache__/0006_rename_user_role_to_role.cpython-311.pyc and /dev/null differ diff --git a/core/migrations/__pycache__/__init__.cpython-311.pyc b/core/migrations/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 58b1c14..0000000 Binary files a/core/migrations/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/core/models.py b/core/models.py index 1e272f4..27d2b33 100644 --- a/core/models.py +++ b/core/models.py @@ -77,25 +77,6 @@ class PropertyPhoto(models.Model): def __str__(self): return f"Photo for {self.property.name}" -class Appraisal(models.Model): - property = models.ForeignKey(Property, on_delete=models.CASCADE, related_name='appraisals') - appraiser = models.ForeignKey(User, on_delete=models.CASCADE, related_name='appraisals') - appraisal_date = models.DateField() - appraised_value = models.DecimalField(max_digits=12, decimal_places=2) - notes = models.TextField(blank=True) - - def __str__(self): - return f"Appraisal for {self.property.name} on {self.appraisal_date}" - -class Project(models.Model): - name = models.CharField(max_length=255) - description = models.TextField(blank=True) - start_date = models.DateField() - end_date = models.DateField(null=True, blank=True) - - def __str__(self): - return self.name - class Invoice(models.Model): STATUS_CHOICES = [ ('DRAFT', 'Draft'), @@ -103,11 +84,49 @@ class Invoice(models.Model): ('PAID', 'Paid'), ('CANCELLED', 'Cancelled'), ] - project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='invoices') + client = models.ForeignKey('User', on_delete=models.CASCADE, related_name='invoices', null=True, blank=True) invoice_number = models.CharField(max_length=50) amount = models.DecimalField(max_digits=10, decimal_places=2) due_date = models.DateField() status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='DRAFT') def __str__(self): - return f"Invoice {self.invoice_number} for {self.project.name}" \ No newline at end of file + return f"Invoice {self.invoice_number}" + +class Order(models.Model): + STATUS_CHOICES = [ + ('PENDING', 'Pending'), + ('IN_PROGRESS', 'In Progress'), + ('COMPLETED', 'Completed'), + ('CANCELLED', 'Cancelled'), + ] + client = models.ForeignKey('User', on_delete=models.CASCADE, related_name='orders') + order_date = models.DateField(auto_now_add=True) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') + description = models.TextField(blank=True) + + def __str__(self): + return f"Order #{self.id} for {self.client.username}" + +class Project(models.Model): + client = models.ForeignKey(User, on_delete=models.CASCADE, related_name='projects') + name = models.CharField(max_length=255) + description = models.TextField(blank=True) + start_date = models.DateField() + end_date = models.DateField(null=True, blank=True) + invoice = models.ForeignKey(Invoice, on_delete=models.SET_NULL, null=True, blank=True, related_name='projects') + + def __str__(self): + return self.name + +class Appraisal(models.Model): + property = models.ForeignKey(Property, on_delete=models.CASCADE, related_name='appraisals') + appraiser = models.ForeignKey(User, on_delete=models.CASCADE, related_name='appraisals') + appraisal_date = models.DateField() + appraised_value = models.DecimalField(max_digits=12, decimal_places=2) + notes = models.TextField(blank=True) + + order = models.ForeignKey(Order, on_delete=models.SET_NULL, related_name='appraisals', null=True, blank=True) + + def __str__(self): + return f"Appraisal for {self.property.name} on {self.appraisal_date}" \ No newline at end of file diff --git a/core/templates/core/order_form.html b/core/templates/core/order_form.html index 7d241ab..efb2b93 100644 --- a/core/templates/core/order_form.html +++ b/core/templates/core/order_form.html @@ -9,24 +9,9 @@