update
This commit is contained in:
parent
15e2b227a6
commit
9051840904
@ -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
|
||||
|
||||
@ -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',
|
||||
),
|
||||
]
|
||||
19
inventory/migrations/0005_additionalservices_arabic_name.py
Normal file
19
inventory/migrations/0005_additionalservices_arabic_name.py
Normal 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,
|
||||
),
|
||||
]
|
||||
@ -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',
|
||||
),
|
||||
]
|
||||
19
inventory/migrations/0007_additionalservices_arabic_name.py
Normal file
19
inventory/migrations/0007_additionalservices_arabic_name.py
Normal 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,
|
||||
),
|
||||
]
|
||||
18
inventory/migrations/0008_salequotation_status.py
Normal file
18
inventory/migrations/0008_salequotation_status.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
18
inventory/migrations/0011_salequotation_posted.py
Normal file
18
inventory/migrations/0011_salequotation_posted.py
Normal 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),
|
||||
),
|
||||
]
|
||||
18
inventory/migrations/0012_salequotation_payment_id.py
Normal file
18
inventory/migrations/0012_salequotation_payment_id.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
18
inventory/migrations/0013_salequotation_is_paid.py
Normal file
18
inventory/migrations/0013_salequotation_is_paid.py
Normal 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),
|
||||
),
|
||||
]
|
||||
@ -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):
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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'),
|
||||
|
||||
@ -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)
|
||||
77
templates/sales/invoice/invoice_detail.html
Normal file
77
templates/sales/invoice/invoice_detail.html
Normal 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 %}
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user