update dashboard

This commit is contained in:
gitea 2025-02-02 13:53:37 +00:00
parent ecf1c375c1
commit 564a724663
20 changed files with 2202 additions and 448 deletions

View File

@ -18,6 +18,7 @@ from django_ledger.models import (
CustomerModel,
ItemModelQuerySet,
)
from django_ledger.io.io_core import get_localdate
from django.db.models import Sum
from decimal import Decimal, InvalidOperation
from django.core.exceptions import ValidationError
@ -318,6 +319,18 @@ class AdditionalServices(models.Model, LocalizedNameMixin):
blank=True,
)
def to_dict(self):
return {
"name": self.name,
"price": str(self.price),
"price_": str(self.price_),
"taxable": self.taxable,
"uom": self.uom,
}
@property
def price_(self):
vat = VatRate.objects.filter(is_active=True).first()
return Decimal(self.price + (self.price * vat.rate)) if self.taxable else self.price
class Meta:
verbose_name = _("Additional Services")
verbose_name_plural = _("Additional Services")
@ -518,21 +531,20 @@ class CarFinance(models.Model):
@property
def total(self):
if self.additional_services.count() > 0:
return self.selling_price + sum(
x.price for x in self.additional_services.all()
)
return self.selling_price
@property
def total_additionals(self):
return sum(x.price_ for x in self.additional_services.all())
@property
def total_discount(self):
if self.discount_amount > 0:
return self.total - self.discount_amount
return self.total
return self.selling_price - self.discount_amount
return self.selling_price
@property
def total_vat(self):
return self.total_discount + self.vat_amount
return round(self.total_discount + self.vat_amount + self.total_additionals,2)
@property
def vat_amount(self):
@ -1574,7 +1586,8 @@ class SaleOrder(models.Model):
next_id = last_order.id + 1
else:
next_id = 1
self.formatted_order_id = f"{next_id:05d}"
year = get_localdate().year
self.formatted_order_id = f"O-{year}-{next_id:09d}"
super().save(*args, **kwargs)
def __str__(self):
return f"Sale Order for {self.full_name}"

View File

@ -159,22 +159,11 @@ def create_ledger_entity(sender, instance, created, **kwargs):
active=True,
)
# Inventory Account
asset_ca_inventory = entity.create_account(
coa_model=coa,
code="1106",
role=roles.ASSET_CA_INVENTORY,
name=_("Inventory"),
balance_type="debit",
active=True,
)
# asset_ca_inventory.role_default = True
# asset_ca_inventory.save()
# VAT Payable Account
liability_ltl_vat_receivable = entity.create_account(
coa_model=coa,
code="1107",
code="1106",
role=roles.ASSET_CA_RECEIVABLES,
name=_("VAT Receivable"),
balance_type="debit",
@ -707,7 +696,7 @@ def update_item_model_cost(sender, instance, created, **kwargs):
product.default_amount = instance.selling_price
product.additional_info = {}
product.additional_info.update({"car_finance":instance.to_dict()})
product.additional_info.update({"additional_services": [to_dict(service) for service in instance.additional_services.all()]})
product.additional_info.update({"additional_services": [service.to_dict() for service in instance.additional_services.all()]})
product.save()
print(f"Inventory item updated with CarFinance data for Car: {instance.car}")

View File

@ -1,3 +1,4 @@
from random import randint
from django import template
from calendar import month_abbr
from django.urls import reverse
@ -36,6 +37,7 @@ def attr(field, args):
@register.inclusion_tag('ledger/reports/components/period_navigator.html', takes_context=True)
def period_navigation(context, base_url: str):
print(context, base_url)
kwargs = dict()
entity_slug = context['view'].kwargs['entity_slug']
kwargs['entity_slug'] = entity_slug
@ -147,7 +149,7 @@ def balance_sheet_statement(context, io_model, to_date=None):
'tx_digest': io_digest.get_io_data(),
}
@register.inclusion_tag('django_ledger/financial_statements/tags/income_statement.html', takes_context=True)
@register.inclusion_tag('ledger/reports/tags/income_statement.html', takes_context=True)
def income_statement_table(context, io_model, from_date=None, to_date=None):
user_model = context['user']
activity = context['request'].GET.get('activity')
@ -178,3 +180,53 @@ def income_statement_table(context, io_model, from_date=None, to_date=None):
'user_model': user_model,
'tx_digest': io_digest.get_io_data()
}
@register.inclusion_tag('django_ledger/financial_statements/tags/cash_flow_statement.html', takes_context=True)
def cash_flow_statement(context, io_model):
user_model = context['user']
entity_slug = context['view'].kwargs.get('entity_slug')
from_date = context['from_date']
to_date = context['to_date']
io_digest = io_model.digest(
cash_flow_statement=True,
by_activity=True,
user_model=user_model,
equity_only=False,
signs=True,
entity_slug=entity_slug,
unit_slug=context['unit_slug'],
by_unit=context['by_unit'],
from_date=from_date,
to_date=to_date,
process_groups=True)
return {
'entity_slug': entity_slug,
'user_model': user_model,
'tx_digest': io_digest.get_io_data()
}
@register.inclusion_tag('django_ledger/components/date_picker.html', takes_context=True)
def date_picker(context, nav_url=None, date_picker_id=None):
try:
entity_slug = context['view'].kwargs.get('entity_slug')
except KeyError:
entity_slug = context['entity_slug']
if not date_picker_id:
date_picker_id = f'djl-datepicker-{randint(10000, 99999)}'
if 'date_picker_ids' not in context:
context['date_picker_ids'] = list()
context['date_picker_ids'].append(date_picker_id)
date_navigation_url = nav_url if nav_url else context.get('date_navigation_url')
return {
'entity_slug': entity_slug,
'date_picker_id': date_picker_id,
'date_navigation_url': date_navigation_url
}

View File

@ -1,9 +1,18 @@
import json
from . import models as m
from datetime import datetime
from django.urls import reverse
from django_ledger import models as lm
from django.test import Client, TestCase
from django.contrib.auth import get_user_model
from django_ledger.io.io_core import get_localdate
from django.core.exceptions import ObjectDoesNotExist
from decimal import Decimal
from unittest.mock import MagicMock
from inventory.models import VatRate
from inventory.utils import CarFinanceCalculator
User = get_user_model()
@ -52,7 +61,7 @@ class ModelTest(TestCase):
id_car_serie=self.car_serie,
year=2020,
id_car_trim=self.trim,
receiving_date="2020-01-01",
receiving_date=get_localdate(),
)
self.car_finances = m.CarFinance.objects.create(
@ -78,7 +87,7 @@ class ModelTest(TestCase):
self.assertIsNotNone(dealer.entity)
self.assertEqual(dealer.entity.name, dealer.name)
self.assertEqual(dealer.entity.get_all_accounts().count(), 19)
self.assertEqual(dealer.entity.get_all_accounts().count(), 57)
self.assertEqual(dealer.entity.get_uom_all().count(), 16)
def test_car_creation_creates_product(self):
@ -233,3 +242,163 @@ class AuthenticationTest(TestCase):
# Check the response
self.assertEqual(response.status_code, 400)
self.assertIn("error", response.json()) # Assuming the view returns an error for missing fields
class CarFinanceCalculatorTests(TestCase):
def setUp(self):
# Common setup for all tests
self.mock_model = MagicMock()
self.vat_rate = VatRate.objects.create(rate=Decimal('0.20'), is_active=True)
def test_no_active_vat_rate_raises_error(self):
VatRate.objects.all().delete() # Ensure no active VAT
with self.assertRaises(ObjectDoesNotExist):
CarFinanceCalculator(self.mock_model)
def test_vat_rate_retrieval(self):
calculator = CarFinanceCalculator(self.mock_model)
self.assertEqual(calculator.vat_rate, Decimal('0.20'))
def test_item_transactions_retrieval(self):
mock_item = MagicMock()
self.mock_model.get_itemtxs_data.return_value = [MagicMock(all=lambda: [mock_item])]
calculator = CarFinanceCalculator(self.mock_model)
self.assertEqual(calculator.item_transactions, [mock_item])
def test_get_car_data(self):
mock_item = MagicMock()
mock_item.ce_quantity = 2
mock_item.item_model = MagicMock()
mock_item.item_model.item_number = '123'
mock_item.item_model.additional_info = {
CarFinanceCalculator.CAR_FINANCE_KEY: {
'selling_price': '10000',
'cost_price': '8000',
'discount_amount': '500',
'total_vat': '2000'
},
CarFinanceCalculator.CAR_INFO_KEY: {
'vin': 'VIN123',
'make': 'Toyota',
'model': 'Camry',
'year': 2020,
'trim': 'LE',
'mileage': 15000
},
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
{'name': 'Service 1', 'price': '200', 'taxable': True, 'price_': '240'}
]
}
calculator = CarFinanceCalculator(self.mock_model)
car_data = calculator._get_car_data(mock_item)
self.assertEqual(car_data['item_number'], '123')
self.assertEqual(car_data['vin'], 'VIN123')
self.assertEqual(car_data['make'], 'Toyota')
self.assertEqual(car_data['selling_price'], '10000')
self.assertEqual(car_data['unit_price'], Decimal('10000'))
self.assertEqual(car_data['quantity'], 2)
self.assertEqual(car_data['total'], Decimal('20000'))
self.assertEqual(car_data['total_vat'], '2000')
self.assertEqual(car_data['additional_services'], [{'name': 'Service 1', 'price': '200', 'taxable': True, 'price_': '240'}])
def test_get_additional_services(self):
mock_item1 = MagicMock()
mock_item1.item_model.additional_info = {
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
{'name': 'Service 1', 'price': '100', 'taxable': True, 'price_': '120'}
]
}
mock_item2 = MagicMock()
mock_item2.item_model.additional_info = {
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
{'name': 'Service 2', 'price': '200', 'taxable': False, 'price_': '200'}
]
}
self.mock_model.get_itemtxs_data.return_value = [MagicMock(all=lambda: [mock_item1, mock_item2])]
calculator = CarFinanceCalculator(self.mock_model)
services = calculator._get_additional_services()
self.assertEqual(len(services), 2)
self.assertEqual(services[0]['name'], 'Service 1')
self.assertEqual(services[1]['name'], 'Service 2')
self.assertEqual(services[0]['price_'], '120')
self.assertEqual(services[1]['price_'], '200')
def test_calculate_totals(self):
mock_item1 = MagicMock()
mock_item1.ce_quantity = 2
mock_item1.item_model.additional_info = {
CarFinanceCalculator.CAR_FINANCE_KEY: {
'selling_price': '10000',
'discount_amount': '500'
},
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
{'price_': '100'},
{'price_': '200'}
]
}
mock_item2 = MagicMock()
mock_item2.quantity = 3
mock_item2.item_model.additional_info = {
CarFinanceCalculator.CAR_FINANCE_KEY: {
'selling_price': '20000',
'discount_amount': '1000'
},
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
{'price_': '300'}
]
}
self.mock_model.get_itemtxs_data.return_value = [MagicMock(all=lambda: [mock_item1, mock_item2])]
calculator = CarFinanceCalculator(self.mock_model)
totals = calculator.calculate_totals()
expected_total_price = (Decimal('10000') * 2 + Decimal('20000') * 3) - (Decimal('500') + Decimal('1000'))
expected_vat = expected_total_price * Decimal('0.15')
expected_additionals = Decimal('100') + Decimal('200') + Decimal('300')
expected_grand_total = (expected_total_price + expected_vat + expected_additionals).quantize(Decimal('0.00'))
self.assertEqual(totals['total_price'], expected_total_price)
self.assertEqual(totals['total_discount'], Decimal('1500'))
self.assertEqual(totals['total_vat_amount'], expected_vat)
self.assertEqual(totals['total_additionals'], expected_additionals)
self.assertEqual(totals['grand_total'], expected_grand_total)
def test_get_finance_data(self):
mock_item = MagicMock()
mock_item.ce_quantity = 1
mock_item.item_model = MagicMock()
mock_item.item_model.item_number = '456'
mock_item.item_model.additional_info = {
CarFinanceCalculator.CAR_FINANCE_KEY: {
'selling_price': '15000',
'discount_amount': '1000',
'total_vat': '2800'
},
CarFinanceCalculator.CAR_INFO_KEY: {
'vin': 'VIN456',
'make': 'Honda',
'model': 'Civic',
'year': 2021
},
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
{'name': 'Service', 'price': '150', 'taxable': True, 'price_': '180'}
]
}
self.mock_model.get_itemtxs_data.return_value = [MagicMock(all=lambda: [mock_item])]
calculator = CarFinanceCalculator(self.mock_model)
finance_data = calculator.get_finance_data()
self.assertEqual(len(finance_data['cars']), 1)
self.assertEqual(finance_data['quantity'], 1)
self.assertEqual(finance_data['total_price'], Decimal('14000')) # 15000 - 1000
self.assertEqual(finance_data['total_vat'], Decimal('14000') + (Decimal('14000') * Decimal('0.20')))
self.assertEqual(finance_data['total_vat_amount'], Decimal('14000') * Decimal('0.20'))
self.assertEqual(finance_data['total_additionals'], Decimal('180'))
self.assertEqual(finance_data['additionals'][0]['name'], 'Service')
self.assertEqual(finance_data['vat'], Decimal('0.20'))

View File

@ -554,15 +554,59 @@ urlpatterns = [
path('entity/<slug:entity_slug>/income-statement/year/<int:year>/',
views.FiscalYearIncomeStatementViewBase.as_view(),
name='entity-ic-year'),
# path('entity/<slug:entity_slug>/income-statement/quarter/<int:year>/<int:quarter>/',
# views.QuarterlyIncomeStatementView.as_view(),
# name='entity-ic-quarter'),
# path('entity/<slug:entity_slug>/income-statement/month/<int:year>/<int:month>/',
# views.MonthlyIncomeStatementView.as_view(),
# name='entity-ic-month'),
# path('entity/<slug:entity_slug>/income-statement/date/<int:year>/<int:month>/<int:day>/',
# views.MonthlyIncomeStatementView.as_view(),
# name='entity-ic-date'),
path('entity/<slug:entity_slug>/income-statement/quarter/<int:year>/<int:quarter>/',
views.QuarterlyIncomeStatementView.as_view(),
name='entity-ic-quarter'),
path('entity/<slug:entity_slug>/income-statement/month/<int:year>/<int:month>/',
views.MonthlyIncomeStatementView.as_view(),
name='entity-ic-month'),
path('entity/<slug:entity_slug>/income-statement/date/<int:year>/<int:month>/<int:day>/',
views.MonthlyIncomeStatementView.as_view(),
name='entity-ic-date'),
# CASH FLOW STATEMENTS...
# Entities...
path('entity/<slug:entity_slug>/cash-flow-statement/',
views.BaseCashFlowStatementRedirectViewBase.as_view(),
name='entity-cf'),
path('entity/<slug:entity_slug>/cash-flow-statement/year/<int:year>/',
views.FiscalYearCashFlowStatementViewBase.as_view(),
name='entity-cf-year'),
path('entity/<slug:entity_slug>/cash-flow-statement/quarter/<int:year>/<int:quarter>/',
views.QuarterlyCashFlowStatementView.as_view(),
name='entity-cf-quarter'),
path('entity/<slug:entity_slug>/cash-flow-statement/month/<int:year>/<int:month>/',
views.MonthlyCashFlowStatementView.as_view(),
name='entity-cf-month'),
path('entity/<slug:entity_slug>/cash-flow-statement/date/<int:year>/<int:month>/<int:day>/',
views.DateCashFlowStatementView.as_view(),
name='entity-cf-date'),
#Dashboard
# DASHBOARD Views...
path('<slug:entity_slug>/dashboard/',
views.EntityModelDetailHandlerViewBase.as_view(),
name='entity-dashboard'),
path('<slug:entity_slug>/dashboard/year/<int:year>/',
views.FiscalYearEntityModelDashboardView.as_view(),
name='entity-dashboard-year'),
path('<slug:entity_slug>/dashboard/quarter/<int:year>/<int:quarter>/',
views.QuarterlyEntityDashboardView.as_view(),
name='entity-dashboard-quarter'),
path('<slug:entity_slug>/dashboard/month/<int:year>/<int:month>/',
views.MonthlyEntityDashboardView.as_view(),
name='entity-dashboard-month'),
path('<slug:entity_slug>/dashboard/date/<int:year>/<int:month>/<int:day>/',
views.DateEntityDashboardView.as_view(),
name='entity-dashboard-date'),
#dashboard api
path('entity/<slug:entity_slug>/data/net-payables/',
views.PayableNetAPIView.as_view(),
name='entity-json-net-payables'),
path('entity/<slug:entity_slug>/data/net-receivables/',
views.ReceivableNetAPIView.as_view(),
name='entity-json-net-receivables'),
path('entity/<slug:entity_slug>/data/pnl/',
views.PnLAPIView.as_view(),
name='entity-json-pnl'),
]

View File

@ -147,7 +147,7 @@ def get_car_finance_data(model):
if i.item_model.additional_info["additional_services"]:
additional_services.extend(
[
{"name": x.name, "price": x.price}
{"name": x.get("name"), "price": x.get("price")}
for x in i.item_model.additional_info["additional_services"]
]
)
@ -281,45 +281,25 @@ def get_financial_values(model):
def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
vat_amount = 0
total_amount = 0
calculator = CarFinanceCalculator(invoice)
finance_data = calculator.get_finance_data()
# if invoice.terms == "on_receipt":
# for x in invoice.get_itemtxs_data()[0].all():
# total_amount += Decimal(x.unit_cost) * Decimal(x.quantity)
ledger = LedgerModel.objects.filter(
name__icontains=str(invoice.pk), entity=entity
).first()
journal = JournalEntryModel.objects.create(
posted=False,
description=f"Payment for Invoice {invoice.invoice_number}",
ledger=ledger,
ledger=invoice.ledger,
locked=False,
origin="Payment",
)
credit_account = entity.get_default_coa_accounts().get(name="Sales Revenue")
debit_account = None
if payment_method == "cash":
debit_account = entity.get_default_coa_accounts().get(name="Cash", active=True)
elif payment_method == "credit":
debit_account = entity.get_default_coa_accounts().get(
name="Accounts Receivable", active=True
)
else:
debit_account = entity.get_default_coa_accounts().get(
name="Cash in Bank", active=True
)
vat_payable_account = entity.get_default_coa_accounts().get(
name="VAT Payable", active=True
)
credit_account = entity.get_default_coa_accounts().get(name="Sales Revenue")
debit_account = entity.get_default_coa_accounts().get(name="Cash", active=True)
vat_payable_account = entity.get_default_coa_accounts().get(name="VAT Payable", active=True)
TransactionModel.objects.create(
journal_entry=journal,
account=debit_account, # Debit Cash
amount=finance_data["grand_total"], # Payment amount
account=debit_account, # Debit Account
amount=Decimal(finance_data["grand_total"]),
tx_type="debit",
description="Payment Received",
)
@ -327,7 +307,7 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
TransactionModel.objects.create(
journal_entry=journal,
account=credit_account, # Credit Accounts Receivable
amount=finance_data["total_price"], # Payment amount
amount=Decimal(finance_data["total_price"] + finance_data["total_additionals"]),
tx_type="credit",
description="Payment Received",
)
@ -336,7 +316,7 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
TransactionModel.objects.create(
journal_entry=journal,
account=vat_payable_account, # Credit VAT Payable
amount=finance_data["total_vat_amount"],
amount=finance_data.get("total_vat_amount"),
tx_type="credit",
description="VAT Payable on Invoice",
)
@ -413,147 +393,304 @@ def transfer_to_dealer(request, cars, to_dealer, remarks=None):
car.dealer = to_dealer
car.save()
class CarTransfer:
def __init__(self, car, transfer):
self.car = car
self.transfer = transfer
self.from_dealer = transfer.from_dealer
self.to_dealer = transfer.to_dealer
self.customer = None
self.invoice = None
self.ledger = None
self.item = None
self.product = None
self.vendor = None
self.bill = None
def transfer_car(car, transfer):
from_dealer = transfer.from_dealer
to_dealer = transfer.to_dealer
# add transfer.to_dealer as customer in transfer.from_dealer entity
def transfer_car(self):
self._create_customer()
self._create_invoice()
self._create_product_in_receiver_ledger()
self._create_vendor_and_bill()
self._finalize_car_transfer()
return True
customer = (
from_dealer.entity.get_customers().filter(email=to_dealer.user.email).first()
)
if not customer:
customer = from_dealer.entity.create_customer(
def _create_customer(self):
self.customer = self._find_or_create_customer()
if self.customer is None:
self.customer = self._create_new_customer()
def _find_or_create_customer(self):
return self.from_dealer.entity.get_customers().filter(email=self.to_dealer.user.email).first()
def _create_new_customer(self):
customer = self.from_dealer.entity.create_customer(
customer_model_kwargs={
"customer_name": to_dealer.name,
"email": to_dealer.user.email,
"address_1": to_dealer.address,
"customer_name": self.to_dealer.name,
"email": self.to_dealer.user.email,
"address_1": self.to_dealer.address,
}
)
customer.additional_info = {}
customer.additional_info.update({"type": "organization"})
customer.save()
return customer
invoice = from_dealer.entity.create_invoice(
customer_model=customer,
def _create_invoice(self):
self.invoice = self.from_dealer.entity.create_invoice(
customer_model=self.customer,
terms=InvoiceModel.TERMS_NET_30,
cash_account=from_dealer.entity.get_default_coa_accounts().get(
name="Cash", active=True
),
prepaid_account=from_dealer.entity.get_default_coa_accounts().get(
name="Accounts Receivable", active=True
),
coa_model=from_dealer.entity.get_default_coa(),
cash_account=self.from_dealer.entity.get_default_coa_accounts().get(name="Cash", active=True),
prepaid_account=self.from_dealer.entity.get_default_coa_accounts().get(name="Accounts Receivable", active=True),
coa_model=self.from_dealer.entity.get_default_coa(),
)
ledger = from_dealer.entity.create_ledger(name=str(invoice.pk))
invoice.ledgar = ledger
ledger.invoicemodel = invoice
ledger.save()
invoice.save()
item = from_dealer.entity.get_items_products().filter(name=car.vin).first()
if not item:
self.ledger = self.from_dealer.entity.create_ledger(name=str(self.invoice.pk))
self.invoice.ledgar = self.ledger
self.ledger.invoicemodel = self.invoice
self.ledger.save()
self.invoice.save()
self._add_car_item_to_invoice()
def _add_car_item_to_invoice(self):
self.item = self.from_dealer.entity.get_items_products().filter(name=self.car.vin).first()
if not self.item:
return
invoice_itemtxs = {
item.item_number: {
"unit_cost": transfer.total_price,
"quantity": transfer.quantity,
"total_amount": transfer.total_price,
self.item.item_number: {
"unit_cost": self.transfer.total_price,
"quantity": self.transfer.quantity,
"total_amount": self.transfer.total_price,
}
}
invoice_itemtxs = invoice.migrate_itemtxs(
invoice_itemtxs = self.invoice.migrate_itemtxs(
itemtxs=invoice_itemtxs,
commit=True,
operation=InvoiceModel.ITEMIZE_APPEND,
)
self.invoice.save()
self.invoice.mark_as_review()
self.invoice.mark_as_approved(self.from_dealer.entity.slug, self.from_dealer.entity.admin)
self.invoice.save()
invoice.save()
invoice.mark_as_review()
invoice.mark_as_approved(from_dealer.entity.slug, from_dealer.entity.admin)
# invoice.mark_as_paid(from_dealer.entity.slug, from_dealer.entity.admin)
invoice.save()
# create car item product in to_dealer entity
uom = to_dealer.entity.get_uom_all().filter(name=item.uom.name).first()
# create item product in the reciever ledger
product = to_dealer.entity.create_item_product(
name=item.name,
def _create_product_in_receiver_ledger(self):
uom = self.to_dealer.entity.get_uom_all().filter(name=self.item.uom.name).first()
self.product = self.to_dealer.entity.create_item_product(
name=self.item.name,
uom_model=uom,
item_type=item.item_type,
coa_model=to_dealer.entity.get_default_coa(),
item_type=self.item.item_type,
coa_model=self.to_dealer.entity.get_default_coa(),
)
product.additional_info.update({"car_info": car.to_dict()})
product.save()
self.product.additional_info.update({"car_info": self.car.to_dict()})
self.product.save()
# add the sender as vendor and create a bill for it
vendor = None
vendor = to_dealer.entity.get_vendors().filter(vendor_name=from_dealer.name).first()
def _create_vendor_and_bill(self):
self.vendor = self._find_or_create_vendor()
self.bill = self.to_dealer.entity.create_bill(
vendor_model=self.vendor,
terms=BillModel.TERMS_NET_30,
cash_account=self.to_dealer.entity.get_default_coa_accounts().get(name="Cash", active=True),
prepaid_account=self.to_dealer.entity.get_default_coa_accounts().get(name="Prepaid Expenses", active=True),
coa_model=self.to_dealer.entity.get_default_coa(),
)
self._add_car_item_to_bill()
def _find_or_create_vendor(self):
vendor = self.to_dealer.entity.get_vendors().filter(vendor_name=self.from_dealer.name).first()
if not vendor:
vendor = VendorModel.objects.create(
entity_model=to_dealer.entity,
vendor_name=from_dealer.name,
additional_info={"info": to_dict(from_dealer)},
entity_model=self.to_dealer.entity,
vendor_name=self.from_dealer.name,
additional_info={"info": to_dict(self.from_dealer)},
)
return vendor
# transfer the car to to_dealer and create items record
bill = to_dealer.entity.create_bill(
vendor_model=vendor,
terms=BillModel.TERMS_NET_30,
cash_account=to_dealer.entity.get_default_coa_accounts().get(
name="Cash", active=True
),
prepaid_account=to_dealer.entity.get_default_coa_accounts().get(
name="Prepaid Expenses", active=True
),
coa_model=to_dealer.entity.get_default_coa(),
)
bill.additional_info = {}
def _add_car_item_to_bill(self):
bill_itemtxs = {
product.item_number: {
"unit_cost": transfer.total_price,
"quantity": transfer.quantity,
"total_amount": transfer.total_price,
self.product.item_number: {
"unit_cost": self.transfer.total_price,
"quantity": self.transfer.quantity,
"total_amount": self.transfer.total_price,
}
}
bill_itemtxs = bill.migrate_itemtxs(
bill_itemtxs = self.bill.migrate_itemtxs(
itemtxs=bill_itemtxs, commit=True, operation=BillModel.ITEMIZE_APPEND
)
self.bill.additional_info = {}
self.bill.additional_info.update({"car_info": self.car.to_dict()})
self.bill.additional_info.update({"car_finance": self.car.finances.to_dict()})
bill.additional_info.update({"car_info": car.to_dict()})
bill.additional_info.update({"car_finance": car.finances.to_dict()})
self.bill.mark_as_review()
self.bill.mark_as_approved(self.to_dealer.entity.slug, self.to_dealer.entity.admin)
self.bill.save()
bill.mark_as_review()
bill.mark_as_approved(to_dealer.entity.slug, to_dealer.entity.admin)
bill.save()
def _finalize_car_transfer(self):
self.car.dealer = self.to_dealer
self.car.vendor = self.vendor
self.car.receiving_date = datetime.datetime.now()
self.car.finances.additional_services.clear()
if hasattr(self.car, "custom_cards"):
self.car.custom_cards.delete()
car.dealer = to_dealer
car.vendor = vendor
car.receiving_date = datetime.datetime.now()
car.finances.additional_services.clear()
if hasattr(car, "custom_cards"):
car.custom_cards.delete()
self.car.finances.cost_price = self.transfer.total_price
self.car.finances.selling_price = 0
self.car.finances.discount_amount = 0
self.car.finances.save()
self.car.location.owner = self.to_dealer
self.car.location.showroom = self.to_dealer
self.car.location.description = ""
self.car.location.save()
self.car.status = models.CarStatusChoices.AVAILABLE
self.transfer.status = models.CarTransferStatusChoices.success
self.transfer.active = False
self.transfer.save()
self.car.save()
car.finances.cost_price = transfer.total_price
car.finances.selling_price = 0
car.finances.discount_amount = 0
car.finances.save()
car.location.owner = to_dealer
car.location.showroom = to_dealer
car.location.description = ""
car.location.save()
car.status = models.CarStatusChoices.AVAILABLE
transfer.status = models.CarTransferStatusChoices.success
transfer.active = False
transfer.save()
car.save()
return True
# pay the pill
# set_bill_payment(to_dealer,to_dealer.entity,bill,transfer.total_price,"credit")
# def transfer_car(car, transfer):
# from_dealer = transfer.from_dealer
# to_dealer = transfer.to_dealer
# # add transfer.to_dealer as customer in transfer.from_dealer entity
# customer = (
# from_dealer.entity.get_customers().filter(email=to_dealer.user.email).first()
# )
# if not customer:
# customer = from_dealer.entity.create_customer(
# customer_model_kwargs={
# "customer_name": to_dealer.name,
# "email": to_dealer.user.email,
# "address_1": to_dealer.address,
# }
# )
# customer.additional_info.update({"type": "organization"})
# customer.save()
# invoice = from_dealer.entity.create_invoice(
# customer_model=customer,
# terms=InvoiceModel.TERMS_NET_30,
# cash_account=from_dealer.entity.get_default_coa_accounts().get(
# name="Cash", active=True
# ),
# prepaid_account=from_dealer.entity.get_default_coa_accounts().get(
# name="Accounts Receivable", active=True
# ),
# coa_model=from_dealer.entity.get_default_coa(),
# )
# ledger = from_dealer.entity.create_ledger(name=str(invoice.pk))
# invoice.ledgar = ledger
# ledger.invoicemodel = invoice
# ledger.save()
# invoice.save()
# item = from_dealer.entity.get_items_products().filter(name=car.vin).first()
# if not item:
# return
# invoice_itemtxs = {
# item.item_number: {
# "unit_cost": transfer.total_price,
# "quantity": transfer.quantity,
# "total_amount": transfer.total_price,
# }
# }
# invoice_itemtxs = invoice.migrate_itemtxs(
# itemtxs=invoice_itemtxs,
# commit=True,
# operation=InvoiceModel.ITEMIZE_APPEND,
# )
# invoice.save()
# invoice.mark_as_review()
# invoice.mark_as_approved(from_dealer.entity.slug, from_dealer.entity.admin)
# # invoice.mark_as_paid(from_dealer.entity.slug, from_dealer.entity.admin)
# invoice.save()
# # create car item product in to_dealer entity
# uom = to_dealer.entity.get_uom_all().filter(name=item.uom.name).first()
# # create item product in the reciever ledger
# product = to_dealer.entity.create_item_product(
# name=item.name,
# uom_model=uom,
# item_type=item.item_type,
# coa_model=to_dealer.entity.get_default_coa(),
# )
# product.additional_info.update({"car_info": car.to_dict()})
# product.save()
# # add the sender as vendor and create a bill for it
# vendor = None
# vendor = to_dealer.entity.get_vendors().filter(vendor_name=from_dealer.name).first()
# if not vendor:
# vendor = VendorModel.objects.create(
# entity_model=to_dealer.entity,
# vendor_name=from_dealer.name,
# additional_info={"info": to_dict(from_dealer)},
# )
# # transfer the car to to_dealer and create items record
# bill = to_dealer.entity.create_bill(
# vendor_model=vendor,
# terms=BillModel.TERMS_NET_30,
# cash_account=to_dealer.entity.get_default_coa_accounts().get(
# name="Cash", active=True
# ),
# prepaid_account=to_dealer.entity.get_default_coa_accounts().get(
# name="Prepaid Expenses", active=True
# ),
# coa_model=to_dealer.entity.get_default_coa(),
# )
# bill.additional_info = {}
# bill_itemtxs = {
# product.item_number: {
# "unit_cost": transfer.total_price,
# "quantity": transfer.quantity,
# "total_amount": transfer.total_price,
# }
# }
# bill_itemtxs = bill.migrate_itemtxs(
# itemtxs=bill_itemtxs, commit=True, operation=BillModel.ITEMIZE_APPEND
# )
# bill.additional_info.update({"car_info": car.to_dict()})
# bill.additional_info.update({"car_finance": car.finances.to_dict()})
# bill.mark_as_review()
# bill.mark_as_approved(to_dealer.entity.slug, to_dealer.entity.admin)
# bill.save()
# car.dealer = to_dealer
# car.vendor = vendor
# car.receiving_date = datetime.datetime.now()
# car.finances.additional_services.clear()
# if hasattr(car, "custom_cards"):
# car.custom_cards.delete()
# car.finances.cost_price = transfer.total_price
# car.finances.selling_price = 0
# car.finances.discount_amount = 0
# car.finances.save()
# car.location.owner = to_dealer
# car.location.showroom = to_dealer
# car.location.description = ""
# car.location.save()
# car.status = models.CarStatusChoices.AVAILABLE
# transfer.status = models.CarTransferStatusChoices.success
# transfer.active = False
# transfer.save()
# car.save()
# return True
def to_dict(obj):
@ -628,9 +765,10 @@ class CarFinanceCalculator:
"additional_services": self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY),
}
def _get_additional_services(self):
return [
{"name": service.name, "price": service.price}
{"name": service.get('name'), "price": service.get('price'), "taxable": service.get('taxable'),"price_": service.get('price_')}
for item in self.item_transactions
for service in self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY) or []
]
@ -638,26 +776,28 @@ class CarFinanceCalculator:
def calculate_totals(self):
total_price = sum(
Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'selling_price')) *
Decimal(self._get_quantity(item))
int(self._get_quantity(item))
for item in self.item_transactions
)
total_additionals = sum(Decimal(x.get('price_')) for x in self._get_additional_services())
total_vat_amount = total_price * self.vat_rate
total_discount = sum(
Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'discount_amount'))
for item in self.item_transactions
)
total_price_discounted = total_price - total_discount
total_vat_amount = total_price_discounted * self.vat_rate
return {
"total_price": total_price,
"total_price": total_price_discounted,
"total_vat_amount": total_vat_amount,
"total_discount": total_discount,
"grand_total": (total_price + total_vat_amount) - total_discount ,
"total_additionals": total_additionals,
"grand_total": round(total_price_discounted + total_vat_amount + total_additionals, 2)
}
def get_finance_data(self):
totals = self.calculate_totals()
return {
"cars": [self._get_car_data(item) for item in self.item_transactions],
"quantity": sum(self._get_quantity(item) for item in self.item_transactions),
@ -665,6 +805,7 @@ class CarFinanceCalculator:
"total_vat": totals['total_vat_amount'] + totals['total_price'],
"total_vat_amount": totals['total_vat_amount'],
"total_discount": totals['total_discount'],
"total_additionals": totals['total_additionals'],
"grand_total": totals['grand_total'],
"additionals": self.additional_services,
"vat": self.vat_rate,

View File

@ -1,3 +1,5 @@
from calendar import month_name
from random import randint
from rich import print
from decimal import Decimal
from django.core.paginator import Paginator
@ -95,7 +97,7 @@ from .utils import (
set_bill_payment,
set_invoice_payment,
to_dict,
transfer_car,
CarTransfer,
)
from django.contrib.auth.models import User
from allauth.account import views
@ -105,7 +107,7 @@ import cv2
import numpy as np
from pyzbar.pyzbar import decode
from django.core.files.storage import default_storage
from django_ledger.utils import accruable_net_summary
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
@ -879,7 +881,9 @@ def car_transfer_accept_reject(request, car_pk, transfer_pk):
elif status == "accepted":
transfer.status = "accept"
transfer.save()
success = transfer_car(car, transfer)
transfer_process = CarTransfer(car, transfer)
success = transfer_process.transfer_car()
# success = CarTransfer(car, transfer)
if success:
messages.success(request, _("Car Transfer Completed successfully."))
models.Notification.objects.create(
@ -1050,7 +1054,9 @@ class CustomerListView(LoginRequiredMixin, ListView):
query = self.request.GET.get("q")
dealer = get_user_type(self.request)
customers = dealer.entity.get_customers().filter(active=True,additional_info__type="customer")
customers = dealer.entity.get_customers().filter(
active=True, additional_info__type="customer"
)
if query:
customers = customers.filter(
@ -1183,7 +1189,11 @@ def CustomerUpdateView(request, pk):
messages.success(request, _("Customer updated successfully."))
return redirect("customer_list")
else:
form = forms.CustomerForm(initial=customer.additional_info["customer_info"] if "customer_info" in customer.additional_info else {})
form = forms.CustomerForm(
initial=customer.additional_info["customer_info"]
if "customer_info" in customer.additional_info
else {}
)
return render(request, "customers/customer_form.html", {"form": form})
@ -1722,7 +1732,11 @@ class OrganizationListView(LoginRequiredMixin, ListView):
def get_queryset(self):
dealer = get_user_type(self.request)
return dealer.entity.get_customers().filter(additional_info__type="organization",active=True).all()
return (
dealer.entity.get_customers()
.filter(additional_info__type="organization", active=True)
.all()
)
class OrganizationDetailView(DetailView):
@ -1735,9 +1749,9 @@ def OrganizationCreateView(request):
if request.method == "POST":
form = forms.OrganizationForm(request.POST)
#upload logo
image = request.FILES.get('logo')
file_name = default_storage.save('images/{}'.format(image.name), image)
# upload logo
image = request.FILES.get("logo")
file_name = default_storage.save("images/{}".format(image.name), image)
file_url = default_storage.url(file_name)
organization_dict = {
@ -1765,8 +1779,7 @@ def OrganizationCreateView(request):
return render(request, "organizations/organization_form.html", {"form": form})
def OrganizationUpdateView(request,pk):
def OrganizationUpdateView(request, pk):
organization = get_object_or_404(CustomerModel, pk=pk)
if request.method == "POST":
form = forms.OrganizationForm(request.POST)
@ -1776,13 +1789,17 @@ def OrganizationUpdateView(request,pk):
}
dealer = get_user_type(request)
instance = dealer.entity.get_customers().get(pk=organization.additional_info['organization_info']['pk'])
instance = dealer.entity.get_customers().get(
pk=organization.additional_info["organization_info"]["pk"]
)
instance.customer_name = organization_dict["name"]
instance.address_1 = organization_dict["address"]
instance.phone = organization_dict["phone_number"]
instance.email = organization_dict["email"]
organization_dict["logo"] = organization.additional_info['organization_info']['logo']
organization_dict["logo"] = organization.additional_info["organization_info"][
"logo"
]
organization_dict["pk"] = str(instance.pk)
instance.additional_info["organization_info"] = organization_dict
instance.additional_info["type"] = "organization"
@ -1790,8 +1807,10 @@ def OrganizationUpdateView(request,pk):
messages.success(request, _("Organization created successfully."))
return redirect("organization_list")
else:
form = forms.OrganizationForm(initial=organization.additional_info["organization_info"] or {})
form.fields.pop('logo', None)
form = forms.OrganizationForm(
initial=organization.additional_info["organization_info"] or {}
)
form.fields.pop("logo", None)
return render(request, "organizations/organization_form.html", {"form": form})
@ -2121,17 +2140,22 @@ class AccountCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
def form_valid(self, form):
dealer = get_user_type(self.request)
form.instance.entity_model = dealer.entity
form.instance.coa_model = dealer.entity.get_default_coa()
form.instance.depth = 0
return super().form_valid(form)
def get_form_kwargs(self):
dealer = get_user_type(self.request)
entity = dealer.entity
kwargs = super().get_form_kwargs()
kwargs["coa_model"] = entity.get_default_coa()
kwargs["coa_model"] = dealer.entity.get_default_coa()
return kwargs
def get_form(self, form_class=None):
form = super().get_form(form_class)
entity = get_user_type(self.request).entity
form.initial['coa_model'] = entity.get_default_coa()
return form
class AccountDetailView(LoginRequiredMixin, DetailView):
model = AccountModel
@ -2188,13 +2212,11 @@ class AccountUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
@login_required
def account_delete(request, pk):
account = get_object_or_404(AccountModel, pk=pk)
if request.method == "POST":
account.delete()
messages.success(request, "Account deleted successfully.")
return redirect("account_list")
return render(
request, "ledger/coa_accounts/account_delete.html", {"account": account}
)
# Estimates
@ -2207,7 +2229,11 @@ class EstimateListView(LoginRequiredMixin, ListView):
def get_queryset(self):
dealer = get_user_type(self.request)
entity = dealer.entity
return entity.get_estimates()
status = self.request.GET.get('status')
queryset = entity.get_estimates()
if status:
queryset = queryset.filter(status=status)
return queryset
# class EstimateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
@ -2352,7 +2378,9 @@ def create_estimate(request):
}
)
form = forms.EstimateModelCreateForm(entity_slug=entity.slug, user_model=entity.admin)
form = forms.EstimateModelCreateForm(
entity_slug=entity.slug, user_model=entity.admin
)
form.fields["customer"].queryset = entity.get_customers().filter(active=True)
car_list = models.Car.objects.filter(
dealer=dealer, finances__selling_price__gt=0
@ -2383,8 +2411,7 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
if estimate.get_itemtxs_data():
calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data()
kwargs['data'] = finance_data
print(finance_data)
kwargs["data"] = finance_data
kwargs["invoice"] = (
InvoiceModel.objects.all().filter(ce_model=estimate).first()
)
@ -2407,20 +2434,26 @@ def create_sale_order(request, pk):
form = forms.SaleOrderForm()
form.fields["estimate"].queryset = EstimateModel.objects.filter(pk=pk)
form.initial['estimate'] = estimate
form.initial["estimate"] = estimate
# data = get_car_finance_data(estimate)
calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data()
return render(
request,
"sales/estimates/sale_order_form.html",
{"form": form, "estimate": estimate, "items": items,"data": finance_data},
{"form": form, "estimate": estimate, "items": items, "data": finance_data},
)
def preview_sale_order(request,pk):
estimate = get_object_or_404(EstimateModel,pk=pk)
def preview_sale_order(request, pk):
estimate = get_object_or_404(EstimateModel, pk=pk)
data = get_car_finance_data(estimate)
return render(request,'sales/estimates/sale_order_preview.html',{'order':estimate.sale_orders.first(),"data":data,"estimate":estimate})
return render(
request,
"sales/estimates/sale_order_preview.html",
{"order": estimate.sale_orders.first(), "data": data, "estimate": estimate},
)
class PaymentRequest(LoginRequiredMixin, DetailView):
model = EstimateModel
@ -2513,7 +2546,10 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
if invoice.get_itemtxs_data():
calculator = CarFinanceCalculator(invoice)
finance_data = calculator.get_finance_data()
print((finance_data["total_vat_amount"]+finance_data["total_price"]) == finance_data["grand_total"])
print(
(finance_data["total_vat_amount"] + finance_data["total_price"])
== finance_data["grand_total"]
)
kwargs["data"] = finance_data
kwargs["payments"] = JournalEntryModel.objects.filter(
ledger=invoice.ledger
@ -3164,7 +3200,6 @@ class BillDetailView(LoginRequiredMixin, DetailView):
)
vat = models.VatRate.objects.filter(is_active=True).first()
if vat:
grand_total += round(Decimal(grand_total) * Decimal(vat.rate), 2)
kwargs["car_and_item_info"] = car_and_item_info
kwargs["grand_total"] = grand_total
@ -3390,16 +3425,14 @@ def bill_create(request):
}
)
car_list = models.Car.objects.filter(
dealer=dealer, finances__selling_price__gt=0,status="available"
dealer=dealer, finances__selling_price__gt=0, status="available"
)
context = {
"form": form,
"items": [
{
"car": x,
"product": entity.get_items_products()
.filter(name=x.vin)
.first(),
"product": entity.get_items_products().filter(name=x.vin).first(),
}
for x in car_list
],
@ -3422,6 +3455,7 @@ class SubscriptionPlans(ListView):
# orders
class OrderListView(ListView):
model = models.SaleOrder
template_name = "sales/orders/order_list.html"
@ -3498,39 +3532,53 @@ def custom_bad_request_view(request, exception=None):
return render(request, "errors/400.html", {})
# from django_ledger.io.io_core import get_localdate
# from django_ledger.views.mixins import (DjangoLedgerSecurityMixIn)
# from django.views.generic import RedirectView
from django_ledger.views.financial_statement import FiscalYearBalanceSheetView,BaseIncomeStatementRedirectView,FiscalYearIncomeStatementView
from django_ledger.views.financial_statement import (
FiscalYearBalanceSheetView,
BaseIncomeStatementRedirectView,
FiscalYearIncomeStatementView,
BaseCashFlowStatementRedirectView,
FiscalYearCashFlowStatementView,
)
from django_ledger.views.entity import EntityModelDetailBaseView,EntityModelDetailHandlerView
from django.views.generic import DetailView, RedirectView
from django_ledger.io.io_core import get_localdate
from django_ledger.models import EntityModel, EntityUnitModel
from django_ledger.views.mixins import (
QuarterlyReportMixIn, YearlyReportMixIn,
MonthlyReportMixIn, DateReportMixIn, DjangoLedgerSecurityMixIn, EntityUnitMixIn,
BaseDateNavigationUrlMixIn, PDFReportMixIn
QuarterlyReportMixIn,
YearlyReportMixIn,
MonthlyReportMixIn,
DateReportMixIn,
DjangoLedgerSecurityMixIn,
EntityUnitMixIn,
BaseDateNavigationUrlMixIn,
PDFReportMixIn,
)
# BALANCE SHEET -----------
class BaseBalanceSheetRedirectView(DjangoLedgerSecurityMixIn, RedirectView):
class BaseBalanceSheetRedirectView(DjangoLedgerSecurityMixIn, RedirectView):
def get_redirect_url(self, *args, **kwargs):
year = get_localdate().year
return reverse('entity-bs-year',
kwargs={
'entity_slug': self.kwargs['entity_slug'],
'year': year
})
return reverse(
"entity-bs-year",
kwargs={"entity_slug": self.kwargs["entity_slug"], "year": year},
)
class FiscalYearBalanceSheetViewBase(FiscalYearBalanceSheetView):
template_name = "ledger/reports/balance_sheet.html"
class QuarterlyBalanceSheetView(FiscalYearBalanceSheetViewBase, QuarterlyReportMixIn):
"""
Quarter Balance Sheet View.
"""
class MonthlyBalanceSheetView(FiscalYearBalanceSheetViewBase, MonthlyReportMixIn):
"""
Monthly Balance Sheet View.
@ -3542,33 +3590,264 @@ class DateBalanceSheetView(FiscalYearBalanceSheetViewBase, DateReportMixIn):
Date Balance Sheet View.
"""
class BaseIncomeStatementRedirectViewBase(BaseIncomeStatementRedirectView):
# Income Statement -----------
class BaseIncomeStatementRedirectViewBase(BaseIncomeStatementRedirectView):
def get_redirect_url(self, *args, **kwargs):
year = get_localdate().year
dealer = get_user_type(self.request)
return reverse('entity-ic-year',
kwargs={
'entity_slug': dealer.entity.slug,
'year': year
})
return reverse(
"entity-ic-year", kwargs={"entity_slug": dealer.entity.slug, "year": year}
)
class FiscalYearIncomeStatementViewBase(FiscalYearIncomeStatementView):
template_name = "ledger/reports/income_statement.html"
class QuarterlyIncomeStatementView(FiscalYearIncomeStatementView, QuarterlyReportMixIn):
class QuarterlyIncomeStatementView(
FiscalYearIncomeStatementViewBase, QuarterlyReportMixIn
):
"""
Quarter Income Statement View.
"""
class MonthlyIncomeStatementView(FiscalYearIncomeStatementView, MonthlyReportMixIn):
class MonthlyIncomeStatementView(FiscalYearIncomeStatementViewBase, MonthlyReportMixIn):
"""
Monthly Income Statement View.
"""
class DateModelIncomeStatementView(FiscalYearIncomeStatementView, DateReportMixIn):
class DateModelIncomeStatementView(FiscalYearIncomeStatementViewBase, DateReportMixIn):
"""
Date Income Statement View.
"""
# Cash Flow -----------
class BaseCashFlowStatementRedirectViewBase(BaseCashFlowStatementRedirectView):
def get_redirect_url(self, *args, **kwargs):
year = get_localdate().year
dealer = get_user_type(self.request)
return reverse(
"entity-cf-year", kwargs={"entity_slug": dealer.entity.slug, "year": year}
)
class FiscalYearCashFlowStatementViewBase(FiscalYearCashFlowStatementView):
template_name = "ledger/reports/cash_flow_statement.html"
class QuarterlyCashFlowStatementView(
FiscalYearCashFlowStatementViewBase, QuarterlyReportMixIn
):
"""
Quarter Cash Flow Statement View.
"""
class MonthlyCashFlowStatementView(
FiscalYearCashFlowStatementViewBase, MonthlyReportMixIn
):
"""
Monthly Cash Flow Statement View.
"""
class DateCashFlowStatementView(FiscalYearCashFlowStatementViewBase, DateReportMixIn):
"""
Date Cash Flow Statement View.
"""
# Dashboard
class EntityModelDetailHandlerViewBase(EntityModelDetailHandlerView):
def get_redirect_url(self, *args, **kwargs):
loc_date = get_localdate()
dealer = get_user_type(self.request)
unit_slug = self.get_unit_slug()
if unit_slug:
return reverse('unit-dashboard-month',
kwargs={
'entity_slug': dealer.entity.slug,
'unit_slug': unit_slug,
'year': loc_date.year,
'month': loc_date.month,
})
return reverse('entity-dashboard-month',
kwargs={
'entity_slug': dealer.entity.slug,
'year': loc_date.year,
'month': loc_date.month,
})
class EntityModelDetailBaseViewBase(EntityModelDetailBaseView):
template_name = "ledger/reports/dashboard.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
dealer = get_user_type(self.request)
entity_model: EntityModel = dealer.entity
context['page_title'] = entity_model.name
context['header_title'] = entity_model.name
context['header_subtitle'] = _('Dashboard')
context['header_subtitle_icon'] = 'mdi:monitor-dashboard'
unit_slug = context.get('unit_slug', self.get_unit_slug())
KWARGS = dict(entity_slug=self.kwargs['entity_slug'])
if unit_slug:
KWARGS['unit_slug'] = unit_slug
url_pointer = 'entity' if not unit_slug else 'unit'
context['pnl_chart_id'] = f'djl-entity-pnl-chart-{randint(10000, 99999)}'
context['pnl_chart_endpoint'] = reverse(f'django_ledger:{url_pointer}-json-pnl', kwargs=KWARGS)
context['payables_chart_id'] = f'djl-entity-payables-chart-{randint(10000, 99999)}'
context['payables_chart_endpoint'] = reverse(f'django_ledger:{url_pointer}-json-net-payables', kwargs=KWARGS)
context['receivables_chart_id'] = f'djl-entity-receivables-chart-{randint(10000, 99999)}'
context['receivables_chart_endpoint'] = reverse(f'django_ledger:{url_pointer}-json-net-receivables',
kwargs=KWARGS)
return context
class FiscalYearEntityModelDashboardView(EntityModelDetailBaseViewBase):
"""
Entity Fiscal Year Dashboard View.
"""
class QuarterlyEntityDashboardView(FiscalYearEntityModelDashboardView, QuarterlyReportMixIn):
"""
Entity Quarterly Dashboard View.
"""
class MonthlyEntityDashboardView(FiscalYearEntityModelDashboardView, MonthlyReportMixIn):
"""
Monthly Entity Dashboard View.
"""
class DateEntityDashboardView(FiscalYearEntityModelDashboardView, DateReportMixIn):
"""
Date-specific Entity Dashboard View.
"""
class PayableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View):
http_method_names = ['get']
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
dealer = get_user_type(request)
bill_qs = BillModel.objects.for_entity(
entity_slug=dealer.entity.slug,
user_model=dealer.entity.admin,
).unpaid()
# todo: implement this...
# unit_slug = self.get_unit_slug()
# if unit_slug:
# bill_qs.filter(ledger__journal_entry__entity_unit__slug__exact=unit_slug)
net_summary = accruable_net_summary(bill_qs)
net_payables = {
'net_payable_data': net_summary
}
return JsonResponse({
'results': net_payables
})
return JsonResponse({
'message': 'Unauthorized'
}, status=401)
class ReceivableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View):
http_method_names = ['get']
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
dealer = get_user_type(request)
invoice_qs = InvoiceModel.objects.for_entity(
entity_slug=dealer.entity.slug,
user_model=dealer.entity.admin,
).unpaid()
# todo: implement this...
# unit_slug = self.get_unit_slug()
# if unit_slug:
# invoice_qs.filter(ledger__journal_entry__entity_unit__slug__exact=unit_slug)
net_summary = accruable_net_summary(invoice_qs)
net_receivable = {
'net_receivable_data': net_summary
}
return JsonResponse({
'results': net_receivable
})
return JsonResponse({
'message': 'Unauthorized'
}, status=401)
class PnLAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View):
http_method_names = ['get']
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
dealer = get_user_type(request)
entity = EntityModel.objects.for_user(
user_model=dealer.entity.admin).get(
slug__exact=dealer.entity.slug)
unit_slug = self.get_unit_slug()
io_digest = entity.digest(
user_model=self.request.user,
unit_slug=unit_slug,
equity_only=True,
signs=False,
by_period=True,
process_groups=True,
from_date=self.request.GET.get('fromDate'),
to_date=self.request.GET.get('toDate'),
# todo: For PnL to display proper period values must not use closing entries.
use_closing_entries=False
)
io_data = io_digest.get_io_data()
group_balance_by_period = io_data['group_balance_by_period']
group_balance_by_period = dict(sorted((k, v) for k, v in group_balance_by_period.items()))
entity_data = {
f'{month_name[k[1]]} {k[0]}': {d: float(f) for d, f in v.items()} for k, v in
group_balance_by_period.items()}
entity_pnl = {
'entity_slug': entity.slug,
'entity_name': entity.name,
'pnl_data': entity_data
}
return JsonResponse({
'results': entity_pnl
})
return JsonResponse({
'message': 'Unauthorized'
}, status=401)

View File

@ -1,3 +1,5 @@
from django_ledger.models.invoice import InvoiceModel
from django_ledger.utils import accruable_net_summary
from decimal import Decimal
from django_ledger.models import EstimateModel,EntityModel
from rich import print
@ -28,18 +30,23 @@ def run():
# # print(bs_report)
# print(ic_report.get_report_data())
estimate = EstimateModel.objects.first()
calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data()
# estimate = EstimateModel.objects.first()
# calculator = CarFinanceCalculator(estimate)
# finance_data = calculator.get_finance_data()
invoice_itemtxs = {
i.get("item_number"): {
"unit_cost": i.get("total_price"),
"quantity": i.get("quantity"),
"total_amount": i.get("total_vat"),
}
for i in finance_data.get("cars")
}
print(finance_data)
# invoice_itemtxs = {
# i.get("item_number"): {
# "unit_cost": i.get("total_price"),
# "quantity": i.get("quantity"),
# "total_amount": i.get("total_vat"),
# }
# for i in finance_data.get("cars")
# }
# invoice = InvoiceModel.objects.first()
entity = EntityModel.objects.filter(name="ismail").first()
invoice_qs = InvoiceModel.objects.for_entity(
entity_slug=entity.slug,
user_model=entity.admin,
).unpaid()
print(accruable_net_summary(invoice_qs))

File diff suppressed because one or more lines are too long

View File

@ -26,6 +26,8 @@
<link rel="manifest" href="{% static 'images/favicons/manifest.json' %}">
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
<meta name="theme-color" content="#ffffff">
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
<script src="{% static 'js/config.js' %}"></script>
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
@ -36,9 +38,9 @@
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&amp;display=swap" rel="stylesheet">
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
<link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet">
<link href="{% static 'vendors/flatpickr/flatpickr.min.css' %}" rel="stylesheet">
<link href="{% static 'vendors/flatpickr/flatpickr.min.css' %}" rel="stylesheet">
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
{% if LANGUAGE_CODE == 'en' %}
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
@ -46,34 +48,45 @@
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
{% endif %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"
integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
{% block customCSS %}
{% endblock %}
</head>
<body>
<main class="main" id="top">
{% include 'header.html' %}
<div class="content">
<div class="content">
{% block period_navigation %}{% endblock period_navigation %}
<section class="content">
{% block content %}
{% endblock content%}
{% block body %}
{% endblock body%}
{% include 'footer.html' %}
</div>
{% block content %}{% endblock content%}
{% block body %}{% endblock body%}
{% include 'footer.html' %}
</div>
</main>
{% block customJS %}
{% endblock %}
<script src="{% static 'django_ledger/bundle/djetler.bundle.js' %}"></script>
<script>
{% if entity_slug %}
let entitySlug = "{{ view.kwargs.entity_slug }}"
{% endif %}
{% if from_date and to_date %}
let fromDate = "{{ from_date | date:'Y-m-d' }}";
let toDate = "{{ to_date | date:'Y-m-d' }}";
{% endif %}
{% if date_navigation_url %}
let dateNavigationUrl = "{{ date_navigation_url }}"
let datePickers = document.querySelectorAll("[id^='djl-datepicker']")
datePickers.forEach(dp => djLedger.getCalendar(dp.attributes.id.value, dateNavigationUrl))
{% endif %}
</script>
{% endblock %}
<!-- ===============================================-->
<!-- JavaScripts-->
@ -89,6 +102,7 @@
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
<script src="{% static 'js/phoenix.js' %}"></script>
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
<script src="{% static 'js/crm-analytics.js' %}"></script>
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
@ -96,11 +110,10 @@
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
<script src="{% static 'vendors/flatpickr/flatpickr.min.js' %}"></script>
<script src="{% static 'django_ledger/bundle/djetler.bundle.js' %}"></script>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script>
{% if messages %}
{% for message in messages %}
@ -121,17 +134,6 @@
{% endfor %}
{% endif %}
/*const Toast = Swal.mixin({
toast: true,
position: "top-end",
showConfirmButton: false,
timer: 2000,
timerProgressBar: false,
didOpen: (toast) => {
toast.onmouseenter = Swal.stopTimer;
toast.onmouseleave = Swal.resumeTimer;
}
});*/
function notify(tag,msg){
Toast.fire({
icon: tag,
@ -139,9 +141,20 @@ function notify(tag,msg){
});
}
{% if entity_slug %}
let entitySlug = "{{ view.kwargs.entity_slug }}"
{% endif %}
{% if from_date and to_date %}
let fromDate = "{{ from_date | date:'Y-m-d' }}";
let toDate = "{{ to_date | date:'Y-m-d' }}";
{% endif %}
</script>
{% if date_navigation_url %}
let dateNavigationUrl = "{{ date_navigation_url }}"
let datePickers = document.querySelectorAll("[id^='djl-datepicker']")
datePickers.forEach(dp => djLedger.getCalendar(dp.attributes.id.value, dateNavigationUrl))
{% endif %}
</script>
</body>
</html>

View File

@ -251,6 +251,24 @@
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-reports">
<li class="nav-item">
{% if request.user.is_authenticated %}
<a class="nav-link" href="{% url 'entity-dashboard' request.user.dealer.entity.slug %}">
{% else %}
<a class="nav-link" href="#">
{% endif %}
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'Dashboard'|capfirst %}</span>
</div>
</a>
{% if request.user.is_authenticated %}
<a class="nav-link" href="{% url 'entity-cf' request.user.dealer.entity.slug %}">
{% else %}
<a class="nav-link" href="#">
{% endif %}
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'Cash Flow'|capfirst %}</span>
</div>
</a>
{% if request.user.is_authenticated %}
<a class="nav-link" href="{% url 'entity-ic' request.user.dealer.entity.slug %}">
{% else %}
<a class="nav-link" href="#">

View File

@ -145,7 +145,7 @@
{% for service in car.finances.additional_services.all %}
<tr>
<td>{{service.name}}</td>
<td>{{ service.price }}</td>
<td>{{ service.price_ }}</td>
</tr>
{% endfor %}
{% endif %}
@ -313,9 +313,7 @@
{% endif %}
</td>
<td>
{% if car.get_transfer.status == "draft" %}
<a class="btn btn-sm btn-phoenix-success" href="{% url 'transfer_detail' car.get_transfer.pk %}?action=cancel">Cancel</a>
{% endif %}
</td>
</tr>
</tbody>

View File

@ -0,0 +1,52 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load custom_filters %}
{% block period_navigation %}
{% if unit_model and entity %}
<div class="col-12">{% period_navigation 'unit-cf' %}</div>
{% elif entity %}
<div class="col-12">{% period_navigation 'entity-cf' %}</div>
{% elif ledger %}
<div class="col-12">{% period_navigation 'ledger-cf' %}</div>
{% elif unit_model %}
<div class="col-12">{% period_navigation 'unit-cf' %}</div>
{% endif %}
{% endblock %}
{% block content %}
<div class="card">
<div class="card-body text-center">
<div class="container mb-4">
<div class="row">
<div class="col">
{% if unit_model and entity %}
<h1 class="display-4 font-weight-light">{{ entity.name }}</h1>
{% elif entity %}
<h1 class="display-4 font-weight-light">{{ entity.name }}</h1>
{% elif ledger %}
<h1 class="display-4 font-weight-light">{{ ledger.name }}</h1>
{% endif %}
<h1 class="display-4 font-weight-bold">{% trans 'Cash Flow Statement' %}</h1>
{% if unit_model %}
<h3 class="h4 font-weight-bold text-success">{{ unit_model.name }} {% trans 'Unit' %}</h3>
{% endif %}
<h2 class="display-4 font-weight-light">
{% if quarter %}{{ year }} | Q{{ quarter }}
{% elif month %}{{ start_date | date:'F, Y' }}
{% else %}{% trans 'Fiscal Year' %} {{ year }}
{% endif %}</h2>
<h3 class="h4 font-italic font-weight-light">
{{ from_date | date:'m/d/Y' }} - {{ to_date | date:'m/d/Y' }}
</h3>
</div>
</div>
</div>
{% cash_flow_statement io_model=object %}
<a class="btn btn-primary w-100 my-2"
href="{{ request.path }}?format=pdf">{% trans 'Download PDF' %}</a>
</div>
</div>
{% endblock %}

View File

@ -16,10 +16,16 @@
<!-- Year Navigation -->
<div class="text-center mb-3">
<p class="mb-1">
<span class="fw-bold">Year:</span>
<a href="{{ previous_year_url }}" class="text-decoration-none me-2"><< {{ previous_year }}</a>
<a href="{{ current_year_url }}" class="text-decoration-none me-2">{{ year }}</a>
<a href="{{ next_year_url }}" class="text-decoration-none">{{ next_year }} >></a>
<span class="fw-bold">Year: </span>
<a href="{{ previous_year_url }}" class="text-decoration-none me-2">
<span class="fas fa-chevron-left"> </span>
<span class="fas fa-chevron-left"> </span>
{{ previous_year }}</a>
<a href="{{ current_year_url }}" class="text-decoration-none me-2 fw-bolder">{{ year }}</a>
<a href="{{ next_year_url }}" class="text-decoration-none">{{ next_year }}
<span class="fas fa-chevron-right"> </span>
<span class="fas fa-chevron-right"> </span>
</a>
</p>
</div>
@ -66,8 +72,6 @@
</div>
<!-- Date Picker -->
<div class="text-center">
{% date_picker date_navigation_url %}
</div>
</div>
</div>

View File

@ -0,0 +1,140 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load django_ledger %}
{% block content %}
<div id="chart" class="echart-basic-bar-chart-example" style="min-height:300px"></div>
<section class="py-5">
<div class="container-fluid">
<div class="row">
<div class="col-lg-3 d-none d-lg-block">
<!-- Sidebar (if needed) -->
</div>
<div class="col-lg-9">
<div class="row row-cols-1 row-cols-md-2 g-4">
<div class="col">
{% include 'django_ledger/includes/widget_bs.html' with tx_digest=tx_digest %}
</div>
<div class="col">
{% include 'django_ledger/includes/widget_ic.html' with tx_digest=equity_digest %}
</div>
<div class="col-md-8">
<div class="card shadow-sm">
<div class="card-body">
{% chart_container pnl_chart_id pnl_chart_endpoint %}
</div>
</div>
</div>
<div class="col-md-4">
{% include 'django_ledger/includes/widget_ratios.html' with tx_digest=tx_digest %}
</div>
</div>
</div>
</div>
</div>
</section>
{# RECEIVABLES SECTION START #}
<section class="bg-light py-5">
<div class="container">
<div class="text-center mb-5">
<h1 class="display-5 fw-bold">{% trans 'Receivables' %}</h1>
<p class="lead">
<i class="bi bi-building me-2"></i>{{ entity.name }}
</p>
</div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{% if invoices %}
<div class="col">
<div class="card shadow-sm">
<div class="card-body">
{% chart_container receivables_chart_id receivables_chart_endpoint %}
</div>
</div>
</div>
{% endif %}
{% for invoice in invoices %}
<div class="col">
{% include 'django_ledger/invoice/includes/card_invoice.html' with invoice=invoice entity_slug=entity.slug style='dashboard' %}
</div>
{% endfor %}
<div class="col">
{% include 'django_ledger/invoice/includes/card_invoice.html' with create_invoice=True entity_slug=entity.slug %}
</div>
</div>
</div>
</section>
{# RECEIVABLES SECTION END #}
{# PAYABLES SECTION START #}
<section class="bg-light py-5">
<div class="container">
<div class="text-center mb-5">
<h1 class="display-5 fw-bold">{% trans 'Payables' %}</h1>
<p class="lead">
<i class="bi bi-building me-2"></i>{{ entity.name }}
</p>
</div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{% if bills %}
<div class="col">
<div class="card shadow-sm">
<div class="card-body">
{% chart_container payables_chart_id payables_chart_endpoint %}
</div>
</div>
</div>
{% endif %}
{% for bill in bills %}
<div class="col">
{% include 'django_ledger/bills/includes/card_bill.html' with bill=bill entity_slug=entity.slug style='dashboard' %}
</div>
{% endfor %}
<div class="col">
{% include 'django_ledger/bills/includes/card_bill.html' with create_bill=True entity_slug=entity.slug style='dashboard' %}
</div>
</div>
</div>
</section>
{# PAYABLES SECTION END #}
{{payables_chart_endpoint}}
{% endblock %}
{% block customJS %}
<script type="text/javascript">
// Initialize the echarts instance based on the prepared dom
let data = []
async function get_data(){
const response = await fetch("{{ rec }}");
data = await response.json();
console.log(data);
}
// Specify the configuration items and data for the chart
var myChart = echarts.init(document.getElementById('chart'));
var option = {
title: {
text: 'ECharts Getting Started Example'
},
tooltip: {},
legend: {
data: ['sales']
},
xAxis: {
data: ['Shirts', 'Cardigans', 'Chiffons', 'Pants', 'Heels', 'Socks']
},
yAxis: {},
series: [
{
name: 'sales',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
};
// Display the chart using the configuration items and data just specified.
myChart.setOption(option);
</script>
{% endblock %}

View File

@ -0,0 +1,525 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load django_ledger %}
{% block content %}
<main class="main" id="top">
<div class="pb-6">
<div class="row align-items-center justify-content-between g-3 mb-6">
<div class="col-12 col-md-auto">
<h2 class="mb-0"></h2>
</div>
</div>
<div class="px-3 mb-6">
<div class="row justify-content-between">
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 "><i class="fa-solid fa-landmark-dome fs-5 lh-1 text-primary"></i>
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_ASSETS | currency_format }}</h1>
<p class="fs-9 mb-0">Assets</p>
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end-md border-bottom pb-4 pb-xxl-0"><i class="fa-solid fa-weight-hanging fs-5 lh-1 text-primary"></i>
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_LIABILITIES | currency_format }}</h1>
<p class="fs-9 mb-0">Liabilities</p>
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-bottom-xxl-0 border-bottom border-end border-end-md-0 pb-4 pb-xxl-0 pt-4 pt-md-0"><i class="fa-solid fa-scale-balanced fs-5 lh-1 text-primary"></i>
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EQUITY | currency_format }}</h1>
<p class="fs-9 mb-0">Equity</p>
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-md border-end-xxl-0 border-bottom border-bottom-md-0 pb-4 pb-xxl-0 pt-4 pt-xxl-0"><i class="fa-solid fs-5 lh-1 text-success fa-money-bill"></i>
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.role_balance.ASSET_CA_CASH | currency_format }}</h1>
<p class="fs-9 mb-0">Cash</p>
</div>
</div>
</div>
<div class="col-12 col-md-auto">
<h2 class="mb-0"></h2>
</div>
<div class="px-3 mb-6">
<div class="row justify-content-between">
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 "><i class="fa-solid fa-coins fs-5 lh-1 text-primary"></i>
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_INCOME | currency_format }}</h1>
<p class="fs-9 mb-0">Revenue</p>
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end-md border-bottom pb-4 pb-xxl-0"><i class="fa-solid fa-hand-holding-dollar fs-5 lh-1 text-danger"></i>
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EXPENSES | reverse_sign | currency_format }}</h1>
<p class="fs-9 mb-0">Expenses</p>
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-bottom-xxl-0 border-bottom border-end border-end-md-0 pb-4 pb-xxl-0 pt-4 pt-md-0"><i class="fa-solid fa-sack-dollar fs-5 lh-1 text-primary"></i>
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EARNINGS | currency_format }}</h1>
<p class="fs-9 mb-0">Earnings (Loss)</p>
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-md border-end-xxl-0 border-bottom border-bottom-md-0 pb-4 pb-xxl-0 pt-4 pt-xxl-0"></div>
</div>
</div>
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-6 pb-3 border-y">
<div class="row gx-6">
<div class="col-12 col-md-6 col-lg-12 col-xl-6 mb-5 mb-md-3 mb-lg-5 mb-xl-2 mb-xxl-3">
<div class="scrollbar">
<h3>Net Receivables</h3>
<p class="text-body-tertiary">According to the sales data.</p>
<div id="net_receivable_chart" class="echart-doughnut-rounded-chart-example" style="min-height:320px"></div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-12 col-xl-6 mb-1 mb-sm-0">
<div class="row g-3 align-items-center">
<div class="col-sm-4 col-md-12 col-lg-8 col-xl-12 col-xxl-12 d-flex justify-content-end-xxl mt-0">
<div class="d-flex flex-1 justify-content-center d-sm-block d-md-flex d-lg-block d-xl-flex d-xxl-block gap-3">
<div class="mb-4 me-6 me-sm-0 me-md-6 me-lg-0 me-xl-6 me-xxl-0">
<h3 class="">Financial Analysis</h3>
<h4>Solvency:</h4>
<h5 class="progress-bar">Current
Ratio: {{ tx_digest.ratios.current_ratio | currency_format }}</h5>
<div class="progress" style="height:12px">
<progress
class="progress-bar {{ tx_digest.ratios.current_ratio | fin_ratio_threshold_class:'current_ratio' }}"
value="{{ tx_digest.ratios.current_ratio }}"
max="{% fin_ratio_max_value ratio='current_ratio' %}">
{{ tx_digest.ratios.current_ratio }}</progress></div>
<h5 class="progress-bar">Quick
Ratio: {{ tx_digest.ratios.quick_ratio | currency_format }} </h5>
<div class="progress" style="height:12px">
<progress
class="progress-bar {{ tx_digest.ratios.quick_ratio | fin_ratio_threshold_class:'quick_ratio' }}"
value="{{ tx_digest.ratios.quick_ratio }}"
max="{% fin_ratio_max_value ratio='quick_ratio' %}">
{{ tx_digest.ratios.current_ratio }}</progress></div>
<h4>Leverage:</h4>
<h5 class="progress-bar">Debt to
Equity: {{ tx_digest.ratios.debt_to_equity | currency_format }} </h5>
<div class="progress" style="height:12px">
<progress
class="progress-bar {{ tx_digest.ratios.debt_to_equity | fin_ratio_threshold_class:'debt_to_equity' }}"
value="{{ tx_digest.ratios.debt_to_equity }}"
max="{% fin_ratio_max_value ratio='debt_to_equity' %}">
{{ tx_digest.ratios.debt_to_equity }}</progress></div>
<h4>Profitability:</h4>
<h5 class="progress-bar">Return on
Equity: {{ tx_digest.ratios.return_on_equity | percentage }} </h5>
<div class="progress" style="height:12px">
<progress
class="progress-bar bg-info {{ tx_digest.ratios.quick_ratio | fin_ratio_threshold_class:'quick_ratio' }}"
value="{{ tx_digest.ratios.return_on_equity }}"
max="{% fin_ratio_max_value ratio='return_on_equity' %}">
{{ tx_digest.ratios.return_on_equity }}</progress></div>
<h5 class="progress-bar">Return on
Assets: {{ tx_digest.ratios.return_on_assets | percentage }} </h5>
<div class="progress" style="height:12px">
<progress
class="progress-bar {{ tx_digest.ratios.return_on_assets | fin_ratio_threshold_class:'return_on_assets' }}"
value="{{ tx_digest.ratios.return_on_assets }}"
max="{% fin_ratio_max_value ratio='return_on_assets' %}">{{ tx_digest.ratios.return_on_assets }}</progress></div>
<h5 class="progress-bar">Net Profit
Margin: {{ tx_digest.ratios.net_profit_margin | percentage }} </h5>
<div class="progress" style="height:12px">
<progress
class="progress-bar {{ tx_digest.ratios.net_profit_margin | fin_ratio_threshold_class:'net_profit_margin' }}"
value="{{ tx_digest.ratios.net_profit_margin }}"
max="{% fin_ratio_max_value ratio='net_profit_margin' %}">{{ tx_digest.ratios.net_profit_margin }}</progress></div>
<h5 class="progress-bar">Gross Profit
Margin: {{ tx_digest.ratios.gross_profit_margin | percentage }} </h5>
<div class="progress" style="height:12px">
<progress
class="progress-bar {{ tx_digest.ratios.gross_profit_margin | fin_ratio_threshold_class:'gross_profit_margin' }}"
value="{{ tx_digest.ratios.gross_profit_margin }}"
max="{% fin_ratio_max_value ratio='gross_profit_margin' %}">{{ tx_digest.ratios.gross_profit_margin }}</progress></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row pt-6 gy-7 gx-6">
<div class="col-12 col-md-12">
<div class="row justify-content-between mb-4">
<div class="col-12">
<h3></h3>
<div class="col-6 col-md-3">
<div class="">
<label class="form-label" for="datepicker">Start Date</label>
<input id="startDate" class="form-control datetimepicker flatpickr-input" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}" type="date" placeholder="dd/mm/yyyy" data-options="{&quot;disableMobile&quot;:true,&quot;dateFormat&quot;:&quot;Y-m-d&quot;}" readonly="readonly">
</div>
<div>
<label class="form-label" for="datepicker">End Date</label>
<input id="endDate" class="form-control datetimepicker flatpickr-input" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}" type="date" placeholder="dd/mm/yyyy" data-options="{&quot;disableMobile&quot;:true,&quot;dateFormat&quot;:&quot;Y-m-d&quot;}" readonly="readonly">
</div>
<button id="apply_filter" class="btn btn-primary mt-3" type="submit">Apply</button>
</div>
<div id="net_receivable_chart1" class="echart-doughnut-rounded-chart-example col-12" style="min-height:450px"></div>
</div>
</div>
</div>
<div class="col-12 col-md-6">
</div>
</div>
</div>
<footer class="footer position-absolute">
<div class="row g-0 justify-content-between align-items-center h-100">
<div class="col-12 col-sm-auto text-center">
<p class="mb-0 mt-2 mt-sm-0 text-body">Thank you for creating with Phoenix<span class="d-none d-sm-inline-block"></span><span class="d-none d-sm-inline-block mx-1">|</span><br class="d-sm-none" />2024 &copy;<a class="mx-1" href="https://themewagon.com">Themewagon</a></p>
</div>
<div class="col-12 col-sm-auto text-center">
<p class="mb-0 text-body-tertiary text-opacity-85">v1.20.1</p>
</div>
</div>
</footer>
</div>
<div class="modal fade" id="searchBoxModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="true" data-phoenix-modal="data-phoenix-modal" style="--phoenix-backdrop-opacity: 1;">
<div class="modal-dialog">
<div class="modal-content mt-15 rounded-pill">
<div class="modal-body p-0">
<div class="search-box navbar-top-search-box" data-list='{"valueNames":["title"]}' style="width: auto;">
<form class="position-relative" data-bs-toggle="search" data-bs-display="static">
<input class="form-control search-input fuzzy-search rounded-pill form-control-lg" type="search" placeholder="Search..." aria-label="Search" />
<span class="fas fa-search search-box-icon"></span>
</form>
<div class="btn-close position-absolute end-0 top-50 translate-middle cursor-pointer shadow-none" data-bs-dismiss="search">
<button class="btn btn-link p-0" aria-label="Close"></button>
</div>
<div class="dropdown-menu border start-0 py-0 overflow-hidden w-100">
<div class="scrollbar-overlay" style="max-height: 30rem;">
<div class="list pb-3">
<h6 class="dropdown-header text-body-highlight fs-10 py-2">24 <span class="text-body-quaternary">results</span></h6>
<hr class="my-0" />
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Recently Searched </h6>
<div class="py-2"><a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
<div class="d-flex align-items-center">
<div class="fw-normal text-body-highlight title"><span class="fa-solid fa-clock-rotate-left" data-fa-transform="shrink-2"></span> Store Macbook</div>
</div>
</a>
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
<div class="d-flex align-items-center">
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-clock-rotate-left" data-fa-transform="shrink-2"></span> MacBook Air - 13″</div>
</div>
</a>
</div>
<hr class="my-0" />
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Products</h6>
<div class="py-2"><a class="dropdown-item py-2 d-flex align-items-center" href="../../apps/e-commerce/landing/product-details.html">
<div class="file-thumbnail me-2"><img class="h-100 w-100 object-fit-cover rounded-3" src="../../assets/img/products/60x60/3.png" alt="" /></div>
<div class="flex-1">
<h6 class="mb-0 text-body-highlight title">MacBook Air - 13″</h6>
<p class="fs-10 mb-0 d-flex text-body-tertiary"><span class="fw-medium text-body-tertiary text-opactity-85">8GB Memory - 1.6GHz - 128GB Storage</span></p>
</div>
</a>
<a class="dropdown-item py-2 d-flex align-items-center" href="../../apps/e-commerce/landing/product-details.html">
<div class="file-thumbnail me-2"><img class="img-fluid" src="../../assets/img/products/60x60/3.png" alt="" /></div>
<div class="flex-1">
<h6 class="mb-0 text-body-highlight title">MacBook Pro - 13″</h6>
<p class="fs-10 mb-0 d-flex text-body-tertiary"><span class="fw-medium text-body-tertiary text-opactity-85">30 Sep at 12:30 PM</span></p>
</div>
</a>
</div>
<hr class="my-0" />
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Quick Links</h6>
<div class="py-2"><a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
<div class="d-flex align-items-center">
<div class="fw-normal text-body-highlight title"><span class="fa-solid fa-link text-body" data-fa-transform="shrink-2"></span> Support MacBook House</div>
</div>
</a>
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
<div class="d-flex align-items-center">
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-link text-body" data-fa-transform="shrink-2"></span> Store MacBook″</div>
</div>
</a>
</div>
<hr class="my-0" />
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Files</h6>
<div class="py-2"><a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
<div class="d-flex align-items-center">
<div class="fw-normal text-body-highlight title"><span class="fa-solid fa-file-zipper text-body" data-fa-transform="shrink-2"></span> Library MacBook folder.rar</div>
</div>
</a>
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
<div class="d-flex align-items-center">
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-file-lines text-body" data-fa-transform="shrink-2"></span> Feature MacBook extensions.txt</div>
</div>
</a>
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
<div class="d-flex align-items-center">
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-image text-body" data-fa-transform="shrink-2"></span> MacBook Pro_13.jpg</div>
</div>
</a>
</div>
<hr class="my-0" />
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Members</h6>
<div class="py-2"><a class="dropdown-item py-2 d-flex align-items-center" href="../../pages/members.html">
<div class="avatar avatar-l status-online me-2 text-body">
<img class="rounded-circle " src="../../assets/img/team/40x40/10.webp" alt="" />
</div>
<div class="flex-1">
<h6 class="mb-0 text-body-highlight title">Carry Anna</h6>
<p class="fs-10 mb-0 d-flex text-body-tertiary">anna@technext.it</p>
</div>
</a>
<a class="dropdown-item py-2 d-flex align-items-center" href="../../pages/members.html">
<div class="avatar avatar-l me-2 text-body">
<img class="rounded-circle " src="../../assets/img/team/40x40/12.webp" alt="" />
</div>
<div class="flex-1">
<h6 class="mb-0 text-body-highlight title">John Smith</h6>
<p class="fs-10 mb-0 d-flex text-body-tertiary">smith@technext.it</p>
</div>
</a>
</div>
<hr class="my-0" />
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Related Searches</h6>
<div class="py-2"><a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
<div class="d-flex align-items-center">
<div class="fw-normal text-body-highlight title"><span class="fa-brands fa-firefox-browser text-body" data-fa-transform="shrink-2"></span> Search in the Web MacBook</div>
</div>
</a>
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
<div class="d-flex align-items-center">
<div class="fw-normal text-body-highlight title"> <span class="fa-brands fa-chrome text-body" data-fa-transform="shrink-2"></span> Store MacBook″</div>
</div>
</a>
</div>
</div>
<div class="text-center">
<p class="fallback fw-bold fs-7 d-none">No Result Found.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
var navbarTopStyle = window.config.config.phoenixNavbarTopStyle;
var navbarTop = document.querySelector('.navbar-top');
if (navbarTopStyle === 'darker') {
navbarTop.setAttribute('data-navbar-appearance', 'darker');
}
var navbarVerticalStyle = window.config.config.phoenixNavbarVerticalStyle;
var navbarVertical = document.querySelector('.navbar-vertical');
if (navbarVertical && navbarVerticalStyle === 'darker') {
navbarVertical.setAttribute('data-navbar-appearance', 'darker');
}
</script>
<div class="support-chat-container">
<div class="container-fluid support-chat">
<div class="card bg-body-emphasis">
<div class="card-header d-flex flex-between-center px-4 py-3 border-bottom border-translucent">
<h5 class="mb-0 d-flex align-items-center gap-2">Demo widget<span class="fa-solid fa-circle text-success fs-11"></span></h5>
<div class="btn-reveal-trigger">
<button class="btn btn-link p-0 dropdown-toggle dropdown-caret-none transition-none d-flex" type="button" id="support-chat-dropdown" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h text-body"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2" aria-labelledby="support-chat-dropdown"><a class="dropdown-item" href="#!">Request a callback</a><a class="dropdown-item" href="#!">Search in chat</a><a class="dropdown-item" href="#!">Show history</a><a class="dropdown-item" href="#!">Report to Admin</a><a class="dropdown-item btn-support-chat" href="#!">Close Support</a></div>
</div>
</div>
<div class="card-body chat p-0">
<div class="d-flex flex-column-reverse scrollbar h-100 p-3">
<div class="text-end mt-6"><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">I need help with something</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">I cant reorder a product I previously ordered</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">How do I place an order?</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a><a class="false d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">My payment method not working</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a>
</div>
<div class="text-center mt-auto">
<div class="avatar avatar-3xl status-online"><img class="rounded-circle border border-3 border-light-subtle" src="../../assets/img/team/30.webp" alt="" /></div>
<h5 class="mt-2 mb-3">Eric</h5>
<p class="text-center text-body-emphasis mb-0">Ask us anything well get back to you here or by email within 24 hours.</p>
</div>
</div>
</div>
<div class="card-footer d-flex align-items-center gap-2 border-top border-translucent ps-3 pe-4 py-3">
<div class="d-flex align-items-center flex-1 gap-3 border border-translucent rounded-pill px-4">
<input class="form-control outline-none border-0 flex-1 fs-9 px-0" type="text" placeholder="Write message" />
<label class="btn btn-link d-flex p-0 text-body-quaternary fs-9 border-0" for="supportChatPhotos"><span class="fa-solid fa-image"></span></label>
<input class="d-none" type="file" accept="image/*" id="supportChatPhotos" />
<label class="btn btn-link d-flex p-0 text-body-quaternary fs-9 border-0" for="supportChatAttachment"> <span class="fa-solid fa-paperclip"></span></label>
<input class="d-none" type="file" id="supportChatAttachment" />
</div>
<button class="btn p-0 border-0 send-btn"><span class="fa-solid fa-paper-plane fs-9"></span></button>
</div>
</div>
</div>
<button class="btn btn-support-chat p-0 border border-translucent"><span class="fs-8 btn-text text-primary text-nowrap">Chat demo</span><span class="ping-icon-wrapper mt-n4 ms-n6 mt-sm-0 ms-sm-2 position-absolute position-sm-relative"><span class="ping-icon-bg"></span><span class="fa-solid fa-circle ping-icon"></span></span><span class="fa-solid fa-headset text-primary fs-8 d-sm-none"></span><span class="fa-solid fa-chevron-down text-primary fs-7"></span></button>
</main>
{% endblock %}
{% block customJS %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.6.0/echarts.min.js" integrity="sha512-XSmbX3mhrD2ix5fXPTRQb2FwK22sRMVQTpBP2ac8hX7Dh/605hA2QDegVWiAvZPiXIxOV0CbkmUjGionDpbCmw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript">
let myChart;
let myChart1;
function use_chart(type, chart_id, title, labels, values, update = false) {
if (chart_id === 'net_receivable_chart') {
if (!myChart) {
myChart = echarts.init(document.getElementById(chart_id));
}
const { getColor, getData } = window.phoenix.utils;
var option = {
title: {
text: title
},
tooltip: {},
legend: {
data: ['Net Receivables']
},
xAxis: {
data: labels
},
yAxis: {},
series: [
{
type: type,
radius: ['40%', '70%'],
center: window.innerWidth < 530 ? ['65%', '55%'] : ['50%', '55%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: getColor('body-highlight-bg'),
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
labelLine: {
show: false
},
data: values
}
]
};
if (update) {
myChart.setOption(option);
} else {
myChart.clear();
myChart.setOption(option);
}
} else if (chart_id === 'net_receivable_chart1') {
if (!myChart1) {
myChart1 = echarts.init(document.getElementById(chart_id));
}
const { getColor, getData } = window.phoenix.utils;
var option = {
title: {
text: title
},
tooltip: {},
legend: {
data: ['Net Receivables']
},
xAxis: {
data: labels
},
yAxis: {},
series: [
{
type: type,
data: values
}
]
};
if (update) {
myChart1.setOption(option);
} else {
myChart.clear();
myChart1.setOption(option);
}
}
}
let data = []
const apply_filter = document.getElementById('apply_filter');
async function get_data(){
const response = await fetch("{% url 'entity-json-net-receivables' request.user.dealer.entity.slug %}");
data = await response.json();
const keys = Object.keys(data.results.net_receivable_data);
const labels = keys;
const values = keys.map(key => data.results.net_receivable_data[key]);
use_chart('pie','net_receivable_chart','Net Receivables',labels,values)
}
async function fetchData() {
const start_date = document.getElementById('startDate').value;
const end_date = document.getElementById('endDate').value;
let url = ''
if(!start_date || !end_date){
url = "{% url 'entity-json-pnl' request.user.dealer.entity.slug %}";
}else{
url = `{% url 'entity-json-pnl' request.user.dealer.entity.slug %}?fromDate=${start_date}&toDate=${end_date}`;
}
try {
const response = await fetch(url);
const data = await response.json();
const months = Object.keys(data.results.pnl_data);
const labels = Object.keys(data.results.pnl_data[months[0]]);
const values = labels.map(label => data.results.pnl_data[months[0]][label]);
use_chart('bar','net_receivable_chart1','Net Receivables',labels,values,true);
} catch (error) {
console.error("Error fetching data:", error);
dataContainer.innerHTML = "<p>Error fetching data.</p>";
}
}
apply_filter.addEventListener('click', () => {
fetchData();
});
get_data()
fetchData()
var options = {
chart: {
type: 'line'
},
series: [{
name: 'sales',
data: [30,40,35,50,49,60,70,91,125]
}],
xaxis: {
categories: [1991,1992,1993,1994,1995,1996,1997, 1998,1999]
}
}
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
</script>
{% endblock %}

View File

@ -5,36 +5,36 @@
{% block period_navigation %}
{% if unit_model %}
<div class="column is-12">{% period_navigation 'unit-ic' %}</div>
<div class="col-12">{% period_navigation 'unit-ic' %}</div>
{% elif entity %}
<div class="column is-12">{% period_navigation 'entity-ic' %}</div>
<div class="col-12">{% period_navigation 'entity-ic' %}</div>
{% elif ledger %}
<div class="column is-12">{% period_navigation 'ledger-ic' %}</div>
<div class="col-12">{% period_navigation 'ledger-ic' %}</div>
{% endif %}
{% endblock %}
{% block content %}
<div class="card">
<div class="card-content has-text-centered">
<div class="card-body text-center">
<div class="container mb-4">
<div class="columns">
<div class="column">
<div class="row">
<div class="col">
{% if entity %}
<h1 class="is-size-2 has-text-weight-light">{{ entity.name }}</h1>
<h1 class="display-4 font-weight-light">{{ entity.name }}</h1>
{% elif ledger %}
<h1 class="is-size-2 has-text-weight-light">{{ ledger.name }}</h1>
<h1 class="display-4 font-weight-light">{{ ledger.name }}</h1>
{% endif %}
{% if unit_model %}
<h3 class="is-size-4 has-text-weight-medium is-italic">{{ unit_model.name }} {% trans 'Unit' %}</h3>
<h3 class="h4 font-weight-medium font-italic">{{ unit_model.name }} {% trans 'Unit' %}</h3>
{% endif %}
<h1 class="is-size-2 has-text-weight-bold">{% trans 'Income Statement' %}</h1>
<h2 class="is-size-2 has-text-weight-light">
<h1 class="display-4 font-weight-bold">{% trans 'Income Statement' %}</h1>
<h2 class="display-4 font-weight-light">
{% if quarter %}{{ year }} | Q{{ quarter }}
{% elif month %}{{ start_date | date:'F, Y' }}
{% else %}Fiscal Year {{ year }}
{% endif %}</h2>
<h3 class="is-size-4 is-italic has-font-weight-light">
<h3 class="h4 font-italic font-weight-light">
{{ from_date | date:'m/d/Y' }} - {{ to_date | date:'m/d/Y' }}
</h3>
</div>
@ -43,19 +43,7 @@
{% income_statement_table io_model=object %}
{% if ledger %}
<a class="button is-fullwidth is-dark my-2"
href="{% url 'django_ledger:ledger-list' entity_slug=view.kwargs.entity_slug %}">Go
Back</a>
{% elif entity %}
<a class="button is-fullwidth is-dark my-2"
href="{% url 'django_ledger:entity-dashboard' entity_slug=view.kwargs.entity_slug %}">Go
Back</a>
{% endif %}
<a class="button is-fullwidth is-light my-2"
href="?by_unit=1">{% trans 'By Unit' %}</a>
<a class="button is-fullwidth is-link my-2"
<a class="btn btn-primary w-100 my-2"
href="{{ request.path }}?format=pdf">{% trans 'Download PDF' %}</a>
</div>
</div>

View File

@ -2,41 +2,27 @@
{% load i18n %}
<div class="table-container">
<table class="table is-fullwidth is-narrow is-striped">
<table class="table is-fullwidth is-narrow is-striped has-text-centered">
<tbody>
{% for bs_role, bs_role_data in tx_digest.balance_sheet.items %}
{% if bs_role_data.is_block %}
<tr>
<td><h2 class="is-size-3">{{ bs_role | upper }}</h2></td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td></td>
<tr class="has-background-light">
<td colspan="6" class="has-text-weight-bold is-size-3">{{ bs_role | upper }}</td>
</tr>
<tr>
<th class="has-text-centered">{% trans 'Account Code' %}</th>
<th class="has-text-centered">{% trans 'Account Name' %}</th>
<tr class="has-background-dark has-text-white">
<th>{% trans 'Account Code' %}</th>
<th>{% trans 'Account Name' %}</th>
{% if tx_digest.by_unit %}
<th class="has-text-centered">{% trans 'Unit' %}</th>
<th>{% trans 'Unit' %}</th>
{% endif %}
<th class="has-text-centered">{% trans 'Balance Type' %}</th>
<th class="has-text-centered">{% trans 'Balance Through' %} {{ tx_digest.to_date | date }}</th>
<th class="has-text-centered">{% trans 'Actions' %}</th>
<th>{% trans 'Balance Type' %}</th>
<th>{% trans 'Balance Through' %} {{ tx_digest.to_date | date }}</th>
<th>{% trans '' %}</th>
</tr>
{% for acc_role, acc_data in bs_role_data.roles.items %}
<tr class="has-background-grey-light">
<td class="p-3 has-text-weight-bold">{{ acc_data.role_name | upper }}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td></td>
<tr class="has-background-grey-light has-text-weight-bold">
<td colspan="6">{{ acc_data.role_name | upper }}</td>
</tr>
{% for acc in acc_data.accounts %}
@ -44,30 +30,11 @@
<td>{{ acc.code }}</td>
<td class="has-text-left">{{ acc.name }}</td>
{% if tx_digest.by_unit %}
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
<td>{{ acc.unit_name|default:"" }}</td>
{% endif %}
<td class="has-text-centered">{{ acc.balance_type.0 | upper }}</td>
<td>{{ acc.balance_type.0 | upper }}</td>
<td class="has-text-right">{% currency_symbol %}{{ acc.balance | currency_format }}</td>
<td>
<div class="dropdown is-hoverable" id="account-action-{{ account.uuid }}">
<div class="dropdown-trigger">
<button class="button is-small is-rounded"
aria-haspopup="true"
aria-controls="dropdown-menu">
<span>{% trans 'Actions' %}</span>
<span class="icon is-small">{% icon 'bi:arrow-down' 24 %}</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu-{{ acc.uuid }}" role="menu">
<div class="dropdown-content">
<a href="{% url 'django_ledger:account-detail' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-success">{% trans 'Detail' %}</a>
<a href="{% url 'django_ledger:account-update' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-warning">{% trans 'Update' %}</a>
</div>
</div>
</div>
</td>
<td></td>
</tr>
{% endfor %}
@ -78,63 +45,58 @@
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td class="has-text-right">
{% currency_symbol %}{{ acc_data.total_balance | currency_format }}</td>
<td class="has-text-right">{% currency_symbol %}{{ acc_data.total_balance | currency_format }}</td>
<td></td>
</tr>
{% endfor %}
{% if bs_role != 'equity' %}
<tr class="has-text-weight-bold is-size-5">
<tr class="has-text-weight-bold is-size-5 has-background-light">
<td>{% trans 'Total' %} {{ bs_role | upper }}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td class="has-text-right">{% currency_symbol %}{{ bs_role_data.total_balance | currency_format }}</td>
<td></td>
<td>{% currency_symbol %}{{ bs_role_data.total_balance | currency_format }}</td>
{% endif %}
</tr>
{% endif %}
{% endif %}
{% endfor %}
<tr class="has-text-weight-bold is-size-5">
<tr class="has-text-weight-bold is-size-5 has-background-grey-lighter">
<td>{% trans 'Retained Earnings' %}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td class="has-text-right">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EARNINGS | currency_format }}</td>
<td></td>
<td>{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EARNINGS | currency_format }}</td>
</tr>
<tr class="has-text-weight-bold is-size-5">
<tr class="has-text-weight-bold is-size-5 has-background-light">
<td>{% trans 'Total EQUITY' %}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td class="has-text-right">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EQUITY | currency_format }}</td>
<td></td>
<td>{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EQUITY | currency_format }}</td>
</tr>
<tr class="has-text-weight-bold is-size-5">
<tr class="has-text-weight-bold is-size-5 has-background-dark has-text-white">
<td>{% trans 'Total Equity + Liabilities' %}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td class="has-text-right">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_LIABILITIES_EQUITY | currency_format }}</td>
<td></td>
<td>{% currency_symbol %}{{ tx_digest.group_balance.GROUP_LIABILITIES_EQUITY | currency_format }}</td>
</tr>
</tbody>
</table>
</div>

View File

@ -0,0 +1,362 @@
{% load django_ledger %}
{% load i18n %}
<div class="table-container">
<table class="table is-fullwidth is-narrow is-striped">
<tr>
<th>{% trans 'Account Number' %}</th>
<th>{% trans 'Description' %}</th>
{% if tx_digest.by_unit %}
<th>{% trans 'Unit' %}</th>
{% endif %}
<th>{% trans 'Balance Type' %}</th>
<th>{% trans 'Balance' %}</th>
<th>{% trans '' %}</th>
</tr>
{# OPERATING INCOME #}
<tr>
<td><h2 class="is-size-3 has-text-left">{% trans 'Operating Revenues' %}</h2></td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td></td>
</tr>
{% for acc in tx_digest.income_statement.operating.revenues %}
<tr>
<td class="has-text-right">{{ acc.code }}</td>
<td>{{ acc.name }}</td>
{% if tx_digest.by_unit %}
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
{% endif %}
<td class="has-text-centered">
{% if acc.balance_type == 'debit' %}
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
{% elif acc.balance_type == 'credit' %}
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
{% endif %}
</td>
<td>{% currency_symbol %}{{ acc.balance | currency_format }}</td>
<td>
</td>
</tr>
{% endfor %}
<tr>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td><h2 class="is-size-5 has-text-right">{% trans 'Net Operating Revenues' %}</h2></td>
<td class="is-size-5 has-text-weight-bold">
{% currency_symbol %}{{ tx_digest.income_statement.operating.net_operating_revenue | currency_format }}</td>
<td></td>
</tr>
{# COGS #}
<tr>
<td><h2 class="is-size-3 has-text-left">{% trans 'Less: Cost of Goods Sold' %}</h2></td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td></td>
</tr>
{% for acc in tx_digest.income_statement.operating.cogs %}
<tr>
<td class="has-text-right">{{ acc.code }}</td>
<td>{{ acc.name }}</td>
{% if tx_digest.by_unit %}
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
{% endif %}
<td class="has-text-centered">
{% if acc.balance_type == 'debit' %}
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
{% elif acc.balance_type == 'credit' %}
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
{% endif %}
</td>
<td>{% currency_symbol %}{{ acc.balance | reverse_sign | currency_format }}</td>
<td>
<div class="dropdown is-hoverable" id="account-action-{{ account.uuid }}">
<div class="dropdown-trigger">
<button class="button is-small is-rounded"
aria-haspopup="true"
aria-controls="dropdown-menu">
<span>{% trans 'Actions' %}</span>
<span class="icon is-small">{% icon 'bi:arrow-down' 24 %}</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu-{{ acc.uuid }}" role="menu">
<div class="dropdown-content">
<a href="{% url 'django_ledger:account-detail' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-success">{% trans 'Detail' %}</a>
<a href="{% url 'django_ledger:account-update' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-warning">{% trans 'Update' %}</a>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
<tr>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td><h2 class="is-size-5 has-text-right">{% trans 'Net COGS' %}</h2></td>
<td class="is-size-5 has-text-weight-bold">
{% currency_symbol %}{{ tx_digest.income_statement.operating.net_cogs | currency_format }}</td>
<td></td>
</tr>
{# GROSS PROFIT #}
<tr>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td><h2 class="is-size-4 has-text-right">{% trans 'Gross Profit' %}</h2></td>
<td class="is-size-4 has-text-weight-bold">
{% currency_symbol %}{{ tx_digest.income_statement.operating.gross_profit | currency_format }}</td>
<td></td>
</tr>
{# OPERATING EXPENSES #}
<tr>
<td><h2 class="is-size-3 has-text-left">{% trans 'Operating Expenses' %}</h2></td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td></td>
</tr>
{% for acc in tx_digest.income_statement.operating.expenses %}
<tr>
<td class="has-text-right">{{ acc.code }}</td>
<td>{{ acc.name }}</td>
{% if tx_digest.by_unit %}
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
{% endif %}
<td class="has-text-centered">
{% if acc.balance_type == 'debit' %}
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
{% elif acc.balance_type == 'credit' %}
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
{% endif %}
</td>
<td>{% currency_symbol %}{{ acc.balance | reverse_sign | currency_format }}</td>
<td>
<div class="dropdown is-hoverable" id="account-action-{{ account.uuid }}">
<div class="dropdown-trigger">
<button class="button is-small is-rounded"
aria-haspopup="true"
aria-controls="dropdown-menu">
<span>{% trans 'Actions' %}</span>
<span class="icon is-small">{% icon 'bi:arrow-down' 24 %}</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu-{{ acc.uuid }}" role="menu">
<div class="dropdown-content">
<a href="{% url 'django_ledger:account-detail' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-success">{% trans 'Detail' %}</a>
<a href="{% url 'django_ledger:account-update' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-warning">{% trans 'Update' %}</a>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
<tr>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td><h2 class="is-size-5 has-text-right">{% trans 'Net Operating Expenses' %}</h2></td>
<td class="is-size-5 has-text-weight-bold">
{% currency_symbol %}{{ tx_digest.income_statement.operating.net_operating_expenses | reverse_sign | currency_format }}</td>
<td></td>
</tr>
{# NET OPERATING INCOME #}
<tr>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td><h2 class="is-size-4 has-text-right">{% trans 'Net Operating Income (Loss)' %}</h2></td>
<td class="is-size-3">
{% currency_symbol %}{{ tx_digest.income_statement.operating.net_operating_income| currency_format }}</td>
<td></td>
</tr>
{# OTHER REVENUES #}
<tr>
<td><h2 class="is-size-3 has-text-left">{% trans 'Other Revenues' %}</h2></td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td></td>
</tr>
{% for acc in tx_digest.income_statement.other.revenues %}
<tr>
<td class="has-text-right">{{ acc.code }}</td>
<td>{{ acc.name }}</td>
{% if tx_digest.by_unit %}
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
{% endif %}
<td class="has-text-centered">
{% if acc.balance_type == 'debit' %}
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
{% elif acc.balance_type == 'credit' %}
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
{% endif %}
</td>
<td class="is-size-5">{% currency_symbol %}{{ acc.balance | currency_format }}</td>
<td>
<div class="dropdown is-hoverable" id="account-action-{{ account.uuid }}">
<div class="dropdown-trigger">
<button class="button is-small is-rounded"
aria-haspopup="true"
aria-controls="dropdown-menu">
<span>{% trans 'Actions' %}</span>
<span class="icon is-small">{% icon 'bi:arrow-down' 24 %}</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu-{{ acc.uuid }}" role="menu">
<div class="dropdown-content">
<a href="{% url 'django_ledger:account-detail' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-success">{% trans 'Detail' %}</a>
<a href="{% url 'django_ledger:account-update' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-warning">{% trans 'Update' %}</a>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
<tr>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td><h2 class="is-size-5 has-text-right">{% trans 'Net Other Revenues' %}</h2></td>
<td class="is-size-5 has-text-weight-bold">
{% currency_symbol %}{{ tx_digest.income_statement.other.net_other_revenues | currency_format }}</td>
<td></td>
</tr>
{# OTHER EXPENSES #}
<tr>
<td><h2 class="is-size-3 has-text-left">{% trans 'Other Expenses' %}</h2></td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td></td>
</tr>
{% for acc in tx_digest.income_statement.other.expenses %}
<tr>
<td class="has-text-right">{{ acc.code }}</td>
<td>{{ acc.name }}</td>
{% if tx_digest.by_unit %}
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
{% endif %}
<td class="has-text-centered">
{% if acc.balance_type == 'debit' %}
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
{% elif acc.balance_type == 'credit' %}
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
{% endif %}
</td>
<td class="is-size-5">{% currency_symbol %}{{ acc.balance | reverse_sign | currency_format }}</td>
<td>
<div class="dropdown is-hoverable" id="account-action-{{ account.uuid }}">
<div class="dropdown-trigger">
<button class="button is-small is-rounded"
aria-haspopup="true"
aria-controls="dropdown-menu">
<span>{% trans 'Actions' %}</span>
<span class="icon is-small">{% icon 'bi:arrow-down' 24 %}</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu-{{ acc.uuid }}" role="menu">
<div class="dropdown-content">
<a href="{% url 'django_ledger:account-detail' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-success">{% trans 'Detail' %}</a>
<a href="{% url 'django_ledger:account-update' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-warning">{% trans 'Update' %}</a>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
<tr>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td><h2 class="is-size-5 has-text-right">{% trans 'Net Other Expenses' %}</h2></td>
<td class="is-size-5 has-text-weight-bold">
{% currency_symbol %}{{ tx_digest.income_statement.other.net_other_expenses | currency_format }}</td>
<td></td>
</tr>
{# NET OTHER INCOME/LOSS #}
<tr>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td><h2 class="is-size-4 has-text-right">{% trans 'Net Other Income (Loss)' %}</h2></td>
<td class="is-size-3">
{% currency_symbol %}{{ tx_digest.income_statement.other.net_other_income | currency_format }}</td>
<td></td>
</tr>
{# NET INCOME #}
<tr>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td>
<h4 class="is-size-4 has-text-right">{{ tx_digest.from_date | date }} {% trans 'through' %} {{ tx_digest.to_date | date }}</h4>
</td>
<td><h2 class="is-size-3 has-text-right">{% trans 'Net Income' %}</h2></td>
<td class="is-size-3 has-text-weight-bold">
{% currency_symbol %}{{ tx_digest.income_statement.net_income| currency_format }}</td>
<td></td>
</tr>
</table>
</div>

View File

@ -29,9 +29,6 @@
</div>
</div>
<!-- ============================================-->
<!-- ============================================-->
<!-- ============================================-->
<!-- <section> begin ============================-->
<section class="pt-5 pb-9 bg-body-emphasis dark__bg-gray-1200 border-top">
@ -154,8 +151,8 @@
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Additional Services" %}</td>
<td class="align-middle text-start fw-semibold">
{% for service in data.additional_services %}
<small><span class="fw-semibold">+ {{service.name}} - {{service.price}}</span></small><br>
{% for service in data.additionals %}
<small><span class="fw-semibold">+ {{service.name}} - {{service.total}}</span></small><br>
{% endfor %}
</td>
</tr>
@ -168,7 +165,6 @@
</tbody>
</table>
</div>
</div>
</div>
<!-- end of .row-->