This commit is contained in:
Marwan Alwali 2025-02-02 11:04:08 +03:00
parent e3c1810b4a
commit a4433631fb
160 changed files with 19527492 additions and 2285 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
.idea/.DS_Store generated vendored

Binary file not shown.

View File

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

View File

@ -5,6 +5,7 @@ from django.conf import settings
from django.conf.urls.i18n import i18n_patterns
from inventory import views
import debug_toolbar
from two_factor.urls import urlpatterns as tf_urls
urlpatterns = [
path('__debug__/', include(debug_toolbar.urls)),
@ -24,7 +25,8 @@ urlpatterns += i18n_patterns(
path('ledger/', include('django_ledger.urls', namespace='django_ledger')),
path("haikalbot/", include("haikalbot.urls")),
path('appointment/', include('appointment.urls')),
# path('plans/', include('plans.urls')),
path('plans/', include('plans.urls')),
path('', include(tf_urls)),
)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

File diff suppressed because it is too large Load Diff

4112
carmake_updated_backup.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,830 @@
[
{
"model": "inventory.carspecification",
"pk": 1,
"fields": {
"name": "Bodywork",
"arabic_name": "",
"id_parent": null
}
},
{
"model": "inventory.carspecification",
"pk": 2,
"fields": {
"name": "Body type",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 3,
"fields": {
"name": "Number of doors",
"arabic_name": "",
"id_parent": 1549
}
},
{
"model": "inventory.carspecification",
"pk": 4,
"fields": {
"name": "Number of seater",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 5,
"fields": {
"name": "Length",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 6,
"fields": {
"name": "Width",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 7,
"fields": {
"name": "Height",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 8,
"fields": {
"name": "Wheelbase",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 9,
"fields": {
"name": "Front track",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 10,
"fields": {
"name": "Rear track",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 11,
"fields": {
"name": "Engine",
"arabic_name": "",
"id_parent": null
}
},
{
"model": "inventory.carspecification",
"pk": 12,
"fields": {
"name": "Engine type",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 13,
"fields": {
"name": "Capacity",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 14,
"fields": {
"name": "Engine power",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 15,
"fields": {
"name": "Max power at RPM",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 16,
"fields": {
"name": "Maximum torque",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 17,
"fields": {
"name": "Injection type",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 19,
"fields": {
"name": "Cylinder layout",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 20,
"fields": {
"name": "Number of cylinders",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 21,
"fields": {
"name": "Compression ratio",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 22,
"fields": {
"name": "Fuel",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 23,
"fields": {
"name": "Gearbox and handling",
"arabic_name": "",
"id_parent": null
}
},
{
"model": "inventory.carspecification",
"pk": 24,
"fields": {
"name": "Gearbox type",
"arabic_name": "",
"id_parent": 23
}
},
{
"model": "inventory.carspecification",
"pk": 26,
"fields": {
"name": "Number of gear",
"arabic_name": "",
"id_parent": 23
}
},
{
"model": "inventory.carspecification",
"pk": 27,
"fields": {
"name": "Drive wheels",
"arabic_name": "",
"id_parent": 23
}
},
{
"model": "inventory.carspecification",
"pk": 29,
"fields": {
"name": "Front brakes",
"arabic_name": "",
"id_parent": 40
}
},
{
"model": "inventory.carspecification",
"pk": 30,
"fields": {
"name": "Rear brakes",
"arabic_name": "",
"id_parent": 40
}
},
{
"model": "inventory.carspecification",
"pk": 31,
"fields": {
"name": "Operating characteristics",
"arabic_name": "",
"id_parent": null
}
},
{
"model": "inventory.carspecification",
"pk": 32,
"fields": {
"name": "Max speed",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 33,
"fields": {
"name": "Acceleration (0-100 km/h)",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 34,
"fields": {
"name": "Curb weight",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 35,
"fields": {
"name": "Fuel tank capacity",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 36,
"fields": {
"name": "Wheel size",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 37,
"fields": {
"name": "Emission standards",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 38,
"fields": {
"name": "Ground clearance",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 39,
"fields": {
"name": "Valves per cylinder",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 40,
"fields": {
"name": "Suspension and brakes",
"arabic_name": "",
"id_parent": null
}
},
{
"model": "inventory.carspecification",
"pk": 41,
"fields": {
"name": "Front suspension",
"arabic_name": "",
"id_parent": 40
}
},
{
"model": "inventory.carspecification",
"pk": 42,
"fields": {
"name": "Back suspension",
"arabic_name": "",
"id_parent": 40
}
},
{
"model": "inventory.carspecification",
"pk": 44,
"fields": {
"name": "Max trunk capacity",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 45,
"fields": {
"name": "Min trunk capacity",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 46,
"fields": {
"name": "Boost type",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 47,
"fields": {
"name": "Cylinder bore",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 48,
"fields": {
"name": "Stroke cycle",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 50,
"fields": {
"name": "City driving fuel consumption per 100 km",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 51,
"fields": {
"name": "Highway driving fuel consumption per 100 km",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 52,
"fields": {
"name": "Mixed driving fuel consumption per 100 km",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 55,
"fields": {
"name": "Engine model",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 57,
"fields": {
"name": "Turning circle",
"arabic_name": "",
"id_parent": 23
}
},
{
"model": "inventory.carspecification",
"pk": 58,
"fields": {
"name": "Full weight",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 59,
"fields": {
"name": "Disc size",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 61,
"fields": {
"name": "Engine placement",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 62,
"fields": {
"name": "Cruising range",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 66,
"fields": {
"name": "Car width with mirrors",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1549,
"fields": {
"name": "General information",
"arabic_name": "",
"id_parent": null
}
},
{
"model": "inventory.carspecification",
"pk": 1551,
"fields": {
"name": "Safety",
"arabic_name": "",
"id_parent": null
}
},
{
"model": "inventory.carspecification",
"pk": 1552,
"fields": {
"name": "Country",
"arabic_name": "",
"id_parent": 1549
}
},
{
"model": "inventory.carspecification",
"pk": 1553,
"fields": {
"name": "Car class",
"arabic_name": "",
"id_parent": 1549
}
},
{
"model": "inventory.carspecification",
"pk": 1554,
"fields": {
"name": "Clearance",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1555,
"fields": {
"name": "Front track width",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1556,
"fields": {
"name": "Back track width",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1559,
"fields": {
"name": "Max power (kW)",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 1561,
"fields": {
"name": "CO2 emissions",
"arabic_name": "",
"id_parent": 31
}
},
{
"model": "inventory.carspecification",
"pk": 1562,
"fields": {
"name": "Safety assessment",
"arabic_name": "",
"id_parent": 1551
}
},
{
"model": "inventory.carspecification",
"pk": 1563,
"fields": {
"name": "Rating name",
"arabic_name": "",
"id_parent": 1551
}
},
{
"model": "inventory.carspecification",
"pk": 1564,
"fields": {
"name": "Turnover of maximum torque",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 1565,
"fields": {
"name": "Payload",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1566,
"fields": {
"name": "Presence of intercooler",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 1567,
"fields": {
"name": "Trailer load (with brakes)",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1568,
"fields": {
"name": "Front/rear axle load",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1569,
"fields": {
"name": "Loading height",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1570,
"fields": {
"name": "Cargo compartment (Length x Width x Height)",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1571,
"fields": {
"name": "Cargo compartment volume",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1631,
"fields": {
"name": "Accumulator battery",
"arabic_name": "",
"id_parent": null
}
},
{
"model": "inventory.carspecification",
"pk": 1632,
"fields": {
"name": "Battery capacity",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1634,
"fields": {
"name": "Electric power reserve",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1635,
"fields": {
"name": "Charging time",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1641,
"fields": {
"name": "Steering wheel position",
"arabic_name": "",
"id_parent": 1549
}
},
{
"model": "inventory.carspecification",
"pk": 1642,
"fields": {
"name": "Dimensions",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1644,
"fields": {
"name": "Pitch Circle Diameter",
"arabic_name": "",
"id_parent": 1
}
},
{
"model": "inventory.carspecification",
"pk": 1645,
"fields": {
"name": "Engine code",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 1647,
"fields": {
"name": "Battery type",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1648,
"fields": {
"name": "Timing belt",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 1649,
"fields": {
"name": "Flow calculation method",
"arabic_name": "",
"id_parent": 11
}
},
{
"model": "inventory.carspecification",
"pk": 1650,
"fields": {
"name": "Battery temperature",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1651,
"fields": {
"name": "Fast charge time",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1652,
"fields": {
"name": "Description of fast charging",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1653,
"fields": {
"name": "Charging connector type",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1654,
"fields": {
"name": "Consumption",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1655,
"fields": {
"name": "Maximum charging power",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1656,
"fields": {
"name": "Battery capacity (available)",
"arabic_name": "",
"id_parent": 1631
}
},
{
"model": "inventory.carspecification",
"pk": 1657,
"fields": {
"name": "Number of charging cycles",
"arabic_name": "",
"id_parent": 1631
}
}
]

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -9,6 +9,8 @@ from phonenumber_field.phonenumber import PhoneNumber
from .mixins import AddClassMixin
from django.forms.models import inlineformset_factory
from django_ledger.forms.invoice import InvoiceModelCreateForm as InvoiceModelCreateFormBase
from django_ledger.forms.estimate import EstimateModelCreateForm as EstimateModelCreateFormBase
from django_ledger.forms.bill import BillModelCreateForm as BillModelCreateFormBase
from django_ledger.forms.vendor import VendorModelForm
from .models import (
@ -68,21 +70,21 @@ class StaffForm(forms.ModelForm):
model = Staff
fields = ["name", "arabic_name", "phone_number", "staff_type"]
def __init__(self, *args, **kwargs):
user_instance = kwargs.get("instance")
if user_instance and user_instance.user:
initial = kwargs.setdefault("initial", {})
initial["email"] = user_instance.user.email
super().__init__(*args, **kwargs)
def save(self, commit=True):
user_instance = super().save(commit=False)
user = user_instance.user
user.email = self.cleaned_data["email"]
if commit:
user.save()
user_instance.save()
return user_instance
# def __init__(self, *args, **kwargs):
# user_instance = kwargs.get("instance")
# if user_instance and user_instance.user:
# initial = kwargs.setdefault("initial", {})
# initial["email"] = user_instance.user.email
# super().__init__(*args, **kwargs)
#
# def save(self, commit=True):
# user_instance = super().save(commit=False)
# user = user_instance.user
# user.email = self.cleaned_data["email"]
# if commit:
# user.save()
# user_instance.save()
# return user_instance
# Dealer Form
@ -676,6 +678,9 @@ class InvoiceModelCreateForm(InvoiceModelCreateFormBase):
self.fields['unearned_account'].widget = forms.HiddenInput()
self.fields['date_draft'] = forms.DateField(widget=DateInput(attrs={'type': 'date'}))
def get_customer_queryset(self):
if 'customer' in self.fields:
self.fields['customer'].queryset = self.USER_MODEL.dealer.entity.get_customers()
class BillModelCreateForm(BillModelCreateFormBase):
def __init__(self, *args, **kwargs):
@ -689,7 +694,18 @@ class BillModelCreateForm(BillModelCreateFormBase):
class SaleOrderForm(forms.ModelForm):
class Meta:
model = SaleOrder
fields = ['payment_method', 'comments']
fields = ['estimate','payment_method', 'comments']
widgets = {
'comments': forms.Textarea(attrs={'rows': 3}),
}
}
class EstimateModelCreateForm(EstimateModelCreateFormBase):
def __init__(self, *args, entity_slug, user_model, **kwargs):
super(EstimateModelCreateForm, self).__init__(*args, entity_slug=entity_slug, user_model=user_model, **kwargs)
self.ENTITY_SLUG = entity_slug
self.USER_MODEL = user_model
self.fields['customer'].queryset = self.get_customer_queryset()
def get_customer_queryset(self):
return self.USER_MODEL.dealer.entity.get_customers()

View File

@ -1,6 +1,8 @@
import logging
from inventory import models
from django.utils import timezone
from django.shortcuts import redirect
from django.urls import reverse
logger = logging.getLogger('user_activity')
@ -41,4 +43,14 @@ class InjectParamsMiddleware:
response = self.get_response(request)
return response
return response
# class OTPVerificationMiddleware:
# def __init__(self, get_response):
# self.get_response = get_response
#
# def __call__(self, request):
# if request.user.is_authenticated and not request.session.get('otp_verified', False):
# return redirect(reverse('verify_otp'))
# return self.get_response(request)

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.5 on 2025-01-28 15:50
# Generated by Django 5.1.5 on 2025-01-30 11:28
import django.db.models.deletion
import inventory.mixins
@ -546,9 +546,13 @@ class Migration(migrations.Migration):
('payment_method', models.CharField(choices=[('cash', 'Cash'), ('finance', 'Finance'), ('lease', 'Lease')], max_length=20)),
('comments', models.TextField(blank=True, null=True)),
('formatted_order_id', models.CharField(editable=False, max_length=10, unique=True)),
('created', models.DateTimeField(auto_now_add=True)),
('estimate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to=settings.DJANGO_LEDGER_ESTIMATE_MODEL, verbose_name='Estimate')),
('invoice', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to=settings.DJANGO_LEDGER_INVOICE_MODEL, verbose_name='Invoice')),
],
options={
'ordering': ['-created'],
},
),
migrations.CreateModel(
name='Staff',

View File

@ -1,20 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-29 12:59
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('inventory', '0011_alter_saleorder_estimate'),
]
operations = [
migrations.AddField(
model_name='saleorder',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
]

View File

@ -1,17 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-29 13:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0012_saleorder_created'),
]
operations = [
migrations.AlterModelOptions(
name='saleorder',
options={'ordering': ['created']},
),
]

View File

@ -689,10 +689,10 @@ def create_item_model(sender, instance, created, **kwargs):
item_type=ItemModel.ITEM_TYPE_MATERIAL,
uom_model=uom,
coa_model=coa,
# additional_info={}
)
product.additional_info = {}
product.save()
product = entity.get_items_all().filter(name=instance.vin).first()
product.additional_info.update({'car_info': instance.to_dict()})
product.save()
@ -703,6 +703,7 @@ def update_item_model_cost(sender, instance, created, **kwargs):
entity = instance.car.dealer.entity
product = entity.get_items_all().filter(name=instance.car.vin).first()
product.default_amount = instance.selling_price
product.additional_info = {}
product.additional_info.update({"car_finance":instance.to_dict()})
@ -864,3 +865,4 @@ def update_car_status_on_reservation_update(sender, instance, **kwargs):
# items = instance.get_itemtxs_data()[0].all()
# total = sum([Decimal(item.item_model.additional_info['car_finance']["selling_price"]) * Decimal(item.ce_quantity) for item in items])

View File

@ -1,48 +1,45 @@
from django.urls import path
from . import views
from allauth.account import views as allauth_views
from django.conf.urls import handler400, handler403, handler404, handler500
urlpatterns = [
# main URLs
path("", views.HomeView.as_view(), name="landing_page"),
path("", views.HomeView.as_view(), name="home"),
path("welcome/", views.WelcomeView.as_view(), name="welcome"),
# Accounts URLs
path("login/", views.Login.as_view(), name="account_login"),
path(
"logout/",
allauth_views.LogoutView.as_view(template_name="account/logout.html"),
name="account_logout",
),
# path("login/", allauth_views.LoginView.as_view(template_name="account/login.html"), name="account_login"),
# path(
# "logout/",
# allauth_views.LogoutView.as_view(template_name="account/logout.html"),
# name="account_logout",
# ),
# path('signup/', allauth_views.SignupView.as_view(template_name='account/signup.html'), name='account_signup'),
path("signup/", views.dealer_signup, name="account_signup"),
path(
"password/change/",
allauth_views.PasswordChangeView.as_view(
template_name="account/password_change.html"
),
name="account_change_password",
),
path(
"password/reset/",
allauth_views.PasswordResetView.as_view(
template_name="account/password_reset.html"
),
name="account_reset_password",
),
path(
"password/reset/done/",
allauth_views.PasswordResetDoneView.as_view(
template_name="account/password_reset_done.html"
),
name="account_password_reset_done",
),
path(
"login/code/",
allauth_views.RequestLoginCodeView.as_view(
template_name="account/request_login_code.html"
),
),
path("otp", views.OTPView.as_view(), name="otp"),
# path(
# "password/change/", allauth_views.PasswordChangeView.as_view(template_name="account/password_change.html"), name="account_change_password",
# ),
# path(
# "password/reset/",
# allauth_views.PasswordResetView.as_view(
# template_name="account/password_reset.html"
# ),
# name="account_reset_password",
# ),
# path(
# "accounts/password/reset/done/",
# allauth_views.PasswordResetDoneView.as_view(
# template_name="account/password_reset_done.html"
# ),
# name="account_password_reset_done",
# ),
# path(
# "accounts/login/code/",
# allauth_views.RequestLoginCodeView.as_view(
# template_name="account/request_login_code.html"
# ),
# ),
# Dashboards
path(
"dashboards/accounting/", views.AccountingDashboard.as_view(), name="accounting"
@ -147,7 +144,7 @@ urlpatterns = [
),
# Vendor URLs
path("vendors", views.VendorListView.as_view(), name="vendor_list"),
path("vendors/<uuid:pk>/", views.VendorDetailView.as_view(), name="vendor_detail"),
path("vendors/<uuid:pk>/", views.vendorDetailView, name="vendor_detail"),
path("vendors/create/", views.VendorCreateView.as_view(), name="vendor_create"),
path(
"vendors/<uuid:pk>/update/",

View File

@ -73,7 +73,11 @@ from django.urls import reverse, reverse_lazy
from django.contrib import messages
from django.db.models import Sum, F, Count
from django.db import transaction
from two_factor.utils import default_device
from two_factor.views import OTPRequiredMixin
from .forms import VendorForm
from .services import (
decodevin,
get_make,
@ -98,7 +102,7 @@ from .utils import (
transfer_car,
)
from django.contrib.auth.models import User
from allauth.account import views
from allauth.account import views as allauth_views
from django.db.models import Count, F, Value
from django.contrib.auth import authenticate
import cv2
@ -107,6 +111,7 @@ from pyzbar.pyzbar import decode
from django.core.files.storage import default_storage
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
@ -183,13 +188,14 @@ def dealer_signup(request, *args, **kwargs):
phone_number=phone,
address=address,
)
user = authenticate(request, email=email, password=password)
if user is not None:
return JsonResponse(
{"message": "User created successfully."}, status=200
)
else:
return JsonResponse({"error": "User creation failed."}, status=400)
# user = authenticate(request, email=email, password=password)
# if user is not None:
# return JsonResponse(
# {"message": "User created successfully."}, status=200
# )
# else:
# return JsonResponse({"error": "User creation failed."}, status=400)
# return redirect("account_login")
except Exception as e:
return JsonResponse({"error": str(e)}, status=400)
@ -204,20 +210,41 @@ def dealer_signup(request, *args, **kwargs):
)
class Login(views.LoginView):
template_name = "account/login.html"
redirect_authenticated_user = True
# class Login(allauth_views.LoginView):
# template_name = "account/login.html"
# redirect_authenticated_user = True
class OTPView(View, LoginRequiredMixin):
template_name = "account/otp_verification.html"
def get(self, request, *args, **kwargs):
# device = default_device(request.user)
# device.generate_challenge()
return render(request, self.template_name)
def post(self, request, *args, **kwargs):
otp_code = request.POST.get("otp_code")
if self.verify_otp(otp_code, request.user):
messages.success(request, _("OTP verified successfully!"))
return redirect("home")
messages.error(request, _("Invalid OTP. Please try again."))
return render(request, self.template_name)
def verify_otp(self, otp_code, user):
device = default_device(user)
if device and device.verify_token(otp_code):
return True
return False
class HomeView(TemplateView):
template_name = "index.html"
def dispatch(self, request, *args, **kwargs):
if (
# not any(hasattr(request.user, attr) for attr in ["dealer", "subdealer"])
not request.user.is_authenticated
):
# messages.error(request, _("You are not associated with any dealer."))
if not request.user.is_authenticated:
return redirect("welcome")
return super().dispatch(request, *args, **kwargs)
@ -1204,13 +1231,17 @@ class VendorListView(LoginRequiredMixin, ListView):
def get_queryset(self):
dealer = get_user_type(self.request)
# vendors = models.Vendor.objects.filter(dealer=dealer)
return dealer.entity.get_vendors().filter(active=True)
# return vendors
class VendorDetailView(LoginRequiredMixin, DetailView):
model = VendorModel
template_name = "vendors/view_vendor.html"
# class VendorDetailView(LoginRequiredMixin, DetailView):
# model = models.Vendor
# template_name = "vendors/view_vendor.html"
def vendorDetailView(request, pk):
vendor = get_object_or_404(models.Vendor, pk=pk)
return render(request, template_name="vendors/view_vendor.html", context={"vendor": vendor})
class VendorCreateView(
@ -1218,7 +1249,7 @@ class VendorCreateView(
SuccessMessageMixin,
CreateView,
):
model = VendorModel
model = models.Vendor
form_class = forms.VendorForm
template_name = "vendors/vendor_form.html"
success_url = reverse_lazy("vendor_list")
@ -1226,9 +1257,10 @@ class VendorCreateView(
def form_valid(self, form):
dealer = get_user_type(self.request)
instance = form.save(commit=False)
instance.entity_model = dealer.entity
instance.save()
form.instance.dealer = dealer
# instance = form.save(commit=False)
# instance.entity_model = dealer.entity
form.instance.save()
return super().form_valid(form)
@ -1237,7 +1269,7 @@ class VendorUpdateView(
SuccessMessageMixin,
UpdateView,
):
model = VendorModel
model = models.Vendor
form_class = forms.VendorForm
template_name = "vendors/vendor_form.html"
success_url = reverse_lazy("vendor_list")
@ -1246,149 +1278,149 @@ class VendorUpdateView(
@login_required
def delete_vendor(request, pk):
vendor = get_object_or_404(VendorModel, pk=pk)
vendor.active = False
vendor.save()
vendor = get_object_or_404(models.Vendor, pk=pk)
# vendor.active = False
vendor.delete()
messages.success(request, _("Vendor deleted successfully."))
return redirect("vendor_list")
class QuotationCreateView(LoginRequiredMixin, CreateView):
model = models.SaleQuotation
form_class = forms.QuotationForm
template_name = "sales/quotation_form.html"
def form_valid(self, form):
form.instance.dealer = get_user_type(self.request)
quotation = form.save()
selected_cars = form.cleaned_data.get("cars")
for car in selected_cars:
car_finance = car.finances
if car_finance:
models.SaleQuotationCar.objects.create(
quotation=quotation,
car=car,
)
messages.success(self.request, _("Quotation created successfully."))
return redirect("quotation_list")
# class QuotationCreateView(LoginRequiredMixin, CreateView):
# model = models.SaleQuotation
# form_class = forms.QuotationForm
# template_name = "sales/quotation_form.html"
#
# def form_valid(self, form):
# form.instance.dealer = get_user_type(self.request)
# quotation = form.save()
# selected_cars = form.cleaned_data.get("cars")
# for car in selected_cars:
# car_finance = car.finances
# if car_finance:
# models.SaleQuotationCar.objects.create(
# quotation=quotation,
# car=car,
# )
#
# messages.success(self.request, _("Quotation created successfully."))
# return redirect("quotation_list")
class QuotationListView(LoginRequiredMixin, ListView):
model = models.SaleQuotation
template_name = "sales/quotation_list.html"
context_object_name = "quotations"
paginate_by = 10
def get_queryset(self):
status = self.request.GET.get("status")
dealer = get_user_type(self.request)
queryset = dealer.sales.all()
if status:
queryset = queryset.filter(status=status)
return queryset
# class QuotationListView(LoginRequiredMixin, ListView):
# model = models.SaleQuotation
# template_name = "sales/quotation_list.html"
# context_object_name = "quotations"
# paginate_by = 10
#
# def get_queryset(self):
# status = self.request.GET.get("status")
# dealer = get_user_type(self.request)
# queryset = dealer.sales.all()
# if status:
# queryset = queryset.filter(status=status)
# return queryset
class QuotationDetailView(LoginRequiredMixin, DetailView):
model = models.SaleQuotation
template_name = "sales/quotation_detail.html"
context_object_name = "quotation"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
quotation = self.object
context_result = get_calculations(quotation)
context.update(context_result)
return context
# class QuotationDetailView(LoginRequiredMixin, DetailView):
# model = models.SaleQuotation
# template_name = "sales/quotation_detail.html"
# context_object_name = "quotation"
#
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# quotation = self.object
#
# context_result = get_calculations(quotation)
# context.update(context_result)
#
# return context
@login_required
def generate_invoice(request, pk):
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
dealer = get_user_type(request)
entity = dealer.entity
if not quotation.is_approved:
messages.error(
request, "Quotation must be approved before converting to an invoice."
)
else:
coa_qs, coa_map = entity.get_all_coa_accounts()
cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
recivable_account = (
coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
)
customer = (
entity.get_customers()
.filter(customer_name=quotation.customer.get_full_name)
.first()
)
invoice_model = entity.create_invoice(
customer_model=customer,
terms=InvoiceModel.TERMS_ON_RECEIPT,
cash_account=cash_account.first(),
prepaid_account=recivable_account.first(),
coa_model=coa_qs.first(),
)
name_list = [
f"{instance.car.year} {instance.car.id_car_make} {instance.car.id_car_model} {instance.car.id_car_trim}"
for instance in quotation.quotation_cars.all()
]
invoices_item_models = invoice_model.get_item_model_qs().filter(
name__in=name_list
)
invoice_itemtxs = {
im.item_number: {
"unit_cost": im.default_amount,
"quantity": 1,
"total_amount": im.default_amount,
}
for im in invoices_item_models
}
invoice_itemtxs = invoice_model.migrate_itemtxs(
itemtxs=invoice_itemtxs, commit=True, operation=InvoiceModel.ITEMIZE_APPEND
)
ledger = (
entity.get_ledgers()
.filter(name=f"Payment Ledger for Invoice {invoice_model}")
.first()
)
if not ledger:
ledger = entity.create_ledger(
name=f"Payment Ledger for Invoice {invoice_model}", posted=True
)
journal_entry = JournalEntryModel.objects.create(
posted=False,
description=f"Payment for Invoice {invoice_model}",
ledger=ledger,
locked=False,
origin="Payment",
)
quotation.payment_id = journal_entry.pk
quotation.is_approved = True
date = datetime.datetime.now()
quotation.date_draft = date
invoice_model.date_draft = date
invoice_model.save()
quotation.save()
if not invoice_model.can_review():
messages.error(request, "Quotation is not ready for review")
return redirect("quotation_detail", pk=pk)
invoice_model.mark_as_review()
invoice_model.date_in_review = date
quotation.date_in_review = date
quotation.status = "In Review"
invoice_model.save()
quotation.save()
# @login_required
# def generate_invoice(request, pk):
# quotation = get_object_or_404(models.SaleQuotation, pk=pk)
# dealer = get_user_type(request)
# entity = dealer.entity
# if not quotation.is_approved:
# messages.error(
# request, "Quotation must be approved before converting to an invoice."
# )
# else:
# coa_qs, coa_map = entity.get_all_coa_accounts()
# cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
# recivable_account = (
# coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
# )
# customer = (
# entity.get_customers()
# .filter(customer_name=quotation.customer.get_full_name)
# .first()
# )
#
# invoice_model = entity.create_invoice(
# customer_model=customer,
# terms=InvoiceModel.TERMS_ON_RECEIPT,
# cash_account=cash_account.first(),
# prepaid_account=recivable_account.first(),
# coa_model=coa_qs.first(),
# )
#
# name_list = [
# f"{instance.car.year} {instance.car.id_car_make} {instance.car.id_car_model} {instance.car.id_car_trim}"
# for instance in quotation.quotation_cars.all()
# ]
#
# invoices_item_models = invoice_model.get_item_model_qs().filter(
# name__in=name_list
# )
#
# invoice_itemtxs = {
# im.item_number: {
# "unit_cost": im.default_amount,
# "quantity": 1,
# "total_amount": im.default_amount,
# }
# for im in invoices_item_models
# }
#
# invoice_itemtxs = invoice_model.migrate_itemtxs(
# itemtxs=invoice_itemtxs, commit=True, operation=InvoiceModel.ITEMIZE_APPEND
# )
# ledger = (
# entity.get_ledgers()
# .filter(name=f"Payment Ledger for Invoice {invoice_model}")
# .first()
# )
# if not ledger:
# ledger = entity.create_ledger(
# name=f"Payment Ledger for Invoice {invoice_model}", posted=True
# )
# journal_entry = JournalEntryModel.objects.create(
# posted=False,
# description=f"Payment for Invoice {invoice_model}",
# ledger=ledger,
# locked=False,
# origin="Payment",
# )
#
# quotation.payment_id = journal_entry.pk
# quotation.is_approved = True
# date = datetime.datetime.now()
# quotation.date_draft = date
# invoice_model.date_draft = date
# invoice_model.save()
# quotation.save()
#
# if not invoice_model.can_review():
# messages.error(request, "Quotation is not ready for review")
# return redirect("quotation_detail", pk=pk)
#
# invoice_model.mark_as_review()
# invoice_model.date_in_review = date
# quotation.date_in_review = date
# quotation.status = "In Review"
# invoice_model.save()
# quotation.save()
# elif status == "approved":
# if qoutation.status == "Approved":
@ -1460,41 +1492,41 @@ def generate_invoice(request, pk):
# invoice_model.mark_as_paid(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user)
# print("paid")
# invoice_model.save()
messages.success(request, "Invoice created")
return redirect("quotation_detail", pk=pk)
# messages.success(request, "Invoice created")
# return redirect("quotation_detail", pk=pk)
# return redirect('django_ledger:invoice-detail', entity_slug=quotation.entity.slug, invoice_pk=invoice.uuid)
@login_required
def post_quotation(request, pk):
qoutation = get_object_or_404(models.SaleQuotation, pk=pk)
dealer = get_user_type(request)
entity = dealer.entity
if qoutation.posted:
messages.error(request, "Quotation is already posted")
return redirect("quotation_detail", pk=pk)
coa_qs, coa_map = entity.get_all_coa_accounts()
cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
recivable_account = (
coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
)
customer = (
entity.get_customers()
.filter(customer_name=qoutation.customer.get_full_name)
.first()
)
invoice_model = (
entity.get_invoices()
.filter(customer=customer, date_paid=qoutation.date_paid)
.first()
)
ledger = (
entity.get_ledgers()
.filter(name=f"Payment Ledger for Invoice {invoice_model}")
.first()
)
return
# @login_required
# def post_quotation(request, pk):
# qoutation = get_object_or_404(models.SaleQuotation, pk=pk)
# dealer = get_user_type(request)
# entity = dealer.entity
# if qoutation.posted:
# messages.error(request, "Quotation is already posted")
# return redirect("quotation_detail", pk=pk)
# coa_qs, coa_map = entity.get_all_coa_accounts()
# cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
# recivable_account = (
# coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
# )
# customer = (
# entity.get_customers()
# .filter(customer_name=qoutation.customer.get_full_name)
# .first()
# )
# invoice_model = (
# entity.get_invoices()
# .filter(customer=customer, date_paid=qoutation.date_paid)
# .first()
# )
# ledger = (
# entity.get_ledgers()
# .filter(name=f"Payment Ledger for Invoice {invoice_model}")
# .first()
# )
# return
# if not ledger:
# ledger = entity.create_ledger(name=f"Payment Ledger for Invoice {invoice_model}",posted=True)
@ -1536,98 +1568,98 @@ def post_quotation(request, pk):
# return redirect("quotation_detail", pk=pk)
@login_required
def mark_quotation(request, pk):
qoutation = get_object_or_404(models.SaleQuotation, pk=pk)
status = request.GET.get("status")
dealer = request.user.dealer
entity = dealer.entity
date = datetime.datetime.now()
customer = (
entity.get_customers()
.filter(customer_name=qoutation.customer.get_full_name)
.first()
)
invoice_model = entity.get_invoices().filter(customer=customer)
if status == "approved":
if qoutation.status == "Approved":
messages.error(request, "Quotation is already approved")
return redirect("quotation_detail", pk=pk)
invoice_model = invoice_model.filter(
date_in_review=qoutation.date_in_review
).first()
if not invoice_model.can_approve():
messages.error(request, "Quotation is not ready for approval")
return redirect("quotation_detail", pk=pk)
invoice_model.mark_as_approved(
entity_slug=entity.slug, user_model=request.user.dealer
)
invoice_model.date_approved = date
qoutation.date_approved = date
invoice_model.save()
qoutation.status = "Approved"
qoutation.save()
for car in qoutation.quotation_cars.all():
car.car.status = "reserved"
car.car.save()
messages.success(request, _("Quotation Approved"))
elif status == "paid":
if qoutation.status == "Paid":
messages.error(request, "Quotation is already paid")
return redirect("quotation_detail", pk=pk)
invoice_model = invoice_model.filter(
date_approved=qoutation.date_approved
).first()
if not invoice_model.can_pay():
messages.error(request, "Quotation is not ready for payment")
return redirect("quotation_detail", pk=pk)
invoice_model.mark_as_paid(
entity_slug=entity.slug, user_model=request.user.dealer
)
invoice_model.date_paid = date
qoutation.date_paid = date
invoice_model.save()
qoutation.status = "Paid"
qoutation.save()
messages.success(request, _("Quotation Paid"))
return redirect("quotation_detail", pk=pk)
# @login_required
# def mark_quotation(request, pk):
# qoutation = get_object_or_404(models.SaleQuotation, pk=pk)
# status = request.GET.get("status")
# dealer = request.user.dealer
# entity = dealer.entity
# date = datetime.datetime.now()
# customer = (
# entity.get_customers()
# .filter(customer_name=qoutation.customer.get_full_name)
# .first()
# )
# invoice_model = entity.get_invoices().filter(customer=customer)
# if status == "approved":
# if qoutation.status == "Approved":
# messages.error(request, "Quotation is already approved")
# return redirect("quotation_detail", pk=pk)
#
# invoice_model = invoice_model.filter(
# date_in_review=qoutation.date_in_review
# ).first()
# if not invoice_model.can_approve():
# messages.error(request, "Quotation is not ready for approval")
# return redirect("quotation_detail", pk=pk)
#
# invoice_model.mark_as_approved(
# entity_slug=entity.slug, user_model=request.user.dealer
# )
# invoice_model.date_approved = date
# qoutation.date_approved = date
# invoice_model.save()
# qoutation.status = "Approved"
# qoutation.save()
# for car in qoutation.quotation_cars.all():
# car.car.status = "reserved"
# car.car.save()
# messages.success(request, _("Quotation Approved"))
# elif status == "paid":
# if qoutation.status == "Paid":
# messages.error(request, "Quotation is already paid")
# return redirect("quotation_detail", pk=pk)
#
# invoice_model = invoice_model.filter(
# date_approved=qoutation.date_approved
# ).first()
# if not invoice_model.can_pay():
# messages.error(request, "Quotation is not ready for payment")
# return redirect("quotation_detail", pk=pk)
#
# invoice_model.mark_as_paid(
# entity_slug=entity.slug, user_model=request.user.dealer
# )
# invoice_model.date_paid = date
# qoutation.date_paid = date
# invoice_model.save()
# qoutation.status = "Paid"
# qoutation.save()
# messages.success(request, _("Quotation Paid"))
# return redirect("quotation_detail", pk=pk)
@login_required
def confirm_quotation(request, pk):
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
if quotation.is_approved:
messages.error(request, _("Quotation already approved."))
return redirect("quotation_detail", pk=pk)
# @login_required
# def confirm_quotation(request, pk):
# quotation = get_object_or_404(models.SaleQuotation, pk=pk)
# if quotation.is_approved:
# messages.error(request, _("Quotation already approved."))
# return redirect("quotation_detail", pk=pk)
#
# try:
# # quotation.confirm()
# # quotation_cars = quotation.quotation_cars.annotate(total_price=F('car__total') * F('quantity'))
# # total = quotation.quotation_cars.aggregate(total_price=Sum(F('car__finances__selling_price') * F('quantity')))
#
# models.SalesOrder.objects.create(
# quotation=quotation,
# total_amount=quotation.total_vat,
# # total_amount=quotation.quotation_cars.aggregate(Sum("total_amount"))["total_amount__sum"],
# )
# quotation.is_approved = True
# quotation.save()
# messages.success(request, _("Quotation confirmed and sales order created."))
# except ValueError as e:
# messages.error(request, str(e))
# return redirect("quotation_detail", pk=pk)
try:
# quotation.confirm()
# quotation_cars = quotation.quotation_cars.annotate(total_price=F('car__total') * F('quantity'))
# total = quotation.quotation_cars.aggregate(total_price=Sum(F('car__finances__selling_price') * F('quantity')))
models.SalesOrder.objects.create(
quotation=quotation,
total_amount=quotation.total_vat,
# total_amount=quotation.quotation_cars.aggregate(Sum("total_amount"))["total_amount__sum"],
)
quotation.is_approved = True
quotation.save()
messages.success(request, _("Quotation confirmed and sales order created."))
except ValueError as e:
messages.error(request, str(e))
return redirect("quotation_detail", pk=pk)
class SalesOrderDetailView(LoginRequiredMixin, DetailView):
model = models.SalesOrder
template_name = "sales/sales_order_detail.html"
context_object_name = "sales_order"
slug_field = "order_id"
slug_url_kwarg = "order_id"
#
# class SalesOrderDetailView(LoginRequiredMixin, DetailView):
# model = models.SalesOrder
# template_name = "sales/sales_order_detail.html"
# context_object_name = "sales_order"
# slug_field = "order_id"
# slug_url_kwarg = "order_id"
# Users
@ -1875,64 +1907,64 @@ class RepresentativeDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteVi
# return HttpResponse(pdf_file, content_type="application/pdf")
@login_required
def download_quotation_pdf(request, quotation_id):
try:
# Retrieve the quotation object
quotation = models.SaleQuotation.objects.get(id=quotation_id)
cars = models.SaleQuotationCar.objects.get(id=quotation_id)
print(cars)
services = cars.finance.additional_services.all()
print(services)
# Create a response object
response = HttpResponse(content_type="application/pdf")
response["Content-Disposition"] = (
f'attachment; filename="quotation_{quotation.id}.pdf"'
)
# Call the PDF generation function
# generate_quotation_pdf(response, quotation, services)
return response
except models.SaleQuotation.DoesNotExist:
return HttpResponse("Quotation not found", status=404)
# @login_required
# def download_quotation_pdf(request, quotation_id):
# try:
# # Retrieve the quotation object
# quotation = models.SaleQuotation.objects.get(id=quotation_id)
# cars = models.SaleQuotationCar.objects.get(id=quotation_id)
# print(cars)
# services = cars.finance.additional_services.all()
# print(services)
#
# # Create a response object
# response = HttpResponse(content_type="application/pdf")
# response["Content-Disposition"] = (
# f'attachment; filename="quotation_{quotation.id}.pdf"'
# )
#
# # Call the PDF generation function
# # generate_quotation_pdf(response, quotation, services)
#
# return response
# except models.SaleQuotation.DoesNotExist:
# return HttpResponse("Quotation not found", status=404)
@login_required
def invoice_detail(request, pk):
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
dealer = request.user.dealer
entity = dealer.entity
customer = (
entity.get_customers()
.filter(customer_name=quotation.customer.get_full_name)
.first()
)
invoice_model = entity.get_invoices()
invoice = invoice_model.filter(
customer=customer, date_draft=quotation.date_draft
).first()
return redirect("quotation_detail", pk=pk)
# @login_required
# def invoice_detail(request, pk):
# quotation = get_object_or_404(models.SaleQuotation, pk=pk)
# dealer = request.user.dealer
# entity = dealer.entity
# customer = (
# entity.get_customers()
# .filter(customer_name=quotation.customer.get_full_name)
# .first()
# )
# invoice_model = entity.get_invoices()
#
# invoice = invoice_model.filter(
# customer=customer, date_draft=quotation.date_draft
# ).first()
# return redirect("quotation_detail", pk=pk)
@login_required
def payment_invoice(request, pk):
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
dealer = request.user.dealer
entity = dealer.entity
customer = (
entity.get_customers()
.filter(customer_name=quotation.customer.get_full_name)
.first()
)
invoice_model = entity.get_invoices()
invoice = invoice_model.filter(
customer=customer, date_draft=quotation.date_draft
).first()
return redirect("quotation_detail", pk=pk)
# @login_required
# def payment_invoice(request, pk):
# quotation = get_object_or_404(models.SaleQuotation, pk=pk)
# dealer = request.user.dealer
# entity = dealer.entity
# customer = (
# entity.get_customers()
# .filter(customer_name=quotation.customer.get_full_name)
# .first()
# )
# invoice_model = entity.get_invoices()
# invoice = invoice_model.filter(
# customer=customer, date_draft=quotation.date_draft
# ).first()
#
# return redirect("quotation_detail", pk=pk)
# class PaymentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
@ -1953,75 +1985,75 @@ def payment_invoice(request, pk):
# return context
def payment_create(request, pk):
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
dealer = get_user_type(request)
if request.method == "POST":
form = forms.PaymentForm(request.POST)
if form.is_valid():
form.instance.quotation = quotation
insatnce = form.save()
dealer = dealer
entity = dealer.entity
customer = (
entity.get_customers()
.filter(customer_name=quotation.customer.get_full_name)
.first()
)
coa_qs, coa_map = entity.get_all_coa_accounts()
cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
recivable_account = (
coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
)
journal_entry = JournalEntryModel.objects.filter(
pk=quotation.payment_id
).first()
TransactionModel.objects.create(
journal_entry=journal_entry,
account=cash_account.first(), # Debit Cash
amount=insatnce.amount, # Payment amount
tx_type="debit",
description="Payment Received",
)
TransactionModel.objects.create(
journal_entry=journal_entry,
account=recivable_account.first(), # Credit Accounts Receivable
amount=insatnce.amount, # Payment amount
tx_type="credit",
description="Payment Received",
)
journal_entry.posted = True
quotation.posted = True
quotation.save()
journal_entry.save()
invoice_model = (
entity.get_invoices()
.filter(date_approved=quotation.date_approved)
.first()
)
invoice_model.mark_as_paid(
entity_slug=entity.slug, user_model=request.user.dealer
)
date = timezone.now()
invoice_model.date_paid = date
quotation.date_paid = date
invoice_model.save()
quotation.status = "Paid"
quotation.save()
messages.success(request, "Payment created successfully.")
return redirect("quotation_detail", pk=pk)
else:
form = forms.PaymentForm()
return render(
request,
"sales/payments/payment_create.html",
{"quotation": quotation, "form": form},
)
# def payment_create(request, pk):
# quotation = get_object_or_404(models.SaleQuotation, pk=pk)
# dealer = get_user_type(request)
# if request.method == "POST":
# form = forms.PaymentForm(request.POST)
# if form.is_valid():
# form.instance.quotation = quotation
# insatnce = form.save()
#
# dealer = dealer
# entity = dealer.entity
# customer = (
# entity.get_customers()
# .filter(customer_name=quotation.customer.get_full_name)
# .first()
# )
# coa_qs, coa_map = entity.get_all_coa_accounts()
# cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
# recivable_account = (
# coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
# )
# journal_entry = JournalEntryModel.objects.filter(
# pk=quotation.payment_id
# ).first()
# TransactionModel.objects.create(
# journal_entry=journal_entry,
# account=cash_account.first(), # Debit Cash
# amount=insatnce.amount, # Payment amount
# tx_type="debit",
# description="Payment Received",
# )
#
# TransactionModel.objects.create(
# journal_entry=journal_entry,
# account=recivable_account.first(), # Credit Accounts Receivable
# amount=insatnce.amount, # Payment amount
# tx_type="credit",
# description="Payment Received",
# )
# journal_entry.posted = True
# quotation.posted = True
# quotation.save()
# journal_entry.save()
#
# invoice_model = (
# entity.get_invoices()
# .filter(date_approved=quotation.date_approved)
# .first()
# )
#
# invoice_model.mark_as_paid(
# entity_slug=entity.slug, user_model=request.user.dealer
# )
# date = timezone.now()
# invoice_model.date_paid = date
# quotation.date_paid = date
# invoice_model.save()
# quotation.status = "Paid"
# quotation.save()
#
# messages.success(request, "Payment created successfully.")
# return redirect("quotation_detail", pk=pk)
# else:
# form = forms.PaymentForm()
# return render(
# request,
# "sales/payments/payment_create.html",
# {"quotation": quotation, "form": form},
# )
# Ledger

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,252 +1,254 @@
aiohappyeyeballs
aiohttp
aiohttp-retry
aiosignal
alabaster
albucore
albumentations
annotated-types
anyio
arabic-reshaper
asgiref
astor
astroid
attrs
autopep8
Babel
beautifulsoup4
bleach
blinker
Brotli
certifi
cffi
chardet
charset-normalizer
click
colorama
commonmark
contourpy
crispy-bootstrap5
cryptography
cssselect2
ctranslate2
cycler
Cython
decorator
desert
dill
distro
dj-rest-auth
dj-shop-cart
Django
django-allauth
django-appointment
django-autoslug
django-bootstrap5
django-classy-tags
django-cors-headers
django-countries
django-crispy-forms
django-debug-toolbar
django-extensions
django-filter
django-formtools
django-ledger
django-money
django-next-url-mixin
django-nine
django-nonefield
django-ordered-model
django-phonenumber-field
django-picklefield
django-plans
django-prometheus
django-q2
django-sekizai
django-sequences
django-silk
django-sms
django-sslserver
django-tables2
django-treebeard
django-view-breadcrumbs
djangocms-admin-style
djangorestframework
djangorestframework_simplejwt
djangoviz
docutils
easy-thumbnails
emoji
et_xmlfile
Faker
filelock
fire
Flask
fonttools
frozenlist
fsspec
gprof2dot
graphqlclient
greenlet
h11
h2
hpack
hstspreload
httpcore
httpx
hyperframe
idna
imageio
imagesize
imgaug
iso4217
isodate
isort
itsdangerous
Jinja2
jiter
joblib
kiwisolver
lazy_loader
ledger
libretranslatepy
lmdb
lxml
Markdown
markdown-it-py
MarkupSafe
marshmallow
matplotlib
mccabe
mdurl
MouseInfo
mpmath
multidict
mypy-extensions
networkx
newrelic
nltk
numpy
oauthlib
ofxtools
openai
opencv-contrib-python
opencv-python
opencv-python-headless
openpyxl
opt_einsum
outcome
packaging
pandas
pango
pdfkit
phonenumbers
pillow
platformdirs
prometheus_client
propcache
protobuf
psycopg
psycopg-binary
psycopg-c
py-moneyed
PyAutoGUI
pyclipper
pycodestyle
pycparser
pydantic
pydantic_core
pydotplus
pydyf
PyGetWindow
Pygments
PyJWT
pylint
PyMsgBox
PyMySQL
pyobjc-core
pyobjc-framework-Cocoa
pyobjc-framework-Quartz
pyparsing
pyperclip
pyphen
pypng
PyRect
PyScreeze
pyserial
PySocks
python-bidi
python-dateutil
python-docx
python-openid
python-stdnum
python3-saml
pytweening
pytz
pyvin
pywa
pywhat
pywhatkit
PyYAML
pyzbar
qrcode
RapidFuzz
regex
reportlab
requests
requests-oauthlib
rfc3986
rich
rubicon-objc
sacremoses
scikit-image
scikit-learn
scipy
selenium
sentencepiece
shapely
simsimd
six
sniffio
snowballstemmer
sortedcontainers
soupsieve
SQLAlchemy
sqlparse
stanza
stringzilla
suds
swapper
sympy
tablib
termcolor
threadpoolctl
tifffile
tinycss2
tinyhtml5
tomli
tomlkit
torch
tqdm
trio
trio-websocket
twilio
typing-inspect
typing_extensions
tzdata
Unidecode
upgrade-requirements
urllib3
vin
vininfo
vishap
vpic-api
weasyprint
webencodings
websocket-client
Werkzeug
wikipedia
wsproto
xmlsec
yarl
zopfli
aiohappyeyeballs==2.4.4
aiohttp==3.11.11
aiohttp-retry==2.9.1
aiosignal==1.3.2
alabaster==1.0.0
albucore==0.0.23
albumentations==2.0.2
annotated-types==0.7.0
anyio==4.8.0
arabic-reshaper==3.0.0
asgiref==3.8.1
astor==0.8.1
astroid==3.3.8
attrs==23.2.0
autopep8==2.3.2
Babel==2.15.0
beautifulsoup4==4.12.3
bleach==6.2.0
blinker==1.9.0
Brotli==1.1.0
certifi==2024.12.14
cffi==1.17.1
chardet==5.2.0
charset-normalizer==3.4.1
click==8.1.8
colorama==0.4.6
commonmark==0.9.1
contourpy==1.3.1
crispy-bootstrap5==2024.10
cryptography==44.0.0
cssselect2==0.7.0
ctranslate2==4.5.0
cycler==0.12.1
Cython==3.1.0a1
decorator==5.1.1
desert==2020.11.18
dill==0.3.9
distro==1.9.0
dj-rest-auth==7.0.1
dj-shop-cart==8.0.0a2
Django==5.1.5
django-allauth==65.3.1
django-appointment==3.7.4
django-autoslug==1.9.9
django-bootstrap5==24.3
django-classy-tags==4.1.0
django-cors-headers==4.6.0
django-countries==7.6.1
django-crispy-forms==2.3
django-debug-toolbar==5.0.1
django-extensions==3.2.3
django-filter==24.3
django-formtools==2.5.1
django-ledger==0.7.3
django-money==3.5.3
django-next-url-mixin==0.4.0
django-nine==0.2.7
django-nonefield==0.4
django-ordered-model==3.7.4
django-otp==1.5.4
django-phonenumber-field==8.0.0
django-picklefield==3.2
django-plans==1.2.0
django-prometheus==2.3.1
django-q2==1.7.6
django-sekizai==4.1.0
django-sequences==3.0
django-silk==5.3.2
django-sms==0.7.0
django-sslserver==0.22
django-tables2==2.7.5
django-treebeard==4.7.1
django-two-factor-auth==1.17.0
django-view-breadcrumbs==2.5.1
djangocms-admin-style==3.3.1
djangorestframework==3.15.2
djangorestframework_simplejwt==5.4.0
djangoviz==0.1.1
docutils==0.21.2
easy-thumbnails==2.10
emoji==2.14.1
et_xmlfile==2.0.0
Faker==35.0.0
filelock==3.17.0
fire==0.7.0
Flask==3.1.0
fonttools==4.55.7
frozenlist==1.5.0
fsspec==2024.12.0
gprof2dot==2024.6.6
graphqlclient==0.2.4
greenlet==3.1.1
h11==0.14.0
h2==4.1.0
hpack==4.1.0
hstspreload==2025.1.1
httpcore==1.0.7
httpx==0.28.1
hyperframe==6.1.0
idna==3.10
imageio==2.37.0
imagesize==1.4.1
imgaug==0.4.0
iso4217==1.12.20240625
isodate==0.7.2
isort==6.0.0
itsdangerous==2.2.0
Jinja2==3.1.5
jiter==0.8.2
joblib==1.4.2
kiwisolver==1.4.8
lazy_loader==0.4
ledger==1.0.1
libretranslatepy==2.1.4
lmdb==1.6.2
lxml==5.3.0
Markdown==3.7
markdown-it-py==3.0.0
MarkupSafe==3.0.2
marshmallow==3.26.0
matplotlib==3.10.0
mccabe==0.7.0
mdurl==0.1.2
MouseInfo==0.1.3
mpmath==1.3.0
multidict==6.1.0
mypy-extensions==1.0.0
networkx==3.4.2
newrelic==10.4.0
nltk==3.9.1
numpy==2.2.2
oauthlib==3.2.2
ofxtools==0.9.5
openai==1.60.2
opencv-contrib-python==4.11.0.86
opencv-python==4.11.0.86
opencv-python-headless==4.11.0.86
openpyxl==3.1.5
opt_einsum==3.4.0
outcome==1.3.0.post0
packaging==24.2
pandas==2.2.3
pango==0.0.1
pdfkit==1.0.0
phonenumbers==8.13.42
pillow==10.4.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.27.2
pydotplus==2.0.2
pydyf==0.11.0
PyGetWindow==0.0.9
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
pyperclip==1.9.0
pyphen==0.17.2
pypng==0.20220715.0
PyRect==0.2.0
PyScreeze==1.0.1
pyserial==3.5
PySocks==1.7.1
python-bidi==0.6.3
python-dateutil==2.9.0.post0
python-docx==1.1.2
python-openid==2.2.5
python-stdnum==1.20
python3-saml==1.16.0
pytweening==1.2.0
pytz==2024.2
pyvin==0.0.2
pywa==2.7.0
pywhat==5.1.0
pywhatkit==5.4
PyYAML==6.0.2
pyzbar==0.1.9
qrcode==7.4.2
RapidFuzz==3.11.0
regex==2024.11.6
reportlab==4.2.5
requests==2.32.3
requests-oauthlib==2.0.0
rfc3986==2.0.0
rich==10.16.2
rubicon-objc==0.5.0
sacremoses==0.1.1
scikit-image==0.25.1
scikit-learn==1.6.1
scipy==1.15.1
selenium==4.28.1
sentencepiece==0.2.0
shapely==2.0.6
simsimd==6.2.1
six==1.17.0
sniffio==1.3.1
snowballstemmer==2.2.0
sortedcontainers==2.4.0
soupsieve==2.6
SQLAlchemy==2.0.37
sqlparse==0.5.3
stanza==1.10.1
stringzilla==3.11.3
suds==1.2.0
swapper==1.3.0
sympy==1.13.1
tablib==3.8.0
termcolor==2.5.0
threadpoolctl==3.5.0
tifffile==2025.1.10
tinycss2==1.4.0
tinyhtml5==2.0.0
tomli==2.2.1
tomlkit==0.13.2
torch==2.5.1
tqdm==4.67.1
trio==0.28.0
trio-websocket==0.11.1
twilio==9.4.4
typing-inspect==0.9.0
typing_extensions==4.12.2
tzdata==2025.1
Unidecode==1.3.8
upgrade-requirements==1.7.0
urllib3==2.3.0
vin==0.6.2
vininfo==1.8.0
vishap==0.1.5
vpic-api==0.7.4
weasyprint==63.1
webencodings==0.5.1
websocket-client==1.8.0
Werkzeug==3.1.3
wikipedia==1.4.0
wsproto==1.2.0
xmlsec==1.3.14
yarl==1.18.3
zopfli==0.2.3.post1

BIN
static/.DS_Store vendored

Binary file not shown.

Binary file not shown.

BIN
static/images/bg/.DS_Store vendored Normal file

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

View File

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 119 KiB

BIN
templates/.DS_Store vendored

Binary file not shown.

View File

@ -1,37 +0,0 @@
{% extends 'base.html' %}
{% load i18n static %}
{% block content %}
<div class="row">
<div class="row flex-center min-vh-100 py-5">
<div class="col-sm-10 col-md-8 col-lg-5 col-xxl-4"><a class="d-flex flex-center text-decoration-none mb-4" href="../../../index.html">
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block"><img src="../../../assets/img/icons/logo.png" alt="phoenix" width="58" />
</div>
</a>
<div class="px-xxl-5">
<div class="text-center mb-6">
<h4 class="text-body-highlight">Enter the verification code</h4>
<p class="text-body-tertiary mb-0">An email containing a 6-digit verification code has been sent to the email address - exa*********.com </p>
<P class="fs-10 mb-5">Dont have access? <a href="#!">Use another method</a></P>
<form class="verification-form" data-2fa-form="data-2fa-form">
<div class="d-flex align-items-center gap-2 mb-3">
<input class="form-control px-2 text-center" type="number" />
<input class="form-control px-2 text-center" type="number" />
<input class="form-control px-2 text-center" type="number" /><span>-</span>
<input class="form-control px-2 text-center" type="number" />
<input class="form-control px-2 text-center" type="number" />
<input class="form-control px-2 text-center" type="number" />
</div>
<div class="form-check text-start mb-4">
<input class="form-check-input" id="2fa-checkbox" type="checkbox" />
<label for="2fa-checkbox">Dont ask again on this device</label>
</div>
<Button class="btn btn-primary w-100 mb-5" type="submit" disabled="disabled">Verify</Button><a class="fs-9" href="#!">Didnt receive the code? </a>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "allauth/layouts/entrance.html" %}
{% load i18n %}
{% load allauth %}
{% block head_title %}
{% translate "Account Inactive" %}
{% endblock head_title %}
{% block content %}
{% element h1 %}
{% translate "Account Inactive" %}
{% endelement %}
{% element p %}
{% translate "This account is inactive." %}
{% endelement %}
{% endblock content %}

View File

@ -1,18 +1,48 @@
{% extends "base.html" %}
{% block title %}Confirm Email Verification Code{% endblock %}
{% extends "account/base_entrance.html" %}
{% load i18n %}
{% load allauth account %}
{% block head_title %}
{% translate "Email Verification" %}
{% endblock head_title %}
{% block content %}
<div class="row">
<h2>Confirm Your Email</h2>
<p>Please enter the verification code sent to your email.</p>
<form method="post" action="{% url 'account_confirm_email' %}">
{% csrf_token %}
<div class="mb-3">
<label for="id_code" class="form-label">Verification Code</label>
<input type="text" id="id_code" name="code" class="form-control">
</div>
<button type="submit" class="btn btn-primary">Verify</button>
</form>
</div>
{% endblock %}
{% element h1 %}
{% translate "Enter Email Verification Code" %}
{% endelement %}
{% setvar email_link %}
<a href="mailto:{{ email }}">{{ email }}</a>
{% endsetvar %}
{% element p %}
{% blocktranslate %}Weve sent a code to {{ email_link }}. The code expires shortly, so please enter it soon.{% endblocktranslate %}
{% endelement %}
{% url 'account_email_verification_sent' as action_url %}
{% element form form=form method="post" action=action_url tags="entrance,email,verification" %}
{% slot body %}
{% csrf_token %}
{% element fields form=form unlabeled=True %}
{% endelement %}
{{ redirect_field }}
{% endslot %}
{% slot actions %}
{% element button type="submit" tags="prominent,confirm" %}
{% translate "Confirm" %}
{% endelement %}
{% if cancel_url %}
{% element button href=cancel_url tags="link,cancel" %}
{% translate "Cancel" %}
{% endelement %}
{% else %}
{% element button type="submit" form="logout-from-stage" tags="link,cancel" %}
{% translate "Cancel" %}
{% endelement %}
{% endif %}
{% endslot %}
{% endelement %}
{% if not cancel_url %}
<form id="logout-from-stage"
method="post"
action="{% url 'account_logout' %}">
<input type="hidden" name="next" value="{% url 'account_login' %}">
{% csrf_token %}
</form>
{% endif %}
{% endblock content %}

View File

@ -1,19 +1,49 @@
{% extends "base.html" %}
{% load crispy_forms_filters %}
{% block title %}Confirm Login Code{% endblock %}
{% load i18n %}
{% load allauth account %}
{% block head_title %}
{% translate "Sign In" %}
{% endblock head_title %}
{% block content %}
<div class="row">
<h2>Confirm Login Code</h2>
<p>Please enter the login code sent to your email or phone.</p>
<form method="post" action="{% url 'account_confirm_login_code' %}">
{% csrf_token %}
<div class="mb-3">
<label for="id_code" class="form-label">Login Code</label>
<input type="text" id="id_code" name="code" class="form-control" required>
<div class="row ">
<div class="row flex-center min-vh-50">
<div class="col-sm-10 col-md-8 col-lg-5 col-xl-5 col-xxl-3">
<a class="d-flex flex-center text-decoration-none mb-4" href="{% url 'home' %}">
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
<img class="d-dark-none" src="{% static 'images/logos/logo-d.png' %}" alt="{% trans 'home' %}" width="58" />
<img class="d-light-none" src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" />
</div>
</div>
<button type="submit" class="btn btn-primary">Verify</button>
</a>
<div class="text-center">
<h3 class="mb-4">{% translate "Enter Sign-In Code" %}</h3>
</div>
{% setvar email_link %}
<a href="mailto:{{ email }}">{{ email }}</a>
{% endsetvar %}
<p>
{% blocktranslate %}Weve sent a code to {{ email_link }}. The code expires shortly, so please enter it soon.{% endblocktranslate %}
</p>
<form method="post" action="{% url 'account_confirm_login_code' %}" class="form needs-validation" novalidate>
{% csrf_token %}
{{ redirect_field }}
{{ form|crispy }}
<button type="submit" class="btn btn-primary btn-sm w-100">{% trans "Sign In" %}</button>
</form>
<p>If you didnt receive a code, <a href="{% url 'account_resend_login_code' %}">click here</a> to resend it.</p>
{% element button type="submit" form="logout-from-stage" tags="link" %}
{% translate "Cancel" %}
{% endelement %}
<form id="logout-from-stage"
method="post"
action="{% url 'account_logout' %}">
<input type="hidden" name="next" value="{% url 'account_login' %}">
{% csrf_token %}
</form>
</div>
</div>
{% endblock %}
</div>
{% endblock content %}

View File

@ -0,0 +1,90 @@
{% extends "account/base_manage_email.html" %}
{% load allauth i18n %}
{% block head_title %}
{% trans "Email Addresses" %}
{% endblock head_title %}
{% block content %}
{% element h1 %}
{% trans "Email Addresses" %}
{% endelement %}
{% if emailaddresses %}
{% element p %}
{% trans 'The following email addresses are associated with your account:' %}
{% endelement %}
{% url 'account_email' as email_url %}
{% element form form=form action=email_url method="post" tags="email,list" %}
{% slot body %}
{% csrf_token %}
{% for radio in emailaddress_radios %}
{% with emailaddress=radio.emailaddress %}
{% element field type="radio" checked=radio.checked name="email" value=emailaddress.email id=radio.id %}
{% slot label %}
{{ emailaddress.email }}
{% if emailaddress.verified %}
{% element badge tags="success,email,verified" %}
{% translate "Verified" %}
{% endelement %}
{% else %}
{% element badge tags="warning,email,unverified" %}
{% translate "Unverified" %}
{% endelement %}
{% endif %}
{% if emailaddress.primary %}
{% element badge tags="email,primary" %}
{% translate "Primary" %}
{% endelement %}
{% endif %}
{% endslot %}
{% endelement %}
{% endwith %}
{% endfor %}
{% endslot %}
{% slot actions %}
{% element button type="submit" name="action_primary" %}
{% trans 'Make Primary' %}
{% endelement %}
{% element button tags="secondary" type="submit" name="action_send" %}
{% trans 'Re-send Verification' %}
{% endelement %}
{% element button tags="danger,delete" type="submit" name="action_remove" %}
{% trans 'Remove' %}
{% endelement %}
{% endslot %}
{% endelement %}
{% else %}
{% include "account/snippets/warn_no_email.html" %}
{% endif %}
{% if can_add_email %}
{% element h2 %}
{% trans "Add Email Address" %}
{% endelement %}
{% url 'account_email' as action_url %}
{% element form form=form method="post" action=action_url tags="email,add" %}
{% slot body %}
{% csrf_token %}
{% element fields form=form %}
{% endelement %}
{% endslot %}
{% slot actions %}
{% element button name="action_add" type="submit" %}
{% trans "Add Email" %}
{% endelement %}
{% endslot %}
{% endelement %}
{% endif %}
{% endblock content %}
{% block extra_body %}
<script>
(function() {
var message = "{% trans 'Do you really want to remove the selected email address?' %}";
var actions = document.getElementsByName('action_remove');
if (actions.length) {
actions[0].addEventListener("click", function(e) {
if (! confirm(message)) {
e.preventDefault();
}
});
}
})();
</script>
{% endblock extra_body %}

View File

@ -0,0 +1,13 @@
{% extends "account/email/base_message.txt" %}
{% load i18n %}
{% block content %}{% autoescape off %}{% blocktrans %}You are receiving this email because you or someone else tried to signup for an
account using email address:
{{ email }}
However, an account using that email address already exists. In case you have
forgotten about this, please use the password forgotten procedure to recover
your account:
{{ password_reset_url }}{% endblocktrans %}{% endautoescape %}{% endblock content %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Account Already Exists{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,7 @@
{% load i18n %}{% autoescape off %}{% blocktrans with site_name=current_site.name %}Hello from {{ site_name }}!{% endblocktrans %}
{% block content %}{% endblock content %}
{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Thank you for using {{ site_name }}!
{{ site_domain }}{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,14 @@
{% extends "account/email/base_message.txt" %}
{% load account %}
{% load i18n %}
{% block content %}{% autoescape off %}{% blocktrans %}You are receiving this mail because the following change was made to your account:{% endblocktrans %}
{% block notification_message %}
{% endblock notification_message%}
{% blocktrans %}If you do not recognize this change then please take proper security precautions immediately. The change to your account originates from:
- IP address: {{ip}}
- Browser: {{user_agent}}
- Date: {{timestamp}}{% endblocktrans %}{% endautoescape %}{% endblock %}

View File

@ -0,0 +1,4 @@
{% extends "account/email/base_notification.txt" %}
{% load i18n %}
{% block notification_message %}{% blocktrans %}Your email has been changed from {{ from_email }} to {{ to_email }}.{% endblocktrans %}{% endblock notification_message %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Email Changed{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,4 @@
{% extends "account/email/base_notification.txt" %}
{% load i18n %}
{% block notification_message %}{% blocktrans %}Your email has been confirmed.{% endblocktrans %}{% endblock notification_message %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Email Confirmation{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,9 @@
{% extends "account/email/base_message.txt" %}
{% load account %}
{% load i18n %}
{% block content %}{% autoescape off %}{% user_display user as user_display %}{% blocktranslate with site_name=current_site.name site_domain=current_site.domain %}You're receiving this email because user {{ user_display }} has given your email address to register an account on {{ site_domain }}.{% endblocktranslate %}
{% if code %}{% blocktranslate %}Your email verification code is listed below. Please enter it in your open browser window.{% endblocktranslate %}
{{ code }}{% else %}{% blocktranslate %}To confirm this is correct, go to {{ activate_url }}{% endblocktranslate %}{% endif %}{% endautoescape %}{% endblock content %}

View File

@ -0,0 +1 @@
{% include "account/email/email_confirmation_message.txt" %}

View File

@ -0,0 +1 @@
{% include "account/email/email_confirmation_subject.txt" %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Please Confirm Your Email Address{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,4 @@
{% extends "account/email/base_notification.txt" %}
{% load i18n %}
{% block notification_message %}{% blocktrans %}Email address {{ deleted_email }} has been removed from your account.{% endblocktrans %}{% endblock notification_message %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Email Removed{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,9 @@
{% extends "account/email/base_message.txt" %}
{% load account %}
{% load i18n %}
{% block content %}{% autoescape off %}{% blocktranslate %}Your sign-in code is listed below. Please enter it in your open browser window.{% endblocktranslate %}{% endautoescape %}
{{ code }}
{% blocktranslate %}This mail can be safely ignored if you did not initiate this action.{% endblocktranslate %}{% endblock content %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Sign-In Code{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,4 @@
{% extends "account/email/base_notification.txt" %}
{% load i18n %}
{% block notification_message %}{% blocktrans %}Your password has been changed.{% endblocktrans %}{% endblock notification_message %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Password Changed{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,9 @@
{% extends "account/email/base_message.txt" %}
{% load i18n %}
{% block content %}{% autoescape off %}{% blocktrans %}You're receiving this email because you or someone else has requested a password reset for your user account.
It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktrans %}
{{ password_reset_url }}{% if username %}
{% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %}{% endif %}{% endautoescape %}{% endblock content %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Password Reset Email{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,4 @@
{% extends "account/email/base_notification.txt" %}
{% load i18n %}
{% block notification_message %}{% blocktrans %}Your password has been reset.{% endblocktrans %}{% endblock notification_message %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Password Reset{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,4 @@
{% extends "account/email/base_notification.txt" %}
{% load i18n %}
{% block notification_message %}{% blocktrans %}Your password has been set.{% endblocktrans %}{% endblock notification_message %}

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