This commit is contained in:
gitea 2024-12-24 13:49:36 +00:00
parent 15e2b227a6
commit 9051840904
18 changed files with 622 additions and 94 deletions

View File

@ -107,13 +107,23 @@ WSGI_APPLICATION = 'car_inventory.wsgi.application'
DATABASES = {
"default": {
"ENGINE": "django_prometheus.db.backends.postgresql",
"NAME": "murad_haikal",
"USER": "f95166",
"PASSWORD": "Kfsh&rc9788",
"NAME": "haikal",
"USER": "haikal",
"PASSWORD": "haikal",
"HOST": "localhost",
"PORT": 5432,
}
}
# DATABASES = {
# "default": {
# "ENGINE": "django_prometheus.db.backends.postgresql",
# "NAME": "murad_haikal",
# "USER": "f95166",
# "PASSWORD": "Kfsh&rc9788",
# "HOST": "localhost",
# "PORT": 5432,
# }
# }
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators

View File

@ -0,0 +1,17 @@
# Generated by Django 4.2.17 on 2024-12-24 08:07
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0003_remove_additionalservices_display_name_and_more'),
]
operations = [
migrations.RemoveField(
model_name='additionalservices',
name='arabic_name',
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2024-12-24 08:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0004_remove_additionalservices_arabic_name'),
]
operations = [
migrations.AddField(
model_name='additionalservices',
name='arabic_name',
field=models.CharField(default='-', max_length=255, verbose_name='Arabic Name'),
preserve_default=False,
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 4.2.17 on 2024-12-24 08:10
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0005_additionalservices_arabic_name'),
]
operations = [
migrations.RemoveField(
model_name='additionalservices',
name='arabic_name',
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2024-12-24 08:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0006_remove_additionalservices_arabic_name'),
]
operations = [
migrations.AddField(
model_name='additionalservices',
name='arabic_name',
field=models.CharField(default='-', max_length=255, verbose_name='Arabic Name'),
preserve_default=False,
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2024-12-24 08:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0007_additionalservices_arabic_name'),
]
operations = [
migrations.AddField(
model_name='salequotation',
name='status',
field=models.CharField(choices=[('DRAFT', 'Draft'), ('CONFIRMED', 'Confirmed'), ('CANCELED', 'Canceled')], default='DRAFT', max_length=10, verbose_name='Status'),
),
]

View File

@ -0,0 +1,48 @@
# Generated by Django 4.2.17 on 2024-12-24 09:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0008_salequotation_status'),
]
operations = [
migrations.AddField(
model_name='salequotation',
name='date_approved',
field=models.DateField(blank=True, null=True, verbose_name='Approved Date'),
),
migrations.AddField(
model_name='salequotation',
name='date_canceled',
field=models.DateField(blank=True, null=True, verbose_name='Canceled Date'),
),
migrations.AddField(
model_name='salequotation',
name='date_draft',
field=models.DateField(blank=True, null=True, verbose_name='Draft Date'),
),
migrations.AddField(
model_name='salequotation',
name='date_in_review',
field=models.DateField(blank=True, null=True, verbose_name='In Review Date'),
),
migrations.AddField(
model_name='salequotation',
name='date_paid',
field=models.DateField(blank=True, null=True, verbose_name='Paid Date'),
),
migrations.AddField(
model_name='salequotation',
name='date_void',
field=models.DateField(blank=True, null=True, verbose_name='Void Date'),
),
migrations.AlterField(
model_name='salequotation',
name='status',
field=models.CharField(choices=[('Draft', 'Draft'), ('Approved', 'Approved'), ('In Review', 'In Review'), ('Paid', 'Paid')], default='Draft', max_length=10, verbose_name='Status'),
),
]

View File

@ -0,0 +1,43 @@
# Generated by Django 4.2.17 on 2024-12-24 09:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0009_salequotation_date_approved_and_more'),
]
operations = [
migrations.AlterField(
model_name='salequotation',
name='date_approved',
field=models.DateTimeField(blank=True, null=True, verbose_name='Approved Date'),
),
migrations.AlterField(
model_name='salequotation',
name='date_canceled',
field=models.DateTimeField(blank=True, null=True, verbose_name='Canceled Date'),
),
migrations.AlterField(
model_name='salequotation',
name='date_draft',
field=models.DateTimeField(blank=True, null=True, verbose_name='Draft Date'),
),
migrations.AlterField(
model_name='salequotation',
name='date_in_review',
field=models.DateTimeField(blank=True, null=True, verbose_name='In Review Date'),
),
migrations.AlterField(
model_name='salequotation',
name='date_paid',
field=models.DateTimeField(blank=True, null=True, verbose_name='Paid Date'),
),
migrations.AlterField(
model_name='salequotation',
name='date_void',
field=models.DateTimeField(blank=True, null=True, verbose_name='Void Date'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2024-12-24 10:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0010_alter_salequotation_date_approved_and_more'),
]
operations = [
migrations.AddField(
model_name='salequotation',
name='posted',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2024-12-24 13:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0011_salequotation_posted'),
]
operations = [
migrations.AddField(
model_name='salequotation',
name='payment_id',
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='Payment ID'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2024-12-24 13:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0012_salequotation_payment_id'),
]
operations = [
migrations.AddField(
model_name='salequotation',
name='is_paid',
field=models.BooleanField(default=False),
),
]

View File

@ -645,9 +645,10 @@ class SaleQuotation(models.Model):
quotation_number = models.CharField(max_length=10, unique=True)
STATUS_CHOICES = [
("DRAFT", _("Draft")),
("CONFIRMED", _("Confirmed")),
("CANCELED", _("Canceled")),
("Draft", _("Draft")),
("Approved", _("Approved")),
("In Review", _("In Review")),
("Paid", _("Paid")),
]
dealer = models.ForeignKey(
Dealer, on_delete=models.CASCADE, related_name="sales", null=True
@ -667,11 +668,21 @@ class SaleQuotation(models.Model):
)
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
is_approved = models.BooleanField(default=False)
# status = models.CharField(
# max_length=10, choices=STATUS_CHOICES, default="DRAFT", verbose_name=_("Status")
# )
status = models.CharField(
max_length=10, choices=STATUS_CHOICES, default="Draft", verbose_name=_("Status")
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
posted = models.BooleanField(default=False)
payment_id = models.CharField(max_length=10, null=True, blank=True, verbose_name=_("Payment ID"))
is_paid = models.BooleanField(default=False)
date_draft = models.DateTimeField(null=True, blank=True, verbose_name=_('Draft Date'))
date_in_review = models.DateTimeField(null=True, blank=True, verbose_name=_('In Review Date'))
date_approved = models.DateTimeField(null=True, blank=True, verbose_name=_('Approved Date'))
date_paid = models.DateTimeField(null=True, blank=True, verbose_name=_('Paid Date'))
date_void = models.DateTimeField(null=True, blank=True, verbose_name=_('Void Date'))
date_canceled = models.DateTimeField(null=True, blank=True, verbose_name=_('Canceled Date'))
@property
def total_quantity(self):

View File

@ -2,7 +2,7 @@ from random import randint
from django.db.models.signals import post_save, post_delete, pre_delete
from django.dispatch import receiver
<<<<<<< HEAD
from django_ledger.views import JournalEntryCreateView
from django_ledger.models import (
EntityModel,
VendorModel,
@ -13,9 +13,6 @@ from django_ledger.models import (
ItemModel
)
from django.contrib.auth import get_user_model
=======
from django_ledger.models import EntityModel
>>>>>>> d57702ea7abaa3ad61315b9d593fbd8c32e314e2
from django.utils.translation import gettext_lazy as _
@ -126,7 +123,6 @@ def update_car_status_on_reservation_delete(sender, instance, **kwargs):
@receiver(post_save, sender=models.Dealer)
def create_ledger_entity(sender, instance, created, **kwargs):
if created:
<<<<<<< HEAD
# Group.objects.get(name=instance.dealer_type.lower()).user_set.add(instance.user)
try:
entity, created = EntityModel.objects.get_or_create(
@ -146,24 +142,6 @@ def create_ledger_entity(sender, instance, created, **kwargs):
activate_accounts=True, coa_model=default_coa
)
print(f"Ledger entity created for Dealer: {instance.name}")
=======
entity, created = EntityModel.objects.get_or_create(
name=instance.get_root_dealer.name,
admin=instance.get_root_dealer.user,
# address_1=instance.address,
accrual_method=False,
fy_start_month=1,
depth=0,
)
print(entity)
if created:
default_coa = entity.create_chart_of_accounts(assign_as_default=True,
commit=True,
coa_name=_("Chart of Accounts"))
if default_coa:
entity.populate_default_coa(activate_accounts=True, coa_model=default_coa)
print(f"Ledger entity created for Dealer: {instance.name}")
>>>>>>> d57702ea7abaa3ad61315b9d593fbd8c32e314e2
entity.create_account(
coa_model=default_coa,
@ -228,7 +206,7 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
entity.create_vendor(
name=instance.name,
vendor_name=instance.name,
vendor_number=instance.crn,
address_1=instance.address,
phone=instance.phone_number,

View File

@ -78,7 +78,9 @@ urlpatterns = [
path('sales/orders/detail/<int:order_id>/', views.SalesOrderDetailView.as_view(), name='order_detail'),
path('quotation/<int:quotation_id>/pdf/', views.download_quotation_pdf, name='quotation_pdf'),
path('generate_invoice/<int:pk>/', views.generate_invoice, name='generate_invoice'),
path('sales/quotations/<int:pk>/mark_quotation/', views.mark_quotation, name='mark_quotation'),
path('sales/quotations/<int:pk>/post_quotation/', views.post_quotation, name='post_quotation'),
path('sales/quotations/<int:pk>/invoice_detail/', views.invoice_detail, name='invoice_detail'),
# Users URLs
path('user/create/', views.UserCreateView.as_view(), name='user_create'),

View File

@ -1,8 +1,10 @@
from django_ledger.models import EntityModel, InvoiceModel,EntityUnitModel,LedgerModel
import logging
import json
import datetime
from decimal import Decimal
from django_ledger.models import TransactionModel, AccountModel,JournalEntryModelfrom .pdf_generator import generate_quotation_pdf
from django_ledger.models import TransactionModel, AccountModel,JournalEntryModel
from .pdf_generator import generate_quotation_pdf
from django.shortcuts import HttpResponse
from django.template.loader import render_to_string
# from weasyprint import HTML
@ -817,18 +819,21 @@ def generate_invoice(request, pk):
entity = quotation.entity
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_NET_30,
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,
@ -841,66 +846,229 @@ def generate_invoice(request, pk):
invoice_itemtxs = invoice_model.migrate_itemtxs(
itemtxs=invoice_itemtxs, commit=True, operation=InvoiceModel.ITEMIZE_APPEND
)
ledger = entity.create_ledger(
name=f"Payment Ledger for Invoice {invoice_model}",
posted=True
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",
)
entity_unit,created = EntityUnitModel.objects.get_or_create(
name="Sales Department",
entity=entity,
document_prefix="SD"
)
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()
# invoice_model = invoice_model.filter(date_draft=quotation.date_draft).first()
# 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
# qoutation.date_in_review = date
# qoutation.status = "In Review"
# invoice_model.save()
# qoutation.save()
# messages.success(request, _("Quotation Reviewed"))
# elif status == "approved":
# if qoutation.status == "Approved":
# messages.error(request, "Quotation is already approved")
# return redirect("quotation_detail", pk=pk)
journal_entry = JournalEntryModel.objects.create(
entity_unit=entity_unit,
posted=False,
description=f"Payment for Invoice {invoice_model}",
ledger=ledger,
locked=False,
origin="Payment",
)
# 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.get_root_dealer.user)
# invoice_model.date_approved = date
# qoutation.date_approved = date
# invoice_model.save()
# qoutation.status = "Approved"
# qoutation.save()
# messages.success(request, _("Quotation Approved"))
# ledger = entity.create_ledger(
# name=f"Payment Ledger for Invoice {invoice_model}",
# posted=True
# )
# entity_unit,created = EntityUnitModel.objects.get_or_create(
# name="Sales Department",
# entity=entity,
# document_prefix="SD"
# )
# journal_entry = JournalEntryModel.objects.create(
# entity_unit=entity_unit,
# posted=False,
# description=f"Payment for Invoice {invoice_model}",
# ledger=ledger,
# locked=False,
# origin="Payment",
# )
accounts_receivable = coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable").first()
if not accounts_receivable:
accounts_receivable = entity.create_account(
code="AR",
role="asset",
name="Accounts Receivable",
coa_model=coa_qs.first(),
balance_type="credit"
)
# accounts_receivable = coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable").first()
# if not accounts_receivable:
# accounts_receivable = entity.create_account(
# code="AR",
# role="asset",
# name="Accounts Receivable",
# coa_model=coa_qs.first(),
# balance_type="credit"
# )
TransactionModel.objects.create(
journal_entry=journal_entry,
account=cash_account.first(), # Debit Cash
amount=invoice_model.amount_due, # Payment amount
tx_type='debit',
description="Payment Received",
)
# TransactionModel.objects.create(
# journal_entry=journal_entry,
# account=cash_account.first(), # Debit Cash
# amount=invoice_model.amount_due, # Payment amount
# tx_type='debit',
# description="Payment Received",
# )
TransactionModel.objects.create(
journal_entry=journal_entry,
account=accounts_receivable, # Credit Accounts Receivable
amount=invoice_model.amount_due, # Payment amount
tx_type='credit',
description="Payment Received",
)
invoice_model.mark_as_review()
print("reviewed")
invoice_model.mark_as_approved(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user)
print("approved")
invoice_model.mark_as_paid(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user)
print("paid")
# TransactionModel.objects.create(
# journal_entry=journal_entry,
# account=accounts_receivable, # Credit Accounts Receivable
# amount=invoice_model.amount_due, # Payment amount
# tx_type='credit',
# description="Payment Received",
# )
# invoice_model.mark_as_review()
# print("reviewed")
# invoice_model.mark_as_approved(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user)
# print("approved")
# 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)
# 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)
if qoutation.posted:
messages.error(request, "Quotation is already posted")
return redirect("quotation_detail", pk=pk)
entity = qoutation.entity
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)
entity_unit,created = EntityUnitModel.objects.get_or_create(
name="Sales Department",
entity=entity,
document_prefix="SD"
)
journal_entry = JournalEntryModel.objects.create(
entity_unit=entity_unit,
posted=False,
description=f"Payment for Invoice {invoice_model}",
ledger=ledger,
locked=False,
origin="Payment",
)
TransactionModel.objects.create(
journal_entry=journal_entry,
account=cash_account.first(), # Debit Cash
amount=invoice_model.amount_due, # Payment amount
tx_type='debit',
description="Payment Received",
)
TransactionModel.objects.create(
journal_entry=journal_entry,
account=recivable_account.first(), # Credit Accounts Receivable
amount=invoice_model.amount_due, # Payment amount
tx_type='credit',
description="Payment Received",
)
journal_entry.posted = True
qoutation.posted = True
qoutation.save()
journal_entry.save()
messages.success(request, "Invoice posted")
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")
entity = qoutation.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 == "in_review":
# if qoutation.status == "In Review":
# messages.error(request, "Quotation is already in review")
# return redirect("quotation_detail", pk=pk)
# invoice_model = invoice_model.filter(date_draft=qoutation.date_draft).first()
# 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
# qoutation.date_in_review = date
# qoutation.status = "In Review"
# invoice_model.save()
# qoutation.save()
# messages.success(request, _("Quotation Reviewed"))
# elif 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.get_root_dealer.user)
# invoice_model.date_approved = date
# qoutation.date_approved = date
# invoice_model.save()
# qoutation.status = "Approved"
# qoutation.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.get_root_dealer.user)
# 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)
@ -1197,4 +1365,25 @@ def download_quotation_pdf(request, quotation_id):
return response
except models.SaleQuotation.DoesNotExist:
return HttpResponse("Quotation not found", status=404)
return HttpResponse("Quotation not found", status=404)
@login_required
def invoice_detail(request,pk):
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
entity = quotation.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)
entity = quotation.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)

View File

@ -0,0 +1,77 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{{ _("View Customer") }}{% endblock title %}
{% block content %}
<!-- Delete Modal -->
<div class="modal fade" id="deleteModal"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="deleteModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-sm ">
<div class="modal-content rounded">
<div class="modal-body d-flex justify-content-center">
<h1 class="text-danger me-2"><i class="bi bi-exclamation-diamond-fill"></i></h1>
<span class="text-danger">
{% trans "Are you sure you want to delete this customer?" %}
</span>
</div>
<div class="btn-group">
<button type="button"
class="btn btn-sm btn-secondary"
data-bs-dismiss="modal">
{% trans 'No' %}
</button>
<a type="button"
class="btn btn-sm btn-danger"
href="{% url 'customer_delete' customer.id %}">
{% trans 'Yes' %}
</a>
</div>
</div>
</div>
</div>
<div class="container my-5">
<div class="card rounded ">
<div class="card-header bg-primary text-white ">
<p class="mb-0">{{ _("Customer Details") }}</p>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<p><strong>{{ _("First Name") }}:</strong> {{ customer.first_name }}</p>
<p><strong>{{ _("Middle Name") }}:</strong> {{ customer.middle_name }}</p>
<p><strong>{{ _("Last Name") }}:</strong> {{ customer.last_name }}</p>
</div>
<div class="col-md-6">
<p><strong>{{ _("Email") }}:</strong> {{ customer.email }}</p>
<p><strong>{{ _("National ID") }}:</strong> {{ customer.national_id }}</p>
<p><strong>{{ _("Phone Number") }}:</strong> {{ customer.phone_number }}</p>
<p><strong>{{ _("Address") }}:</strong> {{ customer.address }}</p>
</div>
</div>
</div>
<div class="card-footer d-flex ">
<a class="btn btn-sm btn-primary me-1" href="{% url 'customer_update' customer.id %}">
<!--<i class="bi bi-pencil-square"></i> -->
{{ _("Edit") }}
</a>
<a class="btn btn-sm btn-danger me-1"
data-bs-toggle="modal"
data-bs-target="#deleteModal">
<!--<i class="bi bi-trash-fill"></i>-->
{{ _("Delete") }}
</a>
<a class="btn btn-sm btn-secondary"
href="{% url 'customer_list' %}">
<!--<i class="bi bi-arrow-left-square-fill"></i>-->
{% trans "Back to List" %}
</a>
</div>
</div>
</div>
{% endblock %}

View File

@ -2,6 +2,7 @@
{% load custom_filters %}
{% load i18n %}
{% block content %}
<!-- Confirm Modal -->
<div class="modal fade" id="confirmModal" tabindex="-1" aria-labelledby="confirmModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
@ -50,6 +51,17 @@
<p><strong>{% trans "Quotation No" %}:</strong> {{ quotation.display_quotation_number }}</p>
<p><strong>{% trans "Date" %}:</strong> {{ quotation.created_at|date }}</p>
<p><strong>{% trans "Remarks" %}:</strong> {{ quotation.remarks }}</p>
<p><strong>{% trans "Status" %}:</strong>
{% if quotation.status == 'Draft' %}
<span class="badge rounded-pill bg-secondary">{{ quotation.status|capfirst }}</span>
{% elif quotation.status == 'In Review' %}
<span class="badge rounded-pill bg-warning">{{ quotation.status|capfirst }}</span>
{% elif quotation.status == 'Approved' %}
<span class="badge rounded-pill bg-info">{{ quotation.status|capfirst }}</span>
{% elif quotation.status == 'Paid' %}
<span class="badge rounded-pill bg-success"><i class="fas fa-check"></i>{{ quotation.status|capfirst }}</span>
{% endif %}
</p>
</div>
</div>
@ -119,22 +131,32 @@
</table>
</div>
<div class="card-footer text-end">
<a href="{% url 'quotation_list' %}" class="btn btn-secondary">{% trans "Back to Quotations" %}</a>
{% if perms.inventory.change_carfinance and quotation.status == 'DRAFT' %}
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#customCardModal">{% trans "Approve Quotation" %}</button>
{% endif %}
<a href="{% url 'quotation_list' %}" class="btn btn-secondary">{% trans "Back to Quotations" %}</a>
<a href="{% url 'quotation_pdf' quotation.pk %}" class="btn btn-primary">Download as PDF</a>
{% if not quotation.is_approved %}
<button type="button"
class="btn btn-success"
data-bs-toggle="modal"
data-bs-target="#confirmModal">
{% trans 'Approve' %}
{% trans 'Acccept' %}
</button>
{% endif %}
{% if quotation.is_approved %}
<a href="{% url 'generate_invoice' quotation.pk %}" class="btn btn-success">Generate Invoice</a>
{% if quotation.status == 'Draft' and not quotation.is_approved %}
<a href="{% url 'confirm_quotation' quotation.pk %}" class="btn btn-secondary">Accept & Approve</a>
{% elif not quotation.date_draft %}
<a href="{% url 'generate_invoice' quotation.pk %}" class="btn btn-secondary">Generate Invoice</a>
{% elif quotation.status == 'Draft' and quotation.is_approved %}
<a href="{% url 'mark_quotation' quotation.pk %}?status=in_review" class="btn btn-secondary">Mark As Review</a>
{% elif quotation.status == 'In Review' %}
<a href="{% url 'mark_quotation' quotation.pk %}?status=approved" class="btn btn-info">Approve</a>
{% elif quotation.status == 'Approved' %}
<a href="{% url 'mark_quotation' quotation.pk %}?status=paid" class="btn btn-success">Mark As Paid</a>
{% else %}
{% if not quotation.posted %}
<a href="{% url 'post_quotation' quotation.pk %}" class="btn btn-success">Post</a>
{% endif %}
{% endif %}
</div>
</div>

View File

@ -28,10 +28,14 @@
<td>{{ quotation.quotation_cars.count }}</td>
<td>{{ quotation.total_vat }}</td>
<td>
{% if quotation.is_approved %}
<span class="badge rounded-pill bg-success">Approved</span>
{% else %}
<span class="badge rounded-pill bg-warning">Pending For Approval</span>
{% if quotation.status == 'Draft' %}
<span class="badge rounded-pill bg-secondary">{{quotation.status|capfirst}}</span>
{% elif quotation.status == 'In Review' %}
<span class="badge rounded-pill bg-warning">{{quotation.status|capfirst}}</span>
{% elif quotation.status == 'Approved' %}
<span class="badge rounded-pill bg-info">{{quotation.status|capfirst}}</span>
{% elif quotation.status == 'Paid' %}
<span class="badge rounded-pill bg-success">{{quotation.status|capfirst}}</span>
{% endif %}
</td>
<td>{{ quotation.created_at|date:"d/m/Y H:i" }}</td>