Django_Basic_Manufacturing_3/reporting_plan.md
2025-08-22 17:05:22 +07:00

945 lines
35 KiB
Markdown

# Reporting Module Implementation Plan with Excel Export
## Overview
This document outlines the implementation plan for the reporting module, including Excel export functionality and various report types for the manufacturing application.
## Reporting Models
### 1. Report Template Model
```python
# reports/models.py
from django.db import models
from django.contrib.auth.models import User
class ReportTemplate(models.Model):
REPORT_TYPES = [
('inventory', 'Inventory Report'),
('sales', 'Sales Report'),
('purchasing', 'Purchasing Report'),
('manufacturing', 'Manufacturing Report'),
('financial', 'Financial Report'),
('custom', 'Custom Report'),
]
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
report_type = models.CharField(max_length=20, choices=REPORT_TYPES)
template_file = models.FileField(upload_to='report_templates/', blank=True, null=True)
is_active = models.BooleanField(default=True)
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class GeneratedReport(models.Model):
REPORT_FORMATS = [
('pdf', 'PDF'),
('excel', 'Excel'),
('csv', 'CSV'),
('html', 'HTML'),
]
report_template = models.ForeignKey(ReportTemplate, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
description = models.TextField(blank=True)
format = models.CharField(max_length=10, choices=REPORT_FORMATS)
file_path = models.CharField(max_length=500, blank=True)
file_size = models.BigIntegerField(default=0)
generated_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
generated_at = models.DateTimeField(auto_now_add=True)
parameters = models.JSONField(default=dict, blank=True,
help_text="Parameters used to generate this report")
def __str__(self):
return self.name
def get_absolute_url(self):
from django.urls import reverse
return reverse('reports:report_detail', kwargs={'report_id': self.id})
```
## Report Generation System
### 2. Report Generator Classes
```python
# reports/generators.py
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment
from openpyxl.utils.dataframe import dataframe_to_rows
from django.http import HttpResponse
from django.template.loader import render_to_string
import io
import csv
from datetime import datetime
class BaseReportGenerator:
"""Base class for all report generators"""
def __init__(self, report_template, parameters=None):
self.report_template = report_template
self.parameters = parameters or {}
def generate_data(self):
"""Generate report data - to be implemented by subclasses"""
raise NotImplementedError
def generate_excel(self, data):
"""Generate Excel report"""
df = pd.DataFrame(data)
# Create Excel workbook
wb = Workbook()
ws = wb.active
ws.title = self.report_template.name
# Add data to worksheet
for r in dataframe_to_rows(df, index=False, header=True):
ws.append(r)
# Style the header row
header_font = Font(bold=True)
header_fill = PatternFill(start_color="CCCCCC", end_color="CCCCCC", fill_type="solid")
for cell in ws[1]:
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal="center")
# Auto-adjust column widths
for column in ws.columns:
max_length = 0
column_letter = column[0].column_letter
for cell in column:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = (max_length + 2)
ws.column_dimensions[column_letter].width = min(adjusted_width, 50)
# Save to bytes
output = io.BytesIO()
wb.save(output)
output.seek(0)
return output
def generate_csv(self, data):
"""Generate CSV report"""
df = pd.DataFrame(data)
output = io.StringIO()
df.to_csv(output, index=False)
output.seek(0)
return output
def generate_html(self, data):
"""Generate HTML report"""
context = {
'report_template': self.report_template,
'data': data,
'generated_at': datetime.now(),
'parameters': self.parameters,
}
html = render_to_string('reports/report_template.html', context)
return io.StringIO(html)
def generate_report(self, format='excel'):
"""Generate report in specified format"""
data = self.generate_data()
if format == 'excel':
return self.generate_excel(data)
elif format == 'csv':
return self.generate_csv(data)
elif format == 'html':
return self.generate_html(data)
else:
raise ValueError(f"Unsupported format: {format}")
class InventoryReportGenerator(BaseReportGenerator):
"""Generate inventory reports"""
def generate_data(self):
from inventory.models import Product, Inventory, Warehouse
# Get inventory data
inventory_data = []
inventories = Inventory.objects.select_related('product', 'warehouse').all()
for inv in inventories:
inventory_data.append({
'Product Code': inv.product.code,
'Product Name': inv.product.name,
'Warehouse': inv.warehouse.name,
'Quantity': float(inv.quantity),
'Reserved Quantity': float(inv.reserved_quantity),
'Available Quantity': float(inv.available_quantity),
'Unit of Measure': inv.product.unit_of_measure.name if inv.product.unit_of_measure else '',
'Reorder Level': float(inv.product.reorder_level),
'Cost Price': float(inv.product.cost_price) if inv.product.cost_price else 0,
'Selling Price': float(inv.product.selling_price) if inv.product.selling_price else 0,
})
return inventory_data
class SalesReportGenerator(BaseReportGenerator):
"""Generate sales reports"""
def generate_data(self):
from sales.models import SalesOrder, SalesOrderItem, Customer
# Get sales data based on parameters
start_date = self.parameters.get('start_date')
end_date = self.parameters.get('end_date')
customer_id = self.parameters.get('customer_id')
sales_orders = SalesOrder.objects.select_related('customer').prefetch_related('items__product')
if start_date:
sales_orders = sales_orders.filter(order_date__gte=start_date)
if end_date:
sales_orders = sales_orders.filter(order_date__lte=end_date)
if customer_id:
sales_orders = sales_orders.filter(customer_id=customer_id)
# Generate sales data
sales_data = []
for order in sales_orders:
for item in order.items.all():
sales_data.append({
'Order Number': order.so_number,
'Order Date': order.order_date.strftime('%Y-%m-%d'),
'Customer': order.customer.name,
'Product Code': item.product.code,
'Product Name': item.product.name,
'Quantity': float(item.quantity),
'Unit Price': float(item.unit_price),
'Total Price': float(item.total_price),
'Status': order.get_status_display(),
})
return sales_data
class PurchasingReportGenerator(BaseReportGenerator):
"""Generate purchasing reports"""
def generate_data(self):
from purchasing.models import PurchaseOrder, PurchaseOrderItem, Supplier
# Get purchasing data based on parameters
start_date = self.parameters.get('start_date')
end_date = self.parameters.get('end_date')
supplier_id = self.parameters.get('supplier_id')
purchase_orders = PurchaseOrder.objects.select_related('supplier').prefetch_related('items__product')
if start_date:
purchase_orders = purchase_orders.filter(order_date__gte=start_date)
if end_date:
purchase_orders = purchase_orders.filter(order_date__lte=end_date)
if supplier_id:
purchase_orders = purchase_orders.filter(supplier_id=supplier_id)
# Generate purchasing data
purchasing_data = []
for order in purchase_orders:
for item in order.items.all():
purchasing_data.append({
'PO Number': order.po_number,
'Order Date': order.order_date.strftime('%Y-%m-%d'),
'Supplier': order.supplier.name,
'Product Code': item.product.code,
'Product Name': item.product.name,
'Quantity': float(item.quantity),
'Unit Price': float(item.unit_price),
'Total Price': float(item.total_price),
'Status': order.get_status_display(),
})
return purchasing_data
class ManufacturingReportGenerator(BaseReportGenerator):
"""Generate manufacturing reports"""
def generate_data(self):
from manufacturing.models import ManufacturingOrder, BillOfMaterial
# Get manufacturing data based on parameters
start_date = self.parameters.get('start_date')
end_date = self.parameters.get('end_date')
status = self.parameters.get('status')
mo_queryset = ManufacturingOrder.objects.select_related('bom__product')
if start_date:
mo_queryset = mo_queryset.filter(scheduled_start_date__gte=start_date)
if end_date:
mo_queryset = mo_queryset.filter(scheduled_start_date__lte=end_date)
if status:
mo_queryset = mo_queryset.filter(status=status)
# Generate manufacturing data
manufacturing_data = []
for mo in mo_queryset:
manufacturing_data.append({
'MO Number': mo.mo_number,
'Product': mo.bom.product.name,
'Quantity to Produce': float(mo.quantity_to_produce),
'Scheduled Start': mo.scheduled_start_date.strftime('%Y-%m-%d'),
'Scheduled End': mo.scheduled_end_date.strftime('%Y-%m-%d'),
'Actual Start': mo.actual_start_date.strftime('%Y-%m-%d') if mo.actual_start_date else '',
'Actual End': mo.actual_end_date.strftime('%Y-%m-%d') if mo.actual_end_date else '',
'Status': mo.get_status_display(),
})
return manufacturing_data
class FinancialReportGenerator(BaseReportGenerator):
"""Generate financial reports"""
def generate_data(self):
from sales.models import SalesOrder
from purchasing.models import PurchaseOrder
# Get date range from parameters
start_date = self.parameters.get('start_date')
end_date = self.parameters.get('end_date')
# Get sales data
sales_queryset = SalesOrder.objects.all()
if start_date:
sales_queryset = sales_queryset.filter(order_date__gte=start_date)
if end_date:
sales_queryset = sales_queryset.filter(order_date__lte=end_date)
total_sales = sales_queryset.aggregate(
total=models.Sum('total_amount')
)['total'] or 0
# Get purchasing data
purchase_queryset = PurchaseOrder.objects.all()
if start_date:
purchase_queryset = purchase_queryset.filter(order_date__gte=start_date)
if end_date:
purchase_queryset = purchase_queryset.filter(order_date__lte=end_date)
total_purchases = purchase_queryset.aggregate(
total=models.Sum('total_amount')
)['total'] or 0
# Generate financial summary
financial_data = [{
'Period': f"{start_date} to {end_date}" if start_date and end_date else 'All Time',
'Total Sales': float(total_sales),
'Total Purchases': float(total_purchases),
'Net Profit/Loss': float(total_sales - total_purchases),
}]
return financial_data
# Factory for creating report generators
class ReportGeneratorFactory:
"""Factory for creating report generators"""
generators = {
'inventory': InventoryReportGenerator,
'sales': SalesReportGenerator,
'purchasing': PurchasingReportGenerator,
'manufacturing': ManufacturingReportGenerator,
'financial': FinancialReportGenerator,
}
@classmethod
def create_generator(cls, report_type, report_template, parameters=None):
generator_class = cls.generators.get(report_type)
if not generator_class:
raise ValueError(f"Unsupported report type: {report_type}")
return generator_class(report_template, parameters)
```
## Report Views
### 3. Report Views Implementation
```python
# reports/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required, permission_required
from django.http import HttpResponse, JsonResponse
from django.contrib import messages
from django.db.models import Q
from .models import ReportTemplate, GeneratedReport
from .generators import ReportGeneratorFactory
from .forms import ReportParametersForm
import os
@login_required
@permission_required('reports.view_report', raise_exception=True)
def reports_dashboard(request):
"""Main reports dashboard view"""
# Get recent reports
recent_reports = GeneratedReport.objects.select_related(
'report_template', 'generated_by'
).order_by('-generated_at')[:10]
# Get available report templates
report_templates = ReportTemplate.objects.filter(is_active=True)
context = {
'recent_reports': recent_reports,
'report_templates': report_templates,
}
return render(request, 'reports/dashboard.html', context)
@login_required
@permission_required('reports.view_report', raise_exception=True)
def inventory_report_view(request):
"""Inventory report view"""
template = get_object_or_404(ReportTemplate, report_type='inventory', is_active=True)
if request.method == 'POST':
# Generate report
return generate_report_view(request, template.id)
context = {
'report_template': template,
'report_type': 'inventory',
}
return render(request, 'reports/inventory_report.html', context)
@login_required
@permission_required('reports.view_report', raise_exception=True)
def sales_report_view(request):
"""Sales report view"""
template = get_object_or_404(ReportTemplate, report_type='sales', is_active=True)
if request.method == 'POST':
# Generate report
return generate_report_view(request, template.id)
context = {
'report_template': template,
'report_type': 'sales',
}
return render(request, 'reports/sales_report.html', context)
@login_required
@permission_required('reports.view_report', raise_exception=True)
def purchasing_report_view(request):
"""Purchasing report view"""
template = get_object_or_404(ReportTemplate, report_type='purchasing', is_active=True)
if request.method == 'POST':
# Generate report
return generate_report_view(request, template.id)
context = {
'report_template': template,
'report_type': 'purchasing',
}
return render(request, 'reports/purchasing_report.html', context)
@login_required
@permission_required('reports.view_report', raise_exception=True)
def manufacturing_report_view(request):
"""Manufacturing report view"""
template = get_object_or_404(ReportTemplate, report_type='manufacturing', is_active=True)
if request.method == 'POST':
# Generate report
return generate_report_view(request, template.id)
context = {
'report_template': template,
'report_type': 'manufacturing',
}
return render(request, 'reports/manufacturing_report.html', context)
@login_required
@permission_required('reports.view_report', raise_exception=True)
def financial_report_view(request):
"""Financial report view"""
template = get_object_or_404(ReportTemplate, report_type='financial', is_active=True)
if request.method == 'POST':
# Generate report
return generate_report_view(request, template.id)
context = {
'report_template': template,
'report_type': 'financial',
}
return render(request, 'reports/financial_report.html', context)
@login_required
@permission_required('reports.generate_report', raise_exception=True)
def generate_report_view(request, template_id):
"""Generate a report"""
template = get_object_or_404(ReportTemplate, id=template_id)
# Get parameters from POST data
parameters = {}
for key, value in request.POST.items():
if key not in ['csrfmiddlewaretoken', 'format']:
parameters[key] = value
# Get format
format = request.POST.get('format', 'excel')
try:
# Create report generator
generator = ReportGeneratorFactory.create_generator(
template.report_type, template, parameters
)
# Generate report data
output = generator.generate_report(format)
# Create GeneratedReport record
report_name = f"{template.name} - {datetime.now().strftime('%Y%m%d_%H%M%S')}"
generated_report = GeneratedReport.objects.create(
report_template=template,
name=report_name,
format=format,
generated_by=request.user,
parameters=parameters,
)
# Return file response
if format == 'excel':
response = HttpResponse(
output.getvalue(),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
response['Content-Disposition'] = f'attachment; filename="{report_name}.xlsx"'
elif format == 'csv':
response = HttpResponse(
output.getvalue(),
content_type='text/csv'
)
response['Content-Disposition'] = f'attachment; filename="{report_name}.csv"'
else:
response = HttpResponse(
output.getvalue(),
content_type='text/html'
)
return response
except Exception as e:
messages.error(request, f"Error generating report: {str(e)}")
return redirect('reports:dashboard')
@login_required
@permission_required('reports.view_report', raise_exception=True)
def report_detail_view(request, report_id):
"""View details of a generated report"""
report = get_object_or_404(GeneratedReport, id=report_id)
context = {
'report': report,
}
return render(request, 'reports/report_detail.html', context)
@login_required
@permission_required('reports.export_report', raise_exception=True)
def export_to_excel_view(request, report_id):
"""Export a report to Excel"""
report = get_object_or_404(GeneratedReport, id=report_id)
# Check if file exists
if report.file_path and os.path.exists(report.file_path):
with open(report.file_path, 'rb') as f:
response = HttpResponse(
f.read(),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
response['Content-Disposition'] = f'attachment; filename="{report.name}.xlsx"'
return response
else:
messages.error(request, "Report file not found.")
return redirect('reports:report_detail', report_id=report_id)
@login_required
@permission_required('reports.export_report', raise_exception=True)
def export_to_pdf_view(request, report_id):
"""Export a report to PDF"""
# Implementation would require a PDF generation library like WeasyPrint or ReportLab
# This is a placeholder for future implementation
messages.info(request, "PDF export is not yet implemented.")
return redirect('reports:report_detail', report_id=report_id)
```
## Report Forms
### 4. Report Parameter Forms
```python
# reports/forms.py
from django import forms
from django.forms.widgets import DateInput
from inventory.models import Product, Warehouse
from sales.models import Customer
from purchasing.models import Supplier
class BaseReportForm(forms.Form):
"""Base form for report parameters"""
start_date = forms.DateField(
required=False,
widget=DateInput(attrs={'type': 'date', 'class': 'form-control'}),
label='Start Date'
)
end_date = forms.DateField(
required=False,
widget=DateInput(attrs={'type': 'date', 'class': 'form-control'}),
label='End Date'
)
class InventoryReportForm(BaseReportForm):
"""Form for inventory report parameters"""
product = forms.ModelChoiceField(
queryset=Product.objects.all(),
required=False,
empty_label="All Products",
widget=forms.Select(attrs={'class': 'form-control'})
)
warehouse = forms.ModelChoiceField(
queryset=Warehouse.objects.all(),
required=False,
empty_label="All Warehouses",
widget=forms.Select(attrs={'class': 'form-control'})
)
class SalesReportForm(BaseReportForm):
"""Form for sales report parameters"""
customer = forms.ModelChoiceField(
queryset=Customer.objects.all(),
required=False,
empty_label="All Customers",
widget=forms.Select(attrs={'class': 'form-control'})
)
product = forms.ModelChoiceField(
queryset=Product.objects.all(),
required=False,
empty_label="All Products",
widget=forms.Select(attrs={'class': 'form-control'})
)
class PurchasingReportForm(BaseReportForm):
"""Form for purchasing report parameters"""
supplier = forms.ModelChoiceField(
queryset=Supplier.objects.all(),
required=False,
empty_label="All Suppliers",
widget=forms.Select(attrs={'class': 'form-control'})
)
product = forms.ModelChoiceField(
queryset=Product.objects.all(),
required=False,
empty_label="All Products",
widget=forms.Select(attrs={'class': 'form-control'})
)
class ManufacturingReportForm(BaseReportForm):
"""Form for manufacturing report parameters"""
product = forms.ModelChoiceField(
queryset=Product.objects.all(),
required=False,
empty_label="All Products",
widget=forms.Select(attrs={'class': 'form-control'})
)
status = forms.ChoiceField(
choices=[
('', 'All Statuses'),
('draft', 'Draft'),
('planned', 'Planned'),
('in_progress', 'In Progress'),
('completed', 'Completed'),
('cancelled', 'Cancelled'),
],
required=False,
widget=forms.Select(attrs={'class': 'form-control'})
)
class FinancialReportForm(BaseReportForm):
"""Form for financial report parameters"""
pass # Uses base form fields
```
## Report Templates
### 5. Report Dashboard Template
```html
<!-- templates/reports/dashboard.html -->
{% extends 'base.html' %}
{% block title %}Reports - Manufacturing App{% endblock %}
{% block content %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Reports</h1>
</div>
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Report Types</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="fas fa-boxes fa-2x mb-2 text-primary"></i>
<h6 class="card-title">Inventory Report</h6>
<p class="card-text">Detailed inventory status and stock levels</p>
<a href="{% url 'reports:inventory_report' %}" class="btn btn-primary btn-sm">
Generate Report
</a>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="fas fa-dollar-sign fa-2x mb-2 text-success"></i>
<h6 class="card-title">Sales Report</h6>
<p class="card-text">Sales orders, revenue, and customer analysis</p>
<a href="{% url 'reports:sales_report' %}" class="btn btn-success btn-sm">
Generate Report
</a>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="fas fa-shopping-cart fa-2x mb-2 text-info"></i>
<h6 class="card-title">Purchasing Report</h6>
<p class="card-text">Purchase orders and supplier analysis</p>
<a href="{% url 'reports:purchasing_report' %}" class="btn btn-info btn-sm">
Generate Report
</a>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="fas fa-cogs fa-2x mb-2 text-warning"></i>
<h6 class="card-title">Manufacturing Report</h6>
<p class="card-text">Production orders and manufacturing analysis</p>
<a href="{% url 'reports:manufacturing_report' %}" class="btn btn-warning btn-sm">
Generate Report
</a>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card">
<div class="card-body text-center">
<i class="fas fa-chart-line fa-2x mb-2 text-danger"></i>
<h6 class="card-title">Financial Report</h6>
<p class="card-text">Financial summary and profit/loss analysis</p>
<a href="{% url 'reports:financial_report' %}" class="btn btn-danger btn-sm">
Generate Report
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Recent Reports</h5>
</div>
<div class="card-body">
{% if recent_reports %}
<div class="list-group">
{% for report in recent_reports %}
<a href="{% url 'reports:report_detail' report.id %}"
class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">{{ report.name }}</h6>
<small>{{ report.generated_at|date:"d M Y H:i" }}</small>
</div>
<small>{{ report.report_template.name }} ({{ report.get_format_display }})</small>
</a>
{% endfor %}
</div>
{% else %}
<p class="text-muted">No recent reports generated.</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
```
### 6. Report Generation Template
```html
<!-- templates/reports/generate_report.html -->
{% extends 'base.html' %}
{% block title %}Generate {{ report_template.name }} - Manufacturing App{% endblock %}
{% block content %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Generate {{ report_template.name }}</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<a href="{% url 'reports:dashboard' %}" class="btn btn-sm btn-outline-secondary">
<i class="fas fa-arrow-left"></i> Back to Reports
</a>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Report Parameters</h5>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.start_date.id_for_label }}" class="form-label">
{{ form.start_date.label }}
</label>
{{ form.start_date }}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.end_date.id_for_label }}" class="form-label">
{{ form.end_date.label }}
</label>
{{ form.end_date }}
</div>
</div>
<!-- Additional form fields based on report type -->
{% for field in form %}
{% if field.name not in ['start_date', 'end_date'] %}
<div class="mb-3">
<label for="{{ field.id_for_label }}" class="form-label">
{{ field.label }}
</label>
{{ field }}
</div>
{% endif %}
{% endfor %}
<div class="mb-3">
<label class="form-label">Export Format</label>
<div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="format"
id="formatExcel" value="excel" checked>
<label class="form-check-label" for="formatExcel">
<i class="fas fa-file-excel text-success"></i> Excel
</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="format"
id="formatCsv" value="csv">
<label class="form-check-label" for="formatCsv">
<i class="fas fa-file-csv text-info"></i> CSV
</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="format"
id="formatPdf" value="pdf" disabled>
<label class="form-check-label" for="formatPdf">
<i class="fas fa-file-pdf text-danger"></i> PDF (Coming Soon)
</label>
</div>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-file-export"></i> Generate Report
</button>
</form>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Report Information</h5>
</div>
<div class="card-body">
<h6>{{ report_template.name }}</h6>
<p>{{ report_template.description }}</p>
<hr>
<h6>Instructions</h6>
<ul>
<li>Select the date range for your report</li>
<li>Choose any additional filters</li>
<li>Select your preferred export format</li>
<li>Click "Generate Report" to download</li>
</ul>
</div>
</div>
</div>
</div>
{% endblock %}
```
## Report URLs
### 7. Report URL Configuration
```python
# reports/urls.py
from django.urls import path
from . import views
app_name = 'reports'
urlpatterns = [
path('', views.reports_dashboard, name='dashboard'),
path('inventory/', views.inventory_report_view, name='inventory_report'),
path('sales/', views.sales_report_view, name='sales_report'),
path('purchasing/', views.purchasing_report_view, name='purchasing_report'),
path('manufacturing/', views.manufacturing_report_view, name='manufacturing_report'),
path('financial/', views.financial_report_view, name='financial_report'),
path('<int:report_id>/', views.report_detail_view, name='report_detail'),
path('<int:report_id>/export/excel/', views.export_to_excel_view, name='export_excel'),
path('<int:report_id>/export/pdf/', views.export_to_pdf_view, name='export_pdf'),
path('generate/<int:template_id>/', views.generate_report_view, name='generate_report'),
]
```
This reporting module implementation plan provides a comprehensive solution for generating various types of reports with Excel export capability. The modular design allows for easy extension to support additional report types and export formats in the future.