This commit is contained in:
gitea 2025-03-07 00:23:02 +00:00
parent 7dd5791d14
commit bada43ba64
1225 changed files with 71034 additions and 54077 deletions

22
.dockerignore Normal file
View File

@ -0,0 +1,22 @@
__pycache__/
*.pyc
*.log
*.sqlite3
*.db
migrations/
media/
static/
node_modules/
venv/
env/
.git/
.gitignore
.DS_Store
docker-compose.yml
README.md
car*.json
.vscode
.idea
*/migrations/*
*/migrations/
inventory/migrations/

39
Dockerfile Normal file
View File

@ -0,0 +1,39 @@
# Use an official Python image as a base
FROM python:3.11.11-slim-bullseye
# Set the working directory to /app
WORKDIR /app
# Create a new user and group
RUN groupadd -r appgroup
RUN useradd -r -g appgroup -G appgroup -m -d /app -s /bin/false appuser
# Copy the requirements file
COPY requirements.txt .
# Install the dependencies
RUN pip install -r requirements.txt
RUN apt-get update && apt-get install -y libgl1
RUN apt-get update && apt-get install -y libglib2.0-dev
RUN apt-get update && apt-get install -y libzbar0
# Copy the application code
COPY . .
# Expose the port
EXPOSE 8000
# Copy the entrypoint script
COPY entrypoint.sh /app/entrypoint.sh
# Make the script executable
RUN chmod +x /app/entrypoint.sh
# Change ownership of the app directory to the new user
RUN chown -R appuser:appgroup /app
RUN find /app -path "*/migrations/*.py" -not -name "__init__.py" -delete
RUN find /app -path "*/migrations/*.pyc" -delete
# Set the entrypoint to execute the script as the new user
ENTRYPOINT ["sh", "-c", "python3 manage.py makemigrations && python3 manage.py migrate && python3 manage.py collectstatic --no-input && python3 manage.py runserver 0.0.0.0:8000"]

Binary file not shown.

Binary file not shown.

31
docker-compose.yml Normal file
View File

@ -0,0 +1,31 @@
version: '3'
services:
db:
image: postgres
restart: always
environment:
- POSTGRES_DB=haikal_db
- POSTGRES_USER=haikal_user
- POSTGRES_PASSWORD=haikal_pass
healthcheck:
test: ["CMD", "pg_isready", "-U", "haikal_user", "-d", "haikal_db"]
interval: 1m30s
timeout: 30s
retries: 5
start_period: 30s
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
environment:
- DATABASE_HOST=db
- DATABASE_PORT=5432
- POSTGRES_DB=haikal_db
- POSTGRES_USER=haikal_user
- POSTGRES_PASSWORD=haikal_pass
volumes:
- .:/app
ports:
- "8000:8000"
depends_on:
- db

5
entrypoint.sh Normal file
View File

@ -0,0 +1,5 @@
#!/bin/sh
python manage.py migrate
python manage.py collectstatic --no-input
python manage.py runserver 0.0.0.0:8000

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.5 on 2025-01-30 11:28
# Generated by Django 5.1.6 on 2025-03-06 01:43
from django.db import migrations, models

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.5 on 2025-01-30 11:28
# Generated by Django 5.1.6 on 2025-03-06 01:43
import django.db.models.deletion
from django.db import migrations, models

View File

@ -1,5 +1,6 @@
# Generated by Django 5.1.5 on 2025-01-30 11:28
# Generated by Django 5.1.6 on 2025-03-06 01:43
import datetime
import django.db.models.deletion
import inventory.mixins
import inventory.models
@ -14,8 +15,12 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('appointment', '__first__'),
('auth', '0012_alter_user_first_name_max_length'),
('contenttypes', '0002_remove_content_type_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
migrations.swappable_dependency(settings.DJANGO_LEDGER_ACCOUNT_MODEL),
migrations.swappable_dependency(settings.DJANGO_LEDGER_CUSTOMER_MODEL),
migrations.swappable_dependency(settings.DJANGO_LEDGER_ENTITY_MODEL),
migrations.swappable_dependency(settings.DJANGO_LEDGER_ESTIMATE_MODEL),
migrations.swappable_dependency(settings.DJANGO_LEDGER_INVOICE_MODEL),
@ -94,25 +99,6 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'payments',
},
),
migrations.CreateModel(
name='SubscriptionPlan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Name of the subscription plan', max_length=100, unique=True)),
('description', models.TextField()),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('max_users', models.PositiveIntegerField(default=1, help_text='Maximum number of users allowed')),
('max_inventory_size', models.PositiveIntegerField(default=50, help_text='Maximum number of cars in inventory')),
('support_level', models.CharField(choices=[('basic', 'Basic Support'), ('priority', 'Priority Support'), ('dedicated', 'Dedicated Support')], default='basic', help_text='Level of support provided', max_length=50)),
('custom_features', models.JSONField(blank=True, help_text='Additional features specific to this plan', null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'Subscription Plan',
'verbose_name_plural': 'Subscription Plans',
},
),
migrations.CreateModel(
name='VatRate',
fields=[
@ -122,23 +108,6 @@ class Migration(migrations.Migration):
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
name='Activity',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField()),
('activity_type', models.CharField(choices=[('call', 'Call'), ('sms', 'SMS'), ('email', 'Email'), ('whatsapp', 'WhatsApp'), ('visit', 'Visit'), ('add_car', 'Add Car'), ('reserve_car', 'Reserve Car'), ('remove_car', 'Remove Car'), ('create_quotation', 'Create Quotation'), ('cancel_quotation', 'Cancel Quotation'), ('create_order', 'Create Order'), ('cancel_order', 'Cancel Order'), ('create_invoice', 'Create Invoice'), ('cancel_invoice', 'Cancel Invoice')], max_length=50, verbose_name='Activity Type')),
('notes', models.TextField(blank=True, null=True, verbose_name='Notes')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='activities_created', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Activity',
'verbose_name_plural': 'Activities',
},
),
migrations.CreateModel(
name='AdditionalServices',
fields=[
@ -168,6 +137,7 @@ class Migration(migrations.Migration):
('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')),
('mileage', models.IntegerField(blank=True, null=True, verbose_name='Mileage')),
('receiving_date', models.DateTimeField(verbose_name='Receiving Date')),
('hash', models.CharField(blank=True, max_length=64, null=True, verbose_name='Hash')),
('vendor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to=settings.DJANGO_LEDGER_VENDOR_MODEL, verbose_name='Vendor')),
('id_car_make', models.ForeignKey(blank=True, db_column='id_car_make', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make')),
],
@ -242,10 +212,10 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('plate_number', models.IntegerField(verbose_name='Plate Number')),
('text1', models.CharField(max_length=1, verbose_name='Text 1')),
('text2', models.CharField(max_length=1, verbose_name='Text 2')),
('text3', models.CharField(max_length=1, verbose_name='Text 3')),
('text2', models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 2')),
('text3', models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 3')),
('registration_date', models.DateTimeField(verbose_name='Registration Date')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car')),
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car')),
],
options={
'verbose_name': 'Registration',
@ -362,6 +332,15 @@ class Migration(migrations.Migration):
('objects', inventory.models.DealerUserManager()),
],
),
migrations.CreateModel(
name='CustomGroup',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.group', verbose_name='')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='groups', to='inventory.dealer')),
],
),
migrations.CreateModel(
name='Customer',
fields=[
@ -378,6 +357,7 @@ class Migration(migrations.Migration):
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='customer_profile', to=settings.AUTH_USER_MODEL)),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customers', to='inventory.dealer')),
],
options={
@ -432,19 +412,78 @@ class Migration(migrations.Migration):
name='dealer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer', verbose_name='Dealer'),
),
migrations.CreateModel(
name='Activity',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField()),
('activity_type', models.CharField(choices=[('call', 'Call'), ('sms', 'SMS'), ('email', 'Email'), ('whatsapp', 'WhatsApp'), ('visit', 'Visit'), ('add_car', 'Add Car'), ('sale_car', 'Sale Car'), ('reserve_car', 'Reserve Car'), ('transfer_car', 'Transfer Car'), ('remove_car', 'Remove Car'), ('create_quotation', 'Create Quotation'), ('cancel_quotation', 'Cancel Quotation'), ('create_order', 'Create Order'), ('cancel_order', 'Cancel Order'), ('create_invoice', 'Create Invoice'), ('cancel_invoice', 'Cancel Invoice')], max_length=50, verbose_name='Activity Type')),
('notes', models.TextField(blank=True, null=True, verbose_name='Notes')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='contenttypes.contenttype')),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='activities_created_by', to=settings.AUTH_USER_MODEL)),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='activities', to='inventory.dealer')),
],
options={
'verbose_name': 'Activity',
'verbose_name_plural': 'Activities',
},
),
migrations.CreateModel(
name='DealerSettings',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('additional_info', models.JSONField(blank=True, default=dict, null=True)),
('bill_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_cash', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('bill_prepaid_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_prepaid', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('bill_unearned_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_unearned', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('dealer', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='settings', to='inventory.dealer')),
('invoice_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_cash', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('invoice_prepaid_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_prepaid', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('invoice_unearned_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_unearned', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
],
),
migrations.CreateModel(
name='Email',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField()),
('from_email', models.TextField(blank=True, null=True, verbose_name='From Email')),
('to_email', models.TextField(blank=True, null=True, verbose_name='To Email')),
('subject', models.TextField(blank=True, null=True, verbose_name='Subject')),
('message', models.TextField(blank=True, null=True, verbose_name='Message')),
('status', models.CharField(choices=[('SENT', 'Sent'), ('FAILED', 'Failed'), ('DELIVERED', 'Delivered'), ('OPEN', 'Open'), ('DRAFT', 'Draft')], default='OPEN', max_length=20, verbose_name='Status')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='emails_created', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Email',
'verbose_name_plural': 'Emails',
},
),
migrations.CreateModel(
name='Lead',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=50, verbose_name='First Name')),
('last_name', models.CharField(max_length=50, verbose_name='Last Name')),
('email', models.EmailField(max_length=254, verbose_name='Email')),
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
('lead_type', models.CharField(choices=[('customer', 'Customer'), ('organization', 'Organization')], default='customer', max_length=50, verbose_name='Lead Type')),
('year', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Year')),
('source', models.CharField(choices=[('referrals', 'Referrals'), ('whatsapp', 'WhatsApp'), ('showroom', 'Showroom'), ('tiktok', 'TikTok'), ('instagram', 'Instagram'), ('x', 'X'), ('facebook', 'Facebook'), ('motory', 'Motory'), ('influencers', 'Influencers'), ('youtube', 'Youtube'), ('campaign', 'Campaign')], max_length=50, verbose_name='Source')),
('channel', models.CharField(choices=[('walk_in', 'Walk In'), ('toll_free', 'Toll Free'), ('website', 'Website'), ('email', 'Email'), ('form', 'Form')], max_length=50, verbose_name='Channel')),
('city', models.CharField(max_length=50, verbose_name='City')),
('crn', models.CharField(blank=True, max_length=10, null=True, unique=True, verbose_name='Commercial Registration Number')),
('vrn', models.CharField(blank=True, max_length=15, null=True, unique=True, verbose_name='VAT Registration Number')),
('address', models.CharField(max_length=50, verbose_name='address')),
('priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], default='medium', max_length=10, verbose_name='Priority')),
('status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], db_index=True, default='new', max_length=50, verbose_name='Status')),
('status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], db_index=True, default='new', max_length=50, verbose_name='Status')),
('created', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leads', to='inventory.customer')),
('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='leads', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL)),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leads', to='inventory.dealer')),
('id_car_make', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make')),
('id_car_model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model')),
@ -554,6 +593,26 @@ class Migration(migrations.Migration):
'ordering': ['-created'],
},
),
migrations.CreateModel(
name='Schedule',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('purpose', models.CharField(choices=[('Product Demo', 'Product Demo'), ('Follow-Up Call', 'Follow-Up Call'), ('Contract Discussion', 'Contract Discussion'), ('Sales Meeting', 'Sales Meeting'), ('Support Call', 'Support Call'), ('Other', 'Other')], max_length=200)),
('scheduled_at', models.DateTimeField()),
('scheduled_type', models.CharField(choices=[('Call', 'Call'), ('Meeting', 'Meeting'), ('Email', 'Email')], default='Call', max_length=200)),
('duration', models.DurationField(default=datetime.timedelta(seconds=300))),
('notes', models.TextField(blank=True, null=True)),
('status', models.CharField(choices=[('Scheduled', 'Scheduled'), ('Completed', 'Completed'), ('Canceled', 'Canceled')], default='Scheduled', max_length=200)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL)),
('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to='inventory.lead')),
('scheduled_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-scheduled_at'],
},
),
migrations.CreateModel(
name='Staff',
fields=[
@ -561,11 +620,11 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=255, verbose_name='Name')),
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
('staff_type', models.CharField(choices=[('manager', 'Manager'), ('inventory', 'Inventory'), ('accountant', 'Accountant'), ('sales', 'Sales'), ('coordinator', 'Coordinator'), ('receptionist', 'Receptionist'), ('agent', 'Agent')], max_length=255, verbose_name='Staff Type')),
('staff_type', models.CharField(choices=[('inventory', 'Inventory'), ('accountant', 'Accountant'), ('sales', 'Sales')], max_length=255, verbose_name='Staff Type')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to='inventory.dealer')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to=settings.AUTH_USER_MODEL)),
('staff_member', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to='appointment.staffmember')),
],
options={
'verbose_name': 'Staff',
@ -582,15 +641,17 @@ class Migration(migrations.Migration):
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stage', models.CharField(choices=[('prospect', 'Prospect'), ('proposal', 'Proposal'), ('negotiation', 'Negotiation'), ('closed_won', 'Closed Won'), ('closed_lost', 'Closed Lost')], max_length=20, verbose_name='Stage')),
('status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], default='new', max_length=20, verbose_name='Status')),
('status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], default='new', max_length=20, verbose_name='Status')),
('probability', models.PositiveIntegerField(validators=[inventory.models.validate_probability])),
('closing_date', models.DateField(verbose_name='Closing Date')),
('closing_date', models.DateField(blank=True, null=True, verbose_name='Closing Date')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
('closed', models.BooleanField(default=False, verbose_name='Closed')),
('car', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.car', verbose_name='Car')),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.customer')),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL)),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.dealer')),
('estimate', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='opportunity', to=settings.DJANGO_LEDGER_ESTIMATE_MODEL)),
('lead', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunity', to='inventory.lead')),
('staff', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='owner', to='inventory.staff', verbose_name='Owner')),
],
options={
@ -602,8 +663,8 @@ class Migration(migrations.Migration):
name='LeadStatusHistory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('old_status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], max_length=50, verbose_name='Old Status')),
('new_status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], max_length=50, verbose_name='New Status')),
('old_status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], max_length=50, verbose_name='Old Status')),
('new_status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], max_length=50, verbose_name='New Status')),
('changed_at', models.DateTimeField(auto_now_add=True, verbose_name='Changed At')),
('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_history', to='inventory.lead')),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='status_changes', to='inventory.staff')),
@ -618,40 +679,6 @@ class Migration(migrations.Migration):
name='staff',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned', to='inventory.staff', verbose_name='Assigned'),
),
migrations.CreateModel(
name='Subscription',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('start_date', models.DateField(help_text='Date when the subscription starts')),
('end_date', models.DateField(help_text='Date when the subscription ends')),
('is_active', models.BooleanField(default=True)),
('billing_cycle', models.CharField(choices=[('monthly', 'Monthly'), ('annual', 'Annual')], default='monthly', help_text='Billing cycle for the subscription', max_length=10)),
('last_payment_date', models.DateField(blank=True, help_text='Date of the last payment made', null=True)),
('next_payment_date', models.DateField(blank=True, help_text='Date of the next payment due', null=True)),
('plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to='inventory.subscriptionplan')),
],
options={
'verbose_name': 'Subscription',
'verbose_name_plural': 'Subscriptions',
},
),
migrations.CreateModel(
name='SubscriptionUser',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.subscription')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Subscription User',
'verbose_name_plural': 'Subscription Users',
},
),
migrations.AddField(
model_name='subscription',
name='users',
field=models.ManyToManyField(through='inventory.SubscriptionUser', to=settings.AUTH_USER_MODEL),
),
migrations.CreateModel(
name='UserActivityLog',
fields=[
@ -704,11 +731,23 @@ class Migration(migrations.Migration):
'unique_together': {('car', 'reserved_until')},
},
),
migrations.CreateModel(
name='DealersMake',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('added_at', models.DateTimeField(auto_now_add=True)),
('car_make', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='car_dealers', to='inventory.carmake')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dealer_makes', to='inventory.dealer')),
],
options={
'unique_together': {('dealer', 'car_make')},
},
),
migrations.CreateModel(
name='CarColors',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.car')),
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.car')),
('exterior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.exteriorcolors')),
('interior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.interiorcolors')),
],

View File

@ -1,19 +0,0 @@
# Generated by Django 5.1.5 on 2025-02-04 04:37
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='carregistration',
name='car',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car'),
),
]

View File

@ -1,21 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-04 09:34
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_CUSTOMER_MODEL),
('inventory', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='lead',
name='customer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leads', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL),
),
]

View File

@ -1,46 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-04 11:38
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import phonenumber_field.modelfields
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_CUSTOMER_MODEL),
('inventory', '0002_alter_lead_customer'),
]
operations = [
migrations.AddField(
model_name='lead',
name='email',
field=models.EmailField(default='x@tenhal.sa', max_length=254, verbose_name='Email'),
preserve_default=False,
),
migrations.AddField(
model_name='lead',
name='first_name',
field=models.CharField(default='test', max_length=50, verbose_name='First Name'),
preserve_default=False,
),
migrations.AddField(
model_name='lead',
name='last_name',
field=models.CharField(default='test', max_length=50, verbose_name='Last Name'),
preserve_default=False,
),
migrations.AddField(
model_name='lead',
name='phone_number',
field=phonenumber_field.modelfields.PhoneNumberField(default='056523656', max_length=128, region='SA', verbose_name='Phone Number'),
preserve_default=False,
),
migrations.AlterField(
model_name='lead',
name='customer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='leads', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL),
),
]

View File

@ -1,23 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-04 14:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0003_lead_email_lead_first_name_lead_last_name_and_more'),
]
operations = [
migrations.RemoveField(
model_name='lead',
name='city',
),
migrations.AddField(
model_name='lead',
name='address',
field=models.CharField(default='', max_length=50, verbose_name='address'),
preserve_default=False,
),
]

View File

@ -1,33 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-04 15:15
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_CUSTOMER_MODEL),
('inventory', '0004_remove_lead_city_lead_address'),
]
operations = [
migrations.CreateModel(
name='Schedule',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('purpose', models.CharField(max_length=200)),
('scheduled_at', models.DateTimeField()),
('notes', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL)),
('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to='inventory.lead')),
('scheduled_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.staff')),
],
options={
'ordering': ['-scheduled_at'],
},
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-04 15:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0005_schedule'),
]
operations = [
migrations.AlterField(
model_name='schedule',
name='purpose',
field=models.CharField(choices=[('Product Demo', 'Product Demo'), ('Follow-Up Call', 'Follow-Up Call'), ('Contract Discussion', 'Contract Discussion'), ('Sales Meeting', 'Sales Meeting'), ('Support Call', 'Support Call'), ('Other', 'Other')], max_length=200),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-04 15:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0006_alter_schedule_purpose'),
]
operations = [
migrations.AddField(
model_name='schedule',
name='scheduled_type',
field=models.CharField(choices=[('Call', 'Call'), ('Meeting', 'Meeting'), ('Email', 'Email')], default='Call', max_length=200),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-05 09:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0007_schedule_scheduled_type'),
]
operations = [
migrations.AddField(
model_name='schedule',
name='status',
field=models.CharField(choices=[('Scheduled', 'Scheduled'), ('Completed', 'Completed'), ('Canceled', 'Canceled')], default='Scheduled', max_length=200),
),
]

View File

@ -1,21 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-05 10:00
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_CUSTOMER_MODEL),
('inventory', '0008_schedule_status'),
]
operations = [
migrations.AlterField(
model_name='opportunity',
name='customer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL),
),
]

View File

@ -1,27 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-05 10:05
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0009_alter_opportunity_customer'),
]
operations = [
migrations.RemoveField(
model_name='lead',
name='id_car_make',
),
migrations.RemoveField(
model_name='lead',
name='id_car_model',
),
migrations.AddField(
model_name='lead',
name='car',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.car', verbose_name='Car'),
),
]

View File

@ -1,25 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-05 10:17
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_CUSTOMER_MODEL),
('inventory', '0010_remove_lead_id_car_make_remove_lead_id_car_model_and_more'),
]
operations = [
migrations.RemoveField(
model_name='lead',
name='year',
),
migrations.AlterField(
model_name='schedule',
name='customer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL),
),
]

View File

@ -1,14 +0,0 @@
# Generated by Django 5.1.5 on 2025-02-06 08:51
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0002_alter_carregistration_car'),
('inventory', '0011_remove_lead_year_alter_schedule_customer'),
]
operations = [
]

View File

@ -1,14 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-06 10:08
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0002_alter_carregistration_car'),
('inventory', '0011_remove_lead_year_alter_schedule_customer'),
]
operations = [
]

View File

@ -1,23 +0,0 @@
# Generated by Django 5.1.5 on 2025-02-07 01:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0012_merge_20250206_1151'),
]
operations = [
migrations.AlterField(
model_name='carregistration',
name='text2',
field=models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 2'),
),
migrations.AlterField(
model_name='carregistration',
name='text3',
field=models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 3'),
),
]

View File

@ -1,33 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-06 12:07
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0013_alter_carregistration_text2_and_more'),
]
operations = [
migrations.RemoveField(
model_name='lead',
name='car',
),
migrations.AddField(
model_name='lead',
name='id_car_make',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make'),
),
migrations.AddField(
model_name='lead',
name='id_car_model',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
),
migrations.AddField(
model_name='lead',
name='year',
field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Year'),
),
]

View File

@ -1,14 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-09 08:16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0012_merge_20250206_1308'),
('inventory', '0014_remove_lead_car_lead_id_car_make_lead_id_car_model_and_more'),
]
operations = [
]

View File

@ -1,19 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-09 08:23
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0015_merge_20250209_1116'),
]
operations = [
migrations.AddField(
model_name='schedule',
name='duration',
field=models.DurationField(default=datetime.timedelta(seconds=300)),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-09 11:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0016_schedule_duration'),
]
operations = [
migrations.AddField(
model_name='car',
name='hash',
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='Hash'),
),
]

View File

@ -1,22 +0,0 @@
# Generated by Django 5.1.5 on 2025-02-11 00:23
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0017_car_hash'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='customer',
name='user',
field=models.OneToOneField(default=4, on_delete=django.db.models.deletion.CASCADE, related_name='customer_profile', to=settings.AUTH_USER_MODEL),
preserve_default=False,
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-12 10:26
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0018_customer_user'),
]
operations = [
migrations.AddField(
model_name='opportunity',
name='lead',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='inventory.lead'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-12 10:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0019_opportunity_lead'),
]
operations = [
migrations.AlterField(
model_name='opportunity',
name='closing_date',
field=models.DateField(blank=True, null=True, verbose_name='Closing Date'),
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-12 10:37
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0020_alter_opportunity_closing_date'),
]
operations = [
migrations.AlterField(
model_name='opportunity',
name='lead',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunity', to='inventory.lead'),
),
]

View File

@ -1,21 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-12 14:09
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_ESTIMATE_MODEL),
('inventory', '0021_alter_opportunity_lead'),
]
operations = [
migrations.AddField(
model_name='opportunity',
name='estimate',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='opportunity', to=settings.DJANGO_LEDGER_ESTIMATE_MODEL),
),
]

View File

@ -1,36 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-13 09:01
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0002_remove_content_type_name'),
('inventory', '0022_opportunity_estimate'),
]
operations = [
migrations.CreateModel(
name='Email',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField()),
('from_email', models.TextField(blank=True, null=True, verbose_name='From Email')),
('to_email', models.TextField(blank=True, null=True, verbose_name='To Email')),
('subject', models.TextField(blank=True, null=True, verbose_name='Subject')),
('body', models.TextField(blank=True, null=True, verbose_name='Body')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='emails_created', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Email',
'verbose_name_plural': 'Emails',
},
),
]

View File

@ -1,22 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-13 09:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0023_email'),
]
operations = [
migrations.RemoveField(
model_name='email',
name='body',
),
migrations.AddField(
model_name='email',
name='message',
field=models.TextField(blank=True, null=True, verbose_name='Message'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-13 09:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0024_remove_email_body_email_message'),
]
operations = [
migrations.AddField(
model_name='email',
name='status',
field=models.CharField(choices=[('SENT', 'Sent'), ('FAILED', 'Failed'), ('DELIVERED', 'Delivered'), ('OPEN', 'Open'), ('DRAFT', 'Draft')], default='OPEN', max_length=20, verbose_name='Status'),
),
]

View File

@ -1,29 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-17 08:54
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0025_email_status'),
]
operations = [
migrations.CreateModel(
name='CarHistory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event_date', models.DateField()),
('event_type', models.CharField(choices=[('PURCHASE', 'Purchase'), ('SALE', 'Sale'), ('TRANSFER', 'Transfer'), ('ACCIDENT', 'Accident'), ('MAINTENANCE', 'Maintenance'), ('SERVICE', 'Service'), ('OTHER', 'Other')], max_length=50)),
('description', models.TextField(blank=True, null=True)),
('mileage', models.IntegerField(blank=True, null=True)),
('cost', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='history', to='inventory.car')),
],
options={
'ordering': ['-event_date'],
},
),
]

View File

@ -1,20 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-17 08:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0026_carhistory'),
]
operations = [
migrations.AddField(
model_name='carhistory',
name='dealer',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='history', to='inventory.dealer'),
preserve_default=False,
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-17 09:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0027_carhistory_dealer'),
]
operations = [
migrations.AddField(
model_name='carhistory',
name='additional_info',
field=models.JSONField(blank=True, default=dict, null=True, verbose_name='Car History Additional Info'),
),
]

View File

@ -1,21 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-17 09:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0028_carhistory_additional_info'),
]
operations = [
migrations.RemoveField(
model_name='carhistory',
name='cost',
),
migrations.RemoveField(
model_name='carhistory',
name='mileage',
),
]

View File

@ -1,21 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-17 09:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0029_remove_carhistory_cost_remove_carhistory_mileage'),
]
operations = [
migrations.AlterField(
model_name='activity',
name='activity_type',
field=models.CharField(choices=[('call', 'Call'), ('sms', 'SMS'), ('email', 'Email'), ('whatsapp', 'WhatsApp'), ('visit', 'Visit'), ('add_car', 'Add Car'), ('sale_car', 'Sale Car'), ('reserve_car', 'Reserve Car'), ('transfer_car', 'Transfer Car'), ('remove_car', 'Remove Car'), ('create_quotation', 'Create Quotation'), ('cancel_quotation', 'Cancel Quotation'), ('create_order', 'Create Order'), ('cancel_order', 'Cancel Order'), ('create_invoice', 'Create Invoice'), ('cancel_invoice', 'Cancel Invoice')], max_length=50, verbose_name='Activity Type'),
),
migrations.DeleteModel(
name='CarHistory',
),
]

View File

@ -1,33 +0,0 @@
# Generated by Django 5.1.5 on 2025-02-17 14:05
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('inventory', '0030_alter_activity_activity_type_delete_carhistory'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='activity',
name='dealer',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='activities', to='inventory.dealer'),
preserve_default=False,
),
migrations.AlterField(
model_name='activity',
name='content_type',
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='contenttypes.contenttype'),
),
migrations.AlterField(
model_name='activity',
name='created_by',
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='activities_created_by', to=settings.AUTH_USER_MODEL),
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 5.1.6 on 2025-02-17 17:40
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0031_activity_dealer_alter_activity_content_type_and_more'),
]
operations = [
migrations.AlterField(
model_name='carcolors',
name='car',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.car'),
),
]

View File

@ -1,32 +0,0 @@
# Generated by Django 5.1.6 on 2025-02-19 05:25
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
# ('appointment', '0002_alter_workinghours_options'),
('inventory', '0032_alter_carcolors_car'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name='staff',
name='user',
),
migrations.AddField(
model_name='staff',
name='staff_member',
field=models.OneToOneField(default=5, on_delete=django.db.models.deletion.CASCADE, related_name='staff', to='appointment.staffmember'),
preserve_default=False,
),
migrations.AlterField(
model_name='schedule',
name='scheduled_by',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -1,38 +0,0 @@
# Generated by Django 5.1.6 on 2025-02-20 01:29
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0033_remove_staff_user_staff_staff_member_and_more'),
]
operations = [
migrations.RemoveField(
model_name='subscription',
name='plan',
),
migrations.RemoveField(
model_name='subscription',
name='users',
),
migrations.RemoveField(
model_name='subscriptionuser',
name='subscription',
),
migrations.RemoveField(
model_name='subscriptionuser',
name='user',
),
migrations.DeleteModel(
name='SubscriptionPlan',
),
migrations.DeleteModel(
name='Subscription',
),
migrations.DeleteModel(
name='SubscriptionUser',
),
]

View File

@ -1,25 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-20 08:16
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
# ('inventory', '0037_alter_schedule_scheduled_type'),
('inventory', '0034_remove_subscription_plan_remove_subscription_users_and_more'),
]
operations = [
migrations.CreateModel(
name='CustomGroup',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer')),
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.group', verbose_name='')),
],
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-20 08:17
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0038_customgroup'),
]
operations = [
migrations.AlterField(
model_name='customgroup',
name='dealer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='groups', to='inventory.dealer'),
),
]

View File

@ -1,33 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-23 14:31
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_ACCOUNT_MODEL),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('inventory', '0039_alter_customgroup_dealer'),
]
operations = [
migrations.CreateModel(
name='UserSettings',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('language', models.CharField(choices=[('en', 'English'), ('ar', 'Arabic')], default='ar', max_length=20)),
('theme', models.CharField(choices=[('default', 'Default'), ('dark', 'Dark')], default='default', max_length=20)),
('additional_info', models.JSONField(default=dict)),
('bill_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_ca', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('bill_payable_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_payable', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('bill_prepaid_expense_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_prepaid_expense', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('invoice_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_ca', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('invoice_payable_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_payable', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('invoice_prepaid_expense_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_prepaid_expense', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-23 14:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0040_usersettings'),
]
operations = [
migrations.AlterField(
model_name='usersettings',
name='additional_info',
field=models.JSONField(blank=True, default=dict, null=True),
),
]

View File

@ -1,21 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-23 15:34
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('inventory', '0041_alter_usersettings_additional_info'),
]
operations = [
migrations.AlterField(
model_name='usersettings',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='settings', to=settings.AUTH_USER_MODEL),
),
]

View File

@ -1,21 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-23 16:23
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('inventory', '0042_alter_usersettings_user'),
]
operations = [
migrations.AlterField(
model_name='usersettings',
name='user',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='settings', to=settings.AUTH_USER_MODEL),
),
]

View File

@ -1,33 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-23 16:31
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_ACCOUNT_MODEL),
('inventory', '0043_alter_usersettings_user'),
]
operations = [
migrations.CreateModel(
name='DealerSettings',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('additional_info', models.JSONField(blank=True, default=dict, null=True)),
('bill_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_ca', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('bill_payable_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_payable', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('bill_prepaid_expense_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_prepaid_expense', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('dealer', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='settings', to='inventory.dealer')),
('invoice_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_ca', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('invoice_payable_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_payable', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
('invoice_prepaid_expense_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_prepaid_expense', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL)),
],
),
migrations.DeleteModel(
name='UserSettings',
),
]

View File

@ -1,39 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-24 09:01
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_ACCOUNT_MODEL),
('inventory', '0044_dealersettings_delete_usersettings'),
]
operations = [
migrations.RemoveField(
model_name='dealersettings',
name='invoice_payable_account',
),
migrations.RemoveField(
model_name='dealersettings',
name='invoice_prepaid_expense_account',
),
migrations.AddField(
model_name='dealersettings',
name='invoice_prepaid_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_prepaid', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL),
),
migrations.AddField(
model_name='dealersettings',
name='invoice_unearned_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_unearned', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL),
),
migrations.AlterField(
model_name='dealersettings',
name='invoice_cash_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_cash', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL),
),
]

View File

@ -1,25 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-24 09:10
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_ACCOUNT_MODEL),
('inventory', '0045_remove_dealersettings_invoice_payable_account_and_more'),
]
operations = [
migrations.RemoveField(
model_name='dealersettings',
name='invoice_prepaid_account',
),
migrations.AddField(
model_name='dealersettings',
name='invoice_recivable_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_recivable', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL),
),
]

View File

@ -1,25 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-24 09:11
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_ACCOUNT_MODEL),
('inventory', '0046_remove_dealersettings_invoice_prepaid_account_and_more'),
]
operations = [
migrations.RemoveField(
model_name='dealersettings',
name='invoice_recivable_account',
),
migrations.AddField(
model_name='dealersettings',
name='invoice_prepaid_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_prepaid', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL),
),
]

View File

@ -1,39 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-24 09:14
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.DJANGO_LEDGER_ACCOUNT_MODEL),
('inventory', '0047_remove_dealersettings_invoice_recivable_account_and_more'),
]
operations = [
migrations.RemoveField(
model_name='dealersettings',
name='bill_payable_account',
),
migrations.RemoveField(
model_name='dealersettings',
name='bill_prepaid_expense_account',
),
migrations.AddField(
model_name='dealersettings',
name='bill_prepaid_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_prepaid', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL),
),
migrations.AddField(
model_name='dealersettings',
name='bill_unearned_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_unearned', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL),
),
migrations.AlterField(
model_name='dealersettings',
name='bill_cash_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_cash', to=settings.DJANGO_LEDGER_ACCOUNT_MODEL),
),
]

View File

@ -1,33 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-26 08:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0048_remove_dealersettings_bill_payable_account_and_more'),
]
operations = [
migrations.AlterField(
model_name='lead',
name='status',
field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], db_index=True, default='new', max_length=50, verbose_name='Status'),
),
migrations.AlterField(
model_name='leadstatushistory',
name='new_status',
field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], max_length=50, verbose_name='New Status'),
),
migrations.AlterField(
model_name='leadstatushistory',
name='old_status',
field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], max_length=50, verbose_name='Old Status'),
),
migrations.AlterField(
model_name='opportunity',
name='status',
field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], default='new', max_length=20, verbose_name='Status'),
),
]

View File

@ -1,22 +0,0 @@
# Generated by Django 5.1.6 on 2025-02-24 17:25
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0048_remove_dealersettings_bill_payable_account_and_more'),
migrations.swappable_dependency(settings.DJANGO_LEDGER_CUSTOMER_MODEL),
]
operations = [
migrations.AddField(
model_name='carreservation',
name='reserved_for',
field=models.ForeignKey(default='dd747dc3-39bc-411f-a17d-c930a50220fe', on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL, verbose_name='Reserved For'),
preserve_default=False,
),
]

View File

@ -1,17 +0,0 @@
# Generated by Django 5.1.6 on 2025-02-25 01:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0049_carreservation_reserved_for'),
]
operations = [
migrations.RemoveField(
model_name='carreservation',
name='reserved_for',
),
]

View File

@ -1,14 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-26 13:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0049_alter_lead_status_alter_leadstatushistory_new_status_and_more'),
('inventory', '0050_remove_carreservation_reserved_for'),
]
operations = [
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-02-27 15:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0051_merge_20250226_1654'),
]
operations = [
migrations.AddField(
model_name='lead',
name='lead_type',
field=models.CharField(choices=[('customer', 'Customer'), ('organization', 'Organization')], default='customer', max_length=50, verbose_name='Lead Type'),
),
]

View File

@ -1,23 +0,0 @@
# Generated by Django 4.2.17 on 2025-03-01 21:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0052_lead_lead_type'),
]
operations = [
migrations.AddField(
model_name='lead',
name='crn',
field=models.CharField(blank=True, max_length=10, null=True, unique=True, verbose_name='Commercial Registration Number'),
),
migrations.AddField(
model_name='lead',
name='vrn',
field=models.CharField(blank=True, max_length=15, null=True, unique=True, verbose_name='VAT Registration Number'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-03-04 01:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0053_lead_crn_lead_vrn'),
]
operations = [
migrations.AlterField(
model_name='staff',
name='staff_type',
field=models.CharField(choices=[('inventory', 'Inventory'), ('accountant', 'Accountant'), ('sales', 'Sales')], max_length=255, verbose_name='Staff Type'),
),
]

View File

@ -1,26 +0,0 @@
# Generated by Django 5.1.6 on 2025-03-03 16:59
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0053_lead_crn_lead_vrn'),
]
operations = [
migrations.CreateModel(
name='DealersMake',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('added_at', models.DateTimeField(auto_now_add=True)),
('car_make', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='car_dealers', to='inventory.carmake')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dealer_makes', to='inventory.dealer')),
],
options={
'unique_together': {('dealer', 'car_make')},
},
),
]

View File

@ -1,14 +0,0 @@
# Generated by Django 4.2.17 on 2025-03-04 21:15
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0054_alter_staff_staff_type'),
('inventory', '0054_dealersmake'),
]
operations = [
]

View File

@ -210,6 +210,11 @@ def dealer_signup(request, *args, **kwargs):
user = User.objects.create(username=email, email=email)
user.set_password(password)
user.save()
group = Group.objects.create(name=f'{user.pk}-Admin')
user.groups.add(group)
for perm in Permission.objects.filter(content_type__app_label__in=["inventory","django_ledger"]):
group.permissions.add(perm)
StaffMember.objects.create(user=user)
models.Dealer.objects.create(
user=user,
@ -1300,7 +1305,7 @@ class CustomerDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView)
return context
@login_required
def add_note_to_customer(request, customer_id):
def add_note_to_customer(request, customer_id):
customer = get_object_or_404(CustomerModel, uuid=customer_id)
if request.method == "POST":
form = forms.NoteForm(request.POST)
@ -2721,7 +2726,7 @@ class UserActivityLogListView(LoginRequiredMixin,ListView):
queryset = super().get_queryset()
if "user" in self.request.GET:
queryset = queryset.filter(user__email=self.request.GET["user"])
return queryset
return queryset[:100] # will update later with better pagination
# CRM RELATED VIEWS

View File

@ -23,7 +23,7 @@ certifi==2025.1.31
cffi==1.17.1
chardet==5.2.0
charset-normalizer==3.4.1
click==8.1.8
click
colorama==0.4.6
commonmark==0.9.1
contourpy==1.3.1
@ -55,7 +55,7 @@ django-extensions==3.2.3
django-filter==25.1
django-formtools==2.5.1
django-import-export==4.3.5
django-ledger==0.7.4.1
django-ledger
django-model-utils==5.0.0
django-money==3.5.3
django-next-url-mixin==0.4.0
@ -89,7 +89,6 @@ et_xmlfile==2.0.0
Faker==36.1.1
filelock==3.17.0
fire==0.7.0
Flask==3.1.0
fonttools==4.56.0
fpdf2==2.8.2
frozenlist==1.5.0
@ -151,22 +150,16 @@ packaging==24.2
pandas==2.2.3
pango==0.0.1
pdfkit==1.0.0
phonenumbers==8.13.55
pillow==11.1.0
platformdirs==4.3.6
prometheus_client==0.21.1
propcache==0.2.1
protobuf==5.29.3
psycopg==3.2.4
psycopg-binary==3.2.4
psycopg-c==3.2.4
py-moneyed==3.0
PyAutoGUI==0.9.54
pyclipper==1.3.0.post6
pycodestyle==2.12.1
pycparser==2.22
pydantic==2.10.6
pydantic_core==2.29.0
pydotplus==2.0.2
pydyf==0.11.0
PyGetWindow==0.0.9
@ -174,10 +167,6 @@ Pygments==2.19.1
PyJWT==2.10.1
pylint==3.3.4
PyMsgBox==1.0.9
PyMySQL==1.1.1
pyobjc-core==11.0
pyobjc-framework-Cocoa==11.0
pyobjc-framework-Quartz==11.0
pyparsing==3.2.1
pypdf==5.3.0
PyPDF2==3.0.1
@ -198,8 +187,6 @@ pytweening==1.2.0
pytz==2025.1
pyvin==0.0.2
pywa==2.7.0
pywhat==5.1.0
pywhatkit==5.4
PyYAML==6.0.2
pyzbar==0.1.9
qrcode==8.0
@ -229,7 +216,6 @@ sqlparse==0.5.3
stanza==1.10.1
stringzilla==3.11.3
suds==1.2.0
swapper==1.4.0
sympy==1.13.1
tablib==3.8.0
termcolor==2.5.0
@ -263,3 +249,5 @@ wsproto==1.2.0
xmlsec==1.3.14
yarl==1.18.3
zopfli==0.2.3.post1
python-dotenv
psycopg2-binary

View File

@ -1,3 +1,5 @@
import os
from dotenv import load_dotenv
from django.contrib.auth.models import Permission
from django.contrib.auth.models import Group
from django_ledger.models.invoice import InvoiceModel
@ -17,6 +19,7 @@ import hashlib
User = get_user_model()
load_dotenv(".env")
def run():
# print(Service.objects.first().pk)
# print(Appointment.objects.first().client)
@ -145,7 +148,8 @@ def run():
# print(Permission.objects.filter(codename__in=['view_car','view_carlocation','view_customcard','view_carcolors','view_cartransfer','view_estimatemodel','view_invoicemodel','view_saleorder']))
# print(Permission.objects.filter(codename__in=['view_estimatemodel','view_invoicemodel','view_saleorder']))
# CustomGroup.objects.filter(name='Accountant').last().set_default_permissions()
CustomGroup.objects.filter(name='Inventory').last().set_default_permissions()
# CustomGroup.objects.filter(name='Inventory').last().set_default_permissions()
# EntityManagementModel.objects.create(entity=,user=)
# print(Permission.objects.filter(codename__icontains='customermodel').first().codename)
print(os.getenv("DJANGO_ALLOWED_HOSTS"))

View File

@ -273,3 +273,7 @@ select.admin-autocomplete {
display: block;
padding: 6px;
}
.errors .select2-selection {
border: 1px solid var(--error-fg);
}

View File

@ -13,6 +13,7 @@ html[data-theme="light"],
--body-fg: #333;
--body-bg: #fff;
--body-quiet-color: #666;
--body-medium-color: #444;
--body-loud-color: #000;
--header-color: #ffc;
@ -22,11 +23,11 @@ html[data-theme="light"],
--breadcrumbs-fg: #c4dce8;
--breadcrumbs-link-fg: var(--body-bg);
--breadcrumbs-bg: var(--primary);
--breadcrumbs-bg: #264b5d;
--link-fg: #417893;
--link-hover-color: #036;
--link-selected-fg: #5b80b2;
--link-selected-fg: var(--secondary);
--hairline-color: #e8e8e8;
--border-color: #ccc;
@ -42,10 +43,10 @@ html[data-theme="light"],
--selected-row: #ffc;
--button-fg: #fff;
--button-bg: var(--primary);
--button-hover-bg: #609ab6;
--default-button-bg: var(--secondary);
--default-button-hover-bg: #205067;
--button-bg: var(--secondary);
--button-hover-bg: #205067;
--default-button-bg: #205067;
--default-button-hover-bg: var(--secondary);
--close-button-bg: #747474;
--close-button-hover-bg: #333;
--delete-button-bg: #ba2121;
@ -56,8 +57,6 @@ html[data-theme="light"],
--object-tools-hover-bg: var(--close-button-hover-bg);
--font-family-primary:
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
system-ui,
Roboto,
@ -86,6 +85,8 @@ html[data-theme="light"],
"Segoe UI Emoji",
"Segoe UI Symbol",
"Noto Color Emoji";
color-scheme: light;
}
html, body {
@ -149,7 +150,6 @@ h1 {
margin: 0 0 20px;
font-weight: 300;
font-size: 1.25rem;
color: var(--body-quiet-color);
}
h2 {
@ -165,7 +165,7 @@ h2.subhead {
h3 {
font-size: 0.875rem;
margin: .8em 0 .3em 0;
color: var(--body-quiet-color);
color: var(--body-medium-color);
font-weight: bold;
}
@ -173,6 +173,7 @@ h4 {
font-size: 0.75rem;
margin: 1em 0 .8em 0;
padding-bottom: 3px;
color: var(--body-medium-color);
}
h5 {
@ -219,6 +220,10 @@ fieldset {
border-top: 1px solid var(--hairline-color);
}
details summary {
cursor: pointer;
}
blockquote {
font-size: 0.6875rem;
color: #777;
@ -315,7 +320,7 @@ td, th {
}
th {
font-weight: 600;
font-weight: 500;
text-align: left;
}
@ -336,7 +341,7 @@ tfoot td {
}
thead th.required {
color: var(--body-loud-color);
font-weight: bold;
}
tr.alt {
@ -484,8 +489,13 @@ textarea {
vertical-align: top;
}
input[type=text], input[type=password], input[type=email], input[type=url],
input[type=number], input[type=tel], textarea, select, .vTextField {
/*
Minifiers remove the default (text) "type" attribute from "input" HTML tags.
Add input:not([type]) to make the CSS stylesheet work the same.
*/
input:not([type]), input[type=text], input[type=password], input[type=email],
input[type=url], input[type=number], input[type=tel], textarea, select,
.vTextField {
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 5px 6px;
@ -494,9 +504,13 @@ input[type=number], input[type=tel], textarea, select, .vTextField {
background-color: var(--body-bg);
}
input[type=text]:focus, input[type=password]:focus, input[type=email]:focus,
input[type=url]:focus, input[type=number]:focus, input[type=tel]:focus,
textarea:focus, select:focus, .vTextField:focus {
/*
Minifiers remove the default (text) "type" attribute from "input" HTML tags.
Add input:not([type]) to make the CSS stylesheet work the same.
*/
input:not([type]):focus, input[type=text]:focus, input[type=password]:focus,
input[type=email]:focus, input[type=url]:focus, input[type=number]:focus,
input[type=tel]:focus, textarea:focus, select:focus, .vTextField:focus {
border-color: var(--body-quiet-color);
}
@ -586,7 +600,7 @@ input[type=button][disabled].default {
font-weight: 400;
font-size: 0.8125rem;
text-align: left;
background: var(--primary);
background: var(--header-bg);
color: var(--header-link-color);
}
@ -722,6 +736,11 @@ div.breadcrumbs a:focus, div.breadcrumbs a:hover {
background: url(../img/icon-viewlink.svg) 0 1px no-repeat;
}
.hidelink {
padding-left: 16px;
background: url(../img/icon-hidelink.svg) 0 1px no-repeat;
}
.addlink {
padding-left: 16px;
background: url(../img/icon-addlink.svg) 0 1px no-repeat;
@ -831,10 +850,6 @@ a.deletelink:focus, a.deletelink:hover {
height: 100%;
}
#container > div {
flex-shrink: 0;
}
#container > .main {
display: flex;
flex: 1 0 auto;
@ -879,9 +894,10 @@ a.deletelink:focus, a.deletelink:hover {
margin-right: -300px;
}
#footer {
clear: both;
padding: 10px;
@media (forced-colors: active) {
#content-related {
border: 1px solid;
}
}
/* COLUMN TYPES */
@ -919,7 +935,6 @@ a.deletelink:focus, a.deletelink:hover {
padding: 10px 40px;
background: var(--header-bg);
color: var(--header-color);
overflow: hidden;
}
#header a:link, #header a:visited, #logout-form button {
@ -930,11 +945,17 @@ a.deletelink:focus, a.deletelink:hover {
text-decoration: underline;
}
@media (forced-colors: active) {
#header {
border-bottom: 1px solid;
}
}
#branding {
display: flex;
}
#branding h1 {
#site-name {
padding: 0;
margin: 0;
margin-inline-end: 20px;
@ -943,7 +964,7 @@ a.deletelink:focus, a.deletelink:hover {
color: var(--header-branding-color);
}
#branding h1 a:link, #branding h1 a:visited {
#site-name a:link, #site-name a:visited {
color: var(--accent);
}
@ -1143,3 +1164,16 @@ a.deletelink:focus, a.deletelink:hover {
.base-svgs {
display: none;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
color: var(--body-fg);
background-color: var(--body-bg);
}

View File

@ -139,6 +139,12 @@
margin: 0 0 0 30px;
}
@media (forced-colors: active) {
#changelist-filter {
border: 1px solid;
}
}
#changelist-filter h2 {
font-size: 0.875rem;
text-transform: uppercase;
@ -215,9 +221,9 @@
color: var(--link-hover-color);
}
#changelist-filter #changelist-filter-clear a {
#changelist-filter #changelist-filter-extra-actions {
font-size: 0.8125rem;
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px solid var(--hairline-color);
}
@ -265,6 +271,15 @@
background-color: var(--selected-row);
}
@media (forced-colors: active) {
#changelist tbody tr.selected {
background-color: SelectedItem;
}
#changelist tbody tr:has(.action-select:checked) {
background-color: SelectedItem;
}
}
#changelist .actions {
padding: 10px;
background: var(--body-bg);

View File

@ -5,7 +5,8 @@
--body-fg: #eeeeee;
--body-bg: #121212;
--body-quiet-color: #e0e0e0;
--body-quiet-color: #d0d0d0;
--body-medium-color: #e0e0e0;
--body-loud-color: #ffffff;
--breadcrumbs-link-fg: #e0e0e0;
@ -29,6 +30,8 @@
--close-button-bg: #333333;
--close-button-hover-bg: #666666;
color-scheme: dark;
}
}
@ -39,7 +42,8 @@ html[data-theme="dark"] {
--body-fg: #eeeeee;
--body-bg: #121212;
--body-quiet-color: #e0e0e0;
--body-quiet-color: #d0d0d0;
--body-medium-color: #e0e0e0;
--body-loud-color: #ffffff;
--breadcrumbs-link-fg: #e0e0e0;
@ -63,6 +67,8 @@ html[data-theme="dark"] {
--close-button-bg: #333333;
--close-button-hover-bg: #666666;
color-scheme: dark;
}
/* THEME SWITCH */
@ -122,16 +128,3 @@ html[data-theme="dark"] .theme-toggle svg.theme-icon-when-dark {
html[data-theme="light"] .theme-toggle svg.theme-icon-when-light {
display: block;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
color: var(--body-fg);
background-color: var(--body-bg);
}

View File

@ -44,7 +44,6 @@ label {
.required label, label.required {
font-weight: bold;
color: var(--body-fg);
}
/* RADIO BUTTONS */
@ -76,6 +75,20 @@ form ul.inline li {
padding-right: 7px;
}
/* FIELDSETS */
fieldset .fieldset-heading,
fieldset .inline-heading,
:not(.inline-related) .collapse summary {
border: 1px solid var(--header-bg);
margin: 0;
padding: 8px;
font-weight: 400;
font-size: 0.8125rem;
background: var(--header-bg);
color: var(--header-link-color);
}
/* ALIGNED FIELDSETS */
.aligned label {
@ -84,14 +97,12 @@ form ul.inline li {
min-width: 160px;
width: 160px;
word-wrap: break-word;
line-height: 1;
}
.aligned label:not(.vCheckboxLabel):after {
content: '';
display: inline-block;
vertical-align: middle;
height: 1.625rem;
}
.aligned label + p, .aligned .checkbox-row + div.help, .aligned label + div.readonly {
@ -158,6 +169,10 @@ form .aligned select + div.help {
padding-left: 10px;
}
form .aligned select option:checked {
background-color: var(--selected-row);
}
form .aligned ul li {
list-style: none;
}
@ -168,11 +183,7 @@ form .aligned table p {
}
.aligned .vCheckboxLabel {
float: none;
width: auto;
display: inline-block;
vertical-align: -3px;
padding: 0 0 5px 5px;
padding: 1px 0 0 5px;
}
.aligned .vCheckboxLabel + p.help,
@ -194,14 +205,8 @@ fieldset .fieldBox {
width: 200px;
}
form .wide p,
form .wide ul.errorlist,
form .wide input + p.help,
form .wide input + div.help {
margin-left: 200px;
}
form .wide p.help,
form .wide ul.errorlist,
form .wide div.help {
padding-left: 50px;
}
@ -215,35 +220,16 @@ form div.help ul {
width: 450px;
}
/* COLLAPSED FIELDSETS */
/* COLLAPSIBLE FIELDSETS */
fieldset.collapsed * {
display: none;
}
fieldset.collapsed h2, fieldset.collapsed {
display: block;
}
fieldset.collapsed {
border: 1px solid var(--hairline-color);
border-radius: 4px;
overflow: hidden;
}
fieldset.collapsed h2 {
background: var(--darkened-bg);
color: var(--body-quiet-color);
}
fieldset .collapse-toggle {
color: var(--header-link-color);
}
fieldset.collapsed .collapse-toggle {
.collapse summary .fieldset-heading,
.collapse summary .inline-heading {
background: transparent;
border: none;
color: currentColor;
display: inline;
color: var(--link-fg);
margin: 0;
padding: 0;
}
/* MONOSPACE TEXTAREAS */
@ -395,14 +381,16 @@ body.popup .submit-row {
position: relative;
}
.inline-related h3 {
.inline-related h4,
.inline-related:not(.tabular) .collapse summary {
margin: 0;
color: var(--body-quiet-color);
color: var(--body-medium-color);
padding: 5px;
font-size: 0.8125rem;
background: var(--darkened-bg);
border-top: 1px solid var(--hairline-color);
border-bottom: 1px solid var(--hairline-color);
border: 1px solid var(--hairline-color);
border-left-color: var(--darkened-bg);
border-right-color: var(--darkened-bg);
}
.inline-related h3 span.delete {
@ -421,16 +409,6 @@ body.popup .submit-row {
width: 100%;
}
.inline-related fieldset.module h3 {
margin: 0;
padding: 2px 5px 3px 5px;
font-size: 0.6875rem;
text-align: left;
font-weight: bold;
background: #bcd;
color: var(--body-bg);
}
.inline-group .tabular fieldset.module {
border: none;
}

View File

@ -21,7 +21,7 @@
}
.login #content {
padding: 20px 20px 0;
padding: 20px;
}
.login #container {

Some files were not shown because too many files have changed in this diff Show More