from django.shortcuts import render, redirect from django.contrib import messages from django.contrib.auth.decorators import login_required from django.urls import reverse from django.utils.text import slugify from .models import Category, Supplier, Product, Unit from .forms_import import ImportFileForm import decimal import logging logger = logging.getLogger(__name__) # Safely handle openpyxl import only when needed def get_openpyxl(): try: import openpyxl return openpyxl except ImportError: return None @login_required def import_categories(request): """ Import categories from an Excel (.xlsx) file. Expected columns: Name (Eng), Name (Ar) """ if request.method == 'POST': form = ImportFileForm(request.POST, request.FILES) if form.is_valid(): excel_file = request.FILES['file'] openpyxl_lib = get_openpyxl() if not openpyxl_lib: messages.error(request, "Error: The 'openpyxl' library is not installed on the server. Please contact support.") return redirect(reverse('inventory') + '#categories-list') try: wb = openpyxl_lib.load_workbook(excel_file) sheet = wb.active count = 0 updated_count = 0 errors = [] # Skip header row (min_row=2) for i, row in enumerate(sheet.iter_rows(min_row=2, values_only=True), start=2): if not any(row): continue # Skip empty rows # Unpack columns with fallbacks for safety # Format: name_en, name_ar name_en = str(row[0]).strip() if row[0] else None name_ar = str(row[1]).strip() if len(row) > 1 and row[1] else name_en if not name_en: errors.append(f"Row {i}: Missing English Name. Skipped.") continue slug = slugify(name_en) category, created = Category.objects.update_or_create( slug=slug, defaults={ 'name_en': name_en, 'name_ar': name_ar, } ) if created: count += 1 else: updated_count += 1 if count > 0 or updated_count > 0: msg = f"Import completed: {count} new categories added" if updated_count > 0: msg += f", {updated_count} categories updated" messages.success(request, msg) if errors: for error in errors: messages.warning(request, error) except Exception as e: messages.error(request, f"Error processing file: {str(e)}") return redirect(reverse('inventory') + '#categories-list') else: form = ImportFileForm() return render(request, 'core/import_categories.html', {'form': form}) @login_required def import_suppliers(request): """ Import suppliers from an Excel (.xlsx) file. Expected columns: Name, Contact Person, Phone """ if request.method == 'POST': form = ImportFileForm(request.POST, request.FILES) if form.is_valid(): excel_file = request.FILES['file'] openpyxl_lib = get_openpyxl() if not openpyxl_lib: messages.error(request, "Error: The 'openpyxl' library is not installed on the server. Please contact support.") return redirect('suppliers') try: wb = openpyxl_lib.load_workbook(excel_file) sheet = wb.active count = 0 updated_count = 0 errors = [] # Skip header row (min_row=2) for i, row in enumerate(sheet.iter_rows(min_row=2, values_only=True), start=2): if not any(row): continue # Skip empty rows # Unpack columns with fallbacks for safety # Format: Name, Contact Person, Phone name = str(row[0]).strip() if row[0] else None contact_person = str(row[1]).strip() if len(row) > 1 and row[1] else '' phone = str(row[2]).strip() if len(row) > 2 and row[2] else '' if not name: errors.append(f"Row {i}: Missing Name. Skipped.") continue supplier, created = Supplier.objects.update_or_create( name=name, defaults={ 'contact_person': contact_person, 'phone': phone, } ) if created: count += 1 else: updated_count += 1 if count > 0 or updated_count > 0: msg = f"Import completed: {count} new suppliers added" if updated_count > 0: msg += f", {updated_count} suppliers updated" messages.success(request, msg) if errors: for error in errors: messages.warning(request, error) except Exception as e: messages.error(request, f"Error processing file: {str(e)}") return redirect('suppliers') else: form = ImportFileForm() return render(request, 'core/import_suppliers.html', {'form': form}) @login_required def import_products(request): """ Import products from an Excel (.xlsx) file. Expected columns: Name (En), Name (Ar), SKU, Cost, Price, Category, Unit, Stock """ if request.method == 'POST': form = ImportFileForm(request.POST, request.FILES) if form.is_valid(): excel_file = request.FILES['file'] openpyxl_lib = get_openpyxl() if not openpyxl_lib: messages.error(request, "Error: The 'openpyxl' library is not installed on the server. Please contact support.") return redirect('inventory') try: wb = openpyxl_lib.load_workbook(excel_file) sheet = wb.active count = 0 updated_count = 0 errors = [] # Skip header row (min_row=2) for i, row in enumerate(sheet.iter_rows(min_row=2, values_only=True), start=2): if not any(row): continue # Skip empty rows # Unpack columns try: name_en = str(row[0]).strip() if row[0] else None name_ar = str(row[1]).strip() if len(row) > 1 and row[1] else name_en sku = str(row[2]).strip() if len(row) > 2 and row[2] else None cost_price = row[3] if len(row) > 3 and row[3] is not None else 0 price = row[4] if len(row) > 4 and row[4] is not None else 0 category_name = str(row[5]).strip() if len(row) > 5 and row[5] else None unit_name = str(row[6]).strip() if len(row) > 6 and row[6] else None stock = row[7] if len(row) > 7 and row[7] is not None else 0 except Exception as e: errors.append(f"Row {i}: Error reading columns. {str(e)}") continue if not name_en or not sku or not price or not category_name: errors.append(f"Row {i}: Missing required fields (Name, SKU, Price, Category). Skipped.") continue # Handle Category category_slug = slugify(category_name) category, _ = Category.objects.get_or_create( slug=category_slug, defaults={'name_en': category_name, 'name_ar': category_name} ) # Handle Unit unit = None if unit_name: unit, _ = Unit.objects.get_or_create( name_en=unit_name, defaults={'name_ar': unit_name, 'short_name': unit_name[:10]} ) product, created = Product.objects.update_or_create( sku=sku, defaults={ 'name_en': name_en, 'name_ar': name_ar, 'category': category, 'unit': unit, 'cost_price': decimal.Decimal(str(cost_price)), 'price': decimal.Decimal(str(price)), 'stock_quantity': decimal.Decimal(str(stock)), } ) if created: count += 1 else: updated_count += 1 if count > 0 or updated_count > 0: msg = f"Import completed: {count} new products added" if updated_count > 0: msg += f", {updated_count} products updated" messages.success(request, msg) if errors: for error in errors: messages.warning(request, error) except Exception as e: messages.error(request, f"Error processing file: {str(e)}") return redirect('inventory') else: form = ImportFileForm() return render(request, 'core/import_products.html', {'form': form})