some changes

This commit is contained in:
gitea 2025-01-15 13:30:36 +00:00
parent 78ffb13703
commit 9437fa10ce
5 changed files with 427 additions and 48 deletions

View File

@ -179,6 +179,10 @@ urlpatterns = [
path('items/expeneses/', views.ItemExpenseListView.as_view(), name='item_expense_list'),
path('items/expeneses/create/', views.ItemExpenseCreateView.as_view(), name='item_expense_create'),
path('items/expeneses/<uuid:pk>/update/', views.ItemExpenseUpdateView.as_view(), name='item_expense_update'),
# Bills
path('items/bills/', views.BillListView.as_view(), name='bill_list'),
path('items/bills/create/', views.BillCreateView.as_view(), name='bill_create'),
# path('items/bills/<uuid:pk>/update/', views.ItemExpenseUpdateView.as_view(), name='item_expense_update'),
]

View File

@ -14,11 +14,13 @@ from django_ledger.models import (
CustomerModel,
LedgerModel,
ItemModel,
BillModel
)
from django_ledger.forms.bank_account import (
BankAccountCreateForm,
BankAccountUpdateForm,
)
from django_ledger.forms.bill import BillModelCreateForm
from django_ledger.forms.invoice import (
DraftInvoiceModelUpdateForm,
ApprovedInvoiceModelUpdateForm,
@ -89,7 +91,6 @@ import numpy as np
from pyzbar.pyzbar import decode
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
@ -439,7 +440,7 @@ class AjaxHandlerView(LoginRequiredMixin, View):
return JsonResponse(serialized_options, safe=False)
@method_decorator(csrf_exempt, name='dispatch')
@method_decorator(csrf_exempt, name="dispatch")
class SearchCodeView(View):
template_name = "inventory/scan_vin.html"
@ -448,27 +449,29 @@ class SearchCodeView(View):
return render(request, self.template_name)
def post(self, request, *args, **kwargs):
image_file = request.FILES.get('image')
image_file = request.FILES.get("image")
if image_file:
print("image received!")
image = cv2.imdecode(np.frombuffer(image_file.read(), np.uint8), cv2.IMREAD_COLOR)
image = cv2.imdecode(
np.frombuffer(image_file.read(), np.uint8), cv2.IMREAD_COLOR
)
decoded_objects = decode(image)
if decoded_objects:
print("image decoded!")
print(decoded_objects[0])
code = decoded_objects[0].data.decode('utf-8')
code = decoded_objects[0].data.decode("utf-8")
print("code received!")
print(code)
car = get_object_or_404(models.Car, vin=code)
name = car.id_car_make.get_local_name
print(name)
return redirect('car_detail', pk=car.pk)
return redirect("car_detail", pk=car.pk)
else:
print("back to else statement")
return JsonResponse({'success': False, 'error': 'No code detected'})
return JsonResponse({"success": False, "error": "No code detected"})
else:
return JsonResponse({'success': False, 'error': 'No image provided'})
return JsonResponse({"success": False, "error": "No image provided"})
class CarInventory(LoginRequiredMixin, ListView):
@ -638,7 +641,9 @@ class CarFinanceCreateView(LoginRequiredMixin, CreateView):
def get_form(self, form_class=None):
form = super().get_form(form_class)
dealer = get_user_type(self.request)
form.fields["additional_finances"].queryset = models.AdditionalServices.objects.filter(dealer=dealer)
form.fields[
"additional_finances"
].queryset = models.AdditionalServices.objects.filter(dealer=dealer)
return form
# def get_initial(self):
@ -1844,27 +1849,35 @@ class AccountDetailView(LoginRequiredMixin, DetailView):
model = AccountModel
template_name = "ledger/coa_accounts/account_detail.html"
context_object_name = "account"
slug_field = 'uuid'
slug_field = "uuid"
DEFAULT_TXS_DAYS = 30
extra_context = {
'DEFAULT_TXS_DAYS': DEFAULT_TXS_DAYS,
'header_subtitle_icon': 'ic:round-account-tree'
"DEFAULT_TXS_DAYS": DEFAULT_TXS_DAYS,
"header_subtitle_icon": "ic:round-account-tree",
}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
account_model: AccountModel = context['object']
context['header_title'] = f'Account {account_model.code} - {account_model.name}'
context['page_title'] = f'Account {account_model.code} - {account_model.name}'
context['total_debits'] = sum(x.amount for x in account_model.transactionmodel_set.filter(tx_type='debit'))
context['total_credits'] = sum(x.amount for x in account_model.transactionmodel_set.filter(tx_type='credit'))
txs_qs = account_model.transactionmodel_set.all().posted().order_by(
'journal_entry__timestamp'
).select_related(
'journal_entry',
'journal_entry__entity_unit',
'journal_entry__ledger__billmodel',
'journal_entry__ledger__invoicemodel',
account_model: AccountModel = context["object"]
context["header_title"] = f"Account {account_model.code} - {account_model.name}"
context["page_title"] = f"Account {account_model.code} - {account_model.name}"
context["total_debits"] = sum(
x.amount for x in account_model.transactionmodel_set.filter(tx_type="debit")
)
context["total_credits"] = sum(
x.amount
for x in account_model.transactionmodel_set.filter(tx_type="credit")
)
txs_qs = (
account_model.transactionmodel_set.all()
.posted()
.order_by("journal_entry__timestamp")
.select_related(
"journal_entry",
"journal_entry__entity_unit",
"journal_entry__ledger__billmodel",
"journal_entry__ledger__invoicemodel",
)
)
return context
@ -1879,10 +1892,11 @@ class AccountUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
def get_form(self, form_class=None):
form = super().get_form(form_class)
form.fields['_ref_node_id'].widget = HiddenInput()
form.fields['_position'].widget = HiddenInput()
form.fields["_ref_node_id"].widget = HiddenInput()
form.fields["_position"].widget = HiddenInput()
return form
@login_required
def account_delete(request, pk):
account = get_object_or_404(AccountModel, pk=pk)
@ -2262,8 +2276,10 @@ def invoice_mark_as(request, pk):
if not invoice.can_approve():
messages.error(request, "invoice is not ready for approval")
return redirect("invoice_detail", pk=invoice.pk)
invoice.mark_as_approved(entity_slug=dealer.entity.slug, user_model=dealer.entity.admin)
invoice.save()
invoice.mark_as_approved(
entity_slug=dealer.entity.slug, user_model=dealer.entity.admin
)
invoice.save()
return redirect("invoice_detail", pk=invoice.pk)
@ -2285,17 +2301,17 @@ def invoice_create(request, pk):
invoice.save()
unit_items = estimate.get_itemtxs_data()[0]
vat = models.VatRate.objects.filter(is_active=True).first()
vat = models.VatRate.objects.filter(is_active=True).first()
total = 0
discount_amount = 0
discount_amount = 0
itemtxs = []
for item in unit_items:
car = models.Car.objects.get(vin=item.item_model.name)
total = Decimal(car.finances.total) * Decimal(item.ce_quantity)
discount_amount = car.finances.discount_amount
for item in unit_items:
car = models.Car.objects.get(vin=item.item_model.name)
total = Decimal(car.finances.total) * Decimal(item.ce_quantity)
discount_amount = car.finances.discount_amount
grand_total = Decimal(total) - Decimal(discount_amount)
vat_amount = round(Decimal(grand_total) * Decimal(vat.rate), 2)
grand_total += Decimal(vat_amount)
@ -2306,9 +2322,9 @@ def invoice_create(request, pk):
"unit_cost": unit_cost,
"unit_revenue": unit_cost,
"quantity": item.ce_quantity,
"total_amount": grand_total
"total_amount": grand_total,
}
)
)
invoice_itemtxs = {
i.get("item_number"): {
"unit_cost": i.get("unit_cost"),
@ -2386,7 +2402,7 @@ def PaymentCreateView(request, pk=None):
if form.is_valid():
amount = form.cleaned_data.get("amount")
invoice = form.cleaned_data.get("invoice")
payment_method = form.cleaned_data.get("payment_method")
payment_method = form.cleaned_data.get("payment_method")
try:
vat_amount = 0
total_amount = 0
@ -2567,9 +2583,9 @@ class LeadDetailView(DetailView):
class LeadCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin):
model = models.Lead
form_class = forms.LeadForm
template_name = 'crm/leads/lead_form.html'
template_name = "crm/leads/lead_form.html"
# success_message = "Lead created successfully!"
success_url = reverse_lazy('lead_list')
success_url = reverse_lazy("lead_list")
def form_valid(self, form):
print("Form data:", form.cleaned_data) # Debug form data
@ -2579,9 +2595,11 @@ class LeadCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin):
def get_car_models(request):
make_id = request.GET.get('id_car_make')
make_id = request.GET.get("id_car_make")
if make_id:
car_models = models.CarModel.objects.filter(id_car_make=make_id).values('id_car_model', 'name', 'arabic_name')
car_models = models.CarModel.objects.filter(id_car_make=make_id).values(
"id_car_model", "name", "arabic_name"
)
return JsonResponse(list(car_models), safe=False)
return JsonResponse([], safe=False)
@ -2589,8 +2607,9 @@ def get_car_models(request):
class LeadUpdateView(UpdateView):
model = models.Lead
form_class = forms.LeadForm
template_name = 'crm/leads/lead_form.html'
success_url = reverse_lazy('lead_list')
template_name = "crm/leads/lead_form.html"
success_url = reverse_lazy("lead_list")
class LeadDeleteView(DeleteView):
model = models.Lead
@ -2742,7 +2761,7 @@ def fetch_notifications(request):
return JsonResponse({"notifications": notifications_data})
class ItemServiceCreateView(LoginRequiredMixin,SuccessMessageMixin,CreateView):
class ItemServiceCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
model = models.AdditionalServices
form_class = forms.AdditionalServiceForm
template_name = "items/service/service_create.html"
@ -2757,7 +2776,8 @@ class ItemServiceCreateView(LoginRequiredMixin,SuccessMessageMixin,CreateView):
form.instance.price = (form.instance.price * vat.rate) + form.instance.price
return super().form_valid(form)
class ItemServiceUpdateView(LoginRequiredMixin,SuccessMessageMixin,UpdateView):
class ItemServiceUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
model = models.AdditionalServices
form_class = forms.AdditionalServiceForm
template_name = "items/service/service_create.html"
@ -2833,6 +2853,36 @@ class ItemExpenseListView(ListView):
return items
class BillListView(ListView):
model = ItemModel
template_name = "ledger/bills/bill_list.html"
context_object_name = "bills"
def get_queryset(self):
dealer = get_user_type(self.request)
items = dealer.entity.get_bills()
return items
class BillCreateView(LoginRequiredMixin,SuccessMessageMixin,CreateView):
model = BillModel
form_class = BillModelCreateForm
template_name = "ledger/bills/bill_form.html"
success_url = reverse_lazy("bill_list")
success_message = _("Bill created successfully.")
def get_form_kwargs(self):
dealer = get_user_type(self.request)
kwargs = super().get_form_kwargs()
kwargs["entity_model"] = dealer.entity
return kwargs
# def form_valid(self, form):
# dealer = get_user_type(self.request)
# form.instance.entity = dealer.entity
# return super().form_valid(form)
class SubscriptionPlans(ListView):
model = models.SubscriptionPlan
template_name = "subscriptions/subscription_plan.html"

View File

@ -0,0 +1,122 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}
{{ page_title }}
{% endblock %}
{% 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 account?' %}</span>
</div>
<div class="btn-group">
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">{% trans 'No' %}</button>
<div class="btn btn-sm btn-danger">
<form action="{% url 'account_delete' account.pk %}" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-danger">{% trans 'Yes' %}</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="row my-5">
<div class="card rounded">
<div class="card-header">
<p class="mb-0">{{ header_title|upper }}</p>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<p>
<strong>{{ _('Account Name') }}:</strong> {{ account.name }}
</p>
<p>
<strong>{{ _('Account Code') }}:</strong> {{ account.code }}
</p>
</div>
<div class="col-md-6">
<p>
<strong>{{ _('Balance Type') }}:</strong> {{ account.balance_type }}
</p>
<p>
<strong>{{ _('Active') }}:</strong> {{ account.active }}
</p>
</div>
</div>
<div class="row">
<div class="col">
<table class="table is-fullwidth is-narrow is-striped is-bordered django-ledger-table-bottom-margin-75">
<tr>
<th class="has-text-centered">{{ _('JE Number') }}</th>
<th class="has-text-centered">{{ _('Date') }}</th>
<th class="has-text-centered">{{ _('Debit') }}</th>
<th class="has-text-centered">{{ _('Credit') }}</th>
<th class="has-text-centered">{{ _('Description') }}</th>
<th class="has-text-centered">{{ _('Unit') }}</th>
<th class="has-text-centered">{{ _('Actions') }}</th>
</tr>
{% for tx in account.transactionmodel_set.all %}
<tr class="has-text-centered">
<td>{{ tx.journal_entry.je_number }}</td>
<td>{{ tx.journal_entry.timestamp }}</td>
<td>
{% if tx.tx_type == 'debit' %}
${{ tx.amount }}
{% endif %}
</td>
<td>
{% if tx.tx_type == 'credit' %}
${{ tx.amount }}
{% endif %}
</td>
<td>{{ tx.description }}</td>
<td>{{ tx.journal_entry.entity_unit.name }}</td>
<td>
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a class="dropdown-item" href="{% url 'payment_details' tx.journal_entry.pk %}">{% trans 'view'|capfirst %}</a>
</div>
</div>
</td>
</tr>
{% endfor %}
<tr class="has-text-weight-bold">
<td></td>
<td class="has-text-right">Total</td>
<td class="has-text-centered">${{ total_debits }}</td>
<td class="has-text-centered">${{ total_credits }}</td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
</div>
</div>
<div class="card-footer d-flex">
<a class="btn btn-sm btn-primary me-1" href="{% url 'account_update' account.pk %}">
<!-- <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 'account_list' %}">
<!-- <i class="bi bi-arrow-left-square-fill"></i> -->
{% trans 'Back to List' %}
</a>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,38 @@
{% extends "base.html" %}
{% load i18n %}
{% load crispy_forms_filters %}
{% block title %}{% trans "account" %}{% endblock title %}
{% block content %}
<div class="row my-5">
<!-- Display Form Errors -->
<div class="card shadow rounded">
<div class="card-header bg-primary text-white">
<p class="mb-0">
{% if account.created %}
<!--<i class="bi bi-pencil-square"></i>-->
{{ _("Edit Account") }}
{% else %}
<!--<i class="bi bi-person-plus"></i> -->
{{ _("Add Account") }}
{% endif %}
</p>
</div>
<div class="card-body">
<form method="post" class="form" novalidate>
{% csrf_token %}
{{ form|crispy }}
{% for error in form.errors %}
<div class="text-danger">{{ error }}</div>
{% endfor %}
<div class="d-flex justify-content-end">
<button class="btn btn-sm btn-success me-1" type="submit">
{{ _("Save") }}
</button>
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger">{% trans "Cancel"|capfirst %}</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,165 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}
{% trans 'Bills' %}
{% endblock %}
{% block bills %}
<a class="nav-link active fw-bold">
{% trans 'Bills'|capfirst %}
<span class="visually-hidden">(current)</span>
</a>
{% endblock %}
{% block content %}
<div class="d-flex flex-column min-vh-100">
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-4">
<main class="d-grid gap-4 p-1">
<!-- Search Bar -->
<div class="row g-4">
<div class="col-12">
<div class="row-fluid p-2">
<form method="get">
<div class="input-group input-group-sm">
<button class="btn btn-sm btn-secondary rounded-start" type="submit">{% trans 'search' %}</button>
<input type="text" name="q" class="form-control form-control-sm rounded-end" value="{{ request.GET.q }}" placeholder="{% trans 'Search bills...' %}" />
{% if request.GET.q %}
<a href="{% url request.resolver_match.view_name %}" class="btn btn-sm btn-outline-danger ms-1 rounded"><i class="bi bi-x-lg"></i></a>
{% endif %}
</div>
</form>
</div>
</div>
</div>
<!-- Customer Table -->
{% if page_obj.object_list %}
<div id="accountsTable">
<div class="table-responsive">
<table class="table table-sm fs-9 mb-0">
<thead>
<tr class="bg-body-highlight">
<th class="border-top border-translucent ps-3">
{% trans 'Account Name' %}
</th>
<th class="border-top border-translucent">
{% trans 'Code' %}
</th>
<th class="border-top border-translucent text-end pe-3">
{% trans 'Balance Type' %}
</th>
<th class="border-top border-translucent text-end pe-3">
{% trans 'Active' %}
</th>
<th class="border-top border-translucent text-end align-middle pe-0 ps-4" scope="col"></th>
</tr>
</thead>
<tbody class="list">
{% for bill in bills %}
<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">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">
{% trans 'Delete Bill' %}
<span data-feather="alert-circle"></span>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center">
<p class="mb-0 text-danger fw-bold">
{% trans 'Are you sure you want to delete this Bill?' %}
</p>
<div class="d-grid gap-2">
<button type="button" class="btn btn-phoenix-secondary btn-sm" data-bs-dismiss="modal">{% trans 'No' %}</button>
<a type="button" class="btn btn-phoenix-danger btn-sm" href="{% url 'account_delete' bill.uuid %}">{% trans 'Yes' %}</a>
</div>
</div>
</div>
</div>
</div>
<tr>
<td class="align-middle ps-3">{{ bill.name }}</td>
<td class="align-middle">{{ bill.code }}</td>
<td class="align-middle text-end py-3 pe-3">
{% if bill.balance_type == 'debit' %}
<div class="badge badge-phoenix fs-10 badge-phoenix-success">
<span class="fw-bold">{{ _('Debit') }}</span><span class="ms-1 fas fa-arrow-circle-down"></span>
</div>
{% else %}
<div class="badge badge-phoenix fs-10 badge-phoenix-danger">
<span class="fw-bold">{{ _('Credit') }}</span><span class="ms-1 fas fa-arrow-circle-up"></span>
</div>
{% endif %}
</td>
<td class="align-middle text-end py-3 pe-3">
{% if bill.active %}
<span class="fw-bold text-success fas fa-check-circle"></span>
{% else %}
<span class="fw-bold text-danger far fa-times-circle"></span>
{% endif %}
</td>
<td>
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a href="{% url 'bill_detail' bill.uuid %}" class="dropdown-item text-success-dark">{% trans 'View' %}</a>
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans 'Delete' %}</button>
</div>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center text-muted">
{% trans 'No bill found.' %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="d-flex justify-content-between mt-3">
<span class="d-none d-sm-inline-block" data-list-info="data-list-info">{{ page_obj.start_index }} {{ _('to') }} {{ page_obj.end_index }}<span class="text-body-tertiary">{{ _('Items of') }}</span>{{ page_obj.paginator.count }}</span>
<div class="d-flex">
<nav aria-label="Page navigation">
<ul class="pagination mb-0">
{% if page_obj.has_previous %}
<li class="page-item py-0">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous"><span aria-hidden="true"><span class="fas fa-chevron-left"></span></span></a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true"><span class="fas fa-chevron-left"></span></span></a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next"><span aria-hidden="true"><span class="fas fa-chevron-right"></span></span></a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true"><span class="fas fa-chevron-right"></span></span></a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
</div>
{% endif %}
</main>
</div>
</div>
{% endblock %}