latest update

This commit is contained in:
gitea 2025-01-30 09:56:33 +00:00
parent 5ef0d68f7c
commit ecf1c375c1
16 changed files with 563 additions and 347 deletions

View File

@ -0,0 +1,20 @@
# Generated by Django 4.2.17 on 2025-01-29 12:59
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('inventory', '0011_alter_saleorder_estimate'),
]
operations = [
migrations.AddField(
model_name='saleorder',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 4.2.17 on 2025-01-29 13:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0012_saleorder_created'),
]
operations = [
migrations.AlterModelOptions(
name='saleorder',
options={'ordering': ['created']},
),
]

View File

@ -1562,6 +1562,10 @@ class SaleOrder(models.Model):
]) ])
comments = models.TextField(blank=True, null=True) comments = models.TextField(blank=True, null=True)
formatted_order_id = models.CharField(max_length=10, unique=True, editable=False) formatted_order_id = models.CharField(max_length=10, unique=True, editable=False)
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created']
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.formatted_order_id: if not self.formatted_order_id:

View File

@ -1,7 +1,7 @@
from django import template from django import template
from calendar import month_abbr from calendar import month_abbr
from django.urls import reverse from django.urls import reverse
from django_ledger.io.io_core import get_localdate from django_ledger.io.io_core import get_localdate,validate_activity
register = template.Library() register = template.Library()
@ -146,3 +146,35 @@ def balance_sheet_statement(context, io_model, to_date=None):
'user_model': user_model, 'user_model': user_model,
'tx_digest': io_digest.get_io_data(), 'tx_digest': io_digest.get_io_data(),
} }
@register.inclusion_tag('django_ledger/financial_statements/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')
activity = validate_activity(activity, raise_404=True)
entity_slug = context['view'].kwargs.get('entity_slug')
if not from_date:
from_date = context['from_date']
if not to_date:
to_date = context['to_date']
io_digest = io_model.digest(
activity=activity,
user_model=user_model,
entity_slug=entity_slug,
unit_slug=context['unit_slug'],
by_unit=context['by_unit'],
from_date=from_date,
to_date=to_date,
equity_only=True,
process_groups=True,
income_statement=True,
signs=True
)
return {
'entity_slug': entity_slug,
'user_model': user_model,
'tx_digest': io_digest.get_io_data()
}

View File

@ -546,7 +546,23 @@ urlpatterns = [
path('entity/<slug:entity_slug>/balance-sheet/date/<int:year>/<int:month>/<int:day>/', path('entity/<slug:entity_slug>/balance-sheet/date/<int:year>/<int:month>/<int:day>/',
views.DateBalanceSheetView.as_view(), views.DateBalanceSheetView.as_view(),
name='entity-bs-date'), name='entity-bs-date'),
# INCOME STATEMENT Reports ----
# Entity .....
path('entity/<slug:entity_slug>/income-statement/',
views.BaseIncomeStatementRedirectViewBase.as_view(),
name='entity-ic'),
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'),
] ]

View File

@ -284,15 +284,11 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
vat_amount = 0 vat_amount = 0
total_amount = 0 total_amount = 0
if invoice.terms == "on_receipt": calculator = CarFinanceCalculator(invoice)
for x in invoice.get_itemtxs_data()[0].all(): finance_data = calculator.get_finance_data()
# vat_amount += models.Car.objects.get( # if invoice.terms == "on_receipt":
# vin=x.item_model.name # for x in invoice.get_itemtxs_data()[0].all():
# ).finances.vat_amount * Decimal(x.quantity) # total_amount += Decimal(x.unit_cost) * Decimal(x.quantity)
total_amount += Decimal(x.unit_cost) * Decimal(x.quantity)
# grand_total = total_amount - Decimal(vat_amount)
total_amount
ledger = LedgerModel.objects.filter( ledger = LedgerModel.objects.filter(
name__icontains=str(invoice.pk), entity=entity name__icontains=str(invoice.pk), entity=entity
@ -323,26 +319,24 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
TransactionModel.objects.create( TransactionModel.objects.create(
journal_entry=journal, journal_entry=journal,
account=debit_account, # Debit Cash account=debit_account, # Debit Cash
amount=amount, # Payment amount amount=finance_data["grand_total"], # Payment amount
tx_type="debit", tx_type="debit",
description="Payment Received", description="Payment Received",
) )
# if total_amount + invoice.
TransactionModel.objects.create( TransactionModel.objects.create(
journal_entry=journal, journal_entry=journal,
account=credit_account, # Credit Accounts Receivable account=credit_account, # Credit Accounts Receivable
amount=total_amount, # Payment amount amount=finance_data["total_price"], # Payment amount
tx_type="credit", tx_type="credit",
description="Payment Received", description="Payment Received",
) )
if vat_amount > 0:
TransactionModel.objects.create( TransactionModel.objects.create(
journal_entry=journal, journal_entry=journal,
account=vat_payable_account, # Credit VAT Payable account=vat_payable_account, # Credit VAT Payable
amount=vat_amount, amount=finance_data["total_vat_amount"],
tx_type="credit", tx_type="credit",
description="VAT Payable on Invoice", description="VAT Payable on Invoice",
) )
@ -622,7 +616,8 @@ class CarFinanceCalculator:
"make": car_info.get('make'), "make": car_info.get('make'),
"model": car_info.get('model'), "model": car_info.get('model'),
"year": car_info.get('year'), "year": car_info.get('year'),
"trim": car_info.get('mileage'), # Verify if this should actually be mileage "trim": car_info.get('trim'),
"mileage": car_info.get('mileage'),
"cost_price": car_finance.get('cost_price'), "cost_price": car_finance.get('cost_price'),
"selling_price": car_finance.get('selling_price'), "selling_price": car_finance.get('selling_price'),
"discount": car_finance.get('discount_amount'), "discount": car_finance.get('discount_amount'),
@ -657,7 +652,7 @@ class CarFinanceCalculator:
"total_price": total_price, "total_price": total_price,
"total_vat_amount": total_vat_amount, "total_vat_amount": total_vat_amount,
"total_discount": total_discount, "total_discount": total_discount,
"grand_total": (total_price + total_vat_amount) - total_discount, "grand_total": (total_price + total_vat_amount) - total_discount ,
} }
def get_finance_data(self): def get_finance_data(self):
@ -668,6 +663,7 @@ class CarFinanceCalculator:
"quantity": sum(self._get_quantity(item) for item in self.item_transactions), "quantity": sum(self._get_quantity(item) for item in self.item_transactions),
"total_price": totals['total_price'], "total_price": totals['total_price'],
"total_vat": totals['total_vat_amount'] + totals['total_price'], "total_vat": totals['total_vat_amount'] + totals['total_price'],
"total_vat_amount": totals['total_vat_amount'],
"total_discount": totals['total_discount'], "total_discount": totals['total_discount'],
"grand_total": totals['grand_total'], "grand_total": totals['grand_total'],
"additionals": self.additional_services, "additionals": self.additional_services,

View File

@ -2381,15 +2381,8 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
estimate = kwargs.get("object") estimate = kwargs.get("object")
if estimate.get_itemtxs_data(): if estimate.get_itemtxs_data():
# data = get_financial_values(estimate)
calculator = CarFinanceCalculator(estimate) calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data() finance_data = calculator.get_finance_data()
# kwargs["vat_amount"] = data["vat_amount"]
# kwargs["total"] = data["grand_total"]
# kwargs["discount_amount"] = data["discount_amount"]
# kwargs["vat"] = data["vat"]
# kwargs["car_and_item_info"] = data["car_and_item_info"]
# kwargs["additional_services"] = data["additional_services"]
kwargs['data'] = finance_data kwargs['data'] = finance_data
print(finance_data) print(finance_data)
kwargs["invoice"] = ( kwargs["invoice"] = (
@ -2518,15 +2511,9 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
invoice = kwargs.get("object") invoice = kwargs.get("object")
if invoice.get_itemtxs_data(): if invoice.get_itemtxs_data():
# data = get_financial_values(invoice)
calculator = CarFinanceCalculator(invoice) calculator = CarFinanceCalculator(invoice)
finance_data = calculator.get_finance_data() finance_data = calculator.get_finance_data()
# kwargs["vat_amount"] = data["vat_amount"] print((finance_data["total_vat_amount"]+finance_data["total_price"]) == finance_data["grand_total"])
# kwargs["total"] = data["grand_total"]
# kwargs["discount_amount"] = data["discount_amount"]
# kwargs["vat"] = data["vat"]
# kwargs["car_and_item_info"] = data["car_and_item_info"]
# kwargs["additional_services"] = data["additional_services"]
kwargs["data"] = finance_data kwargs["data"] = finance_data
kwargs["payments"] = JournalEntryModel.objects.filter( kwargs["payments"] = JournalEntryModel.objects.filter(
ledger=invoice.ledger ledger=invoice.ledger
@ -2627,38 +2614,41 @@ def invoice_create(request, pk):
ledger.save() ledger.save()
invoice.save() invoice.save()
unit_items = estimate.get_itemtxs_data()[0] # unit_items = estimate.get_itemtxs_data()[0]
vat = models.VatRate.objects.filter(is_active=True).first() # vat = models.VatRate.objects.filter(is_active=True).first()
total = 0 calculator = CarFinanceCalculator(estimate)
discount_amount = 0 finance_data = calculator.get_finance_data()
itemtxs = [] # total = 0
for item in unit_items: # discount_amount = 0
car = models.Car.objects.get(vin=item.item_model.name)
total = Decimal(car.finances.total) * Decimal(item.ce_quantity) # itemtxs = []
discount_amount = car.finances.discount_amount # for item in unit_items:
# car = models.Car.objects.get(vin=item.item_model.name)
grand_total = Decimal(total) - Decimal(discount_amount) # total = Decimal(car.finances.total) * Decimal(item.ce_quantity)
vat_amount = round(Decimal(grand_total) * Decimal(vat.rate), 2) # discount_amount = car.finances.discount_amount
grand_total += Decimal(vat_amount)
unit_cost = grand_total / Decimal(item.ce_quantity) # grand_total = Decimal(total) - Decimal(discount_amount)
itemtxs.append( # vat_amount = round(Decimal(grand_total) * Decimal(vat.rate), 2)
{ # grand_total += Decimal(vat_amount)
"item_number": item.item_model.item_number, # unit_cost = grand_total / Decimal(item.ce_quantity)
"unit_cost": unit_cost, # itemtxs.append(
"unit_revenue": unit_cost, # {
"quantity": item.ce_quantity, # "item_number": item.item_model.item_number,
"total_amount": grand_total, # "unit_cost": unit_cost,
} # "unit_revenue": unit_cost,
) # "quantity": item.ce_quantity,
# "total_amount": grand_total,
# }
# )
invoice_itemtxs = { invoice_itemtxs = {
i.get("item_number"): { i.get("item_number"): {
"unit_cost": i.get("unit_cost"), "unit_cost": i.get("total_vat"),
"quantity": i.get("quantity"), "quantity": i.get("quantity"),
"total_amount": i.get("total_amount"), "total_amount": i.get("total_vat"),
} }
for i in itemtxs for i in finance_data.get("cars")
} }
invoice_itemtxs = invoice.migrate_itemtxs( invoice_itemtxs = invoice.migrate_itemtxs(
@ -2705,14 +2695,16 @@ class InvoicePreviewView(LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
invoice = kwargs.get("object") invoice = kwargs.get("object")
if invoice.get_itemtxs_data(): if invoice.get_itemtxs_data():
data = get_financial_values(invoice) # data = get_financial_values(invoice)
calculator = CarFinanceCalculator(invoice)
kwargs["vat_amount"] = data["vat_amount"] finance_data = calculator.get_finance_data()
kwargs["total"] = data["grand_total"] kwargs["data"] = finance_data
kwargs["discount_amount"] = data["discount_amount"] # kwargs["vat_amount"] = data["vat_amount"]
kwargs["vat"] = data["vat"] # kwargs["total"] = data["grand_total"]
kwargs["car_and_item_info"] = data["car_and_item_info"] # kwargs["discount_amount"] = data["discount_amount"]
kwargs["additional_services"] = data["additional_services"] # kwargs["vat"] = data["vat"]
# kwargs["car_and_item_info"] = data["car_and_item_info"]
# kwargs["additional_services"] = data["additional_services"]
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
@ -3435,6 +3427,7 @@ class OrderListView(ListView):
template_name = "sales/orders/order_list.html" template_name = "sales/orders/order_list.html"
context_object_name = "orders" context_object_name = "orders"
# email # email
def send_email_view(request, pk): def send_email_view(request, pk):
estimate = get_object_or_404(EstimateModel, pk=pk) estimate = get_object_or_404(EstimateModel, pk=pk)
@ -3509,7 +3502,7 @@ def custom_bad_request_view(request, exception=None):
# from django_ledger.io.io_core import get_localdate # from django_ledger.io.io_core import get_localdate
# from django_ledger.views.mixins import (DjangoLedgerSecurityMixIn) # from django_ledger.views.mixins import (DjangoLedgerSecurityMixIn)
# from django.views.generic import RedirectView # from django.views.generic import RedirectView
from django_ledger.views.financial_statement import FiscalYearBalanceSheetView from django_ledger.views.financial_statement import FiscalYearBalanceSheetView,BaseIncomeStatementRedirectView,FiscalYearIncomeStatementView
from django.views.generic import DetailView, RedirectView from django.views.generic import DetailView, RedirectView
from django_ledger.io.io_core import get_localdate from django_ledger.io.io_core import get_localdate
@ -3548,3 +3541,34 @@ class DateBalanceSheetView(FiscalYearBalanceSheetViewBase, DateReportMixIn):
""" """
Date Balance Sheet View. Date Balance Sheet View.
""" """
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
})
class FiscalYearIncomeStatementViewBase(FiscalYearIncomeStatementView):
template_name = "ledger/reports/income_statement.html"
class QuarterlyIncomeStatementView(FiscalYearIncomeStatementView, QuarterlyReportMixIn):
"""
Quarter Income Statement View.
"""
class MonthlyIncomeStatementView(FiscalYearIncomeStatementView, MonthlyReportMixIn):
"""
Monthly Income Statement View.
"""
class DateModelIncomeStatementView(FiscalYearIncomeStatementView, DateReportMixIn):
"""
Date Income Statement View.
"""

View File

@ -1,252 +1,252 @@
aiohappyeyeballs==2.4.4 aiohappyeyeballs
aiohttp==3.11.11 aiohttp
aiohttp-retry==2.9.1 aiohttp-retry
aiosignal==1.3.2 aiosignal
alabaster==1.0.0 alabaster
albucore==0.0.23 albucore
albumentations==2.0.1 albumentations
annotated-types==0.7.0 annotated-types
anyio==4.8.0 anyio
arabic-reshaper==3.0.0 arabic-reshaper
asgiref==3.8.1 asgiref
astor==0.8.1 astor
astroid==3.3.8 astroid
attrs==23.2.0 attrs
autopep8==2.3.2 autopep8
Babel==2.15.0 Babel
beautifulsoup4==4.12.3 beautifulsoup4
bleach==6.2.0 bleach
blinker==1.9.0 blinker
Brotli==1.1.0 Brotli
certifi==2024.12.14 certifi
cffi==1.17.1 cffi
chardet==5.2.0 chardet
charset-normalizer==3.4.1 charset-normalizer
click==8.1.8 click
colorama==0.4.6 colorama
commonmark==0.9.1 commonmark
contourpy==1.3.1 contourpy
crispy-bootstrap5==2024.10 crispy-bootstrap5
cryptography==44.0.0 cryptography
cssselect2==0.7.0 cssselect2
ctranslate2==4.5.0 ctranslate2
cycler==0.12.1 cycler
Cython==3.0.11 Cython
decorator==5.1.1 decorator
desert==2022.9.22 desert
dill==0.3.9 dill
distro==1.9.0 distro
dj-rest-auth==7.0.1 dj-rest-auth
dj-shop-cart==7.1.1 dj-shop-cart
Django==5.1.5 Django
django-allauth==65.3.1 django-allauth
django-appointment==3.7.4 django-appointment
django-autoslug==1.9.9 django-autoslug
django-bootstrap5==24.3 django-bootstrap5
django-classy-tags==4.1.0 django-classy-tags
django-cors-headers==4.6.0 django-cors-headers
django-countries==7.6.1 django-countries
django-crispy-forms==2.3 django-crispy-forms
django-debug-toolbar==5.0.1 django-debug-toolbar
django-extensions==3.2.3 django-extensions
django-filter==24.3 django-filter
django-formtools==2.5.1 django-formtools
django-ledger==0.7.3 django-ledger
django-money==3.5.3 django-money
django-next-url-mixin==0.4.0 django-next-url-mixin
django-nine==0.2.7 django-nine
django-nonefield==0.4 django-nonefield
django-ordered-model==3.7.4 django-ordered-model
django-phonenumber-field==8.0.0 django-phonenumber-field
django-picklefield==3.2 django-picklefield
django-plans==1.2.0 django-plans
django-prometheus==2.3.1 django-prometheus
django-q2==1.7.6 django-q2
django-sekizai==4.1.0 django-sekizai
django-sequences==3.0 django-sequences
django-silk==5.3.2 django-silk
django-sms==0.7.0 django-sms
django-sslserver==0.22 django-sslserver
django-tables2==2.7.5 django-tables2
django-treebeard==4.7.1 django-treebeard
django-view-breadcrumbs==2.5.1 django-view-breadcrumbs
djangocms-admin-style==3.3.1 djangocms-admin-style
djangorestframework==3.15.2 djangorestframework
djangorestframework_simplejwt==5.4.0 djangorestframework_simplejwt
djangoviz==0.1.1 djangoviz
docutils==0.21.2 docutils
easy-thumbnails==2.10 easy-thumbnails
emoji==2.14.1 emoji
et_xmlfile==2.0.0 et_xmlfile
Faker==35.0.0 Faker
filelock==3.17.0 filelock
fire==0.7.0 fire
Flask==3.1.0 Flask
fonttools==4.55.6 fonttools
frozenlist==1.5.0 frozenlist
fsspec==2024.12.0 fsspec
gprof2dot==2024.6.6 gprof2dot
graphqlclient==0.2.4 graphqlclient
greenlet==3.1.1 greenlet
h11==0.14.0 h11
h2==4.1.0 h2
hpack==4.1.0 hpack
hstspreload==2025.1.1 hstspreload
httpcore==1.0.7 httpcore
httpx==0.28.1 httpx
hyperframe==6.1.0 hyperframe
idna==3.10 idna
imageio==2.37.0 imageio
imagesize==1.4.1 imagesize
imgaug==0.4.0 imgaug
iso4217==1.12.20240625 iso4217
isodate==0.7.2 isodate
isort==5.13.2 isort
itsdangerous==2.2.0 itsdangerous
Jinja2==3.1.5 Jinja2
jiter==0.8.2 jiter
joblib==1.4.2 joblib
kiwisolver==1.4.8 kiwisolver
lazy_loader==0.4 lazy_loader
ledger==1.0.1 ledger
libretranslatepy==2.1.4 libretranslatepy
lmdb==1.6.2 lmdb
lxml==5.3.0 lxml
Markdown==3.7 Markdown
markdown-it-py==3.0.0 markdown-it-py
MarkupSafe==3.0.2 MarkupSafe
marshmallow==3.26.0 marshmallow
matplotlib==3.10.0 matplotlib
mccabe==0.7.0 mccabe
mdurl==0.1.2 mdurl
MouseInfo==0.1.3 MouseInfo
mpmath==1.3.0 mpmath
multidict==6.1.0 multidict
mypy-extensions==1.0.0 mypy-extensions
networkx==3.4.2 networkx
newrelic==10.4.0 newrelic
nltk==3.9.1 nltk
numpy==2.2.2 numpy
oauthlib==3.2.2 oauthlib
ofxtools==0.9.5 ofxtools
openai==1.60.0 openai
opencv-contrib-python==4.11.0.86 opencv-contrib-python
opencv-python==4.11.0.86 opencv-python
opencv-python-headless==4.11.0.86 opencv-python-headless
openpyxl==3.1.5 openpyxl
opt_einsum==3.4.0 opt_einsum
outcome==1.3.0.post0 outcome
packaging==24.2 packaging
pandas==2.2.3 pandas
pango==0.0.1 pango
pdfkit==1.0.0 pdfkit
phonenumbers==8.13.42 phonenumbers
pillow==10.4.0 pillow
platformdirs==4.3.6 platformdirs
prometheus_client==0.21.1 prometheus_client
propcache==0.2.1 propcache
protobuf==5.29.3 protobuf
psycopg==3.2.4 psycopg
psycopg-binary==3.2.4 psycopg-binary
psycopg-c==3.2.4 psycopg-c
py-moneyed==3.0 py-moneyed
PyAutoGUI==0.9.54 PyAutoGUI
pyclipper==1.3.0.post6 pyclipper
pycodestyle==2.12.1 pycodestyle
pycparser==2.22 pycparser
pydantic==2.10.5 pydantic
pydantic_core==2.27.2 pydantic_core
pydotplus==2.0.2 pydotplus
pydyf==0.11.0 pydyf
PyGetWindow==0.0.9 PyGetWindow
Pygments==2.19.1 Pygments
PyJWT==2.10.1 PyJWT
pylint==3.3.3 pylint
PyMsgBox==1.0.9 PyMsgBox
PyMySQL==1.1.1 PyMySQL
pyobjc-core==11.0 pyobjc-core
pyobjc-framework-Cocoa==11.0 pyobjc-framework-Cocoa
pyobjc-framework-Quartz==11.0 pyobjc-framework-Quartz
pyparsing==3.2.1 pyparsing
pyperclip==1.9.0 pyperclip
pyphen==0.17.2 pyphen
pypng==0.20220715.0 pypng
PyRect==0.2.0 PyRect
PyScreeze==1.0.1 PyScreeze
pyserial==3.5 pyserial
PySocks==1.7.1 PySocks
python-bidi==0.6.3 python-bidi
python-dateutil==2.9.0.post0 python-dateutil
python-docx==1.1.2 python-docx
python-openid==2.2.5 python-openid
python-stdnum==1.20 python-stdnum
python3-saml==1.16.0 python3-saml
pytweening==1.2.0 pytweening
pytz==2024.2 pytz
pyvin==0.0.2 pyvin
pywa==2.7.0 pywa
pywhat==5.1.0 pywhat
pywhatkit==5.4 pywhatkit
PyYAML==6.0.2 PyYAML
pyzbar==0.1.9 pyzbar
qrcode==8.0 qrcode
RapidFuzz==3.11.0 RapidFuzz
regex==2024.11.6 regex
reportlab==4.2.5 reportlab
requests==2.32.3 requests
requests-oauthlib==2.0.0 requests-oauthlib
rfc3986==2.0.0 rfc3986
rich==13.9.4 rich
rubicon-objc==0.5.0 rubicon-objc
sacremoses==0.1.1 sacremoses
scikit-image==0.25.0 scikit-image
scikit-learn==1.6.1 scikit-learn
scipy==1.15.1 scipy
selenium==4.28.1 selenium
sentencepiece==0.2.0 sentencepiece
shapely==2.0.6 shapely
simsimd==6.2.1 simsimd
six==1.17.0 six
sniffio==1.3.1 sniffio
snowballstemmer==2.2.0 snowballstemmer
sortedcontainers==2.4.0 sortedcontainers
soupsieve==2.6 soupsieve
SQLAlchemy==2.0.37 SQLAlchemy
sqlparse==0.5.3 sqlparse
stanza==1.10.1 stanza
stringzilla==3.11.3 stringzilla
suds==1.2.0 suds
swapper==1.3.0 swapper
sympy==1.13.1 sympy
tablib==3.8.0 tablib
termcolor==2.5.0 termcolor
threadpoolctl==3.5.0 threadpoolctl
tifffile==2025.1.10 tifffile
tinycss2==1.4.0 tinycss2
tinyhtml5==2.0.0 tinyhtml5
tomli==2.2.1 tomli
tomlkit==0.13.2 tomlkit
torch==2.5.1 torch
tqdm==4.67.1 tqdm
trio==0.28.0 trio
trio-websocket==0.11.1 trio-websocket
twilio==9.4.3 twilio
typing-inspect==0.9.0 typing-inspect
typing_extensions==4.12.2 typing_extensions
tzdata==2025.1 tzdata
Unidecode==1.3.8 Unidecode
upgrade-requirements==1.7.0 upgrade-requirements
urllib3==2.3.0 urllib3
vin==0.6.2 vin
vininfo==1.8.0 vininfo
vishap==0.1.5 vishap
vpic-api==0.7.4 vpic-api
weasyprint==63.1 weasyprint
webencodings==0.5.1 webencodings
websocket-client==1.8.0 websocket-client
Werkzeug==3.1.3 Werkzeug
wikipedia==1.4.0 wikipedia
wsproto==1.2.0 wsproto
xmlsec==1.3.14 xmlsec
yarl==1.18.3 yarl
zopfli==0.2.3.post1 zopfli

View File

@ -35,11 +35,11 @@ def run():
invoice_itemtxs = { invoice_itemtxs = {
i.get("item_number"): { i.get("item_number"): {
"unit_cost": i.get("total"), "unit_cost": i.get("total_price"),
"quantity": i.get("quantity"), "quantity": i.get("quantity"),
"total_amount": i.get("total_vat"), "total_amount": i.get("total_vat"),
} }
for i in finance_data.get("cars") for i in finance_data.get("cars")
} }
print(invoice_itemtxs) print(finance_data)

View File

@ -236,13 +236,36 @@
<span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'bills'|capfirst %}</span> <span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'bills'|capfirst %}</span>
</div> </div>
</a> </a>
</li>
</ul>
</div>
</div>
<div class="nav-item-wrapper">
<a class="nav-link dropdown-indicator label-1" href="#nv-reports" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-reports">
<div class="d-flex align-items-center">
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div>
<span class="nav-link-icon"><span class="fas fa-money-check-alt"></span></span><span class="nav-link-text">{% trans 'Reports' %}</span>
</div>
</a>
<div class="parent-wrapper label-1">
<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-ic' 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 'Income Statement'|capfirst %}</span>
</div>
</a>
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<a class="nav-link" href="{% url 'entity-bs' request.user.dealer.entity.slug %}"> <a class="nav-link" href="{% url 'entity-bs' request.user.dealer.entity.slug %}">
{% else %} {% else %}
<a class="nav-link" href="#"> <a class="nav-link" href="#">
{% endif %} {% endif %}
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'Reports'|capfirst %}</span> <span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'Balance Sheet'|capfirst %}</span>
</div> </div>
</a> </a>
</li> </li>

View File

@ -0,0 +1,63 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load custom_filters %}
{% block period_navigation %}
{% if unit_model %}
<div class="column is-12">{% period_navigation 'unit-ic' %}</div>
{% elif entity %}
<div class="column is-12">{% period_navigation 'entity-ic' %}</div>
{% elif ledger %}
<div class="column is-12">{% period_navigation 'ledger-ic' %}</div>
{% endif %}
{% endblock %}
{% block content %}
<div class="card">
<div class="card-content has-text-centered">
<div class="container mb-4">
<div class="columns">
<div class="column">
{% if entity %}
<h1 class="is-size-2 has-text-weight-light">{{ entity.name }}</h1>
{% elif ledger %}
<h1 class="is-size-2 has-text-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>
{% endif %}
<h1 class="is-size-2 has-text-weight-bold">{% trans 'Income Statement' %}</h1>
<h2 class="is-size-2 has-text-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">
{{ from_date | date:'m/d/Y' }} - {{ to_date | date:'m/d/Y' }}
</h3>
</div>
</div>
</div>
{% 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"
href="{{ request.path }}?format=pdf">{% trans 'Download PDF' %}</a>
</div>
</div>
{% endblock %}

View File

@ -45,9 +45,12 @@
{% elif estimate.status == 'in_review' %} {% elif estimate.status == 'in_review' %}
<button id="accept_estimate" onclick="setFormAction('approved')" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block">{% trans 'Mark As Accept' %}</span></button> <button id="accept_estimate" onclick="setFormAction('approved')" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block">{% trans 'Mark As Accept' %}</span></button>
{% elif estimate.status == 'approved' %} {% elif estimate.status == 'approved' %}
<a href="{% url 'create_sale_order' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Create Sale Order' %}</span></a> {% if estimate.sale_orders.first %}
<a href="{% url 'invoice_create' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Create Invoice' %}</span></a> <a href="{% url 'invoice_create' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Create Invoice' %}</span></a>
{% else %}
<a href="{% url 'create_sale_order' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Create Sale Order' %}</span></a>
<a href="{% url 'preview_sale_order' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview Sale Order' %}</span></a> <a href="{% url 'preview_sale_order' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview Sale Order' %}</span></a>
{% endif %}
{% elif estimate.status == 'in_review' %} {% elif estimate.status == 'in_review' %}
<a href="{% url 'estimate_preview' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview' %}</span></a> <a href="{% url 'estimate_preview' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview' %}</span></a>
{% endif %} {% endif %}
@ -137,15 +140,15 @@
</tr> </tr>
{% endfor %} {% endfor %}
<tr class="bg-body-secondary total-sum"> <tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Discount Amount" %}</td> <td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Vat" %} ({{data.vat}}%)</td>
<td class="align-middle text-start fw-semibold"> <td class="align-middle text-start fw-semibold">
<span id="grand-total">- {{data.total_discount}}</span> <span id="grand-total">+ {{data.total_vat_amount}}</span>
</td> </td>
</tr> </tr>
<tr class="bg-body-secondary total-sum"> <tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Vat" %} ({{data.vat}}%)</td> <td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Discount Amount" %}</td>
<td class="align-middle text-start fw-semibold"> <td class="align-middle text-start text-danger fw-semibold ">
<span id="grand-total"></span> <span id="grand-total">- {{data.total_discount}}</span>
</td> </td>
</tr> </tr>
<tr class="bg-body-secondary total-sum"> <tr class="bg-body-secondary total-sum">

View File

@ -84,6 +84,20 @@
<p class="ps-6 ps-sm-0 fw-semibold mb-0">{{ estimate.customer.address_1 }}</p> <p class="ps-6 ps-sm-0 fw-semibold mb-0">{{ estimate.customer.address_1 }}</p>
</td> </td>
</tr> </tr>
<tr>
<td class="py-2">
<div class="d-flex align-items-center">
<div class="d-flex bg-info-subtle rounded-circle flex-center me-3" style="width:24px; height:24px">
<span class="text-info-dark" data-feather="trending-up" style="width:16px; height:16px"></span>
</div>
<p class="fw-bold mb-0">Total Discount</p>
</div>
</td>
<td class="py-2 d-none d-sm-block pe-sm-2">:</td>
<td class="py-2">
<p class="ps-6 ps-sm-0 fw-semibold mb-0">${{ data.total_discount }}</p>
</td>
</tr>
<tr> <tr>
<td class="py-2"> <td class="py-2">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
@ -98,6 +112,7 @@
<p class="ps-6 ps-sm-0 fw-semibold mb-0">${{ data.grand_total }}</p> <p class="ps-6 ps-sm-0 fw-semibold mb-0">${{ data.grand_total }}</p>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
</div> </div>
@ -143,7 +158,7 @@
<td class="create_by align-middle white-space-nowrap fw-semibold text-body-highlight">{{car.year}}</td> <td class="create_by align-middle white-space-nowrap fw-semibold text-body-highlight">{{car.year}}</td>
<td class="last_activity align-middle text-center py-2"> <td class="last_activity align-middle text-center py-2">
<div class="d-flex align-items-center flex-1"> <div class="d-flex align-items-center flex-1">
<span class="fw-bold fs-9 text-body">${{car.total_vat}}</span> <span class="fw-bold fs-9 text-body">${{car.total}}</span>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -237,7 +237,7 @@
<tr class="bg-body-secondary total-sum"> <tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "VAT" %} ({{data.vat}}%)</td> <td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "VAT" %} ({{data.vat}}%)</td>
<td class="align-middle text-start fw-semibold"> <td class="align-middle text-start fw-semibold">
<span id="grand-total">+ {{vat_amount}}</span> <span id="grand-total">+ {{data.total_vat_amount}}</span>
</td> </td>
</tr> </tr>
<tr class="bg-body-secondary total-sum"> <tr class="bg-body-secondary total-sum">

View File

@ -208,11 +208,11 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for item in car_and_item_info %} {% for item in data.cars %}
<tr> <tr>
<td class="">{{item.info.make}}</td> <td class="">{{item.make}}</td>
<td class="align-middle">{{item.quantity}}</td> <td class="align-middle">{{item.quantity}}</td>
<td class="align-middle ps-5">{{item.finances.selling_price}}</td> <td class="align-middle ps-5">{{item.selling_price}}</td>
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td> <td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -222,10 +222,10 @@
<!-- Additional Charges (VAT and Services) --> <!-- Additional Charges (VAT and Services) -->
<div class="additional-charges"> <div class="additional-charges">
<p><strong>VAT/ضريبة القيمة المضافة ({{vat}}%):</strong> <span class="highlight">{{vat_amount}}&nbsp;{{ _("SAR") }}</span></p> <p><strong>VAT/ضريبة القيمة المضافة ({{vat}}%):</strong> <span class="highlight">{{data.vat}}&nbsp;{{ _("SAR") }}</span></p>
<p><strong>Additional Services/ الخدمات الإضافية</strong> <p><strong>Additional Services/ الخدمات الإضافية</strong>
<br> <br>
{% for service in additional_services %} {% for service in data.additional_services %}
<span class="highlight">{{service.name}} - {{service.price}}&nbsp;{{ _("SAR") }}</span><br> <span class="highlight">{{service.name}} - {{service.price}}&nbsp;{{ _("SAR") }}</span><br>
{% endfor %} {% endfor %}
</p> </p>
@ -233,7 +233,7 @@
<!-- Total --> <!-- Total -->
<div class="invoice-total"> <div class="invoice-total">
<p><strong>Total/الإجمالي</strong> <span class="highlight">{{total}}&nbsp;{{ _("SAR") }}</span></p> <p><strong>Total/الإجمالي</strong> <span class="highlight">{{data.grand_total}}&nbsp;{{ _("SAR") }}</span></p>
</div> </div>
<!-- Footer Note --> <!-- Footer Note -->

View File

@ -21,8 +21,11 @@
<tr class="hover-actions-trigger btn-reveal-trigger position-static"> <tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="align-middle product white-space-nowrap py-0">{{ order.formatted_order_id }}</td> <td class="align-middle product white-space-nowrap py-0">{{ order.formatted_order_id }}</td>
<td class="align-middle product white-space-nowrap py-0">{{ order.estimate.customer.customer_name }}</td> <td class="align-middle product white-space-nowrap py-0">{{ order.estimate.customer.customer_name }}</td>
<td class="align-middle product white-space-nowrap">{{ order.estimate }}</td> <td class="align-middle product white-space-nowrap">
<a href="{% url 'estimate_detail' order.estimate.pk %}">
{{ order.estimate }}
</a>
</td>
<td class="text-center"> <td class="text-center">
{% comment %} <a href="{% url 'estimate_detail' estimate.pk %}" {% comment %} <a href="{% url 'estimate_detail' estimate.pk %}"
class="btn btn-sm btn-phoenix-success"> class="btn btn-sm btn-phoenix-success">