From 89eb33ae7787a21312a369630743ef46e82ad700 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 8 Feb 2026 16:57:39 +0000 Subject: [PATCH] Autosave: 20260208-165737 --- config/__pycache__/settings.cpython-311.pyc | Bin 6879 -> 6546 bytes config/settings.py | 16 +- core/__pycache__/helpers.cpython-311.pyc | Bin 0 -> 5790 bytes core/__pycache__/urls.cpython-311.pyc | Bin 13508 -> 13756 bytes core/__pycache__/utils.cpython-311.pyc | Bin 5979 -> 6012 bytes core/__pycache__/views.cpython-311.pyc | Bin 62449 -> 69268 bytes core/helpers.py | 122 ++++++++ core/templates/core/purchase_edit.html | 299 ++++++++++++++++++++ core/templates/core/purchases.html | 3 + core/urls.py | 2 + core/utils.py | 139 ++++----- core/views.py | 192 ++++++++++++- move_project.py | 34 +-- requirements.txt | 3 +- test_write.txt | 1 + 15 files changed, 672 insertions(+), 139 deletions(-) create mode 100644 core/__pycache__/helpers.cpython-311.pyc create mode 100644 core/helpers.py create mode 100644 core/templates/core/purchase_edit.html create mode 100644 test_write.txt diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 9b2cfe0e9bf2f2413477ec416e3b95eb40dadc53..1a0c2bfcbb74ee6d7cd574be134783fb8594f51b 100644 GIT binary patch delta 705 zcmca_I?0%CIWI340}z-sb!1vdOyrYbn!~zL;~P_bit#cg28Pu@3;|K9Obn^aSzyH= zkra~@Q#2K7shlZhRon~=QR+aJIhdsZWLbb%K-v;YTY>0w<|s`x6RlHh;3nFGZH>}G zQ(~LK1y=%5p$$}_gQm(Z#eQ-Pi)^Sanz%y>FPd&WG!>2*D)iA*I8A=XVk>NbChDBx zGWi3GwWlGPxNC|VnvF&&?kx;xsytGSP*p83p3KN9%*>GDIr#>wyt+xMVG&b`SCm1D zca&+0Pm~!Do2U3jS#UC>Fs3l)SVmc|&tuuVM|1;|Iu`?dno3nu`%g0sv!JiJ$-g delta 1012 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 literal 0 HcmV?d00001 diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 366bd668829734226a7d96a06ff0e61d4cc98a76..a8145bded291097fe7460eab590b96f1d74eed39 100644 GIT binary patch delta 2456 zcmZ{mSxg&87=SVUDQk{ElaPd6u5kF`aKI#`ki#S~!8VseIBX7Mf-&Mv-1sq-LpK%Fc`_>44(lu4rKNiINZ$gWkW2G z6qDE3$0TmpyYY9{*G7_SNXvN~F~(*w#DhO2gSMafv$-eFez}|efiWfJBmTkbwjnq_ z_G@>F?Y9k<+m~kD7oVFxdp}(aG7y_{34iH`z%J>ER9go)u$RVK^Rj z%*DsTvyo~&vaQQ59@@B<|A%B)*tk<#WJsMpL_RBfWuv9sY)GHJ%x5rtg+wd5GMD+S zfq!O>=8%UK#|i_fIjN68x^Z%O=W@^M11k#@EGk$SSTtjRl$aGmfE+d#@-}EL=Ix@n z@P%X_EsG?~d(*SU{f^}QXpXqgO-VEHZ1Sz1w}#g8DL7SdGH_}yis()er6%)Q8ne3S zsR0UuDhx6h)OHDB_w$9k0I9c>B!spPM^SD?*dE(@28AXSniw=``64VonFPzTtVJm_ zsL;TmK~qGy0K@xk9b~(#)ZwDgtU@z`W=*VzRO2EHadD8{fLN zNTE@MMh1;qg$SFs5-2M@tgx0gv{LY>;9=m=ECI6HT7qk6C%N@}M0>|pS|$ed8n#BwUpdH>n)MI`w+e0sZq3K- z$HGpxCwtmO+b-I;XoSVhDA1cgo&IvI3oLk3|Clh5sn6hasw-*X5f zj1ohoVmga(-ara$a%B`Lrl&1mjWF|Z>IqXARbiCDs5XJh1rn=Nq>BiX$Rf;Kr;7TPos^mm-s#u-lsasVjSc$NrI{LDQf>#AE1Fv=h zm6N(6mYAP{VG1D?LJUHh8x^mv6tCv7{ro$0JS~F{l{Q@|T1{gm^(4PKTWZJ9X;M?I zNZkl$h`(A%^dR)=okZS7+T2T_PlY}PeOezX{kmdZt6=sM)XVqLhzcVNMzjDb!@Bb9 zP79$iq8svxn-$bk@!7fWv zNOAINjUrt_m?n2Q%phDA8Ll8KiVRl~mN3ILr0dBHrUZh^NP-SoYBq`jwvZ(brx04n zyBs_SUb5+st9%G;#;rP6tj;k@6Fq*KLbnRt47#;;R62|#t6Hw=MC#Hzh>hW-jXnx( zDzq_Z)BLFP8p(Iod{ZA%zme>B=1T#jAbInEVj4ggG?Le9c}QPR3z6aNl^^P z$Y3om9YUO3ujTbYm?rPlDjhQjvwD{L^$0uUr~OkD;wr=$#I-q8=5@ugmd9)@wEY}~ zaTUfHjB5+1ERl>lMY@J?omAKHQX;s3`0M13X27!uTQ#z+9b}ar+7+WPp~3`%3GD2k1-NUXBxJT+qi7XVS{t h>qgKpBgAIldkbWntdUrCVeo*ON?idlW2^_n$*O^+r0STn=|tndsT zCvmM#b_@$HpX41O8Wpw`IIedxN7;G-uQC%A<7}Djgc}X;I|%0JWX^4 zOT4l;O)!H;ymDWfAY(<<=N7py%%U?4(ki4mq_tU6<}9TqSHRr?)|Y0GQ6a-2qs^1D zAfVIzqPR$O5x>+&TOzoGe|RNvncy<+sF%en1Xr=CUhZ8XxMmIF+jxOD^|Ry!1`8@I za9Ge*NqNP#b=4)=NnYk>F9#WfR0wehX>B%hncDFkJ->q>fS>9RBnaU{eXuaWF)K;V z#(Alp6k-roA)2xC-MO#poTd0Tx_EPh#qdLakvLDCXYsyY7S9o!$DjT3>IH&J)@kN`d5Il) z;=Qs=%4JJ2FQ9{ko*4WJDOWAUT-_elJI-K2g$WK5+6pPxEX8~#g1EfjDXvmiC%&;? z7Q4WFp1bj$4m|`%aj02ZjS%$0*8X~WA^XTUW*JZ4;9CdlAgi@a;rgzH7d K=ll)*zxO{(9%@Vg diff --git a/core/__pycache__/utils.cpython-311.pyc b/core/__pycache__/utils.cpython-311.pyc index a6171c4cc82b7a3a1a744862f0e341a18f04ebd7..7b4b15ee8b011959d8db70916884b82c0b709499 100644 GIT binary patch delta 1231 zcmbu7O-NKx6vxkfAM^1(#y9iE(LqBeu^h@FlgL5GC?;Bj1{#KLg1iQeTKQ%sTig*T zkrqMOT_}_=w6LNGqL5b6Vis~ohv^U%?{I>IT!&-FcKL5hjugYO0r@);Qh@fFn^q9wETr z1;VmTgS^*hRdpJ`o%Yy~u$0qhbj%mO(_|WM%EK^CqP6_75m&|C^}pUQLd1AxZ+M)s zsq9!fljh&ed4Ag$H4UdvEYUB*1^&jDR1K8Pe9Lz@dJVn-GD zP*>aU5%x0)0g!WE1VAnV&;-oQyv!p2SW%EGd`m5-F)d$H!rTh$OiNki6!mTM(7<&5 zK2R6=2QQ^jWnxYkf|znG0A^Ivc3B#gh6P2&j8UWszg0Bdu(Kta_&0kY;tXM_s9B_E z!=Tqjvy)?2GZ(YYgfoR1WN9hbp^Cd`<{yHw@HndUy%I~>E|xz1k)v+j9=dcahAf4l zKivC}M Ia|CDq0y*6HwEzGB delta 1264 zcmbu7Ur19?9LLYO=ic4F+wQ!ZV@#K3iIX-Gr8L(=G3cd+Bm@Z(=<%U7EAO`S;moY8 zryi0M6qvCmD~uw7h@dC;&|_~wVMXtOP>&(~?&h5il^!~m{m%Dyet&lUeEuZAB)w0( zUJb!mynEx~w*_w`*#*u6MA1ANK@@|alODl~2BZ1rf4U$7QlMEQ?E%uBNME@j=RzD_;M7@-yT)kaBV z;v%B*l2DSiZvnL7=1}F4Q1URuFjYaxDk??l)tn-OmJHg1smHsb>I!!4M#EE1qiJYgLSXw)oNZZCwG z#;+Qoh*i>NHOQ{c(|8)qkn?C7$AkQ_G4OiL2w{?WX7LU09aXU>riv5!LZ099bt}4k z2AbI!{?3;cb&#C+51DBY`I~TOb7);bQYSwaddR=~Q|6i#y??xHJw5vN)Q7Vlts$N; zmpbAn%(R782g6`1mX_`(l~yy|ODYNM=w2z^&wrXNZ8MFu5TJ$Zpaq@>Xb^YMA~w(> zZoTt=(6X(|UbIL|+N8w;o2ml~x{(&yp@j*+3n?jk^5S6%lXu}oWonfd{!&k3gMZcA z_x*zk9y1~?6O!&Q!Ku*>9O$!pl>?X?I9RZX+z8%l&DiJQapOky-9H=j%6M^d;&S0a z(azaZ{C%*8Z}^(I6lyVZ4g|Mr5NI_BKJ5m;ARlcW8|VfIf@n1r23rYg=>bxSndv@K zNn%I$NnqpHc5W~(*(YJL9m?!6{<}GWNBRD6JcY11=Xbt!u@kqhqq-TwU5>wpu@6I| EzgpJoTmS$7 diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 8244ec091df496e7bcae5a22fbea3fd0fe78b622..9bb892fa29b0c8a1efcb2943bac28189afa55628 100644 GIT binary patch delta 5909 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 100: + res += units[num // 100] + " Hundred " + num %= 100 + if num >= 20: + res += tens[num // 10] + " " + num %= 10 + if num > 0: + res += units[num] + return res.strip() + + try: + parts = str(float(number)).split('.') + integer_part = int(parts[0]) + fractional_part = int(parts[1]) if len(parts) > 1 else 0 + except ValueError: + return "Invalid Number" + + res = "" + if integer_part == 0: + res = "Zero" + else: + idx = 0 + while integer_part > 0: + if integer_part % 1000 != 0: + res = _convert_less_than_thousand(integer_part % 1000) + " " + thousands[idx] + " " + res + integer_part //= 1000 + idx += 1 + + words = res.strip() + + if fractional_part > 0: + frac_str = parts[1] + denom = 10 ** len(frac_str) + words += f" and {fractional_part}/{denom}" + + return words + +def number_to_words_ar(number): + return number_to_words_en(number) + +def send_whatsapp_message(phone, message): + try: + import requests + from .models import SystemSetting + + settings = SystemSetting.objects.first() + if not settings or not settings.wablas_enabled: + return False, "WhatsApp gateway is disabled." + + if not settings.wablas_token or not settings.wablas_server_url: + return False, "Wablas configuration is incomplete." + + phone = ''.join(filter(str.isdigit, str(phone))) + server_url = settings.wablas_server_url.rstrip('/') + url = f"{server_url}/api/send-message" + + headers = { + "Authorization": settings.wablas_token, + "Secret": settings.wablas_secret_key + } + + payload = {"phone": phone, "message": message} + + response = requests.post(url, data=payload, headers=headers, timeout=10) + data = response.json() + if response.status_code == 200 and data.get('status') == True: + return True, "Message sent successfully." + else: + return False, data.get('message', 'Unknown error from Wablas.') + except Exception as e: + return False, str(e) + +def send_whatsapp_document(phone, document_url, caption=""): + try: + import requests + from .models import SystemSetting + + settings = SystemSetting.objects.first() + if not settings or not settings.wablas_enabled: + return False, "WhatsApp gateway is disabled." + + if not settings.wablas_token or not settings.wablas_server_url: + return False, "Wablas configuration is incomplete." + + phone = ''.join(filter(str.isdigit, str(phone))) + server_url = settings.wablas_server_url.rstrip('/') + url = f"{server_url}/api/send-document" + + headers = { + "Authorization": settings.wablas_token, + "Secret": settings.wablas_secret_key + } + + payload = { + "phone": phone, + "document": document_url, + "caption": caption + } + + response = requests.post(url, data=payload, headers=headers, timeout=15) + data = response.json() + if response.status_code == 200 and data.get('status') == True: + return True, "Document sent successfully." + else: + return False, data.get('message', 'Unknown error from Wablas.') + except Exception as e: + return False, str(e) \ No newline at end of file diff --git a/core/templates/core/purchase_edit.html b/core/templates/core/purchase_edit.html new file mode 100644 index 0000000..5fd5240 --- /dev/null +++ b/core/templates/core/purchase_edit.html @@ -0,0 +1,299 @@ +{% extends 'base.html' %} +{% load i18n l10n %} + +{% block title %}{% trans "Edit Purchase" %} | {{ site_settings.business_name }}{% endblock %} + +{% block content %} +
+
+ +
+
+
+
+
{% trans "Edit Purchase Invoice" %} #[[ invoiceNumber || '{{ purchase.id }}' ]]
+ + {% trans "Back to List" %} + +
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+ +
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
{% trans "Product" %}{% trans "Cost Price" %}{% trans "Quantity" %}{% trans "Total" %}
+
[[ item.name_en ]]
+
[[ item.sku ]]
+
+ + + + [[ currencySymbol ]][[ (parseFloat(item.cost_price) * parseFloat(item.quantity)).toFixed(decimalPlaces) ]] + +
+ {% trans "Search and add products to this purchase." %} +
+
+
+
+
+ + +
+
+
+
{% trans "Purchase Summary" %}
+ +
+ {% trans "Total Amount" %} + [[ currencySymbol ]][[ grandTotal.toFixed(decimalPlaces) ]] +
+ +
+ +
+

{% trans "Grand Total" %}

+

[[ currencySymbol ]][[ grandTotal.toFixed(decimalPlaces) ]]

+
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+
+
+
+ + + +{% localize off %} + +{% endlocalize %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/purchases.html b/core/templates/core/purchases.html index 2dd1e00..f67b2cc 100644 --- a/core/templates/core/purchases.html +++ b/core/templates/core/purchases.html @@ -69,6 +69,9 @@ + + + {% if purchase.balance_due > 0 %}