diff --git a/inventory/override.py b/inventory/override.py index b36f094c..947b0ada 100644 --- a/inventory/override.py +++ b/inventory/override.py @@ -22,6 +22,11 @@ from django.shortcuts import get_object_or_404 from django.urls import reverse from django_ledger.models import ItemTransactionModel,InvoiceModel,LedgerModel,EntityModel from django.views.generic.detail import DetailView +from django.views.generic.edit import CreateView +from django_ledger.forms.chart_of_accounts import ( + ChartOfAccountsModelCreateForm, + ChartOfAccountsModelUpdateForm, +) from django_ledger.forms.purchase_order import ( ApprovedPurchaseOrderModelUpdateForm, BasePurchaseOrderModelUpdateForm, @@ -30,7 +35,7 @@ from django_ledger.forms.purchase_order import ( get_po_itemtxs_formset_class, ) from django_ledger.views.purchase_order import PurchaseOrderModelModelViewQuerySetMixIn -from django_ledger.models import PurchaseOrderModel, EstimateModel, BillModel +from django_ledger.models import PurchaseOrderModel, EstimateModel, BillModel, ChartOfAccountModel from django.views.generic.detail import SingleObjectMixin from django.views.generic.edit import UpdateView from django.views.generic.base import RedirectView @@ -973,3 +978,112 @@ class InvoiceModelUpdateView(LoginRequiredMixin, PermissionRequiredMixin, Update # if not valid, return formset with errors... return self.render_to_response(context=self.get_context_data(itemtxs_formset=itemtxs_formset)) return super(InvoiceModelUpdateView, self).post(request, **kwargs) + + + +class ChartOfAccountModelModelBaseViewMixIn(LoginRequiredMixin, PermissionRequiredMixin): + queryset = None + permission_required = [] + def get_queryset(self): + if self.queryset is None: + entity_model = self.request.dealer.entity + self.queryset = entity_model.chartofaccountmodel_set.all().order_by('-updated') + return super().get_queryset() + def get_redirect_url(self, *args, **kwargs): + return reverse('coa-list', kwargs={'dealer_slug': self.request.dealer.slug, + 'entity_slug': self.request.entity.slug}) + +class ChartOfAccountModelListView(ChartOfAccountModelModelBaseViewMixIn, ListView): + template_name = 'chart_of_accounts/coa_list.html' + context_object_name = 'coa_list' + inactive = False + + def get_queryset(self): + qs = super().get_queryset() + if self.inactive: + return qs.filter(active=False) + return qs.active() + + def get_context_data(self, *, object_list=None, **kwargs): + context = super().get_context_data(object_list=None, **kwargs) + context['inactive'] = self.inactive + context['header_subtitle'] = self.request.entity.name + context['header_subtitle_icon'] = 'gravity-ui:hierarchy' + context['page_title'] = 'Inactive Chart of Account List' if self.inactive else 'Chart of Accounts List' + context['header_title'] = 'Inactive Chart of Account List' if self.inactive else 'Chart of Accounts List' + return context + +class ChartOfAccountModelCreateView(ChartOfAccountModelModelBaseViewMixIn, CreateView): + template_name = 'chart_of_accounts/coa_create.html' + extra_context = { + 'header_title': _('Create Chart of Accounts'), + 'page_title': _('Create Chart of Account'), + } + + def get_initial(self): + return { + 'entity': self.request.entity, + } + + def get_form(self, form_class=None): + return ChartOfAccountsModelCreateForm( + entity_model=self.request.entity, + **self.get_form_kwargs() + ) + + def get_context_data(self, *, object_list=None, **kwargs): + context = super().get_context_data(object_list=None, **kwargs) + context['header_subtitle'] = f'New Chart of Accounts: {self.request.entity.name}' + context['header_subtitle_icon'] = 'gravity-ui:hierarchy' + return context + + def get_success_url(self): + return reverse('coa-list', kwargs={'dealer_slug': self.request.dealer.slug, + 'entity_slug': self.request.entity.slug}) + + +class ChartOfAccountModelUpdateView(ChartOfAccountModelModelBaseViewMixIn, UpdateView): + context_object_name = 'coa_model' + slug_url_kwarg = 'coa_slug' + template_name = 'chart_of_accounts/coa_update.html' + form_class = ChartOfAccountsModelUpdateForm + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + chart_of_accounts_model: ChartOfAccountModel = self.object + context['page_title'] = f'Update Chart of Account {chart_of_accounts_model.name}' + context['header_title'] = f'Update Chart of Account {chart_of_accounts_model.name}' + return context + + def get_success_url(self): + return reverse('coa-list', kwargs={'dealer_slug': self.request.dealer.slug, + 'entity_slug': self.request.entity.slug}) + + +class CharOfAccountModelActionView(ChartOfAccountModelModelBaseViewMixIn, + RedirectView, + SingleObjectMixin): + http_method_names = ['get'] + slug_url_kwarg = 'coa_slug' + action_name = None + commit = True + + def get(self, request, *args, **kwargs): + kwargs['user_model'] = self.request.user + if not self.action_name: + raise ImproperlyConfigured('View attribute action_name is required.') + response = super(CharOfAccountModelActionView, self).get(request, *args, **kwargs) + coa_model: ChartOfAccountModel = self.get_object() + + try: + getattr(coa_model, self.action_name)(commit=self.commit, **kwargs) + messages.add_message(request, level=messages.SUCCESS, extra_tags='is-success', + message=_('Successfully updated {} Default Chart of Account to '.format( + request.entity.name) + + '{}'.format(coa_model.name))) + except ValidationError as e: + messages.add_message(request, + message=e.message, + level=messages.ERROR, + extra_tags='is-danger') + return response diff --git a/inventory/urls.py b/inventory/urls.py index 5b86a8dd..6124a486 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -738,27 +738,27 @@ urlpatterns = [ name="bank_account_delete", ), path( - "/coa_accounts/", + "/coa_accounts//", views.AccountListView.as_view(), name="account_list", ), path( - "/coa_accounts//", + "/coa_accounts///", views.AccountDetailView.as_view(), name="account_detail", ), path( - "/coa_accounts/create/", + "/coa_accounts//create/", views.AccountCreateView.as_view(), name="account_create", ), path( - "/coa_accounts//update/", + "/coa_accounts///update/", views.AccountUpdateView.as_view(), name="account_update", ), path( - "/coa_accounts//delete/", + "/coa_accounts///delete/", views.account_delete, name="account_delete", ), @@ -1094,6 +1094,29 @@ urlpatterns = [ path('/chart-of-accounts//list/', views.ChartOfAccountModelListView.as_view(), name='coa-list'), + path('/chart-of-accounts//list/inactive/', + views.ChartOfAccountModelListView.as_view(inactive=True), + name='coa-list-inactive'), + path('//create/', + views.ChartOfAccountModelCreateView.as_view(), + name='coa-create'), + path('//detail//', + views.ChartOfAccountModelListView.as_view(), + name='coa-detail'), + path('//update//', + views.ChartOfAccountModelUpdateView.as_view(), + name='coa-update'), + + # ACTIONS.... + path('//action//mark-as-default/', + views.CharOfAccountModelActionView.as_view(action_name='mark_as_default'), + name='coa-action-mark-as-default'), + path('//action//mark-as-active/', + views.CharOfAccountModelActionView.as_view(action_name='mark_as_active'), + name='coa-action-mark-as-active'), + path('//action//mark-as-inactive/', + views.CharOfAccountModelActionView.as_view(action_name='mark_as_inactive'), + name='coa-action-mark-as-inactive'), # CASH FLOW STATEMENTS... # Entities... path( diff --git a/inventory/views.py b/inventory/views.py index c5d6e2d9..7fe42acd 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -152,6 +152,10 @@ from .override import ( BaseBillActionView as BaseBillActionViewBase, InventoryListView as InventoryListViewBase, InvoiceModelUpdateView as InvoiceModelUpdateViewBase, + ChartOfAccountModelCreateView as ChartOfAccountModelCreateViewBase, + ChartOfAccountModelListView as ChartOfAccountModelListViewBase, + ChartOfAccountModelUpdateView as ChartOfAccountModelUpdateViewBase, + CharOfAccountModelActionView as CharOfAccountModelActionViewBase, ) from django_ledger.models import ( @@ -168,6 +172,7 @@ from django_ledger.models import ( BillModel, LedgerModel, PurchaseOrderModel, + ChartOfAccountModel ) from django_ledger.views.financial_statement import ( FiscalYearBalanceSheetView, @@ -4396,6 +4401,10 @@ class AccountListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): dealer = get_user_type(self.request) accounts = dealer.entity.get_all_accounts() return apply_search_filters(accounts, query) + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["url_kwargs"] = self.kwargs + return context class AccountCreateView( @@ -4461,10 +4470,17 @@ class AccountCreateView( def get_success_url(self): return reverse( - "account_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]} + "account_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"], "coa_pk": self.kwargs["coa_pk"]} ) - - + def get_context_data(self,**kwargs): + context = super().get_context_data(**kwargs) + context["url_kwargs"] = self.kwargs + coa_pk = context["url_kwargs"]["coa_pk"] + try: + kwargs["coa_model"] = ChartOfAccountModel.objects.get(pk=coa_pk) or self.request.entity.get_default_coa() + except Exception: + kwargs["coa_model"] = self.request.entity.get_default_coa() + return context class AccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): """ Represents the detailed view for an account with additional context data related to account @@ -4522,6 +4538,7 @@ class AccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView) "journal_entry__ledger__billmodel", "journal_entry__ledger__invoicemodel", ) + context["url_kwargs"] = self.kwargs return context @@ -4565,13 +4582,21 @@ class AccountUpdateView( def get_success_url(self): return reverse_lazy( - "account_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]} + "account_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"],"coa_pk":self.kwargs["coa_pk"]} ) - + def get_context_data(self,**kwargs): + context = super().get_context_data(**kwargs) + context["url_kwargs"] = self.kwargs + coa_pk = context["url_kwargs"]["coa_pk"] + try: + kwargs["coa_model"] = ChartOfAccountModel.objects.get(pk=coa_pk) or self.request.entity.get_default_coa() + except Exception: + kwargs["coa_model"] = self.request.entity.get_default_coa() + return context @login_required @permission_required("django_ledger.delete_accountmodel") -def account_delete(request, dealer_slug, pk): +def account_delete(request, dealer_slug,coa_pk, pk): """ Handles the deletion of an account object identified by its primary key (pk). Ensures that the user has the necessary permissions to perform the deletion. Successfully @@ -4590,7 +4615,7 @@ def account_delete(request, dealer_slug, pk): account.delete() messages.success(request, _("Account deleted successfully")) - return redirect("account_list", dealer_slug=dealer_slug) + return redirect("account_list", dealer_slug=dealer_slug, coa_pk=coa_pk) # Sales list @@ -11499,4 +11524,16 @@ def ticket_update(request, ticket_id): class ChartOfAccountModelListView(ChartOfAccountModelListViewBase): - template_name = 'chart_of_accounts/coa_list.html' \ No newline at end of file + template_name = 'chart_of_accounts/coa_list.html' + permission_required = 'django_ledger.view_chartofaccountmodel' +class ChartOfAccountModelCreateView(ChartOfAccountModelCreateViewBase): + template_name = 'chart_of_accounts/coa_create.html' + permission_required = 'django_ledger.add_chartofaccountmodel' +class ChartOfAccountModelListView(ChartOfAccountModelListViewBase): + template_name = 'chart_of_accounts/coa_list.html' + permission_required = 'django_ledger.view_chartofaccountmodel' +class ChartOfAccountModelUpdateView(ChartOfAccountModelUpdateViewBase): + template_name = 'chart_of_accounts/coa_update.html' + permission_required = 'django_ledger.change_chartofaccountmodel' +class CharOfAccountModelActionView(CharOfAccountModelActionViewBase): + permission_required = 'django_ledger.change_chartofaccountmodel' diff --git a/templates/chart_of_accounts/coa_create.html b/templates/chart_of_accounts/coa_create.html new file mode 100644 index 00000000..d8fef027 --- /dev/null +++ b/templates/chart_of_accounts/coa_create.html @@ -0,0 +1,54 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load static %} +{% load django_ledger %} +{% load widget_tweaks %} + +{% block content %} +
+
+
+

{% trans 'Create Chart of Accounts' %}

+
+
+
+
+
+ {% csrf_token %} + + {# Bootstrap form rendering #} +
+ {{ form.name.label_tag }} + {{ form.name|add_class:"form-control" }} + {% if form.name.help_text %} + {{ form.name.help_text }} + {% endif %} + {% for error in form.name.errors %} +
{{ error }}
+ {% endfor %} +
+
+ {{ form.description.label_tag }} + {{ form.description|add_class:"form-control" }} + {% if form.description.help_text %} + {{ form.description.help_text }} + {% endif %} + {% for error in form.description.errors %} +
{{ error }}
+ {% endfor %} +
+ + + +
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/chart_of_accounts/coa_list.html b/templates/chart_of_accounts/coa_list.html index 5ca6639c..574798e6 100644 --- a/templates/chart_of_accounts/coa_list.html +++ b/templates/chart_of_accounts/coa_list.html @@ -10,17 +10,17 @@
{% if not inactive %} - + {% trans 'Show Inactive' %} {% else %} - + {% trans 'Show Active' %} {% endif %} diff --git a/templates/chart_of_accounts/coa_update.html b/templates/chart_of_accounts/coa_update.html new file mode 100644 index 00000000..75ed24aa --- /dev/null +++ b/templates/chart_of_accounts/coa_update.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load static %} +{% load widget_tweaks %} + +{% block content %} +
+
+
+
+
+ {% csrf_token %} +
+ {{ form.name.label_tag }} + {{ form.name|add_class:"form-control" }} + {% if form.name.help_text %} + {{ form.name.help_text }} + {% endif %} + {% for error in form.name.errors %} +
{{ error }}
+ {% endfor %} +
+
+ {{ form.description.label_tag }} + {{ form.description|add_class:"form-control" }} + {% if form.description.help_text %} + {{ form.description.help_text }} + {% endif %} + {% for error in form.description.errors %} +
{{ error }}
+ {% endfor %} +
+ + +
+ + + Back + +
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/chart_of_accounts/includes/coa_card.html b/templates/chart_of_accounts/includes/coa_card.html index e3d56ecd..35afd177 100644 --- a/templates/chart_of_accounts/includes/coa_card.html +++ b/templates/chart_of_accounts/includes/coa_card.html @@ -84,30 +84,30 @@