@@ -427,15 +427,15 @@ def generate_svg_bar_chart( # noqa: PLR0914
427427 return "" .join (svg_parts )
428428
429429 def generate_svg_pie_chart (self , data : dict [str , Decimal ]) -> str : # noqa: PLR0914
430- """Generate a simple SVG pie chart for category distribution."""
430+ """Generate a simple SVG pie chart for category distribution with legend ."""
431431 if not data or sum (data .values ()) == 0 :
432432 return ""
433433
434- width = 400
434+ width = 500
435435 height = 400
436- radius = 150
437- center_x = width / 2
438- center_y = height / 2
436+ radius = 120
437+ center_x = 200
438+ center_y = 200
439439
440440 # Category colors
441441 colors = {
@@ -498,6 +498,33 @@ def generate_svg_pie_chart(self, data: dict[str, Decimal]) -> str: # noqa: PLR0
498498
499499 start_angle = end_angle
500500
501+ # Add legend
502+ legend_x = 420
503+ legend_y = 50
504+ legend_spacing = 25
505+
506+ idx = 0
507+ for category , value in data .items ():
508+ if value == 0 :
509+ continue
510+
511+ color = colors .get (category , "#999" )
512+ y_pos = legend_y + idx * legend_spacing
513+
514+ # Legend color box
515+ svg_parts .append (
516+ f'<rect x="{ legend_x } " y="{ y_pos } " width="15" height="15" '
517+ f'fill="{ color } " stroke="white" stroke-width="1"/>'
518+ )
519+
520+ # Legend text
521+ svg_parts .append (
522+ f'<text x="{ legend_x + 20 } " y="{ y_pos + 12 } " font-size="11" fill="#333">'
523+ f"{ category } : €{ value :,.0f} ({ value / total * 100 :.0f} %)</text>"
524+ )
525+
526+ idx += 1
527+
501528 svg_parts .append ("</svg>" )
502529 return "" .join (svg_parts )
503530
@@ -631,14 +658,13 @@ def generate_svg_daily_chart( # noqa: PLR0914
631658 f"</rect>"
632659 )
633660
634- # Show day label every 5 days
635- if day % 5 == 1 or day == num_days :
636- label_x = x + bar_width / 2
637- label_y = height - padding + 12
638- svg_parts .append (
639- f'<text x="{ label_x } " y="{ label_y } " text-anchor="middle" '
640- f'font-size="9" fill="#666">{ day } </text>'
641- )
661+ # Show all day labels
662+ label_x = x + bar_width / 2
663+ label_y = height - padding + 12
664+ svg_parts .append (
665+ f'<text x="{ label_x } " y="{ label_y } " text-anchor="middle" '
666+ f'font-size="9" fill="#666">{ day } </text>'
667+ )
642668
643669 svg_parts .append ("</svg>" )
644670 return "" .join (svg_parts )
@@ -746,8 +772,11 @@ def get_context_data(self, **kwargs):
746772 context ["current_month" ] = month
747773
748774 # Generate charts
775+ # Always generate pie chart for both views
776+ context ["pie_chart_svg" ] = self .generate_svg_pie_chart (income_data )
777+
749778 if month :
750- # For monthly view, show category pie chart and daily chart
779+ # For monthly view, show daily chart
751780 # Get all invoices for the month
752781 month_invoices = list (
753782 Invoice .objects .filter (
@@ -757,18 +786,16 @@ def get_context_data(self, **kwargs):
757786 ).prefetch_related ("invoiceitem_set" )
758787 )
759788
760- context ["chart_svg" ] = self .generate_svg_pie_chart (income_data )
761789 context ["daily_chart_svg" ] = self .generate_svg_daily_chart (
762790 year , month , month_invoices
763791 )
764792 context ["is_monthly" ] = True
765793 else :
766- # For yearly view, show stacked monthly chart and category pie
794+ # For yearly view, show stacked monthly chart
767795 monthly_data , invoices = self .get_monthly_data (year )
768796 context ["chart_svg" ] = self .generate_svg_stacked_bar_chart (
769797 monthly_data , invoices
770798 )
771- context ["pie_chart_svg" ] = self .generate_svg_pie_chart (income_data )
772799 context ["monthly_data" ] = monthly_data
773800 context ["monthly_category_data" ], _ = self .get_monthly_category_data (year )
774801 context ["is_monthly" ] = False
0 commit comments