diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc
index 9b2cfe0..1a0c2bf 100644
Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ
diff --git a/config/settings.py b/config/settings.py
index 8d6caf3..a93a9a6 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -156,23 +156,11 @@ USE_I18N = True
USE_TZ = True
-# Script Name (for subpath deployment)
-FORCE_SCRIPT_NAME = os.getenv("FORCE_SCRIPT_NAME")
-
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
-# Default to 'static/' but allow override or adjust based on FORCE_SCRIPT_NAME
-if FORCE_SCRIPT_NAME:
- # Ensure FORCE_SCRIPT_NAME starts with / and ends without / for consistency if needed,
- # but normally users provide "/meezan".
- # We strip trailing slash from script name when forming static url
- _script_prefix = FORCE_SCRIPT_NAME.rstrip('/')
- STATIC_URL = os.getenv("STATIC_URL", f"{_script_prefix}/static/")
- MEDIA_URL = os.getenv("MEDIA_URL", f"{_script_prefix}/media/")
-else:
- STATIC_URL = os.getenv("STATIC_URL", "static/")
- MEDIA_URL = os.getenv("MEDIA_URL", "media/")
+STATIC_URL = os.getenv("STATIC_URL", "/static/")
+MEDIA_URL = os.getenv("MEDIA_URL", "/media/")
# Collect static into a separate folder; avoid overlapping with STATICFILES_DIRS.
STATIC_ROOT = BASE_DIR / 'staticfiles'
diff --git a/core/__pycache__/helpers.cpython-311.pyc b/core/__pycache__/helpers.cpython-311.pyc
new file mode 100644
index 0000000..658002a
Binary files /dev/null and b/core/__pycache__/helpers.cpython-311.pyc differ
diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc
index 366bd66..a8145bd 100644
Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ
diff --git a/core/__pycache__/utils.cpython-311.pyc b/core/__pycache__/utils.cpython-311.pyc
index a6171c4..7b4b15e 100644
Binary files a/core/__pycache__/utils.cpython-311.pyc and b/core/__pycache__/utils.cpython-311.pyc differ
diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc
index 8244ec0..9bb892f 100644
Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ
diff --git a/core/helpers.py b/core/helpers.py
new file mode 100644
index 0000000..24477cc
--- /dev/null
+++ b/core/helpers.py
@@ -0,0 +1,122 @@
+def number_to_words_en(number):
+ """
+ Converts a number to English words.
+ Handles decimals up to 3 places.
+ """
+ if number == 0:
+ return "Zero"
+
+ units = ["", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten",
+ "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"]
+ tens = ["", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"]
+ thousands = ["", "Thousand", "Million", "Billion"]
+
+ def _convert_less_than_thousand(num):
+ res = ""
+ if num >= 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 "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 %}