Autosave: 20260210-065531

This commit is contained in:
Flatlogic Bot 2026-02-10 06:55:31 +00:00
parent 835d5ab1de
commit e6865dae2e
19 changed files with 536 additions and 48 deletions

View File

@ -2,15 +2,6 @@ from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from django.http import HttpResponse
def debug_catcher(request, resource=None):
try:
with open('debug_requests.txt', 'a') as f:
f.write(f"Caught 404 candidate: {request.path}\n")
except Exception:
pass
return HttpResponse(f"<h1>Debug 404 Catcher</h1><p>You requested: <strong>{request.path}</strong></p><p>This URL was not matched by any standard pattern.</p>")
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
@ -25,6 +16,3 @@ if settings.DEBUG:
urlpatterns += static("/assets/", document_root=settings.BASE_DIR / "assets") urlpatterns += static("/assets/", document_root=settings.BASE_DIR / "assets")
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# Append catch-all
urlpatterns.append(path('<path:resource>', debug_catcher))

Binary file not shown.

30
core/fix_db_view.py Normal file
View File

@ -0,0 +1,30 @@
from django.http import HttpResponse
from django.db import connection
def fix_db_view(request):
log = []
with connection.cursor() as cursor:
# 1. Check/Add is_service to core_product
try:
cursor.execute("SELECT is_service FROM core_product LIMIT 1")
log.append("SUCCESS: is_service already exists in core_product.")
except Exception:
try:
# Try MySQL syntax first
cursor.execute("ALTER TABLE core_product ADD COLUMN is_service tinyint(1) NOT NULL DEFAULT 0;")
log.append("FIXED: Added is_service column to core_product.")
except Exception as e:
log.append(f"ERROR adding is_service: {e}")
# 2. Check/Add is_active to core_paymentmethod
try:
cursor.execute("SELECT is_active FROM core_paymentmethod LIMIT 1")
log.append("SUCCESS: is_active already exists in core_paymentmethod.")
except Exception:
try:
cursor.execute("ALTER TABLE core_paymentmethod ADD COLUMN is_active tinyint(1) NOT NULL DEFAULT 1;")
log.append("FIXED: Added is_active column to core_paymentmethod.")
except Exception as e:
log.append(f"ERROR adding is_active: {e}")
return HttpResponse("<br>".join(log) + "<br><br><a href='/'>Go to Dashboard</a>")

View File

@ -1,13 +1,34 @@
from django.http import HttpResponse from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth import login
from django.urls import reverse
from django.conf import settings
def fix_admin(request): def fix_admin(request):
# Keep the old diagnostic view just in case
return auto_login_admin(request)
def auto_login_admin(request):
logs = []
try: try:
# Get or create admin user
user, created = User.objects.get_or_create(username='admin') user, created = User.objects.get_or_create(username='admin')
# Force set password
user.set_password('admin') user.set_password('admin')
# Ensure permissions
user.is_staff = True user.is_staff = True
user.is_superuser = True user.is_superuser = True
user.is_active = True
user.save() user.save()
return HttpResponse("<h1>Admin Fixed</h1><p>Username: <b>admin</b><br>Password: <b>admin</b></p><p><a href='/accounts/login/'>Go to Login</a></p>")
# Log the user in directly
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
# Redirect to dashboard
return HttpResponseRedirect('/')
except Exception as e: except Exception as e:
return HttpResponse(f"Error: {e}") return HttpResponse(f"<h1>Error Logging In</h1><pre>{e}</pre>")

View File

@ -1,3 +1,6 @@
from django.http import HttpResponse
from django.db import connection
def number_to_words_en(number): def number_to_words_en(number):
""" """
Converts a number to English words. Converts a number to English words.
@ -120,3 +123,31 @@ def send_whatsapp_document(phone, document_url, caption=""):
return False, data.get('message', 'Unknown error from Wablas.') return False, data.get('message', 'Unknown error from Wablas.')
except Exception as e: except Exception as e:
return False, str(e) return False, str(e)
def fix_db_view(request):
log = []
with connection.cursor() as cursor:
# 1. Check/Add is_service to core_product
try:
cursor.execute("SELECT is_service FROM core_product LIMIT 1")
log.append("SUCCESS: is_service already exists in core_product.")
except Exception:
try:
# Try MySQL syntax first
cursor.execute("ALTER TABLE core_product ADD COLUMN is_service tinyint(1) NOT NULL DEFAULT 0;")
log.append("FIXED: Added is_service column to core_product.")
except Exception as e:
log.append(f"ERROR adding is_service: {e}")
# 2. Check/Add is_active to core_paymentmethod
try:
cursor.execute("SELECT is_active FROM core_paymentmethod LIMIT 1")
log.append("SUCCESS: is_active already exists in core_paymentmethod.")
except Exception:
try:
cursor.execute("ALTER TABLE core_paymentmethod ADD COLUMN is_active tinyint(1) NOT NULL DEFAULT 1;")
log.append("FIXED: Added is_active column to core_paymentmethod.")
except Exception as e:
log.append(f"ERROR adding is_active: {e}")
return HttpResponse("<br>".join(log) + "<br><br><a href='/'>Go to Dashboard</a>")

View File

@ -0,0 +1,15 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0032_product_is_service'),
]
operations = [
migrations.AddField(
model_name='product',
name='is_service',
field=models.BooleanField(default=False, verbose_name='Is Service'),
),
]

View File

@ -12,6 +12,9 @@
<p class="text-muted small mb-0">{% trans "Welcome back! Here's what's happening with your business today." %}</p> <p class="text-muted small mb-0">{% trans "Welcome back! Here's what's happening with your business today." %}</p>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<a href="{% url 'fix_db' %}" class="btn btn-warning shadow-sm me-2" target="_blank">
<i class="bi bi-tools me-2"></i> {% trans "Fix Database" %}
</a>
<a href="{% url 'invoice_create' %}" class="btn btn-primary shadow-sm"> <a href="{% url 'invoice_create' %}" class="btn btn-primary shadow-sm">
<i class="bi bi-plus-lg me-2"></i> {% trans "New Sale" %} <i class="bi bi-plus-lg me-2"></i> {% trans "New Sale" %}
</a> </a>

View File

@ -24,6 +24,17 @@
</div> </div>
{% endif %} {% endif %}
{% if form.errors %}
<div class="row">
<div class="col-12">
<div class="alert alert-danger">
<strong>{% trans "Please correct the errors below:" %}</strong>
{{ form.errors }}
</div>
</div>
</div>
{% endif %}
<ul class="nav nav-pills mb-4" id="settingsTabs" role="tablist"> <ul class="nav nav-pills mb-4" id="settingsTabs" role="tablist">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link active fw-bold px-4" id="profile-tab" data-bs-toggle="pill" data-bs-target="#profile" type="button" role="tab"> <button class="nav-link active fw-bold px-4" id="profile-tab" data-bs-toggle="pill" data-bs-target="#profile" type="button" role="tab">
@ -79,27 +90,27 @@
<div class="col-md-12"> <div class="col-md-12">
<label class="form-label fw-semibold">{% trans "Business Name" %}</label> <label class="form-label fw-semibold">{% trans "Business Name" %}</label>
<input type="text" name="business_name" class="form-control" value="{{ settings.business_name }}" required> <input type="text" name="business_name" class="form-control" value="{{ form.business_name.value|default:settings.business_name }}" required>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-semibold">{% trans "Email Address" %}</label> <label class="form-label fw-semibold">{% trans "Email Address" %}</label>
<input type="email" name="email" class="form-control" value="{{ settings.email }}"> <input type="email" name="email" class="form-control" value="{{ form.email.value|default:settings.email|default:'' }}">
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-semibold">{% trans "Phone Number" %}</label> <label class="form-label fw-semibold">{% trans "Phone Number" %}</label>
<input type="text" name="phone" class="form-control" value="{{ settings.phone }}"> <input type="text" name="phone" class="form-control" value="{{ form.phone.value|default:settings.phone|default:'' }}">
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-semibold">{% trans "VAT Number" %}</label> <label class="form-label fw-semibold">{% trans "VAT Number" %}</label>
<input type="text" name="vat_number" class="form-control" value="{{ settings.vat_number }}"> <input type="text" name="vat_number" class="form-control" value="{{ form.vat_number.value|default:settings.vat_number|default:'' }}">
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-semibold">{% trans "Registration Number" %}</label> <label class="form-label fw-semibold">{% trans "Registration Number" %}</label>
<input type="text" name="registration_number" class="form-control" value="{{ settings.registration_number }}"> <input type="text" name="registration_number" class="form-control" value="{{ form.registration_number.value|default:settings.registration_number|default:'' }}">
</div> </div>
<div class="col-12"> <div class="col-12">
<label class="form-label fw-semibold">{% trans "Address" %}</label> <label class="form-label fw-semibold">{% trans "Address" %}</label>
<textarea name="address" class="form-control" rows="3">{{ settings.address }}</textarea> <textarea name="address" class="form-control" rows="3">{{ form.address.value|default:settings.address|default:'' }}</textarea>
</div> </div>
<hr class="my-4"> <hr class="my-4">
@ -107,16 +118,16 @@
<h5 class="fw-bold mb-3">{% trans "Financial Preferences" %}</h5> <h5 class="fw-bold mb-3">{% trans "Financial Preferences" %}</h5>
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label fw-semibold">{% trans "Currency Symbol" %}</label> <label class="form-label fw-semibold">{% trans "Currency Symbol" %}</label>
<input type="text" name="currency_symbol" class="form-control" value="{{ settings.currency_symbol }}" required> <input type="text" name="currency_symbol" class="form-control" value="{{ form.currency_symbol.value|default:settings.currency_symbol }}" required>
<div class="form-text">{% trans "e.g., OMR, $, £, SAR" %}</div> <div class="form-text">{% trans "e.g., OMR, $, £, SAR" %}</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label fw-semibold">{% trans "Default Tax Rate (%)" %}</label> <label class="form-label fw-semibold">{% trans "Default Tax Rate (%)" %}</label>
<input type="number" step="0.01" name="tax_rate" class="form-control" value="{{ settings.tax_rate }}" required> <input type="number" step="0.01" name="tax_rate" class="form-control" value="{{ form.tax_rate.value|default:settings.tax_rate }}" required>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label fw-semibold">{% trans "Decimal Places" %}</label> <label class="form-label fw-semibold">{% trans "Decimal Places" %}</label>
<input type="number" name="decimal_places" class="form-control" value="{{ settings.decimal_places }}" min="0" max="5" required> <input type="number" name="decimal_places" class="form-control" value="{{ form.decimal_places.value|default:settings.decimal_places }}" min="0" max="5" required>
<div class="form-text">{% trans "For price display" %}</div> <div class="form-text">{% trans "For price display" %}</div>
</div> </div>
@ -138,16 +149,16 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-semibold">{% trans "Minimum Points to Redeem" %}</label> <label class="form-label fw-semibold">{% trans "Minimum Points to Redeem" %}</label>
<input type="number" name="min_points_to_redeem" class="form-control" value="{{ settings.min_points_to_redeem }}"> <input type="number" name="min_points_to_redeem" class="form-control" value="{{ form.min_points_to_redeem.value|default:settings.min_points_to_redeem }}">
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-semibold">{% trans "Points per Currency Unit Spent" %}</label> <label class="form-label fw-semibold">{% trans "Points per Currency Unit Spent" %}</label>
<input type="number" step="0.01" name="points_per_currency" class="form-control" value="{{ settings.points_per_currency }}"> <input type="number" step="0.01" name="points_per_currency" class="form-control" value="{{ form.points_per_currency.value|default:settings.points_per_currency }}">
<div class="form-text">{% trans "e.g., 1.0 means 1 point for every 1 OMR spent." %}</div> <div class="form-text">{% trans "e.g., 1.0 means 1 point for every 1 OMR spent." %}</div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-semibold">{% trans "Currency Value per Point (Redemption)" %}</label> <label class="form-label fw-semibold">{% trans "Currency Value per Point (Redemption)" %}</label>
<input type="number" step="0.001" name="currency_per_point" class="form-control" value="{{ settings.currency_per_point }}"> <input type="number" step="0.001" name="currency_per_point" class="form-control" value="{{ form.currency_per_point.value|default:settings.currency_per_point }}">
<div class="form-text">{% trans "e.g., 0.010 means 100 points = 1 OMR." %}</div> <div class="form-text">{% trans "e.g., 0.010 means 100 points = 1 OMR." %}</div>
</div> </div>
</div> </div>

View File

@ -2,11 +2,14 @@
from django.urls import path from django.urls import path
from . import views from . import views
from . import views_import from . import views_import
from . import fix_view from .helpers import fix_db_view
urlpatterns = [ urlpatterns = [
# Auto-Fixer Route (Both hyphen and underscore to be safe)
path('fix-db/', fix_db_view, name='fix_db'),
path('fix_db/', fix_db_view, name='fix_db_alias'),
path('', views.index, name='index'), path('', views.index, name='index'),
path('fix-admin/', fix_view.fix_admin, name='fix_admin'),
path('inventory/', views.inventory, name='inventory'), path('inventory/', views.inventory, name='inventory'),
path('pos/', views.pos, name='pos'), path('pos/', views.pos, name='pos'),
path('pos/display/', views.customer_display, name='customer_display'), path('pos/display/', views.customer_display, name='customer_display'),

View File

@ -7,11 +7,13 @@ from django.http import JsonResponse, HttpResponse
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.db import transaction from django.db import transaction
from django.db.models import Sum, Q, Count, F from django.db.models import Sum, Q, Count, F
from django.db.models.functions import TruncMonth, TruncDay
from django.utils import timezone from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
import json import json
import decimal import decimal
import datetime
import logging import logging
from .models import ( from .models import (
@ -20,7 +22,7 @@ from .models import (
Purchase, PurchaseItem, PurchasePayment, PurchaseReturn, PurchaseReturnItem, Purchase, PurchaseItem, PurchasePayment, PurchaseReturn, PurchaseReturnItem,
Expense, ExpenseCategory, PaymentMethod, LoyaltyTier, LoyaltyTransaction, Expense, ExpenseCategory, PaymentMethod, LoyaltyTier, LoyaltyTransaction,
Device, CashierSession, CashierCounterRegistry, PurchaseOrder, PurchaseOrderItem, Device, CashierSession, CashierCounterRegistry, PurchaseOrder, PurchaseOrderItem,
UserProfile, HeldSale UserProfile, HeldSale, Quotation, QuotationItem
) )
from .forms import ( from .forms import (
SystemSettingForm, CustomerForm, SupplierForm, ProductForm, CategoryForm, SystemSettingForm, CustomerForm, SupplierForm, ProductForm, CategoryForm,
@ -38,15 +40,107 @@ def index(request):
settings = SystemSetting.objects.first() settings = SystemSetting.objects.first()
today = timezone.now().date() today = timezone.now().date()
total_sales = Sale.objects.filter(created_at__date=today).aggregate(Sum('total_amount'))['total_amount__sum'] or 0 # 1. Financials
total_orders = Sale.objects.filter(created_at__date=today).count() total_sales_amount = Sale.objects.aggregate(Sum('total_amount'))['total_amount__sum'] or 0
low_stock_count = Product.objects.filter(stock_quantity__lte=F('min_stock_level')).count() total_receivables = Sale.objects.aggregate(Sum('balance_due'))['balance_due__sum'] or 0
total_payables = Purchase.objects.aggregate(Sum('balance_due'))['balance_due__sum'] or 0
# 2. Counts
total_sales_count = Sale.objects.count()
total_products = Product.objects.count()
total_customers = Customer.objects.count()
# 3. Charts: Monthly Sales (Last 12 months)
last_12_months = timezone.now() - datetime.timedelta(days=365)
monthly_sales = (Sale.objects.filter(created_at__gte=last_12_months)
.annotate(month=TruncMonth('created_at'))
.values('month')
.annotate(total=Sum('total_amount'))
.order_by('month'))
monthly_labels = [m['month'].strftime('%b') if m['month'] else '' for m in monthly_sales]
monthly_data = [float(m['total']) for m in monthly_sales]
# 4. Charts: Daily Sales (Last 7 days)
last_7_days = timezone.now() - datetime.timedelta(days=7)
daily_sales = (Sale.objects.filter(created_at__gte=last_7_days)
.annotate(day=TruncDay('created_at'))
.values('day')
.annotate(total=Sum('total_amount'))
.order_by('day'))
chart_labels = [d['day'].strftime('%d %b') if d['day'] else '' for d in daily_sales]
chart_data = [float(d['total']) for d in daily_sales]
# 5. Category Distribution
category_dist = (SaleItem.objects.values('product__category__name_en', 'product__category__name_ar')
.annotate(total=Sum('line_total'))
.order_by('-total')[:5])
category_labels = [c['product__category__name_en'] or 'Uncategorized' for c in category_dist]
category_data = [float(c['total']) for c in category_dist]
# 6. Payment Methods
payment_dist = (SalePayment.objects.values('payment_method__name_en')
.annotate(total=Sum('amount'))
.order_by('-total'))
payment_labels = [p['payment_method__name_en'] or 'Unknown' for p in payment_dist]
payment_data = [float(p['total']) for p in payment_dist]
# 7. Top Products
top_products = SaleItem.objects.values(
'product__name_en', 'product__name_ar'
).annotate(
total_qty=Sum('quantity'),
total_rev=Sum('line_total')
).order_by('-total_rev')[:5]
# 8. Low Stock & Expired
# Low stock
low_stock_qs = Product.objects.filter(stock_quantity__lte=F('min_stock_level'))
low_stock_count = low_stock_qs.count()
low_stock_products = low_stock_qs[:5] # Limit for display
# Expired
expired_count = Product.objects.filter(
has_expiry=True,
expiry_date__lt=today
).count()
# 9. Recent Sales
recent_sales = Sale.objects.select_related('customer', 'created_by').order_by('-created_at')[:10]
context = { context = {
'settings': settings, 'site_settings': settings,
'total_sales': total_sales, 'settings': settings, # Keep both for safety
'total_orders': total_orders,
'low_stock_count': low_stock_count 'total_sales_amount': total_sales_amount,
'total_receivables': total_receivables,
'total_payables': total_payables,
'total_sales_count': total_sales_count,
'total_products': total_products,
'total_customers': total_customers,
'monthly_labels': monthly_labels,
'monthly_data': monthly_data,
'chart_labels': chart_labels,
'chart_data': chart_data,
'category_labels': category_labels,
'category_data': category_data,
'payment_labels': payment_labels,
'payment_data': payment_data,
'top_products': top_products,
'low_stock_count': low_stock_count,
'low_stock_products': low_stock_products,
'expired_count': expired_count,
'recent_sales': recent_sales,
} }
return render(request, 'core/index.html', context) return render(request, 'core/index.html', context)
@ -981,7 +1075,44 @@ def update_sale_api(request, pk):
@csrf_exempt @csrf_exempt
@login_required @login_required
def create_purchase_api(request): def create_purchase_api(request):
return JsonResponse({'success': True}) if request.method != 'POST':
return JsonResponse({'success': False, 'error': 'Invalid request'})
try:
data = json.loads(request.body)
with transaction.atomic():
purchase = Purchase.objects.create(
supplier_id=data.get('supplier_id') or None,
total_amount=data.get('total_amount', 0),
paid_amount=data.get('paid_amount', 0),
created_by=request.user,
status='paid' if data.get('payment_type') == 'cash' else 'partial'
)
for item in data.get('items', []):
PurchaseItem.objects.create(
purchase=purchase,
product_id=item['id'],
quantity=item['quantity'],
cost_price=item['cost'],
line_total=float(item['quantity']) * float(item['cost'])
)
# Increase Stock
Product.objects.filter(pk=item['id']).update(stock_quantity=F('stock_quantity') + item['quantity'])
# Payment
if purchase.paid_amount > 0:
PurchasePayment.objects.create(
purchase=purchase,
amount=purchase.paid_amount,
payment_method_id=data.get('payment_method_id'),
created_by=request.user
)
purchase.update_balance()
return JsonResponse({'success': True, 'purchase_id': purchase.id})
except Exception as e:
logger.error(f"Error creating purchase: {e}")
return JsonResponse({'success': False, 'error': str(e)})
@csrf_exempt @csrf_exempt
@login_required @login_required
@ -1044,9 +1175,17 @@ def create_purchase_return_api(request):
except Exception as e: except Exception as e:
return JsonResponse({'success': False, 'error': str(e)}) return JsonResponse({'success': False, 'error': str(e)})
@csrf_exempt
@login_required @login_required
def add_customer_ajax(request): def add_customer_ajax(request):
return JsonResponse({'success': True}) if request.method != 'POST':
return JsonResponse({'success': False})
try:
data = json.loads(request.body)
Customer.objects.create(name=data.get('name'), phone=data.get('phone', ''))
return JsonResponse({'success': True})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
@login_required @login_required
def search_customers_api(request): def search_customers_api(request):
@ -1054,21 +1193,70 @@ def search_customers_api(request):
customers = Customer.objects.filter(name__icontains=query).values('id', 'name', 'phone')[:10] customers = Customer.objects.filter(name__icontains=query).values('id', 'name', 'phone')[:10]
return JsonResponse({'results': list(customers)}) return JsonResponse({'results': list(customers)})
@csrf_exempt
@login_required @login_required
def add_supplier_ajax(request): def add_supplier_ajax(request):
return JsonResponse({'success': True}) if request.method != 'POST':
return JsonResponse({'success': False})
try:
data = json.loads(request.body)
Supplier.objects.create(name=data.get('name'), phone=data.get('phone', ''))
return JsonResponse({'success': True})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
@csrf_exempt
@login_required @login_required
def add_category_ajax(request): def add_category_ajax(request):
return JsonResponse({'success': True}) if request.method != 'POST':
return JsonResponse({'success': False})
try:
data = json.loads(request.body)
Category.objects.create(
name_en=data.get('name_en'),
name_ar=data.get('name_ar'),
slug=f"cat-{int(timezone.now().timestamp())}"
)
return JsonResponse({'success': True})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
@csrf_exempt
@login_required @login_required
def add_unit_ajax(request): def add_unit_ajax(request):
return JsonResponse({'success': True}) if request.method != 'POST':
return JsonResponse({'success': False})
try:
data = json.loads(request.body)
Unit.objects.create(
name_en=data.get('name_en'),
name_ar=data.get('name_ar'),
short_name=data.get('short_name')
)
return JsonResponse({'success': True})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
@csrf_exempt
@login_required @login_required
def add_payment_method_ajax(request): def add_payment_method_ajax(request):
return JsonResponse({'success': True}) if request.method != 'POST':
return JsonResponse({'success': False, 'error': 'Invalid method'})
try:
data = json.loads(request.body)
pm = PaymentMethod.objects.create(
name_en=data.get('name_en'),
name_ar=data.get('name_ar'),
is_active=data.get('is_active', True)
)
return JsonResponse({
'success': True,
'id': pm.id,
'name_en': pm.name_en,
'name_ar': pm.name_ar
})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
@login_required @login_required
def get_customer_loyalty_api(request, pk): def get_customer_loyalty_api(request, pk):

32
force_reset_admin.py Normal file
View File

@ -0,0 +1,32 @@
import os
import sys
import django
# Add project root to path
sys.path.append(os.getcwd())
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
django.setup()
from django.contrib.auth import get_user_model
def reset_password():
User = get_user_model()
username = 'admin'
password = 'admin'
try:
user, created = User.objects.get_or_create(username=username)
user.set_password(password)
user.is_staff = True
user.is_superuser = True
user.save()
action = "created" if created else "reset"
print(f"Successfully {action} password for user '{username}' to '{password}'.")
except Exception as e:
print(f"Error resetting password: {e}")
if __name__ == "__main__":
reset_password()

32
manual_db_fix.py Normal file
View File

@ -0,0 +1,32 @@
from django.db import connection
def fix_db():
print("Starting DB Fix...")
with connection.cursor() as cursor:
# 1. Check/Add is_service to core_product
try:
cursor.execute("SELECT is_service FROM core_product LIMIT 1")
print("SUCCESS: is_service already exists in core_product.")
except Exception:
print("Attempting to add is_service column...")
try:
# Try MySQL syntax first
cursor.execute("ALTER TABLE core_product ADD COLUMN is_service tinyint(1) NOT NULL DEFAULT 0;")
print("FIXED: Added is_service column to core_product.")
except Exception as e:
print(f"ERROR adding is_service: {e}")
# 2. Check/Add is_active to core_paymentmethod
try:
cursor.execute("SELECT is_active FROM core_paymentmethod LIMIT 1")
print("SUCCESS: is_active already exists in core_paymentmethod.")
except Exception:
print("Attempting to add is_active column...")
try:
cursor.execute("ALTER TABLE core_paymentmethod ADD COLUMN is_active tinyint(1) NOT NULL DEFAULT 1;")
print("FIXED: Added is_active column to core_paymentmethod.")
except Exception as e:
print(f"ERROR adding is_active: {e}")
if __name__ == '__main__':
fix_db()

134
patch_views_fixes.py Normal file
View File

@ -0,0 +1,134 @@
import os
import re
file_path = 'core/views.py'
with open(file_path, 'r') as f:
content = f.read()
# Implement add_payment_method_ajax
# It currently looks like:
# @login_required
# def add_payment_method_ajax(request):
# return JsonResponse({'success': True})
new_add_payment_method = """@csrf_exempt
@login_required
def add_payment_method_ajax(request):
if request.method != 'POST':
return JsonResponse({'success': False, 'error': 'Invalid method'})
try:
data = json.loads(request.body)
pm = PaymentMethod.objects.create(
name_en=data.get('name_en'),
name_ar=data.get('name_ar'),
is_active=data.get('is_active', True)
)
return JsonResponse({
'success': True,
'id': pm.id,
'name_en': pm.name_en,
'name_ar': pm.name_ar
})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})"""
content = re.sub(
r'@login_required\s+def add_payment_method_ajax\(request\):\s+return JsonResponse\({\s*["']success["']: True\s*}\)',
new_add_payment_method,
content
)
# Implement create_purchase_api
new_create_purchase = """@csrf_exempt
@login_required
def create_purchase_api(request):
if request.method != 'POST':
return JsonResponse({'success': False, 'error': 'Invalid request'})
try:
data = json.loads(request.body)
with transaction.atomic():
purchase = Purchase.objects.create(
supplier_id=data.get('supplier_id') or None,
total_amount=data.get('total_amount', 0),
paid_amount=data.get('paid_amount', 0),
created_by=request.user,
status='paid' if data.get('payment_type') == 'cash' else 'partial'
)
for item in data.get('items', []):
PurchaseItem.objects.create(
purchase=purchase,
product_id=item['id'],
quantity=item['quantity'],
cost_price=item['cost'],
line_total=float(item['quantity']) * float(item['cost'])
)
# Increase Stock
Product.objects.filter(pk=item['id']).update(stock_quantity=F('stock_quantity') + item['quantity'])
# Payment
if purchase.paid_amount > 0:
PurchasePayment.objects.create(
purchase=purchase,
amount=purchase.paid_amount,
payment_method_id=data.get('payment_method_id'),
created_by=request.user
)
purchase.update_balance()
return JsonResponse({'success': True, 'purchase_id': purchase.id})
except Exception as e:
logger.error(f"Error creating purchase: {e}")
return JsonResponse({'success': False, 'error': str(e)})"""
content = re.sub(
r'@csrf_exempt\s+@login_required\s+def create_purchase_api\(request\):\s+return JsonResponse\({\s*["']success["']: True\s*}\)',
new_create_purchase,
content
)
# Implement add_customer_ajax
new_add_customer = """@csrf_exempt
@login_required
def add_customer_ajax(request):
if request.method != 'POST':
return JsonResponse({'success': False})
try:
data = json.loads(request.body)
Customer.objects.create(name=data.get('name'), phone=data.get('phone', ''))
return JsonResponse({'success': True})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})"""
content = re.sub(
r'@login_required\s+def add_customer_ajax\(request\):\s+return JsonResponse\({\s*["']success["']: True\s*}\)',
new_add_customer,
content
)
# Implement add_supplier_ajax
new_add_supplier = """@csrf_exempt
@login_required
def add_supplier_ajax(request):
if request.method != 'POST':
return JsonResponse({'success': False})
try:
data = json.loads(request.body)
Supplier.objects.create(name=data.get('name'), phone=data.get('phone', ''))
return JsonResponse({'success': True})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})"""
content = re.sub(
r'@login_required\s+def add_supplier_ajax\(request\):\s+return JsonResponse\({\s*["']success["']: True\s*}\)',
new_add_supplier,
content
)
with open(file_path, 'w') as f:
f.write(content)
print("Patched core/views.py successfully.")