From cfa7d80eccdb9e453f82844b46e5bdc142c15eef Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 10 Feb 2026 17:53:28 +0000 Subject: [PATCH] final final --- accounting/__pycache__/urls.cpython-311.pyc | Bin 1330 -> 1420 bytes accounting/__pycache__/views.cpython-311.pyc | Bin 15167 -> 17870 bytes .../templates/accounting/vat_report.html | 121 ++++++++++++++++++ accounting/urls.py | 1 + accounting/views.py | 62 ++++++++- core/__pycache__/views.cpython-311.pyc | Bin 98042 -> 98949 bytes core/templates/core/customer_statement.html | 82 ++++++++++-- core/templates/core/reports.html | 12 ++ core/templates/core/supplier_statement.html | 82 ++++++++++-- core/views.py | 44 ++++++- 10 files changed, 374 insertions(+), 30 deletions(-) create mode 100644 accounting/templates/accounting/vat_report.html diff --git a/accounting/__pycache__/urls.cpython-311.pyc b/accounting/__pycache__/urls.cpython-311.pyc index 38d3eff1ae9c7ea24e6f8732f76905041f2bcf4a..2953fcdab1c2ccdb3df08d9b3dacb906104b180a 100644 GIT binary patch delta 194 zcmdnQ)x)j6oR^o20SL}ycV{kSW?*;>;=lkul<|4pM)g+4i5ploB~zKRcwy2hQft_j z0rji~VhBj*j^ay^4rb7l*}RR>oKcdeD77HJsH9lGEU`rY78j6=hlosm!xYWPKiPvh znyE-=@&e{CE=iyXj6huMIEm$fP>0+M(JPXg8+b0T*j{9@y~1KU`8JCOzc>#+Qv){$ J7KsDZ0s!-_EvWzi delta 117 zcmeC--o&N8oR^o20SKOEHD)F;GcY^`abSQC%J|&1QN5LsE1fHfH$^g-K~rk;Ek<+3 y$=b|OjC_;Zn4>2@V-Dt$04in#;$p4I&MXfm2eC>3u{OUbH$PJYHwYGq0c8Pi!57s4 diff --git a/accounting/__pycache__/views.cpython-311.pyc b/accounting/__pycache__/views.cpython-311.pyc index 60fe96357c91ca2609b50ee19b7b7061c0491937..30c15097e8e923e3292b5abbdc2a70ed89593955 100644 GIT binary patch delta 4853 zcma)AX>1$E72aLmN_bHKGD_3IuVB9u80#q{3Fr0tVb4`PCLtE(Fb=_PwPg*-~0( zvHbkz%{On}do%NPG!0( z{oa(X-dyVMnJGiZH@>KE8WMwWx#xr7}7t!s(4 z1+?xZ+E&okpmyUu-nD_+vxM9ZTJI8VN5Z<7iTLS7_UA#3qTL%41;zMSmc)l*lAstK zBbh`tE=P3q5iZoGu-hZ7V%(AemJ}CImXj%AG?Nx0oMKGGWC7cX79{!#R~xcG1~l;d z^oW*W&N4Y>SWOASBuoG9anpAi9oD7l2JRvYA;GWmjBzIhc~8Z@cR14VR6J z9MH(F`wnw188&IbAl;@##1`)CzNmK>k6sS+P#=sv0z9G zs`b2LjAscE(($9C{#ZPYAtcj-ofr@iMlwY17?M+B#I0y{Y7% zBeLX#R(R?2Y zw%~3px>uCkD`wo?)9&tTyNd1|CHIaAbJ^m=A!}|q^MNxxr`DfYKjUnfcDB5+uIOA{ za;}~*er{%}94C5b__}Gn?&@%1|1$-?uE@tqd~AkKPV>pa;aq`F7WuIfKQ_MYmfc&j zhiB}~)Ar_T!-Z#t3ijrrJz26RXY7Zk?T2Tf!m!)8g8lHv4CC0&eWv0U43l^4<354L zJ`O1RxgiewlWKpG8-HY0&jjmV-*A4z73u1_OZm(Ba~q0*4W+<_>$*~4dm*sW4_^~Xo~;GXBRANB)>Za~UbmgMUDu3BDW* z6^y>JD|n`F#?>_KYAU*xmt4zd8H1@!g^8`7>lv5toa?n<$rmX)TT0HBiLJMMwdds5 z@)Dr$k$Eumif@6zsR>1_z=9ejh=HoMPu`X zt!xYzmaQrpR~0}ohHnQ~Ud2Q7I5;Yk1q>tHyna>+h=($vQ8rvEm!vwgJLwCDchUiyxy zJ=6zA6g`~cqFQ;xI+d}sS=Ki2x&=(szrxnv3oawgflE0VbV)yEq+}Nlsl(v z{F596m#}`gTy^1L9CQRhtwvmBI=rFIr-mN3d358T8Kyt5ZDsqWrfq*=*>U>1qnABK z|LN$eRbBi(8hHufMF0uSp${;7cXdE_0-726?dltB-!GOqIgbc!_#j#Ce5~Z7Bf=$? ztfk#F&~}fk86#gnLyUJyndcbQ(TmP`W(WgXNZBu_7)6H^MrECBgg{wDZA$ ztk{M#S(1*4Q6Vjpq#%*ApnV=$MSyYUL#w}JePG7gI&E#ejTK)Hc zL5|UX1iCFlC?*j^+7*0Dk5)z>pr_oP(;;gVlRz+!5kFb9;;M%)V?SPs$T0xL0ADCE z2_Jz8RN*~O(Z}RWDj6rQU_(b@=|Mr!h(emI2L;W98uhOt_c%Qt3R=`9!0E_~^mn0p z%Xt(}BT)LUkiQ%KR9Hd68F5(_9(C_4u2d|Yg_PW#nxo_juwR+lTsxy>U!wkohTwj* zm_on>Bm)2uj_gEvFYRw=0^0KpEB51pRd%-;Yd<#U(*=Qzz`jjXcAT-Y<9>n^b@%ju zq}WI{orVv3^iWL1I}UjZ`*+f+Wj=o|60CPV`cb(23<`w&29(#QHZJ2?*xgo-Z)%`% zEw@TtEe?mO?&7tkvSPz1J&T}*ps7qPn^-D$er>jyrA-ead+&qm*n{+k4|YFJ{OF?PuB@xc<(r6B=sjW>bx z+0^l_FK|(gJrrdvxBx_5l+^uGvU^nn7i8(ks;`w2U|ZmS9T1#kr(aY-?n)*D~f4;c%^J=howwf zy^a$6%H`YZ#3036qv7gscl_2w3^+_&N7G}YZ%V{LhUT4 Qszz97O|`GHvRZWhzrKv#z5oCK delta 2614 zcma)7Yitx%6yCe9K6bY)eRtb#cUx$86WZEJp$}eth+s(8x&D0Fg+QRx0oQ!!sPf|KQ)Tt!(5Vh1pQ(-45{K-NOVqkdeY{@SrD{Va z7!L~Dr#5C9*R_fB+ zRJo2+@NDH;=N}F}TRG9x099Z?geCyP2@npm79_%ap=vEz#kW>QY7Opng+f=c>;ST?u&H+33Aw*fe=9nMF)m3} z*>MaQ%ssM5(#rAOD_`k2(Wf}iRIUG zW;SOgQrH@-haYa~@ebhpAivtumlqinOPZ#eSiUNp5jCtpYZXEzfK`!YG)2wiRDw|z zJmr-cE8w@3F~*cn>p7-LYJzGeQ>ejIU>`-VuK`|i+$sxlDSU%O3f++fw|6IyY%lk> z5BVNOa~;A4zN`I;7XuHq^6M>i`I$sYXPK@kGo!|wqEMpLv7qP~hWXg}3q_vBVHv_+ z0IMiXO;v#w*oRatQeMf_GfI*@g98tfv^2F`D%C7cO4TLPZZL0>QAir*`D{mx_gRci z@Q*v1d`nuh3I0<@#HZs_3L(w?(clm!VG(NJJD`|%FR`Dj>Wrl2phU|{HNy^r_l|-T zU2u~)ALwjrZp9V*5m0f~0}yjsZj_xh@`Ii2;5XOVE96|3{%UkyDkkKb%~DOa`?T2Z z(=57R(^($TL{>S=X_}&?6O)oEX|TnPp~q45@Fu6QK1MMN789}4D3g*&J*SnK36;ue z*s3SM<<-KzEQq9tVRLu);PNX=z$13OoKyQe2_mlPag zrOSlgjS(oc6<92wNWw0`V>m0C`8Zl`fLO`h-N{ZNyRz!dDFpv=wAyW zJdpQS4t{drfxYJ)d}I*4^}#_B<(CGBc3E!x#Ke5dL=vYFp405EAmVfxMGjRF2Rqw_ z$@y)4-bVHvgeYG>)Vp~WO{_C}7vU^`flDrSA^zK)7`UTM!kD7eWEa5cYT^9QU#QCL zrUdb!0!*mVfB%}KwzN5acJSj{9;hbZ?fV6nzl0FsV_Ru{7|nA4v7&$1;$^oAoYvlD|As>tvPu`bapBUjWO0XDVkZs$r{BgM7(Sh<$jv zrwv_WJ8=1Ng#8FA!eN9H2)ILp4B{m(LS2x+z4UkS#*8lKRJxg6gCT*RL8`ejdb+iL d-f^FskB+rhG|xNkxp{spT=;a%Pu%#Be*i_cTD|}P diff --git a/accounting/templates/accounting/vat_report.html b/accounting/templates/accounting/vat_report.html new file mode 100644 index 0000000..e5a5b9d --- /dev/null +++ b/accounting/templates/accounting/vat_report.html @@ -0,0 +1,121 @@ +{% extends 'base.html' %} +{% load i18n %} + +{% block content %} +
+ +
+
+

{% trans "VAT Report" %} / تقرير ضريبة القيمة المضافة

+

{% trans "Tax Declaration Summary" %} / ملخص الإقرار الضريبي

+
+
+ +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + +
+ +
+
+
+
+ 1. {% trans "Sales (Output VAT)" %} / المبيعات (ضريبة المخرجات) +
+
+
+ + + + + + + + + + + + + + + +
{% trans "Total Sales (Excl. VAT)" %}
إجمالي المبيعات (غير شامل الضريبة)
{{ total_sales_subtotal|floatformat:3 }} {{ global_settings.currency_symbol }}
{% trans "Total VAT Collected" %}
إجمالي الضريبة المحصلة
{{ total_output_vat|floatformat:3 }} {{ global_settings.currency_symbol }}
{% trans "Total Gross Sales" %}
إجمالي المبيعات (شامل الضريبة)
{{ total_sales_gross|floatformat:3 }} {{ global_settings.currency_symbol }}
+
+
+
+ + +
+
+
+
+ 2. {% trans "Purchases (Input VAT)" %} / المشتريات (ضريبة المدخلات) +
+
+
+ + + + + + + + + + + + + + + +
{% trans "Total Purchases (Excl. VAT)" %}
إجمالي المشتريات (غير شامل الضريبة)
{{ total_purchases_subtotal|floatformat:3 }} {{ global_settings.currency_symbol }}
{% trans "Total VAT Paid (Recoverable)" %}
إجمالي الضريبة المدفوعة (القابلة للاسترداد)
{{ total_input_vat|floatformat:3 }} {{ global_settings.currency_symbol }}
{% trans "Total Gross Purchases" %}
إجمالي المشتريات (شامل الضريبة)
{{ total_purchases_gross|floatformat:3 }} {{ global_settings.currency_symbol }}
+
+
+
+ + +
+
+
+

{% trans "Net VAT Payable / (Refundable)" %}

+
صافي الضريبة المستحقة الدفع / (المستردة)
+

+ {{ net_vat|floatformat:3 }} {{ global_settings.currency_symbol }} +

+

+ {% if net_vat > 0 %} + {% trans "Amount to be paid to Tax Authority" %} / المبلغ المستحق للدفع للهيئة الضريبية + {% else %} + {% trans "Amount to be refunded from Tax Authority" %} / المبلغ المستحق للاسترداد من الهيئة الضريبية + {% endif %} +

+
+
+
+
+
+{% endblock %} diff --git a/accounting/urls.py b/accounting/urls.py index b1d6a56..8b4699a 100644 --- a/accounting/urls.py +++ b/accounting/urls.py @@ -9,6 +9,7 @@ urlpatterns = [ path('journal-entries/', views.journal_entries, name='journal_entries'), path('journal-entries/manual/', views.manual_journal_entry, name='manual_journal_entry'), path('ledger//', views.account_ledger, name='account_ledger'), + path('reports/vat/', views.vat_report, name='vat_report'), path('trial-balance/', views.trial_balance, name='trial_balance'), path('balance-sheet/', views.balance_sheet, name='balance_sheet'), path('profit-loss/', views.profit_loss, name='profit_loss'), diff --git a/accounting/views.py b/accounting/views.py index 6a563f6..28476a5 100644 --- a/accounting/views.py +++ b/accounting/views.py @@ -4,13 +4,71 @@ from django.contrib.auth.decorators import login_required from django.contrib import messages from .models import Account, JournalEntry, JournalItem from .forms import AccountForm, JournalEntryForm -from django.db.models import Sum, Q, Value, DecimalField +from core.models import Sale, Purchase, Product +from django.db.models import Sum, Q, Value, DecimalField, F from django.db.models.functions import Coalesce from django.utils import timezone -from datetime import datetime +from datetime import datetime, date from django.db import transaction import json +@login_required +def vat_report(request): + start_date = request.GET.get('start_date') + end_date = request.GET.get('end_date') + + if not start_date: + start_date = timezone.now().replace(day=1).strftime('%Y-%m-%d') + if not end_date: + end_date = timezone.now().strftime('%Y-%m-%d') + + # Convert strings to date objects for filtering + # Note: We filter by the day inclusive + + sales = Sale.objects.filter(created_at__date__gte=start_date, created_at__date__lte=end_date).exclude(status='cancelled') + purchases = Purchase.objects.filter(created_at__date__gte=start_date, created_at__date__lte=end_date).exclude(status='cancelled').prefetch_related('items__product') + + # Output VAT (Sales) + total_sales_subtotal = sales.aggregate(sum=Sum('subtotal'))['sum'] or 0 + total_output_vat = sales.aggregate(sum=Sum('vat_amount'))['sum'] or 0 + total_sales_gross = sales.aggregate(sum=Sum('total_amount'))['sum'] or 0 + + # Input VAT (Purchases) - Estimated based on Product VAT rate + # Since Purchase model doesn't store VAT explicitly, we calculate it from items + total_purchases_subtotal = 0 + total_input_vat = 0 + + for purchase in purchases: + purchase_vat = 0 + purchase_subtotal = 0 + for item in purchase.items.all(): + # Assume item line_total is cost * quantity + # We calculate VAT on top. + rate = float(item.product.vat) + line_total = float(item.line_total) + tax = line_total * (rate / 100.0) + purchase_vat += tax + purchase_subtotal += line_total + + total_input_vat += purchase_vat + total_purchases_subtotal += purchase_subtotal + + total_purchases_gross = total_purchases_subtotal + total_input_vat + + context = { + 'start_date': start_date, + 'end_date': end_date, + 'total_sales_subtotal': total_sales_subtotal, + 'total_output_vat': total_output_vat, + 'total_sales_gross': total_sales_gross, + 'total_purchases_subtotal': total_purchases_subtotal, + 'total_input_vat': total_input_vat, + 'total_purchases_gross': total_purchases_gross, + 'net_vat': float(total_output_vat) - total_input_vat, + 'currency': 'OMR', + } + return render(request, 'accounting/vat_report.html', context) + @login_required def accounting_dashboard(request): total_assets = sum(acc.balance for acc in Account.objects.filter(account_type='asset')) diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2433d2158975b278144eb31e3148e1a44876206f..1e00527794dbe5e8abd39a19e4cd15e107750aeb 100644 GIT binary patch delta 4363 zcmeHKeQ;A%7SDOfdre+anwBJ_-)-7bf)FV9fglAPgi?yMqAV`j5@=`(^n=@gADi03 zAR@?am%Dgz$8k^)WkIZyao^$!bOlxcg~C**MQIsWMMSM)anyC)b6#4g6?OmE{ckgq zUw-eLbI(2R+;i_e`Q|(Mkw=sP&qqhA2K;QVT3gZ5_40tXU*mf(Tcm$VJZAI)Ua;G2 zE0yX@_?7Xi@ZaB^!JBWiLzK=scH66TCwI}wZ~Z;?;1m*8(GYKOw@dnVw> z!aZ5M;fok^oiSu)u8@&0f6kQcLYJlDjW%hnD8D+8kWJ(A^QV4?% zSwg0e98yZ-o-)^2gM+TPGlUGK14Gs;bH?hLnFg=p4z>q5lX&vp)L$17O~MytDFpfW z*lw-ySCp3cE321Q1z7<~wi(+kwM#2Yg4IF);&wApGHJIGo9AEbubLlVhiUv<1aBjF z&Aec5_H7Y|sy;9>COX1}?znTR`%wJj?tgZ~OlG>m@7^n?pAof;55zf<%ON)n8V|0%hpAmc5FWwKjw_ z{O~XLdeq}+vK&{Xb|X(~9u?cA7#z-qg0|SWhWs{U)A^D8xkkHu6Sp65@E4ksqmqu< zXSLdA@wb|D>k~!E`jYou@f&@|T;5if_kE{(WAZU)cB?b{eRtx<>BrndTirvuq71IA z&kZJ*`X#|?wM#X)J)5p;9pq~nG`cB$wXMyWx=C$yUf1Fr`NERbY8z_Dx4Ls$+&4BA zt&YZ+T&ZozzP4Cbn=7r&k<{kSBoZGpxGUBYqjVV%8m4qR__hOC|KBroLj14x%;Wn~ z$Fx#qU1Q_0gQTDh!wwPq(T-@|mu>4?tNh$PkI}{wdulwMtA}FJdn$Y%xp{}&tRd)A z;k;mfV%#wj{;-&9_IuQmDBelM{45r;&uNcqnEG=CJv!>XV~*lhM={SoaC`kp=aA#h zA^%Y|d;c5N>`eJVHQVF+RdaaTfku=4U3$|O3EBxx@u`PK$nPON6|Ot9)~IwKHJHK! zj!cx`G@sG(pmGK!swsS=Wf!1i_3?X^vnX+z!WWJ&1LYsclT6_Stz$s>2zk0Gyycxw zA@)P$%t2Y-LU0~}xPg>+pQJWP$a`JDI`Z>}OCd}~@e9hUc@SgUE0lN73 z_lnd@sJWRm_wrNchJ>5nTMN+53r^?BU!i6qfAaKs`5R>Ud{xJ6_?mZgWUJrd^Bp*b zeH*rSmV<1N42C*o_C8+mp+YM0j3EUpMJ z7K4V%$j~hD*iJ_{^`mJZ$D%OAt1q0C9mrPjJs0z&ffDcRi03;$9;CWZxQfOc;Py_Z zIAww1W;PIoY#;C?cRKj+i?OmBpV#yqx%J{eIRS;g?JG?G#3?7CaD8~(C$~xV6mpVE zkVcTs*L_wjXCQqd-1XToV`hIn86BnNJ3&CVA@lHwmok6sD0peIoQ>w2!+S4n2k`Jk z-Kmhp*LJ7LUKBU+rtUV`hwRVcb(g1`VHiL4uU+>3MZ&jyUvJH!S-WViQ5$?P7lv>6 z9gLG_q4d>PjzX~vvxQHA1mzx-m}Kn^1!jOc2Olgl)=K{RRb#Y{D98u6PYks{hEj~G z7+K7;z$fZlQtjt4`WLaEqg)K@c@(fv=w!#G{ z69;YJk;_r!)w*nOJ-|YdH2?<7i%{g#3J1V_0F`2A3@n7vTD%?VB&Zf54o1TgaX1cA z{1Jd!Q5P#&QsJUHR=76E9btLl0ojXlzPk|w_eh`Yz6qr3`8tEP& zc#uFp#`I(IA$nX%Ni*UOV9N*|CRrh|dQp=KCt<9Xmj)RCjbd>+Jf=kah;P&35Aa9P z=z*IcN4)8QQOY-~Y4Y%s!^$;EuM=~Hgx&W14 zOQfZkE~HGDCqIdz0<9twI&IGTX{`QsqbaCc308>W9Prrn_tM`%f17ℑ-KT5ZiKK zWKT4rBL{Mn<>aGLxJJRS%>IK&b(T^vlRV!=P#_kJ0$m+AqAETb0H+d3eh?q=Jw`dY%Yol#Oz!cW@{wp)Q@6p zqA?eS*!0)!$)?z!3zOtcXw#`hPk@_D)@P9?C}9m1Ul*TD^9QXY7dX!*SHtdpiw32vh|jns>HEDy$6uZFWh9LFR2(p?ah*?&IFgZWfsI~3#P&%C0fr9>J45aJ0VWb0PoclU{twg_8e+i zPizf^FanB1Q{gYg)?Ijw*c^Jr_Qr;8+oc*NU@9btRYj0w-HxJh6sNCGoC4#_(!MEz za{$k49W&t#@O+P1xJ$2Xuk|OA^XA<>x!7jx&BYgYgI9hD=lGSDHVeLzWDWUzE$JQ@ zBPG3v{3o5&%)yh2f@eDgp97ipUC0;FqBfE6WxRzhyS0uv5F-_TOe=KpYJf4<4v@wr z(~3Sru$kZtLH{$Fygf~dp6LsUA=CO-9DP3}3ZTz|ZZ3wiQg%0uxQy@%33}0g^_^P$ zkY?SDP9G(w7Yu!zX3T?b2}X+e08H%N4kZB?7O68+Z*DgR;0Ehn^dQKCRSzpc`yv1v zKz<#?JH+E<@R)T!(k9AVWHa!&Gm}M8IZT%Sj*3^rQ{_-6zln60NU4DJ>S3h2$^4-9 zY6Wmeo`3>&D?+=qbfLehtlBrfx@tk>s+^=PTm%E9diqPmsFh^YXEExq7&TRl`Y1*X z6r;|GQB%aI9b(k^Flurb^*4-~8b+NAqaKD)+rp?-Vbq4`Ee4~Of>AfYsC!`49k2v~ zB&i;e?s!J`Go#y>(M8PYMrCw~vN;5FO)|P48Qp-4u0BSW9a}^oQwVf_F}kW49qWva zN=7FOqa%gUre?Hb7?m`mT4q$1S&XWK(P}bU5Ju@|lz2u-W|UrbSZu0-Wc3VkoXJo- oTzdnbEjJH^Nzi2={M=}e>?>2cBwaKc@TrGiQ>`UF03FJ|0n^xBNB{r; delta 3679 zcmcguX;4&G7Vh)9--GVgKqE~z`!3LKQ4zPq1ze&+A_R<(K@kN7M8S(14G?rjtx+?n zkvs8LDk&XPMn~f$COzXzDv7ux8Z$0KwAA1jWrzuhh&YsSi<3F$wJjFL%AeG9Reycn z`OZD}EVti1t5++x@5=t${roHj{5bR1mzB1^>woMczPsHl4U~95%3PlLu`h=!gQYqX z-!Ltn?;C0ZS(pC#eSlJ@)SB4xhP|Oi(=M4u>>a~r{3C#u?{)GM*8_a(jI}az)EfE9 zHyvTM@-E+PU@*LeU(W|=#Y<<@D!a@N(NHVZqK{go*z&qu=d(D-yVPK)H59A0KHH4w z5Wk*Jj=^Ppn(gHsjnTH%sA3Vg7mGr0$$Z|vB>wikXrqON@$6MWTsa&tD4L9R5|9s< zFMEgN&k2su(jch)iHoX(fx{XHZ#?WYS^ZZ`;Fk}NHYsu% zaNh%id?K4{(@)u^^RWj;hew}{9B?+!an=!Y)*jKRDgjET0b#|H*ZsNmVDJBb$?Jc` zl4Hf1eXEyLRaKOgFmJK2VpFk9E?`HgdJlEvwL?j~=}X#i5cmZW*r>wRTTm*jNN(j$9=P!SQ z47dx=?UyXyp{SZLyN$e}EySJv)p~$7e*8kV`FETAACwZgvo#N{@v7D&%XNJ2gX>tAdrNC6$T#tMfZP9)QL_Aq;t-^|%Jt1s zzO{mM2Y6arto##Fqule_G5~IH_jj2VNirD5k(*n*r6X8$`hnT~@})T7lP`~jqYYm< zgYLIpM$^e;de>{Zj_0(8gNcuA3$@=j^Fgy`$jl(|qN{fI?)D6j&8W<4=r|)=QJBf6 zc21I1i7#pmh@EAxkuxAxz0e)kTwb!3WE93=ZSt5rp1#f3!+Dwix=EXv{_Z^SsVsUlcJFonv< zNUA1SMUO^2_G~G^^Q6lnX_+Kq(!e2X@vsjDYfbSG4^SblC%{_8<4vsU4S#~AA~X?( zflFj3!eAwZ)(;VD6Je6XBylnk5}{7qPK0Qso~RWfItlu#x&u9uMPhmqB>9a;(q;Bs zLKDRLB$%x>pek9$@|Vh@s0_lyo^L`RDhyIyAltQKb1L*p_&GPN(Ysn~(C~S3TIwV^skmg?T#Z&1pUVRlcA5rqlJ;`g>MW;!9 z4b?xOn$6JPf?E4T7-_;lk}~u_-pPT{mL0&#$@vgV63KB5TO`S)_(Vf5x)lMgQSFX%W$!OQOpgPya% zoO@|!^5KXi??!Hn_GSSLmtwb}{3z#0v2YQa2|?CPRDAmLvh9*snGeBH-ixlVsJv(y zdk?86aXYqyY<5aK?RJP(S_lDBQY+Q;>+Vbf9+AnWA_$4;c`wk~o_jk)dlC4_f5V+} zwEq@Cpt0W-vg_#H;}O-O*rN1=)CZi3{1S+f_aHe(d$9!KjgTzfD21_V_dleszG!0^ z*gfaV=j`)YIgz4yK1{SUBC&yp z5br%?78fe;1@ngr2$uIF`-<3J0S%T9nyLbF<2UbJvt<$yVr z3+H++=qI$Ug%Bjw)7;KzjAS(BF`DlfjdYBrI7Z_eqq&XISjK1|V>Ej)8nGBnSByp~ zMspOS35wCI#ArTZH2E-^YnYjUW*9~@3$qdQBCt#Ki27tfQvstHfYJ5O=niLeYqJ~z zx|dlV0bQ<)ZdFG2D4Rnd)1GuSGP(s>0|C8W8NFs0y;Km8iEh^#Ghle JZ8dN~`5*TSqeuV% diff --git a/core/templates/core/customer_statement.html b/core/templates/core/customer_statement.html index 0f755ca..7c23d2f 100644 --- a/core/templates/core/customer_statement.html +++ b/core/templates/core/customer_statement.html @@ -3,11 +3,44 @@ {% block content %}
-
+

Customer Statement

+ {% if selected_customer %} + + {% endif %}
-
+ + +
Filter
@@ -40,28 +73,45 @@
{% if selected_customer %} -
+
-
Statement for: {{ selected_customer.name }}
+
+

Customer Statement

+
+
+
{{ selected_customer.name }}
+ {% if selected_customer.phone %}

Phone: {{ selected_customer.phone }}

{% endif %} +
+
+

Date Range:

+

+ {% if start_date %}{{ start_date }}{% else %}Start{% endif %} + to + {% if end_date %}{{ end_date }}{% else %}Present{% endif %} +

+
+
+
+
- + - - - + + + {% for sale in sales %} - - - - + + + + {% empty %} @@ -69,6 +119,14 @@ {% endfor %} + + + + + + + +
Date Invoice #TotalPaidBalanceTotalPaidBalance
{{ sale.created_at|date:"Y-m-d" }}{{ sale.invoice_number }}{{ sale.total_amount }}{{ sale.paid_amount }}{{ sale.balance_due }}{{ sale.invoice_number|default:sale.id }}{{ sale.total_amount }}{{ sale.paid_amount }}{{ sale.balance_due }}
Totals:{{ total_amount|floatformat:2 }}{{ total_paid|floatformat:2 }}{{ total_balance|floatformat:2 }}
diff --git a/core/templates/core/reports.html b/core/templates/core/reports.html index af7edff..d2a4683 100644 --- a/core/templates/core/reports.html +++ b/core/templates/core/reports.html @@ -58,6 +58,17 @@
+
@@ -121,6 +132,7 @@ .bg-success-soft { background-color: rgba(25, 135, 84, 0.1); } .bg-primary-soft { background-color: rgba(13, 110, 253, 0.1); } .bg-warning-soft { background-color: rgba(255, 193, 7, 0.1); } + .bg-info-soft { background-color: rgba(13, 202, 240, 0.1); } .lift-hover { transition: transform 0.2s, box-shadow 0.2s; } .lift-hover:hover { transform: translateY(-5px); box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important; } diff --git a/core/templates/core/supplier_statement.html b/core/templates/core/supplier_statement.html index 191622e..4a95fa5 100644 --- a/core/templates/core/supplier_statement.html +++ b/core/templates/core/supplier_statement.html @@ -3,11 +3,44 @@ {% block content %}
-
+

Supplier Statement

+ {% if selected_supplier %} + + {% endif %}
-
+ + +
Filter
@@ -40,28 +73,45 @@
{% if selected_supplier %} -
+
-
Statement for: {{ selected_supplier.name }}
+
+

Supplier Statement

+
+
+
{{ selected_supplier.name }}
+ {% if selected_supplier.phone %}

Phone: {{ selected_supplier.phone }}

{% endif %} +
+
+

Date Range:

+

+ {% if start_date %}{{ start_date }}{% else %}Start{% endif %} + to + {% if end_date %}{{ end_date }}{% else %}Present{% endif %} +

+
+
+
+
- + - - - + + + {% for purchase in purchases %} - - - - + + + + {% empty %} @@ -69,6 +119,14 @@ {% endfor %} + + + + + + + +
Date Ref #TotalPaidBalanceTotalPaidBalance
{{ purchase.created_at|date:"Y-m-d" }}{{ purchase.invoice_number }}{{ purchase.total_amount }}{{ purchase.paid_amount }}{{ purchase.balance_due }}{{ purchase.invoice_number|default:purchase.id }}{{ purchase.total_amount }}{{ purchase.paid_amount }}{{ purchase.balance_due }}
Totals:{{ total_amount|floatformat:2 }}{{ total_paid|floatformat:2 }}{{ total_balance|floatformat:2 }}
diff --git a/core/views.py b/core/views.py index 2af5d92..a594402 100644 --- a/core/views.py +++ b/core/views.py @@ -900,24 +900,42 @@ def customer_statement(request): selected_customer = None sales = [] + # Totals + total_amount = 0 + total_paid = 0 + total_balance = 0 + customer_id = request.GET.get('customer') start_date = request.GET.get('start_date') end_date = request.GET.get('end_date') if customer_id: selected_customer = get_object_or_404(Customer, id=customer_id) - sales = Sale.objects.filter(customer=selected_customer).order_by('-created_at') + sales = Sale.objects.filter(customer=selected_customer).order_by('created_at') if start_date: sales = sales.filter(created_at__date__gte=start_date) if end_date: sales = sales.filter(created_at__date__lte=end_date) + # Calculate totals + aggregates = sales.aggregate( + sum_total=Sum('total_amount'), + sum_paid=Sum('paid_amount'), + sum_balance=Sum('balance_due') + ) + total_amount = aggregates['sum_total'] or 0 + total_paid = aggregates['sum_paid'] or 0 + total_balance = aggregates['sum_balance'] or 0 + context = { 'customers': customers, 'selected_customer': selected_customer, 'sales': sales, 'start_date': start_date, - 'end_date': end_date + 'end_date': end_date, + 'total_amount': total_amount, + 'total_paid': total_paid, + 'total_balance': total_balance } return render(request, 'core/customer_statement.html', context) @@ -927,24 +945,42 @@ def supplier_statement(request): selected_supplier = None purchases = [] + # Totals + total_amount = 0 + total_paid = 0 + total_balance = 0 + supplier_id = request.GET.get('supplier') start_date = request.GET.get('start_date') end_date = request.GET.get('end_date') if supplier_id: selected_supplier = get_object_or_404(Supplier, id=supplier_id) - purchases = Purchase.objects.filter(supplier=selected_supplier).order_by('-created_at') + purchases = Purchase.objects.filter(supplier=selected_supplier).order_by('created_at') if start_date: purchases = purchases.filter(created_at__date__gte=start_date) if end_date: purchases = purchases.filter(created_at__date__lte=end_date) + # Calculate totals + aggregates = purchases.aggregate( + sum_total=Sum('total_amount'), + sum_paid=Sum('paid_amount'), + sum_balance=Sum('balance_due') + ) + total_amount = aggregates['sum_total'] or 0 + total_paid = aggregates['sum_paid'] or 0 + total_balance = aggregates['sum_balance'] or 0 + context = { 'suppliers': suppliers, 'selected_supplier': selected_supplier, 'purchases': purchases, 'start_date': start_date, - 'end_date': end_date + 'end_date': end_date, + 'total_amount': total_amount, + 'total_paid': total_paid, + 'total_balance': total_balance } return render(request, 'core/supplier_statement.html', context)