editing users
This commit is contained in:
parent
cfa7d80ecc
commit
739a0d397f
Binary file not shown.
Binary file not shown.
BIN
assets/pasted-20260210-182651-b15404c1.png
Normal file
BIN
assets/pasted-20260210-182651-b15404c1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -200,7 +200,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Direct Print Mode */
|
||||
/* Direct Print Mode Overrides */
|
||||
body.direct-print-mode @page {
|
||||
size: auto;
|
||||
margin: 0;
|
||||
@ -209,53 +209,91 @@
|
||||
.label-sheet {
|
||||
position: relative;
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
min-height: 297mm; /* Ensure full page height for sheets */
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
page-break-after: always;
|
||||
box-sizing: border-box;
|
||||
display: block; /* Default fallback */
|
||||
}
|
||||
|
||||
/* Direct print container override */
|
||||
body.direct-print-mode .label-sheet {
|
||||
width: auto;
|
||||
min-height: auto;
|
||||
page-break-after: auto;
|
||||
display: block;
|
||||
font-size: 0; /* Remove whitespace between inline-blocks */
|
||||
}
|
||||
|
||||
.label-item {
|
||||
box-sizing: border-box;
|
||||
display: inline-block; /* More robust than flex for print grids */
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
font-size: initial; /* Reset font size */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%; /* Fills the grid cell or container */
|
||||
height: 100%;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
/* --- SHEET GRIDS --- */
|
||||
|
||||
/* Avery L7651 (5x13 = 65) */
|
||||
.sheet-l7651 {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(5, 38.1mm) !important;
|
||||
grid-auto-rows: 21.2mm !important;
|
||||
padding: 10.7mm 9.75mm !important;
|
||||
gap: 0;
|
||||
}
|
||||
.label-l7651 {
|
||||
width: 38.1mm !important;
|
||||
height: 21.2mm !important;
|
||||
padding: 1mm;
|
||||
}
|
||||
|
||||
/* Other layouts ... */
|
||||
.sheet-a4-24 { padding: 0 !important; }
|
||||
.label-a4-24 { width: 70mm; height: 37.125mm; padding: 2mm; }
|
||||
|
||||
.sheet-a4-40 { padding: 0 !important; }
|
||||
.label-a4-40 { width: 52.5mm; height: 29.7mm; padding: 1mm; }
|
||||
|
||||
.sheet-l7656 { padding: 31.95mm 13mm !important; }
|
||||
.label-l7656 {
|
||||
width: 46mm; height: 11.1mm; padding: 0.5mm;
|
||||
.label-l7651 { padding: 1mm; }
|
||||
|
||||
/* Avery L7656 (4x21 = 84) */
|
||||
.sheet-l7656 {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(4, 46mm) !important;
|
||||
grid-auto-rows: 11.1mm !important;
|
||||
padding: 31.95mm 13mm !important;
|
||||
gap: 0;
|
||||
}
|
||||
.label-l7656 { padding: 0.5mm; }
|
||||
.label-l7656 .inner-flex {
|
||||
display: flex; flex-direction: row; justify-content: space-around; align-items: center; height: 100%; width: 100%;
|
||||
}
|
||||
.label-l7656 svg { height: 8mm !important; width: auto; }
|
||||
.label-l7656 .label-text { font-size: 5pt !important; width: auto !important; margin: 0 2px; }
|
||||
|
||||
.sheet-l7156 { padding: 15.15mm 9.75mm !important; }
|
||||
.label-l7156 { width: 63.5mm; height: 38.1mm; padding: 2mm; }
|
||||
/* Avery L7156 (3x7 = 21) */
|
||||
.sheet-l7156 {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(3, 63.5mm) !important;
|
||||
grid-auto-rows: 38.1mm !important;
|
||||
padding: 15.15mm 9.75mm !important;
|
||||
gap: 0;
|
||||
}
|
||||
.label-l7156 { padding: 2mm; }
|
||||
|
||||
/* A4 24 (3x8) */
|
||||
.sheet-a4-24 {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(3, 63.5mm) !important;
|
||||
grid-auto-rows: 33.9mm !important;
|
||||
padding: 12.9mm 9.75mm !important;
|
||||
gap: 0;
|
||||
}
|
||||
.label-a4-24 { padding: 2mm; }
|
||||
|
||||
/* A4 40 (4x10) */
|
||||
.sheet-a4-40 {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(4, 48.5mm) !important;
|
||||
grid-auto-rows: 25.4mm !important;
|
||||
padding: 21.5mm 8mm !important;
|
||||
gap: 0;
|
||||
}
|
||||
.label-a4-40 { padding: 1mm; }
|
||||
|
||||
.label-item svg {
|
||||
max-width: 100%;
|
||||
@ -568,7 +606,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// --- PREPARE PRINT (Full Queue) ---
|
||||
function preparePrint() {
|
||||
if (document.body.classList.contains('direct-print-mode')) return;
|
||||
// Ensure we are not in direct print mode
|
||||
document.body.classList.remove('direct-print-mode');
|
||||
|
||||
printArea.innerHTML = '';
|
||||
const labelType = document.getElementById('labelType').value;
|
||||
@ -595,7 +634,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
for (let s = 0; s < allLabels.length; s += labelsPerSheet) {
|
||||
const sheetLabels = allLabels.slice(s, s + labelsPerSheet);
|
||||
const sheet = document.createElement('div');
|
||||
sheet.className = `label-sheet sheet-${labelType}`;
|
||||
|
||||
// Add specific classes
|
||||
if (labelType === 'a4-24') sheet.classList.add('sheet-a4-24');
|
||||
@ -604,6 +642,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
else if (labelType === 'l7656') sheet.classList.add('sheet-l7656');
|
||||
else if (labelType === 'l7156') sheet.classList.add('sheet-l7156');
|
||||
|
||||
sheet.classList.add('label-sheet');
|
||||
sheet.classList.add('sheet-' + labelType);
|
||||
|
||||
printArea.appendChild(sheet);
|
||||
|
||||
sheetLabels.forEach((p, idx) => {
|
||||
|
||||
103
core/views.py
103
core/views.py
@ -18,6 +18,8 @@ import logging
|
||||
import base64
|
||||
import os
|
||||
from django.conf import settings as django_settings
|
||||
from django.contrib.auth.models import User, Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from .models import (
|
||||
SystemSetting, Customer, Supplier, Product, Category, Unit,
|
||||
@ -166,6 +168,7 @@ def index(request):
|
||||
|
||||
@login_required
|
||||
def inventory(request):
|
||||
logger.info("Inventory view accessed")
|
||||
products = Product.objects.all().order_by('name_en')
|
||||
categories = Category.objects.all()
|
||||
units = Unit.objects.all()
|
||||
@ -176,6 +179,7 @@ def inventory(request):
|
||||
next_30_days = today + datetime.timedelta(days=30)
|
||||
|
||||
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 = SystemSetting.objects.first()
|
||||
if not settings:
|
||||
settings = SystemSetting.objects.create()
|
||||
@ -296,11 +300,99 @@ def profile_view(request):
|
||||
|
||||
@login_required
|
||||
def user_management(request):
|
||||
return render(request, 'core/users.html')
|
||||
if request.method == 'POST':
|
||||
action = request.POST.get('action')
|
||||
|
||||
try:
|
||||
if action == 'add':
|
||||
username = request.POST.get('username')
|
||||
email = request.POST.get('email')
|
||||
password = request.POST.get('password')
|
||||
group_ids = request.POST.getlist('groups')
|
||||
|
||||
if User.objects.filter(username=username).exists():
|
||||
messages.error(request, _("Username already exists."))
|
||||
else:
|
||||
user = User.objects.create_user(username=username, email=email, password=password)
|
||||
if group_ids:
|
||||
user.groups.set(group_ids)
|
||||
messages.success(request, _("User created successfully."))
|
||||
|
||||
elif action == 'edit_user':
|
||||
user_id = request.POST.get('user_id')
|
||||
user = get_object_or_404(User, id=user_id)
|
||||
|
||||
user.email = request.POST.get('email')
|
||||
password = request.POST.get('password')
|
||||
if password:
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
|
||||
group_ids = request.POST.getlist('groups')
|
||||
user.groups.set(group_ids)
|
||||
messages.success(request, _("User updated successfully."))
|
||||
|
||||
elif action == 'toggle_status':
|
||||
user_id = request.POST.get('user_id')
|
||||
if int(user_id) == request.user.id:
|
||||
messages.error(request, _("You cannot deactivate yourself."))
|
||||
else:
|
||||
user = get_object_or_404(User, id=user_id)
|
||||
user.is_active = not user.is_active
|
||||
user.save()
|
||||
status = _("activated") if user.is_active else _("deactivated")
|
||||
messages.success(request, _(f"User {status}."))
|
||||
|
||||
elif action == 'add_group':
|
||||
name = request.POST.get('name')
|
||||
perm_ids = request.POST.getlist('permissions')
|
||||
|
||||
if Group.objects.filter(name=name).exists():
|
||||
messages.error(request, _("Group name already exists."))
|
||||
else:
|
||||
group = Group.objects.create(name=name)
|
||||
if perm_ids:
|
||||
group.permissions.set(perm_ids)
|
||||
messages.success(request, _("Group created successfully."))
|
||||
|
||||
elif action == 'edit_group':
|
||||
group_id = request.POST.get('group_id')
|
||||
group = get_object_or_404(Group, id=group_id)
|
||||
group.name = request.POST.get('name')
|
||||
group.save()
|
||||
|
||||
perm_ids = request.POST.getlist('permissions')
|
||||
group.permissions.set(perm_ids)
|
||||
messages.success(request, _("Group updated successfully."))
|
||||
|
||||
elif action == 'delete_group':
|
||||
group_id = request.POST.get('group_id')
|
||||
group = get_object_or_404(Group, id=group_id)
|
||||
group.delete()
|
||||
messages.success(request, _("Group deleted successfully."))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"User Management Error: {e}")
|
||||
messages.error(request, _(f"An error occurred: {e}"))
|
||||
|
||||
return redirect('user_management')
|
||||
|
||||
users = User.objects.all().order_by('username')
|
||||
groups = Group.objects.all().order_by('name')
|
||||
# Filter permissions to exclude internal/system apps if desired, or show all
|
||||
permissions = Permission.objects.select_related('content_type').order_by('content_type__app_label', 'content_type__model')
|
||||
|
||||
return render(request, 'core/users.html', {
|
||||
'users': users,
|
||||
'groups': groups,
|
||||
'permissions': permissions
|
||||
})
|
||||
|
||||
@login_required
|
||||
def group_details_api(request, pk):
|
||||
return JsonResponse({})
|
||||
group = get_object_or_404(Group, pk=pk)
|
||||
permissions = list(group.permissions.values_list('id', flat=True))
|
||||
return JsonResponse({'permissions': permissions})
|
||||
|
||||
# --- POS Views ---
|
||||
|
||||
@ -687,13 +779,18 @@ def edit_purchase(request, pk):
|
||||
decimal_places = site_settings.decimal_places or 2
|
||||
|
||||
cart_items = []
|
||||
logger.info(f"EDIT_PURCHASE: Processing {purchase.items.count()} items for purchase {pk}")
|
||||
for item in purchase.items.all().select_related('product'):
|
||||
# Debugging attributes
|
||||
if hasattr(item, 'unit_price'):
|
||||
logger.warning(f"Item {item.id} has unit_price attribute! {item.unit_price}")
|
||||
|
||||
cart_items.append({
|
||||
'id': item.product.id,
|
||||
'name_en': item.product.name_en,
|
||||
'name_ar': item.product.name_ar,
|
||||
'sku': item.product.sku,
|
||||
'cost_price': float(item.unit_price),
|
||||
'cost_price': float(item.cost_price),
|
||||
'quantity': float(item.quantity)
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user