update
This commit is contained in:
parent
d15cb61a12
commit
be055f2de7
@ -104,14 +104,21 @@ WSGI_APPLICATION = 'car_inventory.wsgi.application'
|
|||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||||
|
|
||||||
|
# DATABASES = {
|
||||||
|
# "default": {
|
||||||
|
# "ENGINE": "django_prometheus.db.backends.postgresql",
|
||||||
|
# "NAME": "murad_haikal",
|
||||||
|
# "USER": "f95166",
|
||||||
|
# "PASSWORD": "Kfsh&rc9788",
|
||||||
|
# "HOST": "localhost",
|
||||||
|
# "PORT": 5432,
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
"default": {
|
'default': {
|
||||||
"ENGINE": "django_prometheus.db.backends.postgresql",
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
"NAME": "murad_haikal",
|
'NAME': 'db.sqlite3',
|
||||||
"USER": "f95166",
|
|
||||||
"PASSWORD": "Kfsh&rc9788",
|
|
||||||
"HOST": "localhost",
|
|
||||||
"PORT": 5432,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-26 07:13
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0018_additionalservices_taxable'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='additionalservices',
|
||||||
|
name='taxable',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='salequotation',
|
||||||
|
name='entity',
|
||||||
|
),
|
||||||
|
]
|
||||||
20
inventory/migrations/0020_dealer_entity.py
Normal file
20
inventory/migrations/0020_dealer_entity.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-26 07:22
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
||||||
|
('inventory', '0019_remove_additionalservices_taxable_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='dealer',
|
||||||
|
name='entity',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.entitymodel'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -498,6 +498,7 @@ class Dealer(models.Model, LocalizedNameMixin):
|
|||||||
logo = models.ImageField(
|
logo = models.ImageField(
|
||||||
upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo")
|
upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo")
|
||||||
)
|
)
|
||||||
|
entity = models.ForeignKey(EntityModel, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
joined_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Joined At"))
|
joined_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Joined At"))
|
||||||
email = models.EmailField(unique=True, verbose_name=_("Email"))
|
email = models.EmailField(unique=True, verbose_name=_("Email"))
|
||||||
parent_dealer = models.ForeignKey(
|
parent_dealer = models.ForeignKey(
|
||||||
@ -676,8 +677,7 @@ class SaleQuotation(models.Model):
|
|||||||
]
|
]
|
||||||
dealer = models.ForeignKey(
|
dealer = models.ForeignKey(
|
||||||
Dealer, on_delete=models.CASCADE, related_name="sales", null=True
|
Dealer, on_delete=models.CASCADE, related_name="sales", null=True
|
||||||
)
|
)
|
||||||
entity = models.ForeignKey(EntityModel, on_delete=models.CASCADE)
|
|
||||||
customer = models.ForeignKey(
|
customer = models.ForeignKey(
|
||||||
Customer,
|
Customer,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
|||||||
@ -10,7 +10,6 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# @receiver(post_save, sender=models.SaleQuotation)
|
# @receiver(post_save, sender=models.SaleQuotation)
|
||||||
# def link_quotation_to_entity(sender, instance, created, **kwargs):
|
# def link_quotation_to_entity(sender, instance, created, **kwargs):
|
||||||
# if created:
|
# if created:
|
||||||
@ -163,23 +162,24 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
|
|||||||
@receiver(post_save, sender=models.Customer)
|
@receiver(post_save, sender=models.Customer)
|
||||||
def create_customer(sender, instance, created, **kwargs):
|
def create_customer(sender, instance, created, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
entity = EntityModel.objects.filter(name=instance.dealer.get_root_dealer.name).first()
|
dealer = instance.dealer.get_root_dealer
|
||||||
|
entity = dealer.entity
|
||||||
name = f"{instance.first_name} {instance.middle_name} {instance.last_name}"
|
name = f"{instance.first_name} {instance.middle_name} {instance.last_name}"
|
||||||
|
if entity:
|
||||||
|
entity.create_customer(
|
||||||
|
customer_model_kwargs={
|
||||||
|
"customer_name": name,
|
||||||
|
"address_1": instance.address,
|
||||||
|
"phone": instance.phone_number,
|
||||||
|
"email": instance.email,
|
||||||
|
"sales_tax_rate": 0.15,
|
||||||
|
"active": True,
|
||||||
|
"hidden": False,
|
||||||
|
"additional_info": {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
entity.create_customer(
|
print(f"Customer created: {name}")
|
||||||
customer_model_kwargs={
|
|
||||||
"customer_name": name,
|
|
||||||
"address_1": instance.address,
|
|
||||||
"phone": instance.phone_number,
|
|
||||||
"email": instance.email,
|
|
||||||
"sales_tax_rate": 0.15,
|
|
||||||
"active": True,
|
|
||||||
"hidden": False,
|
|
||||||
"additional_info": {}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"Customer created: {name}")
|
|
||||||
|
|
||||||
|
|
||||||
# Create Item
|
# Create Item
|
||||||
|
|||||||
@ -5,7 +5,6 @@ from django.conf.urls import (
|
|||||||
handler400, handler403, handler404, handler500
|
handler400, handler403, handler404, handler500
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# main URLs
|
# main URLs
|
||||||
path('', views.HomeView.as_view(), name='landing_page'),
|
path('', views.HomeView.as_view(), name='landing_page'),
|
||||||
@ -46,7 +45,6 @@ urlpatterns = [
|
|||||||
path('vendors/<int:pk>/update/', views.VendorUpdateView.as_view(), name='vendor_update'),
|
path('vendors/<int:pk>/update/', views.VendorUpdateView.as_view(), name='vendor_update'),
|
||||||
path('vendors/<int:pk>/delete/', views.VendorDetailView.as_view(), name='vendor_delete'),
|
path('vendors/<int:pk>/delete/', views.VendorDetailView.as_view(), name='vendor_delete'),
|
||||||
|
|
||||||
|
|
||||||
# Car URLs
|
# Car URLs
|
||||||
path('cars/inventory/',
|
path('cars/inventory/',
|
||||||
views.CarInventory.as_view(),
|
views.CarInventory.as_view(),
|
||||||
|
|||||||
@ -22,6 +22,12 @@ def get_financial_value(name,vat=False):
|
|||||||
|
|
||||||
|
|
||||||
def get_total_financials(instance,vat=False):
|
def get_total_financials(instance,vat=False):
|
||||||
|
total = 0
|
||||||
|
if instance.additional_services.exists():
|
||||||
|
total = sum(x.price for x in instance.additional_services.all()) + instance.selling_price
|
||||||
|
|
||||||
|
if vat:
|
||||||
|
total = (total * settings.VAT_RATE).quantize(Decimal('0.01')) + total
|
||||||
# price_after_discount = get_financial_value(instance,"selling_price",vat) - get_financial_value(instance,"discount_amount",vat)
|
# price_after_discount = get_financial_value(instance,"selling_price",vat) - get_financial_value(instance,"discount_amount",vat)
|
||||||
# subtotal = (
|
# subtotal = (
|
||||||
# price_after_discount +
|
# price_after_discount +
|
||||||
@ -30,9 +36,9 @@ def get_total_financials(instance,vat=False):
|
|||||||
# get_financial_value("transportation_fee",vat) +
|
# get_financial_value("transportation_fee",vat) +
|
||||||
# get_financial_value("custom_card_fee",vat))
|
# get_financial_value("custom_card_fee",vat))
|
||||||
|
|
||||||
return 1000
|
return total
|
||||||
|
|
||||||
def get_total(instance):
|
def get_total(instance):
|
||||||
total = get_total_financials(instance)
|
total = get_total_financials(instance,vat=True)
|
||||||
total_vat = get_total_financials(instance,vat=True)
|
# total_vat = get_total_financials(instance,vat=True)
|
||||||
return total + total_vat
|
return total
|
||||||
@ -759,10 +759,8 @@ class QuotationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVie
|
|||||||
permission_required = ("inventory.add_salequotation",)
|
permission_required = ("inventory.add_salequotation",)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
dealer = self.request.user.dealer.get_root_dealer
|
dealer = self.request.user.dealer.get_root_dealer
|
||||||
entity = EntityModel.objects.get(name=dealer.get_root_dealer.name)
|
|
||||||
form.instance.dealer = dealer
|
form.instance.dealer = dealer
|
||||||
form.instance.entity = entity
|
|
||||||
quotation = form.save()
|
quotation = form.save()
|
||||||
selected_cars = form.cleaned_data.get("cars")
|
selected_cars = form.cleaned_data.get("cars")
|
||||||
for car in selected_cars:
|
for car in selected_cars:
|
||||||
@ -785,9 +783,7 @@ class QuotationListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
|||||||
permission_required = ("inventory.view_salequotation",)
|
permission_required = ("inventory.view_salequotation",)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
status = self.request.GET.get("status")
|
status = self.request.GET.get("status")
|
||||||
# queryset = models.SaleQuotation.objects.all()
|
|
||||||
print(self.request.user.dealer.get_root_dealer.sales.all())
|
|
||||||
queryset = self.request.user.dealer.get_root_dealer.sales.all()
|
queryset = self.request.user.dealer.get_root_dealer.sales.all()
|
||||||
if status:
|
if status:
|
||||||
queryset = queryset.filter(status=status)
|
queryset = queryset.filter(status=status)
|
||||||
@ -813,12 +809,13 @@ class QuotationDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie
|
|||||||
@login_required
|
@login_required
|
||||||
def generate_invoice(request, pk):
|
def generate_invoice(request, pk):
|
||||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||||
|
dealer = request.user.dealer.get_root_dealer
|
||||||
if not quotation.is_approved:
|
if not quotation.is_approved:
|
||||||
messages.error(
|
messages.error(
|
||||||
request, "Quotation must be approved before converting to an invoice."
|
request, "Quotation must be approved before converting to an invoice."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
entity = quotation.entity
|
entity = dealer.entity
|
||||||
coa_qs, coa_map = entity.get_all_coa_accounts()
|
coa_qs, coa_map = entity.get_all_coa_accounts()
|
||||||
cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
|
cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
|
||||||
recivable_account = coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
|
recivable_account = coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
|
||||||
@ -979,36 +976,36 @@ def post_quotation(request, pk):
|
|||||||
# document_prefix="SD"
|
# document_prefix="SD"
|
||||||
# )
|
# )
|
||||||
|
|
||||||
journal_entry = JournalEntryModel.objects.create(
|
# journal_entry = JournalEntryModel.objects.create(
|
||||||
entity_unit=entity_unit,
|
# entity_unit=entity_unit,
|
||||||
posted=False,
|
# posted=False,
|
||||||
description=f"Payment for Invoice {invoice_model}",
|
# description=f"Payment for Invoice {invoice_model}",
|
||||||
ledger=ledger,
|
# ledger=ledger,
|
||||||
locked=False,
|
# locked=False,
|
||||||
origin="Payment",
|
# origin="Payment",
|
||||||
)
|
# )
|
||||||
|
|
||||||
TransactionModel.objects.create(
|
# TransactionModel.objects.create(
|
||||||
journal_entry=journal_entry,
|
# journal_entry=journal_entry,
|
||||||
account=cash_account.first(), # Debit Cash
|
# account=cash_account.first(), # Debit Cash
|
||||||
amount=invoice_model.amount_due, # Payment amount
|
# amount=invoice_model.amount_due, # Payment amount
|
||||||
tx_type='debit',
|
# tx_type='debit',
|
||||||
description="Payment Received",
|
# description="Payment Received",
|
||||||
)
|
# )
|
||||||
|
|
||||||
TransactionModel.objects.create(
|
# TransactionModel.objects.create(
|
||||||
journal_entry=journal_entry,
|
# journal_entry=journal_entry,
|
||||||
account=recivable_account.first(), # Credit Accounts Receivable
|
# account=recivable_account.first(), # Credit Accounts Receivable
|
||||||
amount=invoice_model.amount_due, # Payment amount
|
# amount=invoice_model.amount_due, # Payment amount
|
||||||
tx_type='credit',
|
# tx_type='credit',
|
||||||
description="Payment Received",
|
# description="Payment Received",
|
||||||
)
|
# )
|
||||||
journal_entry.posted = True
|
# journal_entry.posted = True
|
||||||
qoutation.posted = True
|
# qoutation.posted = True
|
||||||
qoutation.save()
|
# qoutation.save()
|
||||||
journal_entry.save()
|
# journal_entry.save()
|
||||||
messages.success(request, "Invoice posted")
|
# messages.success(request, "Invoice posted")
|
||||||
return redirect("quotation_detail", pk=pk)
|
# return redirect("quotation_detail", pk=pk)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def mark_quotation(request, pk):
|
def mark_quotation(request, pk):
|
||||||
@ -1358,8 +1355,9 @@ def download_quotation_pdf(request, quotation_id):
|
|||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def invoice_detail(request,pk):
|
def invoice_detail(request,pk):
|
||||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||||
entity = quotation.entity
|
dealer = request.user.dealer.get_root_dealer
|
||||||
|
entity = dealer.entity
|
||||||
customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first()
|
customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first()
|
||||||
invoice_model = entity.get_invoices()
|
invoice_model = entity.get_invoices()
|
||||||
|
|
||||||
@ -1368,8 +1366,9 @@ def invoice_detail(request,pk):
|
|||||||
return redirect('quotation_detail', pk=pk)
|
return redirect('quotation_detail', pk=pk)
|
||||||
@login_required
|
@login_required
|
||||||
def payment_invoice(request,pk):
|
def payment_invoice(request,pk):
|
||||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||||
entity = quotation.entity
|
dealer = request.user.dealer.get_root_dealer
|
||||||
|
entity = dealer.entity
|
||||||
customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first()
|
customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first()
|
||||||
invoice_model = entity.get_invoices()
|
invoice_model = entity.get_invoices()
|
||||||
invoice = invoice_model.filter(customer=customer,date_draft=quotation.date_draft).first()
|
invoice = invoice_model.filter(customer=customer,date_draft=quotation.date_draft).first()
|
||||||
@ -1398,13 +1397,13 @@ def payment_invoice(request,pk):
|
|||||||
|
|
||||||
def payment_create(request, pk):
|
def payment_create(request, pk):
|
||||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||||
|
dealer = request.user.dealer.get_root_dealer
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = forms.PaymentForm(request.POST)
|
form = forms.PaymentForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.instance.quotation = quotation
|
form.instance.quotation = quotation
|
||||||
insatnce = form.save()
|
insatnce = form.save()
|
||||||
|
entity = dealer.entity
|
||||||
entity = quotation.entity
|
|
||||||
customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first()
|
customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first()
|
||||||
coa_qs, coa_map = entity.get_all_coa_accounts()
|
coa_qs, coa_map = entity.get_all_coa_accounts()
|
||||||
cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
|
cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
|
||||||
|
|||||||
@ -42,6 +42,7 @@
|
|||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
|
||||||
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
|
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
|
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/sweetalert2@11.15.3/dist/sweetalert2.min.css" rel="stylesheet">
|
||||||
{% if LANGUAGE_CODE == 'ar' %}
|
{% if LANGUAGE_CODE == 'ar' %}
|
||||||
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
||||||
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
||||||
@ -418,7 +419,9 @@
|
|||||||
{% if user.is_authenticated and user.dealer or user.subdealer%}
|
{% if user.is_authenticated and user.dealer or user.subdealer%}
|
||||||
<li class="nav-item dropdown"><a class="nav-link lh-1 pe-0" id="navbarDropdownUser" role="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-haspopup="true" aria-expanded="false">
|
<li class="nav-item dropdown"><a class="nav-link lh-1 pe-0" id="navbarDropdownUser" role="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-haspopup="true" aria-expanded="false">
|
||||||
<div class="avatar avatar-l ">
|
<div class="avatar avatar-l ">
|
||||||
|
{% if user.dealer.logo %}
|
||||||
<img class="rounded-circle " src="{{ user.dealer.logo.url }}" alt="" />
|
<img class="rounded-circle " src="{{ user.dealer.logo.url }}" alt="" />
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
@ -427,8 +430,9 @@
|
|||||||
<div class="card-body p-0">
|
<div class="card-body p-0">
|
||||||
<div class="text-center pt-4 pb-3">
|
<div class="text-center pt-4 pb-3">
|
||||||
<div class="avatar avatar-xl ">
|
<div class="avatar avatar-xl ">
|
||||||
|
{% if user.dealer.logo %}
|
||||||
<img class="rounded-circle " src="{{ user.dealer.logo.url }}" alt="" />
|
<img class="rounded-circle " src="{{ user.dealer.logo.url }}" alt="" />
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<h6 class="mt-2 text-body-emphasis">{{ user.dealer.get_local_name }}</h6>
|
<h6 class="mt-2 text-body-emphasis">{{ user.dealer.get_local_name }}</h6>
|
||||||
</div>
|
</div>
|
||||||
@ -710,6 +714,7 @@
|
|||||||
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
||||||
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
||||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.15.3/dist/sweetalert2.all.min.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user