Autosave: 20260210-152237
This commit is contained in:
parent
55c69b5fdc
commit
b30330b17b
Binary file not shown.
107
core/views.py
107
core/views.py
@ -50,11 +50,9 @@ def index(request):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Migration Fix Failed: {e}")
|
logger.error(f"Migration Fix Failed: {e}")
|
||||||
|
|
||||||
settings = None
|
|
||||||
try:
|
|
||||||
settings = SystemSetting.objects.first()
|
settings = SystemSetting.objects.first()
|
||||||
except Exception as e:
|
if not settings:
|
||||||
logger.error(f"Failed to load settings in index: {e}")
|
settings = SystemSetting.objects.create()
|
||||||
|
|
||||||
today = timezone.now().date()
|
today = timezone.now().date()
|
||||||
|
|
||||||
@ -178,13 +176,9 @@ def inventory(request):
|
|||||||
next_30_days = today + datetime.timedelta(days=30)
|
next_30_days = today + datetime.timedelta(days=30)
|
||||||
|
|
||||||
expired_products = products.filter(has_expiry=True, expiry_date__lt=today)
|
expired_products = products.filter(has_expiry=True, expiry_date__lt=today)
|
||||||
expiring_soon_products = products.filter(has_expiry=True, expiry_date__gte=today, expiry_date__lte=next_30_days)
|
|
||||||
|
|
||||||
settings = None
|
|
||||||
try:
|
|
||||||
settings = SystemSetting.objects.first()
|
settings = SystemSetting.objects.first()
|
||||||
except Exception:
|
if not settings:
|
||||||
pass
|
settings = SystemSetting.objects.create()
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'products': products,
|
'products': products,
|
||||||
@ -209,14 +203,9 @@ def suppliers(request):
|
|||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def settings_view(request):
|
def settings_view(request):
|
||||||
settings = None
|
|
||||||
try:
|
|
||||||
settings = SystemSetting.objects.first()
|
settings = SystemSetting.objects.first()
|
||||||
if not settings:
|
if not settings:
|
||||||
settings = SystemSetting.objects.create()
|
settings = SystemSetting.objects.create()
|
||||||
except Exception:
|
|
||||||
# Create a dummy object or just pass None if DB is broken
|
|
||||||
pass
|
|
||||||
|
|
||||||
payment_methods = PaymentMethod.objects.filter(is_active=True)
|
payment_methods = PaymentMethod.objects.filter(is_active=True)
|
||||||
expense_categories = ExpenseCategory.objects.all()
|
expense_categories = ExpenseCategory.objects.all()
|
||||||
@ -325,11 +314,9 @@ def pos(request):
|
|||||||
messages.warning(request, _("Please open a session to start selling."))
|
messages.warning(request, _("Please open a session to start selling."))
|
||||||
return redirect('start_session')
|
return redirect('start_session')
|
||||||
|
|
||||||
settings = None
|
|
||||||
try:
|
|
||||||
settings = SystemSetting.objects.first()
|
settings = SystemSetting.objects.first()
|
||||||
except Exception:
|
if not settings:
|
||||||
pass
|
settings = SystemSetting.objects.create()
|
||||||
|
|
||||||
products = Product.objects.filter(is_active=True)
|
products = Product.objects.filter(is_active=True)
|
||||||
|
|
||||||
@ -398,14 +385,11 @@ def invoice_list(request):
|
|||||||
Q(invoice_number__icontains=query)
|
Q(invoice_number__icontains=query)
|
||||||
)
|
)
|
||||||
|
|
||||||
paginator = Paginator(sales, 25)
|
|
||||||
|
|
||||||
settings = None
|
|
||||||
try:
|
|
||||||
settings = SystemSetting.objects.first()
|
settings = SystemSetting.objects.first()
|
||||||
except Exception:
|
if not settings:
|
||||||
pass
|
settings = SystemSetting.objects.create()
|
||||||
|
|
||||||
|
paginator = Paginator(sales, 20)
|
||||||
context = {
|
context = {
|
||||||
'sales': paginator.get_page(request.GET.get('page')),
|
'sales': paginator.get_page(request.GET.get('page')),
|
||||||
'customers': Customer.objects.all(),
|
'customers': Customer.objects.all(),
|
||||||
@ -415,9 +399,25 @@ def invoice_list(request):
|
|||||||
}
|
}
|
||||||
return render(request, 'core/invoices.html', context)
|
return render(request, 'core/invoices.html', context)
|
||||||
|
|
||||||
|
@login_required
|
||||||
@login_required
|
@login_required
|
||||||
def invoice_create(request):
|
def invoice_create(request):
|
||||||
return redirect('pos')
|
customers = Customer.objects.all().order_by('name')
|
||||||
|
products = Product.objects.filter(is_active=True).select_related('category')
|
||||||
|
payment_methods = PaymentMethod.objects.filter(is_active=True)
|
||||||
|
site_settings = SystemSetting.objects.first()
|
||||||
|
if not site_settings:
|
||||||
|
site_settings = SystemSetting.objects.create()
|
||||||
|
|
||||||
|
decimal_places = site_settings.decimal_places or 2
|
||||||
|
|
||||||
|
return render(request, 'core/invoice_create.html', {
|
||||||
|
'customers': customers,
|
||||||
|
'products': products,
|
||||||
|
'payment_methods': payment_methods,
|
||||||
|
'site_settings': site_settings,
|
||||||
|
'decimal_places': decimal_places,
|
||||||
|
})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def invoice_detail(request, pk):
|
def invoice_detail(request, pk):
|
||||||
@ -430,11 +430,9 @@ def edit_invoice(request, pk):
|
|||||||
customers = Customer.objects.all()
|
customers = Customer.objects.all()
|
||||||
products = Product.objects.filter(is_active=True).select_related('category')
|
products = Product.objects.filter(is_active=True).select_related('category')
|
||||||
payment_methods = PaymentMethod.objects.filter(is_active=True)
|
payment_methods = PaymentMethod.objects.filter(is_active=True)
|
||||||
site_settings = None
|
|
||||||
try:
|
|
||||||
site_settings = SystemSetting.objects.first()
|
site_settings = SystemSetting.objects.first()
|
||||||
except Exception:
|
if not site_settings:
|
||||||
pass
|
site_settings = SystemSetting.objects.create()
|
||||||
|
|
||||||
decimal_places = 2
|
decimal_places = 2
|
||||||
if site_settings:
|
if site_settings:
|
||||||
@ -638,11 +636,19 @@ def purchases(request):
|
|||||||
purchases = Purchase.objects.all().order_by('-created_at')
|
purchases = Purchase.objects.all().order_by('-created_at')
|
||||||
return render(request, 'core/purchases.html', {'purchases': purchases})
|
return render(request, 'core/purchases.html', {'purchases': purchases})
|
||||||
|
|
||||||
|
@login_required
|
||||||
@login_required
|
@login_required
|
||||||
def purchase_create(request):
|
def purchase_create(request):
|
||||||
suppliers = Supplier.objects.all()
|
suppliers = Supplier.objects.all()
|
||||||
products = Product.objects.filter(is_active=True)
|
products = Product.objects.filter(is_active=True)
|
||||||
return render(request, 'core/purchase_create.html', {'suppliers': suppliers, 'products': products})
|
payment_methods = PaymentMethod.objects.filter(is_active=True)
|
||||||
|
settings = SystemSetting.objects.first()
|
||||||
|
return render(request, 'core/purchase_create.html', {
|
||||||
|
'suppliers': suppliers,
|
||||||
|
'products': products,
|
||||||
|
'payment_methods': payment_methods,
|
||||||
|
'decimal_places': settings.decimal_places if settings else 3
|
||||||
|
})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def purchase_detail(request, pk):
|
def purchase_detail(request, pk):
|
||||||
@ -888,10 +894,10 @@ def cashflow_report(request):
|
|||||||
@login_required
|
@login_required
|
||||||
def add_product(request):
|
def add_product(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = ProductForm(request.POST, request.FILES, instance=product)
|
form = ProductForm(request.POST, request.FILES)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(request, _("Product updated."))
|
messages.success(request, _("Product added."))
|
||||||
return redirect(reverse('inventory') + '#items')
|
return redirect(reverse('inventory') + '#items')
|
||||||
return redirect('inventory')
|
return redirect('inventory')
|
||||||
|
|
||||||
@ -1125,7 +1131,7 @@ def send_invoice_whatsapp(request):
|
|||||||
receipt_url = request.build_absolute_uri(reverse('sale_receipt', args=[sale.pk]))
|
receipt_url = request.build_absolute_uri(reverse('sale_receipt', args=[sale.pk]))
|
||||||
|
|
||||||
message = (
|
message = (
|
||||||
f"Hello {sale.customer.name if sale.customer else 'Guest'},
|
f"Hello {sale.customer.name if sale.customer else 'Guest'},\n"
|
||||||
f"Here is your invoice #{sale.invoice_number or sale.id}.\n"
|
f"Here is your invoice #{sale.invoice_number or sale.id}.\n"
|
||||||
f"Total: {sale.total_amount}\n"
|
f"Total: {sale.total_amount}\n"
|
||||||
f"View Invoice: {receipt_url}\n"
|
f"View Invoice: {receipt_url}\n"
|
||||||
@ -1226,13 +1232,18 @@ def create_sale_api(request):
|
|||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
sale = Sale.objects.create(
|
sale = Sale.objects.create(
|
||||||
customer_id=data.get('customer_id') or None,
|
customer_id=data.get('customer_id') or None,
|
||||||
|
invoice_number=data.get('invoice_number', ''),
|
||||||
|
subtotal=data.get('subtotal', 0),
|
||||||
|
vat_amount=data.get('vat_amount', 0),
|
||||||
total_amount=data.get('total_amount', 0),
|
total_amount=data.get('total_amount', 0),
|
||||||
paid_amount=data.get('paid_amount', 0),
|
paid_amount=data.get('paid_amount', 0),
|
||||||
payment_type=data.get('payment_type', 'cash'),
|
payment_type=data.get('payment_type', 'cash'),
|
||||||
created_by=request.user,
|
created_by=request.user,
|
||||||
status='paid' if data.get('payment_type') == 'cash' else 'partial',
|
status='paid' if data.get('payment_type') == 'cash' else 'partial',
|
||||||
discount=data.get('discount', 0),
|
discount=data.get('discount', 0),
|
||||||
loyalty_points_redeemed=data.get('loyalty_points_redeemed', 0)
|
loyalty_points_redeemed=data.get('loyalty_points_redeemed', 0),
|
||||||
|
notes=data.get('notes', ''),
|
||||||
|
due_date=data.get('due_date') if data.get('due_date') else None
|
||||||
)
|
)
|
||||||
for item in data.get('items', []):
|
for item in data.get('items', []):
|
||||||
SaleItem.objects.create(
|
SaleItem.objects.create(
|
||||||
@ -1367,20 +1378,36 @@ def create_purchase_api(request):
|
|||||||
try:
|
try:
|
||||||
data = json.loads(request.body)
|
data = json.loads(request.body)
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
|
payment_type = data.get('payment_type', 'cash')
|
||||||
|
paid_amount = decimal.Decimal(str(data.get('paid_amount', 0)))
|
||||||
|
total_amount = decimal.Decimal(str(data.get('total_amount', 0)))
|
||||||
|
|
||||||
|
status = 'paid'
|
||||||
|
if payment_type == 'credit':
|
||||||
|
status = 'unpaid'
|
||||||
|
elif payment_type == 'partial' or (paid_amount < total_amount and paid_amount > 0):
|
||||||
|
status = 'partial'
|
||||||
|
elif paid_amount == 0 and total_amount > 0:
|
||||||
|
status = 'unpaid'
|
||||||
|
|
||||||
purchase = Purchase.objects.create(
|
purchase = Purchase.objects.create(
|
||||||
supplier_id=data.get('supplier_id') or None,
|
supplier_id=data.get('supplier_id') or None,
|
||||||
total_amount=data.get('total_amount', 0),
|
invoice_number=data.get('invoice_number', ''),
|
||||||
paid_amount=data.get('paid_amount', 0),
|
total_amount=total_amount,
|
||||||
|
paid_amount=paid_amount,
|
||||||
|
payment_type=payment_type,
|
||||||
|
due_date=data.get('due_date') or None,
|
||||||
|
notes=data.get('notes', ''),
|
||||||
created_by=request.user,
|
created_by=request.user,
|
||||||
status='paid' if data.get('payment_type') == 'cash' else 'partial'
|
status=status
|
||||||
)
|
)
|
||||||
for item in data.get('items', []):
|
for item in data.get('items', []):
|
||||||
PurchaseItem.objects.create(
|
PurchaseItem.objects.create(
|
||||||
purchase=purchase,
|
purchase=purchase,
|
||||||
product_id=item['id'],
|
product_id=item['id'],
|
||||||
quantity=item['quantity'],
|
quantity=item['quantity'],
|
||||||
cost_price=item['cost'],
|
cost_price=item['price'],
|
||||||
line_total=float(item['quantity']) * float(item['cost'])
|
line_total=float(item['quantity']) * float(item['price'])
|
||||||
)
|
)
|
||||||
# Increase Stock
|
# Increase Stock
|
||||||
Product.objects.filter(pk=item['id']).update(stock_quantity=F('stock_quantity') + item['quantity'])
|
Product.objects.filter(pk=item['id']).update(stock_quantity=F('stock_quantity') + item['quantity'])
|
||||||
|
|||||||
BIN
media/profile_pics/meezan.png
Normal file
BIN
media/profile_pics/meezan.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
@ -57,6 +57,7 @@ body {
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar.active {
|
#sidebar.active {
|
||||||
@ -64,16 +65,16 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#sidebar .sidebar-header {
|
#sidebar .sidebar-header {
|
||||||
padding: 20px;
|
padding: 15px 20px;
|
||||||
background: var(--meezan-sidebar-bg);
|
background: var(--meezan-sidebar-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar ul.components {
|
#sidebar ul.components {
|
||||||
padding: 20px 0;
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar ul li a {
|
#sidebar ul li a {
|
||||||
padding: 12px 25px;
|
padding: 8px 25px;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -103,7 +104,7 @@ body {
|
|||||||
|
|
||||||
/* Collapsible Sidebar Styles */
|
/* Collapsible Sidebar Styles */
|
||||||
#sidebar ul.components li.sidebar-group-header > a {
|
#sidebar ul.components li.sidebar-group-header > a {
|
||||||
padding: 12px 25px;
|
padding: 8px 25px;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|||||||
BIN
staticfiles/pasted-20260210-082544-8d8f3bac.png
Normal file
BIN
staticfiles/pasted-20260210-082544-8d8f3bac.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
Loading…
x
Reference in New Issue
Block a user