From 6aa2ed130c5f7cce497d126d4d1e8a5d21d7b4be Mon Sep 17 00:00:00 2001 From: ismail Date: Thu, 12 Jun 2025 16:42:53 +0300 Subject: [PATCH] update in the bill and po --- inventory/forms.py | 6 +- inventory/templatetags/custom_filters.py | 10 + inventory/urls.py | 52 +- inventory/views.py | 481 +++++++++++------- templates/bill/bill_create.html | 5 +- templates/bill/bill_detail.html | 275 ++++++++++ templates/bill/bill_update.html | 57 +++ templates/bill/includes/card_bill.html | 307 +++++++++++ templates/bill/includes/card_markdown.html | 25 + templates/bill/includes/card_vendor.html | 30 ++ templates/bill/includes/mark_as.html | 14 + templates/bill/tags/bill_item_formset.html | 159 ++++++ templates/bill/tags/bill_table.html | 60 +++ templates/ledger/bills/bill_form-copy.html | 160 ++++++ templates/ledger/bills/bill_form.html | 139 +---- templates/ledger/bills/bill_list.html | 16 +- .../car_inventory_item_form.html | 21 + .../includes/inventory_item_form.html | 46 +- .../includes/po_item_formset.html | 2 +- .../purchase_orders/inventory_item_form.html | 70 +-- .../purchase_orders/partials/po-select.html | 11 +- 21 files changed, 1543 insertions(+), 403 deletions(-) create mode 100644 templates/bill/bill_detail.html create mode 100644 templates/bill/bill_update.html create mode 100644 templates/bill/includes/card_bill.html create mode 100644 templates/bill/includes/card_markdown.html create mode 100644 templates/bill/includes/card_vendor.html create mode 100644 templates/bill/includes/mark_as.html create mode 100644 templates/bill/tags/bill_item_formset.html create mode 100644 templates/bill/tags/bill_table.html create mode 100644 templates/ledger/bills/bill_form-copy.html create mode 100644 templates/purchase_orders/car_inventory_item_form.html diff --git a/inventory/forms.py b/inventory/forms.py index ac494e51..84934642 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -1325,9 +1325,9 @@ class BillModelCreateForm(BillModelCreateFormBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields["cash_account"].widget = forms.HiddenInput() - self.fields["prepaid_account"].widget = forms.HiddenInput() - self.fields["unearned_account"].widget = forms.HiddenInput() + # self.fields["cash_account"].widget = forms.HiddenInput() + # self.fields["prepaid_account"].widget = forms.HiddenInput() + # self.fields["unearned_account"].widget = forms.HiddenInput() class SaleOrderForm(forms.ModelForm): diff --git a/inventory/templatetags/custom_filters.py b/inventory/templatetags/custom_filters.py index c773c0d3..6813139f 100644 --- a/inventory/templatetags/custom_filters.py +++ b/inventory/templatetags/custom_filters.py @@ -380,3 +380,13 @@ def po_item_formset_table(context, po_model, itemtxs_formset): 'po_model': po_model, 'itemtxs_formset': itemtxs_formset, } + + +@register.inclusion_tag('bill/tags/bill_item_formset.html', takes_context=True) +def bill_item_formset_table(context, item_formset): + return { + 'entity_slug': context['view'].kwargs['entity_slug'], + 'bill_pk': context['view'].kwargs['bill_pk'], + 'total_amount__sum': context['total_amount__sum'], + 'item_formset': item_formset, + } diff --git a/inventory/urls.py b/inventory/urls.py index da5f3c65..5557e101 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -693,7 +693,53 @@ path( ), # Bills path("items/bills/", views.BillListView.as_view(), name="bill_list"), - path("items/bills/create/", views.BillModelCreateViewView.as_view(), name="bill_create"), + # path("items/bills/create/", views.BillModelCreateViewView.as_view(), name="bill_create"), + path('items/bills//create/', + views.BillModelCreateViewView.as_view(), + name='bill-create'), + path('items/bills//create/purchase-order//', + views.BillModelCreateViewView.as_view(for_purchase_order=True), + name='bill-create-po'), + path('items/bills//create/estimate//', + views.BillModelCreateViewView.as_view(for_estimate=True), + name='bill-create-estimate'), + path('items/bills//detail//', + views.BillModelDetailViewView.as_view(), + name='bill-detail'), + path('items/bills//update//', + views.BillModelUpdateViewView.as_view(), + name='bill-update'), + path('items/bills//update//items/', + views.BillModelUpdateViewView.as_view(action_update_items=True), + name='bill-update-items'), + ############################################################ + path('items/bills//actions//mark-as-draft/', + views.BillModelActionMarkAsDraftView.as_view(), + name='bill-action-mark-as-draft'), + path('items/bills//actions//mark-as-review/', + views.BillModelActionMarkAsInReviewView.as_view(), + name='bill-action-mark-as-review'), + path('items/bills//actions//mark-as-approved/', + views.BillModelActionMarkAsApprovedView.as_view(), + name='bill-action-mark-as-approved'), + path('items/bills//actions//mark-as-paid/', + views.BillModelActionMarkAsPaidView.as_view(), + name='bill-action-mark-as-paid'), + path('items/bills//actions//mark-as-void/', + views.BillModelActionVoidView.as_view(), + name='bill-action-mark-as-void'), + path('items/bills//actions//mark-as-canceled/', + views.BillModelActionCanceledView.as_view(), + name='bill-action-mark-as-canceled'), + path('items/bills//actions//lock-ledger/', + views.BillModelActionLockLedgerView.as_view(), + name='bill-action-lock-ledger'), + path('items/bills//actions//unlock-ledger/', + views.BillModelActionUnlockLedgerView.as_view(), + name='bill-action-unlock-ledger'), + path('items/bills//actions//force-migration/', + views.BillModelActionForceMigrateView.as_view(), + name='bill-action-force-migrate'), # path("items/bills/create/", views.bill_create, name="bill_create"), path( "items/bills//bill_detail/", @@ -818,8 +864,8 @@ path( path('purchase_orders//update//update-items/', views.PurchaseOrderUpdateView.as_view(action_update_items=True), name='purchase_order_update_items'), - path('purchase_orders/inventory_item//create/', views.InventoryItemCreateView, name='inventory_item_create'), - path('purchase_orders//inventory_items_filter/', views.inventory_items_filter, name='inventory_items_filter'), + path('purchase_orders/inventory_item/create/', views.InventoryItemCreateView, name='inventory_item_create'), + path('purchase_orders/inventory_items_filter/', views.inventory_items_filter, name='inventory_items_filter'), path('purchase_orders//delete//', views.PurchaseOrderModelDeleteView.as_view(), name='po-delete'), diff --git a/inventory/views.py b/inventory/views.py index bb4f2e5d..2971ae31 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -26,7 +26,7 @@ from django.db.models import Q from django.conf import settings from django.db.models import Func from django.contrib import messages -from django.http import Http404, HttpResponseRedirect, JsonResponse, HttpResponseForbidden +from django.http import Http404, HttpResponseNotFound, HttpResponseRedirect, JsonResponse, HttpResponseForbidden from django.forms import HiddenInput, ValidationError from django.shortcuts import HttpResponse @@ -84,12 +84,16 @@ from django_ledger.forms.bank_account import ( BankAccountUpdateForm, ) from django_ledger.views.bill import ( - BillModelCreateView - # BillModelUpdateView as BillModelUpdateViewBase + BillModelCreateView, + BillModelDetailView, + BillModelUpdateView, + BaseBillActionView as BaseBillActionViewBase, ) from django_ledger.forms.bill import ( ApprovedBillModelUpdateForm, InReviewBillModelUpdateForm, + get_bill_itemtxs_formset_class, + ) from django_ledger.forms.invoice import ( DraftInvoiceModelUpdateForm, @@ -6067,6 +6071,10 @@ class BillListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): qs = dealer.entity.get_bills() return qs + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["entity"] = get_user_type(self.request).entity + return context class BillDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): """ @@ -6282,139 +6290,216 @@ def bill_mark_as_paid(request, pk): -@login_required -@permission_required("django_ledger.add_billmodel", raise_exception=True) -def bill_create(request): - """ - Handles creation of a bill in the system, including the validation of input data, - creation of transactions associated with the bill, and rendering of the appropriate - response or form. Ensures the user creating the bill has the necessary permissions and - correct input parameters for successful bill creation and itemization. +class BillModelCreateViewView(BillModelCreateView): + template_name = 'bill/bill_create.html' + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["entity"] = get_user_type(self.request).entity + return context +class BillModelDetailViewView(BillModelDetailView): + template_name = 'bill/bill_detail.html' +class BillModelUpdateViewView(BillModelUpdateView): + template_name = 'bill/bill_update.html' + def post(self, request, *args, **kwargs): + if self.action_update_items: - :param request: Django HttpRequest object containing metadata and data of the HTTP request. - :type request: HttpRequest - :return: JsonResponse with success/error information if the request is processed, - or HttpResponse rendering the form for bill creation. - :rtype: JsonResponse or HttpResponse - """ - dealer = get_user_type(request) - entity = dealer.entity + if not request.user.is_authenticated: + return HttpResponseForbidden() - if request.method == "POST": - data = json.loads(request.body) - vendor_id = data.get("vendor") - terms = data.get("terms") - vendor = entity.get_vendors().filter(pk=vendor_id).first() + queryset = self.get_queryset() + entity_model: EntityModel = self.get_authorized_entity_instance() + bill_model: BillModel = self.get_object(queryset=queryset) + bill_pk = bill_model.uuid - items = data.get("item", []) - quantities = data.get("quantity", []) + self.object = bill_model - if not all([items, quantities]): - return JsonResponse( - {"status": "error", "message": _("Items and Quantities are required")}, - status=400, + bill_itemtxs_formset_class = get_bill_itemtxs_formset_class(bill_model) + itemtxs_formset = bill_itemtxs_formset_class( + request.POST, + bill_model=bill_model, + entity_model=entity_model ) - if isinstance(quantities, list): - if "0" in quantities: - return JsonResponse( - { - "status": "error", - "message": _("Quantity must be greater than zero"), - } - ) - else: - if int(quantities) <= 0: - return JsonResponse( - { - "status": "error", - "message": _("Quantity must be greater than zero"), - } - ) - bill = entity.create_bill(vendor_model=vendor, terms=terms) - if isinstance(items, list): - item_quantity_map = {} - for item, quantity in zip(items, quantities): - if item in item_quantity_map: - item_quantity_map[item] += int(quantity) - else: - item_quantity_map[item] = int(quantity) - item_list = list(item_quantity_map.keys()) - quantity_list = list(item_quantity_map.values()) + if itemtxs_formset.has_changed(): + if itemtxs_formset.is_valid(): + itemtxs_list = itemtxs_formset.save(commit=False) - items_list = [ - {"item_id": item_list[i], "quantity": quantity_list[i]} - for i in range(len(item_list)) - ] - items_txs = [] - for item in items_list: - item_instance = ItemModel.objects.get(pk=item.get("item_id")) - car = models.Car.objects.get(vin=item_instance.name) - quantity = Decimal(item.get("quantity")) - items_txs.append( - { - "item_number": item_instance.item_number, - "quantity": quantity, - "unit_cost": car.finances.cost_price, - "total_amount": car.finances.cost_price * quantity, - } - ) + for itemtxs in itemtxs_list: + itemtxs.bill_model_id = bill_model.uuid + itemtxs.clean() - bill_itemtxs = { - item.get("item_number"): { - "unit_cost": item.get("unit_cost"), - "quantity": item.get("quantity"), - "total_amount": item.get("total_amount"), - } - for item in items_txs - } - else: - item = entity.get_items_all().filter(pk=items).first() - instance = models.Car.objects.get(vin=item.name) - bill_itemtxs = { - item.item_number: { - "unit_cost": instance.finances.cost_price, - "quantity": Decimal(quantities), - "total_amount": instance.finances.cost_price * Decimal(quantities), - } - } + itemtxs_formset.save() + itemtxs_qs = bill_model.update_amount_due() + bill_model.get_state(commit=True) + bill_model.clean() + bill_model.save( + update_fields=[ + 'amount_due', + 'amount_receivable', + 'amount_unearned', + 'amount_earned', + 'updated' + ]) - bill_itemtxs = bill.migrate_itemtxs( - itemtxs=bill_itemtxs, - commit=True, - operation=BillModel.ITEMIZE_APPEND, - ) + bill_model.migrate_state( + entity_slug=self.kwargs['entity_slug'], + user_model=self.request.user, + itemtxs_qs=itemtxs_qs, + raise_exception=False + ) - url = reverse("bill_detail", kwargs={"pk": bill.pk}) - return JsonResponse( - { - "status": "success", - "message": _("Bill created successfully"), - "url": f"{url}", - } - ) + messages.add_message(request, + message=f'Items for Invoice {bill_model.bill_number} saved.', + level=messages.SUCCESS,) - form = forms.BillModelCreateForm(entity_model=entity) - form.initial.update( - { - "cash_account": dealer.settings.bill_cash_account, - "prepaid_account": dealer.settings.bill_prepaid_account, - "unearned_account": dealer.settings.bill_unearned_account, - } - ) - car_list = models.Car.objects.filter(dealer=dealer) - context = { - "form": form, - "items": [ - { - "car": x, - "product": entity.get_items_products().filter(name=x.vin).first(), - } - for x in car_list - ], - } + # if valid get saved formset from DB + return HttpResponseRedirect( + redirect_to=reverse('bill-update', + kwargs={ + 'entity_slug': entity_model.slug, + 'bill_pk': bill_pk + }) + ) + context = self.get_context_data(itemtxs_formset=itemtxs_formset) + return self.render_to_response(context=context) + return super(BillModelUpdateViewView, self).post(request, **kwargs) - return render(request, "ledger/bills/bill_form.html", context) + def get_success_url(self): + return reverse("bill-update", kwargs={"entity_slug": self.kwargs["entity_slug"], "bill_pk": self.kwargs["bill_pk"]}) + +# @login_required +# @permission_required("django_ledger.add_billmodel", raise_exception=True) +# def bill_create(request): +# """ +# Handles creation of a bill in the system, including the validation of input data, +# creation of transactions associated with the bill, and rendering of the appropriate +# response or form. Ensures the user creating the bill has the necessary permissions and +# correct input parameters for successful bill creation and itemization. + +# :param request: Django HttpRequest object containing metadata and data of the HTTP request. +# :type request: HttpRequest +# :return: JsonResponse with success/error information if the request is processed, +# or HttpResponse rendering the form for bill creation. +# :rtype: JsonResponse or HttpResponse +# """ +# dealer = get_user_type(request) +# entity = dealer.entity + +# if request.method == "POST": +# data = json.loads(request.body) +# vendor_id = data.get("vendor") +# terms = data.get("terms") +# vendor = entity.get_vendors().filter(pk=vendor_id).first() + +# items = data.get("item", []) +# quantities = data.get("quantity", []) + +# if not all([items, quantities]): +# return JsonResponse( +# {"status": "error", "message": _("Items and Quantities are required")}, +# status=400, +# ) +# if isinstance(quantities, list): +# if "0" in quantities: +# return JsonResponse( +# { +# "status": "error", +# "message": _("Quantity must be greater than zero"), +# } +# ) +# else: +# if int(quantities) <= 0: +# return JsonResponse( +# { +# "status": "error", +# "message": _("Quantity must be greater than zero"), +# } +# ) + +# bill = entity.create_bill(vendor_model=vendor, terms=terms) +# if isinstance(items, list): +# item_quantity_map = {} +# for item, quantity in zip(items, quantities): +# if item in item_quantity_map: +# item_quantity_map[item] += int(quantity) +# else: +# item_quantity_map[item] = int(quantity) +# item_list = list(item_quantity_map.keys()) +# quantity_list = list(item_quantity_map.values()) + +# items_list = [ +# {"item_id": item_list[i], "quantity": quantity_list[i]} +# for i in range(len(item_list)) +# ] +# items_txs = [] +# for item in items_list: +# item_instance = ItemModel.objects.get(pk=item.get("item_id")) +# car = models.Car.objects.get(vin=item_instance.name) +# quantity = Decimal(item.get("quantity")) +# items_txs.append( +# { +# "item_number": item_instance.item_number, +# "quantity": quantity, +# "unit_cost": car.finances.cost_price, +# "total_amount": car.finances.cost_price * quantity, +# } +# ) + +# bill_itemtxs = { +# item.get("item_number"): { +# "unit_cost": item.get("unit_cost"), +# "quantity": item.get("quantity"), +# "total_amount": item.get("total_amount"), +# } +# for item in items_txs +# } +# else: +# item = entity.get_items_all().filter(pk=items).first() +# instance = models.Car.objects.get(vin=item.name) +# bill_itemtxs = { +# item.item_number: { +# "unit_cost": instance.finances.cost_price, +# "quantity": Decimal(quantities), +# "total_amount": instance.finances.cost_price * Decimal(quantities), +# } +# } + +# bill_itemtxs = bill.migrate_itemtxs( +# itemtxs=bill_itemtxs, +# commit=True, +# operation=BillModel.ITEMIZE_APPEND, +# ) + +# url = reverse("bill_detail", kwargs={"pk": bill.pk}) +# return JsonResponse( +# { +# "status": "success", +# "message": _("Bill created successfully"), +# "url": f"{url}", +# } +# ) + +# form = forms.BillModelCreateForm(entity_model=entity) +# form.initial.update( +# { +# "cash_account": dealer.settings.bill_cash_account, +# "prepaid_account": dealer.settings.bill_prepaid_account, +# "unearned_account": dealer.settings.bill_unearned_account, +# } +# ) +# car_list = models.Car.objects.filter(dealer=dealer) +# context = { +# "form": form, +# "items": [ +# { +# "car": x, +# "product": entity.get_items_products().filter(name=x.vin).first(), +# } +# for x in car_list +# ], +# } + +# return render(request, "ledger/bills/bill_form.html", context) @login_required @@ -8312,34 +8397,73 @@ def PurchaseOrderCreateView(request): form = PurchaseOrderModelCreateForm(entity_slug=entity.slug, user_model=entity.admin) return render(request, "purchase_orders/po_form.html", {"form": form}) -def InventoryItemCreateView(request,pk): - po = get_object_or_404(PurchaseOrderModel, pk=pk) +def InventoryItemCreateView(request): dealer = get_user_type(request) entity = dealer.entity coa = entity.get_default_coa() - inventory_accounts = entity.get_coa_all().get(name='ASSET_CA_INVENTORY') + + inventory_accounts = entity.get_coa_accounts().filter(role='asset_ca_inv') + cogs_accounts = entity.get_coa_accounts().filter(role='cogs_regular') + if(request.method == "POST"): - make = request.POST.get("make") - model = request.POST.get("model") - serie = request.POST.get("serie") - trim = request.POST.get("trim") + name = request.POST.get("name") + account = request.POST.get("account") + account = inventory_accounts.get(pk=account) + inventory_name = None + if name: + inventory_name = name + else: + make = request.POST.get("make") + model = request.POST.get("model") + serie = request.POST.get("serie") + trim = request.POST.get("trim") - make_name = models.CarMake.objects.get(pk=make) - model_name = models.CarModel.objects.get(pk=model) - serie_name = models.CarSerie.objects.get(pk=serie) - trim_name = models.CarTrim.objects.get(pk=trim) + make_name = models.CarMake.objects.get(pk=make) + model_name = models.CarModel.objects.get(pk=model) + serie_name = models.CarSerie.objects.get(pk=serie) + trim_name = models.CarTrim.objects.get(pk=trim) - inventory_name = f"{make_name.name} - {model_name.name} - {serie_name.name} - {trim_name.name}" + inventory_name = f"{make_name.name} - {model_name.name} - {serie_name.name} - {trim_name.name}" uom = entity.get_uom_all().get(name='Unit') entity.create_item_inventory( name=inventory_name, uom_model=uom, - item_type=ItemModel.ITEM_TYPE_MATERIAL + item_type=ItemModel.ITEM_TYPE_MATERIAL, + inventory_account=account, + coa_model=coa ) messages.success(request, _("Inventory item created successfully")) - return redirect('purchase_order_detail', pk=po.pk) + return redirect('purchase_order_list') + return render(request,'purchase_orders/inventory_item_form.html',{"make_data":models.CarMake.objects.all(),"inventory_accounts":inventory_accounts,"cogs_accounts":cogs_accounts}) +def inventory_items_filter(request): + dealer = get_user_type(request) + make = request.GET.get('make') + model = request.GET.get('model') + serie = request.GET.get('serie') + + model_data = models.CarModel.objects.none() + serie_data = models.CarSerie.objects.none() + trim_data = models.CarTrim.objects.none() + if make: + make = models.CarMake.objects.get(pk=make) + model_data = make.carmodel_set.all() + elif model: + model = models.CarModel.objects.get(pk=model) + serie_data = model.carserie_set.all() + elif serie: + serie = models.CarSerie.objects.get(pk=serie) + trim_data = serie.cartrim_set.all() + context = { + 'model_data': model_data, + 'serie_data': serie_data, + 'trim_data': trim_data, + # 'inventory_items': dealer.entity.get_items_inventory(), + # 'entity_slug': dealer.entity.slug, + } + return render(request, "purchase_orders/car_inventory_item_form.html", context) + class PurchaseOrderDetailView(PurchaseOrderModelDetailViewBase): template_name = 'purchase_orders/po_detail.html' context_object_name = 'po_model' @@ -8360,37 +8484,6 @@ class PurchaseOrderDetailView(PurchaseOrderModelDetailViewBase): return context -def inventory_items_filter(request,po_pk): - dealer = get_user_type(request) - make = request.GET.get('make') - model = request.GET.get('model') - serie = request.GET.get('serie') - make_data = models.CarMake.objects.all() - model_data = models.CarModel.objects.none() - serie_data = models.CarSerie.objects.none() - trim_data = models.CarTrim.objects.none() - if make: - make = models.CarMake.objects.get(pk=make) - model_data = make.carmodel_set.all() - elif model: - model = models.CarModel.objects.get(pk=model) - serie_data = model.carserie_set.all() - elif serie: - serie = models.CarSerie.objects.get(pk=serie) - trim_data = serie.cartrim_set.all() - context = { - 'make_data': make_data, - 'model_data': model_data, - 'serie_data': serie_data, - 'trim_data': trim_data, - 'inventory_accounts': dealer.entity.get_coa_accounts().filter(role="asset_ca_inv"), - 'inventory_items': dealer.entity.get_items_inventory(), - 'entity_slug': dealer.entity.slug, - 'po_model': get_object_or_404(PurchaseOrderModel, pk=po_pk) - } - return render(request, "purchase_orders/po_detail.html", context) - - # def PurchaseOrderDetailView(request, pk): # po = get_object_or_404(PurchaseOrderModel, pk=pk) # dealer = get_user_type(request) @@ -8497,7 +8590,7 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase): if create_bill_uuids: item_uuids = ','.join(create_bill_uuids) redirect_url = reverse( - 'django_ledger:bill-create-po', + 'bill-create-po', kwargs={ 'entity_slug': self.kwargs['entity_slug'], 'po_pk': po_model.uuid, @@ -8604,8 +8697,50 @@ class PurchaseOrderMarkAsCanceledView(BasePurchaseOrderActionActionView): class PurchaseOrderMarkAsVoidView(BasePurchaseOrderActionActionView): action_name = 'mark_as_void' - ##############################bil +class BaseBillActionView(BaseBillActionViewBase): + def get_redirect_url(self, entity_slug, bill_pk, *args, **kwargs): + return reverse('bill-update', + kwargs={ + 'entity_slug': entity_slug, + 'bill_pk': bill_pk + }) -class BillModelCreateViewView(BillModelCreateView): - template_name = 'ledger/bills/bill_form.html' \ No newline at end of file +class BillModelActionMarkAsDraftView(BaseBillActionView): + action_name = 'mark_as_draft' + + +class BillModelActionMarkAsInReviewView(BaseBillActionView): + action_name = 'mark_as_review' + + +class BillModelActionMarkAsApprovedView(BaseBillActionView): + action_name = 'mark_as_approved' + + +class BillModelActionMarkAsPaidView(BaseBillActionView): + action_name = 'mark_as_paid' + + +class BillModelActionDeleteView(BaseBillActionView): + action_name = 'mark_as_delete' + + +class BillModelActionVoidView(BaseBillActionView): + action_name = 'mark_as_void' + + +class BillModelActionCanceledView(BaseBillActionView): + action_name = 'mark_as_canceled' + + +class BillModelActionLockLedgerView(BaseBillActionView): + action_name = 'lock_ledger' + + +class BillModelActionUnlockLedgerView(BaseBillActionView): + action_name = 'unlock_ledger' + + +class BillModelActionForceMigrateView(BaseBillActionView): + action_name = 'migrate_state' \ No newline at end of file diff --git a/templates/bill/bill_create.html b/templates/bill/bill_create.html index 44ca7ed8..9984d13f 100644 --- a/templates/bill/bill_create.html +++ b/templates/bill/bill_create.html @@ -2,6 +2,7 @@ {% load i18n %} {% load static %} {% load django_ledger %} +{% load crispy_forms_filters %} {% block content %}
@@ -27,7 +28,7 @@ {% endif %}
- {{ form|add_class:"form-control" }} + {{ form|crispy }}
@@ -37,7 +38,7 @@ id="djl-bill-create-button" class="btn btn-primary btn-lg">{% trans 'Create' %} - {% trans 'Cancel' %} diff --git a/templates/bill/bill_detail.html b/templates/bill/bill_detail.html new file mode 100644 index 00000000..63ded748 --- /dev/null +++ b/templates/bill/bill_detail.html @@ -0,0 +1,275 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load static %} +{% load django_ledger %} + +{% block title %}Bill Details - {{ block.super }}{% endblock %} + +{% block customCSS %} + +{% endblock %} + +{% block content %} + +
+
+ +
+
+
+ {% include 'bill/includes/card_bill.html' with bill=bill entity_slug=view.kwargs.entity_slug style='bill-detail' %} +
+ {% include 'django_ledger/vendor/includes/card_vendor.html' with vendor=bill.vendor %} + +
+
+
+ + +
+ {% if bill.is_configured %} +
+
+
+
+
+
+ {% trans 'Cash Account' %}: + + {{ bill.cash_account.code }} + +
+

+ {% currency_symbol %}{{ bill.get_amount_cash | absolute | currency_format }} +

+
+
+ {% if bill.accrue %} +
+
+
+ {% trans 'Prepaid Account' %}: + + {{ bill.prepaid_account.code }} + +
+

+ {% currency_symbol %}{{ bill.get_amount_prepaid | currency_format }} +

+
+
+
+
+
+ {% trans 'Accounts Payable' %}: + + {{ bill.unearned_account.code }} + +
+

+ {% currency_symbol %}{{ bill.get_amount_unearned | currency_format }} +

+
+
+
+
+
+ {% trans 'Accrued' %} {{ bill.get_progress | percentage }} +
+

+ {% currency_symbol %}{{ bill.get_amount_earned | currency_format }} +

+
+
+ {% else %} +
+
+
+ {% trans 'You Still Owe' %} +
+

+ {% currency_symbol %}{{ bill.get_amount_open | currency_format }} +

+
+
+ {% endif %} +
+
+
+ {% endif %} + + +
+
+
+ +
{% trans 'Bill Items' %}
+
+
+
+
+ + + + + + + + + + + + + {% for bill_item in itemtxs_qs %} + + + + + + + + + {% endfor %} + + + + + + + + + +
{% trans 'Item' %}{% trans 'Entity Unit' %}{% trans 'Unit Cost' %}{% trans 'Quantity' %}{% trans 'Total' %}{% trans 'PO' %}
+
+
+
{{ bill_item.item_model }}
+
+
+
+ + {% if bill_item.entity_unit %} + {{ bill_item.entity_unit }} + {% endif %} + + + + {% currency_symbol %}{{ bill_item.unit_cost | currency_format }} + + + {{ bill_item.quantity }} + + + {% currency_symbol %}{{ bill_item.total_amount | currency_format }} + + + {% if bill_item.po_model_id %} + + {% trans 'View PO' %} + + {% endif %} +
{% trans 'Total' %} + + {% currency_symbol %}{{ total_amount__sum | currency_format }} + +
+
+
+
+ + {% if bill.is_active %} + + + + {% endif %} + + +
+
+
+ +
{% trans 'Bill Transactions' %}
+
+
+
+ {% transactions_table bill %} +
+
+ + +
+
+
+ +
{% trans 'Bill Notes' %}
+
+
+
+ {% include 'bill/includes/card_markdown.html' with style='card_1' title='' notes_html=bill.notes_html %} +
+
+
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/bill/bill_update.html b/templates/bill/bill_update.html new file mode 100644 index 00000000..b82dbe55 --- /dev/null +++ b/templates/bill/bill_update.html @@ -0,0 +1,57 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load static %} +{% load django_ledger %} +{% load custom_filters %} +{% load widget_tweaks crispy_forms_filters %} + +{% block content %} +
+
+ +
+ {% include 'bill/includes/card_vendor.html' with vendor=bill_model.vendor %} +
+ + +
+
+
+ {% include 'bill/includes/card_bill.html' with bill=bill_model style='bill-detail' entity_slug=view.kwargs.entity_slug %} + +
+ {% csrf_token %} + +
+ {{ form|crispy }} +
+ + + + + {% trans 'Back to Bill Detail' %} + + + + {% trans 'Bill List' %} + + +
+
+
+
+ + +
+ {% bill_item_formset_table itemtxs_formset %} +
+ + +
+
+ {% include "bill/includes/mark_as.html" %} +{% endblock %} \ No newline at end of file diff --git a/templates/bill/includes/card_bill.html b/templates/bill/includes/card_bill.html new file mode 100644 index 00000000..2ae2c3ce --- /dev/null +++ b/templates/bill/includes/card_bill.html @@ -0,0 +1,307 @@ +{% load django_ledger %} +{% load i18n %} +
+ {% if not create_bill %} + {% if style == 'dashboard' %} + +
+
+
+
+ {% trans 'Bill' %} +
+ {{ bill.get_bill_status_display }} +
+

{{ bill.vendor.vendor_name }}

+

{{ bill.vendor.address_1 }}

+ + {% if not bill.is_past_due %} +

+ {% trans 'Due in' %}: {{ bill.date_due | timeuntil }} +

+ {% else %} +

+ {% trans 'Past Due' %}: {{ bill.date_due | timesince }} {% trans 'ago' %} +

+ {% endif %} + +
+ {% trans 'Accrued' %}: + {% if bill.accrue %} + + {% else %} + + {% endif %} +
+ +
+

+ {% trans 'Amount Due' %}: {% currency_symbol %}{{ bill.get_amount_open | currency_format }} +

+

+ {% trans 'Amount Paid' %}: {% currency_symbol %}{{ bill.amount_paid | currency_format }} +

+

+ {% trans 'Progress' %}: {{ bill.get_progress | percentage }} +

+
+
+
+
+
+ + + {% modal_action bill 'get' entity_slug %} +
+ + + {% trans 'View' %} + + + {% trans 'Update' %} + + {% if bill.can_pay %} + + + {% endif %} + {% if bill.can_cancel %} + + {% endif %} +
+
+
+ + {% elif style == 'bill-detail' %} + +
+
+
+ +

+ {% trans 'Bill' %} {{ bill.bill_number }} +

+
+
+
+ {% if bill.is_draft %} +

{% trans 'This bill is' %} {{ bill.get_bill_status_display }}

+
+

+ {% trans 'Amount Due' %}: + {% currency_symbol %}{{ bill.amount_due | currency_format }} +

+

+ {% trans 'Due Date' %}: + {{ bill.date_due | timeuntil }} +

+

+ {% trans 'Is Accrued' %}: + {% if bill.accrue %} + + {% else %} + + {% endif %} +

+
+ {% elif bill.is_review %} +

{% trans 'This bill is' %} {{ bill.get_bill_status_display }}

+
+

+ {% trans 'Amount Due' %}: + {% currency_symbol %}{{ bill.amount_due | currency_format }} +

+

+ {% trans 'Due Date' %}: + {{ bill.date_due | timeuntil }} +

+

+ {% trans 'Is Accrued' %}: + {% if bill.accrue %} + + {% else %} + + {% endif %} +

+
+ {% if bill.xref %} +

{% trans 'External Ref' %}: {{ bill.xref }}

+ {% endif %} + {% elif bill.is_approved %} +

{% trans 'This bill is' %} {{ bill.get_bill_status_display }}

+
+

+ {% trans 'Amount Due' %}: + {% currency_symbol %}{{ bill.amount_due | currency_format }} +

+

+ {% trans 'Due Date' %}: + {{ bill.date_due | timeuntil }} +

+

+ {% trans 'Amount Paid' %}: + {% currency_symbol %}{{ bill.amount_paid | currency_format }} +

+

+ {% trans 'Progress' %}: + {{ bill.get_progress | percentage }} +

+
+
+
+
+
+ {% if bill.xref %} +

{% trans 'External Ref' %}: {{ bill.xref }}

+ {% endif %} + {% elif bill.is_paid %} +

{% trans 'This bill is' %} {{ bill.get_bill_status_display }}

+
+

+ {% trans 'Amount Paid' %}: + {% currency_symbol %}{{ bill.amount_paid | currency_format }} +

+

+ {% trans 'Paid Date' %}: + {{ bill.date_paid | date }} +

+
+ {% if bill.xref %} +

{% trans 'External Ref' %}: {{ bill.xref }}

+ {% endif %} + {% else %} +
+

+ {% trans 'Bill Amount' %}: + {% currency_symbol %}{{ bill.amount_due | currency_format }} +

+

+ {{ bill.get_bill_status_display | upper }} +

+
+ {% endif %} +
+ +
+ + {% endif %} + {% else %} + + + {% endif %} +
+ + + + \ No newline at end of file diff --git a/templates/bill/includes/card_markdown.html b/templates/bill/includes/card_markdown.html new file mode 100644 index 00000000..7c28046e --- /dev/null +++ b/templates/bill/includes/card_markdown.html @@ -0,0 +1,25 @@ +{% load trans from i18n %} +{% load django_ledger %} + +{% if style == 'card_1' %} +
+
+
+

{% if title %}{{ title }}{% else %} + {% trans 'Notes' %} + {% endif %}

+
+
+
+
+ {% if notes_html %} + {% autoescape off %} + {{ notes_html | safe }} + {% endautoescape %} + {% else %} +

{% trans 'No available notes to display...' %}

+ {% endif %} +
+
+
+{% endif %} diff --git a/templates/bill/includes/card_vendor.html b/templates/bill/includes/card_vendor.html new file mode 100644 index 00000000..9a1d2fc9 --- /dev/null +++ b/templates/bill/includes/card_vendor.html @@ -0,0 +1,30 @@ +{% load i18n %} +{% load django_ledger %} + +
+
+

+ + + + {% trans 'Vendor Info' %} +

+
+ +
+

{{ vendor.vendor_name }}

+ +

+ {% if vendor.address_1 %}{{ vendor.address_1 }}{% endif %} + {% if vendor.address_2 %}{{ vendor.address_2 }}{% endif %} + {% if vendor.get_cszc %}{{ vendor.get_cszc }}{% endif %} + {% if vendor.phone %}{{ vendor.phone }}{% endif %} + {% if vendor.email %}{{ vendor.email }}{% endif %} + {% if vendor.website %}{{ vendor.website }}{% endif %} +

+
+ + +
\ No newline at end of file diff --git a/templates/bill/includes/mark_as.html b/templates/bill/includes/mark_as.html new file mode 100644 index 00000000..3d1497ec --- /dev/null +++ b/templates/bill/includes/mark_as.html @@ -0,0 +1,14 @@ + + \ No newline at end of file diff --git a/templates/bill/tags/bill_item_formset.html b/templates/bill/tags/bill_item_formset.html new file mode 100644 index 00000000..0cd853f0 --- /dev/null +++ b/templates/bill/tags/bill_item_formset.html @@ -0,0 +1,159 @@ +{% load i18n %} +{% load static %} +{% load django_ledger %} +{% load widget_tweaks %} + +
+
+ +
+
+

+ + {% trans 'Bill Items' %} +

+
+
+
+ + +
+
+ {% csrf_token %} + {{ item_formset.non_form_errors }} + {{ item_formset.management_form }} + + +
+
+ +
+ + + + + + + + + + + + + + + {% for f in item_formset %} + + + + + + + + + + + + + + + + + + + + + + + + + + {% endfor %} + + + + + + + + + + + +
{% trans 'Item' %}{% trans 'PO Qty' %}{% trans 'PO Amount' %}{% trans 'Quantity' %}{% trans 'Unit Cost' %}{% trans 'Unit' %}{% trans 'Total' %}{% trans 'Delete' %}
+
+ {% for hidden_field in f.hidden_fields %} + {{ hidden_field }} + {% endfor %} + {{ f.item_model|add_class:"form-control" }} + {% if f.errors %} + {{ f.errors }} + {% endif %} +
+
+ + {% if f.instance.po_quantity %}{{ f.instance.po_quantity }}{% endif %} + + + {% if f.instance.po_total_amount %} +
+ + {% currency_symbol %}{{ f.instance.po_total_amount | currency_format }} + + + {% trans 'View PO' %} + +
+ {% endif %} +
+
+ {{ f.quantity|add_class:"form-control" }} +
+
+
+ {{ f.unit_cost|add_class:"form-control" }} +
+
+ {{ f.entity_unit|add_class:"form-control" }} + + + {% currency_symbol %}{{ f.instance.total_amount | currency_format }} + + + {% if item_formset.can_delete %} +
+ {{ f.DELETE }} +
+ {% endif %} +
{% trans 'Total' %} + + {% currency_symbol %}{{ total_amount__sum | currency_format }} + +
+
+
+
+
+
+ + +
+
+
+ {% if not item_formset.has_po %} + + + {% trans 'New Item' %} + + {% endif %} + +
+
+
+
+
diff --git a/templates/bill/tags/bill_table.html b/templates/bill/tags/bill_table.html new file mode 100644 index 00000000..30594351 --- /dev/null +++ b/templates/bill/tags/bill_table.html @@ -0,0 +1,60 @@ +{% load django_ledger %} +{% load i18n %} + +
+ + + + + + + + + + + + + + + + {% for bill in bills %} + + + + + + + + + + + {% endfor %} + +
{% trans 'Number' %}{% trans 'Status' %}{% trans 'Status Date' %}{% trans 'Vendor' %}{% trans 'Amount Due' %}{% trans 'Payments' %}{% trans 'Past Due' %}{% trans 'Actions' %}
{{ bill.bill_number }}{{ bill.get_bill_status_display }}{{ bill.get_status_action_date }}{{ bill.vendor.vendor_name }} + {% currency_symbol %}{{ bill.amount_due | currency_format }} + {% currency_symbol %}{{ bill.amount_paid | currency_format }} + {% if bill.is_past_due %} + {% icon 'bi:check-circle-fill' 24 %} + {% endif %} + + +
+ +
diff --git a/templates/ledger/bills/bill_form-copy.html b/templates/ledger/bills/bill_form-copy.html new file mode 100644 index 00000000..1ef7d1c5 --- /dev/null +++ b/templates/ledger/bills/bill_form-copy.html @@ -0,0 +1,160 @@ +{% extends "base.html" %} +{% load crispy_forms_filters %} +{% load i18n static %} + +{% block title %}{{ _("Create Bill") }}{% endblock title %} + +{% block content %} +
+

{% trans "Create Bill" %}

+
+ {% csrf_token %} +
+ {{ form|crispy }} +
+
+

Unit Items

+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+
+ + + +
+ + {% trans "Cancel" %} +
+ +
+
+{% endblock content %} + +{% block customJS %} + +{% endblock customJS %} \ No newline at end of file diff --git a/templates/ledger/bills/bill_form.html b/templates/ledger/bills/bill_form.html index 1ef7d1c5..33068b02 100644 --- a/templates/ledger/bills/bill_form.html +++ b/templates/ledger/bills/bill_form.html @@ -11,33 +11,8 @@ {% csrf_token %}
{{ form|crispy }} -
-
-

Unit Items

-
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
{% trans "Cancel" %} @@ -45,116 +20,4 @@
-{% endblock content %} - -{% block customJS %} - -{% endblock customJS %} \ No newline at end of file +{% endblock content %} \ No newline at end of file diff --git a/templates/ledger/bills/bill_list.html b/templates/ledger/bills/bill_list.html index 72da3311..e9aa7bf0 100644 --- a/templates/ledger/bills/bill_list.html +++ b/templates/ledger/bills/bill_list.html @@ -14,11 +14,9 @@
- -
@@ -40,9 +38,9 @@
- - + +
@@ -53,9 +51,9 @@ - + - {% for bill in bills %} + {% for bill in bills %} {% empty %} @@ -107,5 +105,5 @@ - + {% endblock %} diff --git a/templates/purchase_orders/car_inventory_item_form.html b/templates/purchase_orders/car_inventory_item_form.html new file mode 100644 index 00000000..28f8954a --- /dev/null +++ b/templates/purchase_orders/car_inventory_item_form.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% load static i18n crispy_forms_tags %} + +{% block content %} + + {% csrf_token %} + {% include "purchase_orders/partials/po-select.html" with name="make" target="model" data=make_data pk=po_model.pk %} + {% include "purchase_orders/partials/po-select.html" with name="model" target="serie" data=model_data pk=po_model.pk %} + {% include "purchase_orders/partials/po-select.html" with name="serie" target="trim" data=serie_data pk=po_model.pk %} + {% include "purchase_orders/partials/po-select.html" with name="trim" target="none" data=trim_data pk=po_model.pk %} +
+ + +
+ + +{% endblock content %} \ No newline at end of file diff --git a/templates/purchase_orders/includes/inventory_item_form.html b/templates/purchase_orders/includes/inventory_item_form.html index 2841e8e5..0aeb9f5d 100644 --- a/templates/purchase_orders/includes/inventory_item_form.html +++ b/templates/purchase_orders/includes/inventory_item_form.html @@ -1,21 +1,27 @@ +{% extends "base.html" %} +{% load static i18n crispy_forms_tags %} - - {% csrf_token %} - {% include "purchase_orders/partials/po-select.html" with name="make" target="model" data=make_data pk=po_model.pk %} - {% include "purchase_orders/partials/po-select.html" with name="model" target="serie" data=model_data pk=po_model.pk %} - {% include "purchase_orders/partials/po-select.html" with name="serie" target="trim" data=serie_data pk=po_model.pk %} - {% include "purchase_orders/partials/po-select.html" with name="trim" target="none" data=trim_data pk=po_model.pk %} -
- - -
-
- - -
- - +{% block content %} + + + {% csrf_token %} + {% include "purchase_orders/partials/po-select.html" with name="make" target="model" data=make_data pk=po_model.pk %} + {% include "purchase_orders/partials/po-select.html" with name="model" target="serie" data=model_data pk=po_model.pk %} + {% include "purchase_orders/partials/po-select.html" with name="serie" target="trim" data=serie_data pk=po_model.pk %} + {% include "purchase_orders/partials/po-select.html" with name="trim" target="none" data=trim_data pk=po_model.pk %} +
+ + +
+
+ + +
+ + + +{% endblock content %} \ No newline at end of file diff --git a/templates/purchase_orders/includes/po_item_formset.html b/templates/purchase_orders/includes/po_item_formset.html index fc3ebe53..b2d70c45 100644 --- a/templates/purchase_orders/includes/po_item_formset.html +++ b/templates/purchase_orders/includes/po_item_formset.html @@ -58,7 +58,7 @@ {{ f.create_bill|add_class:"form-check-input" }} {% elif f.instance.bill_model %} + href="{% url 'bill-detail' entity_slug=entity_slug bill_pk=f.instance.bill_model_id %}"> {% trans 'View Bill' %} {% endif %} diff --git a/templates/purchase_orders/inventory_item_form.html b/templates/purchase_orders/inventory_item_form.html index 307f68eb..60d3547a 100644 --- a/templates/purchase_orders/inventory_item_form.html +++ b/templates/purchase_orders/inventory_item_form.html @@ -1,53 +1,23 @@ {% extends "base.html" %} -{% load i18n %} -{% load crispy_forms_filters %} -{% block title %} - {# Check if an 'object' exists in the context #} - {% if object %} - {% trans 'Update Inventory Item'%} - {% else %} - {% trans 'Add New Inventory Item'%} - {% endif %} -{% endblock %} +{% load static i18n crispy_forms_tags %} + {% block content %} -
-
-
-
- -

- {% if vendor.created %} - - {{ _("Edit Inventory Item") }} - {% else %} - - {{ _("Add New Inventory Item") }} - {% endif %} -

-
-
-
- -
-
- -
- {% csrf_token %} - {{ redirect_field }} - {{ form|crispy }} - {% for error in form.errors %} -
{{ error }}
- {% endfor %} -
- - {% trans "Cancel" %} -
- - -
-
+
+
+ {% csrf_token %} +
+ +
-{% endblock %} \ No newline at end of file +
+ + +
+ + +
+{% endblock content %} \ No newline at end of file diff --git a/templates/purchase_orders/partials/po-select.html b/templates/purchase_orders/partials/po-select.html index 8f68abfb..89f3ae4c 100644 --- a/templates/purchase_orders/partials/po-select.html +++ b/templates/purchase_orders/partials/po-select.html @@ -2,10 +2,13 @@
{% trans 'Action'%}
@@ -84,7 +82,7 @@ {% trans 'View' %} - +