update
This commit is contained in:
parent
5397363716
commit
940b9c0292
@ -25,6 +25,7 @@ admin.site.register(models.Organization)
|
||||
admin.site.register(models.Representative)
|
||||
admin.site.register(models.CarTrim)
|
||||
admin.site.register(models.AdditionalServices)
|
||||
admin.site.register(models.Payment)
|
||||
|
||||
@admin.register(models.CarMake)
|
||||
class CarMakeAdmin(admin.ModelAdmin):
|
||||
|
||||
@ -17,8 +17,9 @@ from .models import (
|
||||
CarLocation,
|
||||
Organization,
|
||||
Representative,
|
||||
SaleQuotationCar,
|
||||
AdditionalServices
|
||||
Payment,
|
||||
SaleQuotationCar,
|
||||
AdditionalServices
|
||||
|
||||
)
|
||||
from django.forms import ModelMultipleChoiceField
|
||||
@ -28,6 +29,10 @@ from django.forms import formset_factory
|
||||
|
||||
|
||||
|
||||
class PaymentForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Payment
|
||||
fields = ['amount','payment_method', 'reference_number']
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
class Meta:
|
||||
|
||||
43
inventory/migrations/0014_payment_refund.py
Normal file
43
inventory/migrations/0014_payment_refund.py
Normal file
@ -0,0 +1,43 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-24 13:56
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0013_salequotation_is_paid'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Payment',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')),
|
||||
('payment_method', models.CharField(choices=[('cash', 'cash'), ('credit', 'credit'), ('transfer', 'transfer'), ('debit', 'debit'), ('SADAD', 'SADAD')], max_length=50, verbose_name='method')),
|
||||
('reference_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='reference number')),
|
||||
('payment_date', models.DateField(auto_now_add=True, verbose_name='date')),
|
||||
('quotation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='inventory.salequotation')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'payment',
|
||||
'verbose_name_plural': 'payments',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Refund',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')),
|
||||
('reason', models.TextField(blank=True, verbose_name='reason')),
|
||||
('refund_date', models.DateField(auto_now_add=True, verbose_name='refund date')),
|
||||
('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='refund', to='inventory.payment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'refund',
|
||||
'verbose_name_plural': 'refunds',
|
||||
},
|
||||
),
|
||||
]
|
||||
18
inventory/migrations/0015_alter_salequotation_payment_id.py
Normal file
18
inventory/migrations/0015_alter_salequotation_payment_id.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-24 14:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0014_payment_refund'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='salequotation',
|
||||
name='payment_id',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Payment ID'),
|
||||
),
|
||||
]
|
||||
@ -675,7 +675,7 @@ class SaleQuotation(models.Model):
|
||||
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"))
|
||||
payment_id = models.CharField(max_length=255, 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'))
|
||||
@ -838,7 +838,7 @@ class Payment(models.Model):
|
||||
verbose_name_plural = _("payments")
|
||||
|
||||
def __str__(self):
|
||||
return f"Payment of {self.amount} on {self.date} for {self.order}"
|
||||
return f"Payment of {self.amount} on {self.payment_date} for {self.quotation}"
|
||||
|
||||
|
||||
class Refund(models.Model):
|
||||
|
||||
@ -92,7 +92,8 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
activate_accounts=True, coa_model=default_coa
|
||||
)
|
||||
print(f"Ledger entity created for Dealer: {instance.name}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to create Ledger entity for Dealer: {instance.name}. Error: {e}")
|
||||
# entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code=1010,
|
||||
|
||||
@ -82,6 +82,10 @@ urlpatterns = [
|
||||
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'),
|
||||
|
||||
#Payment URLs
|
||||
# path('sales/quotations/<int:pk>/payment/', views.PaymentCreateView.as_view(), name='payment_create'),
|
||||
path('sales/quotations/<int:pk>/payment/', views.payment_create, name='payment_create'),
|
||||
|
||||
# Users URLs
|
||||
path('user/create/', views.UserCreateView.as_view(), name='user_create'),
|
||||
path('user/<int:pk>/update/', views.UserUpdateView.as_view(), name='user_update'),
|
||||
|
||||
@ -859,7 +859,7 @@ def generate_invoice(request, pk):
|
||||
origin="Payment",
|
||||
)
|
||||
|
||||
quotation.payment_id = journal_entry.pk
|
||||
quotation.payment_id = journal_entry.pk
|
||||
quotation.is_approved = True
|
||||
date = datetime.datetime.now()
|
||||
quotation.date_draft = date
|
||||
@ -867,18 +867,18 @@ def generate_invoice(request, pk):
|
||||
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)
|
||||
|
||||
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"))
|
||||
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":
|
||||
# messages.error(request, "Quotation is already approved")
|
||||
@ -968,16 +968,16 @@ def post_quotation(request, pk):
|
||||
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()
|
||||
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"
|
||||
)
|
||||
# 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,
|
||||
@ -1017,58 +1017,44 @@ def mark_quotation(request, pk):
|
||||
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 = 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_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 = 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()
|
||||
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.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 = 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"))
|
||||
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)
|
||||
|
||||
|
||||
@ -1389,4 +1375,73 @@ def payment_invoice(request,pk):
|
||||
invoice = invoice_model.filter(customer=customer,date_draft=quotation.date_draft).first()
|
||||
|
||||
|
||||
return redirect('quotation_detail', pk=pk)
|
||||
return redirect('quotation_detail', pk=pk)
|
||||
|
||||
|
||||
# class PaymentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
# model = models.Payment
|
||||
# form_class = forms.PaymentForm
|
||||
# template_name = "sales/payments/payment_form.html"
|
||||
# success_url = reverse_lazy("quotation_list")
|
||||
# success_message = "Payment created successfully."
|
||||
|
||||
# def form_valid(self, form):
|
||||
# quotation = get_object_or_404(models.SaleQuotation, pk=self.kwargs["pk"])
|
||||
# form.instance.quotation = quotation
|
||||
# form.save()
|
||||
# return super().form_valid(form)
|
||||
# def get_context_data(self, **kwargs):
|
||||
# context = super().get_context_data(**kwargs)
|
||||
# context["quotation"] = get_object_or_404(models.SaleQuotation, pk=self.kwargs["pk"])
|
||||
# return context
|
||||
|
||||
|
||||
def payment_create(request, pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
if request.method == "POST":
|
||||
form = forms.PaymentForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.instance.quotation = quotation
|
||||
insatnce = form.save()
|
||||
|
||||
entity = quotation.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.get_root_dealer.user)
|
||||
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})
|
||||
|
||||
0
templates/sales/payments/confirm.html
Normal file
0
templates/sales/payments/confirm.html
Normal file
24
templates/sales/payments/payment_create.html
Normal file
24
templates/sales/payments/payment_create.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load i18n %}
|
||||
{% block title %}{{ _("Payyment Create") }}{% endblock title %}
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ _("Payment Create") }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="{% url 'payment_create' pk=quotation.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@ -148,13 +148,9 @@
|
||||
{% 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 %}
|
||||
<a href="{% url 'mark_quotation' quotation.pk %}?status=approved" class="btn btn-info">Approve Quotation</a>
|
||||
{% elif quotation.status == 'Approved' %}
|
||||
<a href="{% url 'payment_create' quotation.pk %}" class="btn btn-success">Pay</a>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user