=head4 list_stunden

C<list_stunden( [Person] [Kunde] [Prompt] [Ticket] [Beginn] [Ende] [Flags] )>

Listet alle Stunden einer Person / eines Kunden in einem
bestimmten Zeitraum auf.

Ist das rechte Bit in Flags gesetzt, erfolgt eine Ausgabe als CSV.

=cut

use utf8;
use warnings; no warnings "redefine";
use strict;
use Loader qw( check_perm check_vorgesetzter current_user
               edit_kunden edit_personen edit_tickets
               line_in line_printer
	       select_date );
use Dbase::Globals qw(
	content
	iso_intervall
	ist_unterkunde
	kpersinfo
	name_kunde
	no_crlf
	stunden_bereich
	test_flag
  );
use Dbase::Help qw(DoFn DoSelect isotime);
use Fehler qw(problem warnung);
use noris::Ticket::API qw(get_pooled_connection);
use Umlaut qw(binmodus);

sub list_stunden(;$$$$$$$) {
	my($pid,$kid,$kn,$ticket,$beginn,$ende,$flags) = @_;
	$flags = 0 unless defined $flags;

	my $ssum=0;
	my $psum=0;
	my $sum=0;
	my $ksum=0;

	unless(defined $pid) {
		$pid = edit_personen(1,1+4,"Stundenliste:");
		return undef unless defined $pid;
	}
	$pid = undef if $pid eq "-";

	unless(defined $kid) {
		$kid = edit_kunden(1+4,"Stundenliste:");
		return undef unless defined $kid;
	}
	$kid = undef if $kid eq "-";

	my $api = get_pooled_connection();

	content( $ticket = edit_tickets($kid,1+4,"Stundenliste:") ) or return
	  unless defined $ticket;

	if($ticket eq "-") {
		$ticket = undef;
	} else {
		($ticket) = $api->get_ticket( $ticket, ['merge_root'] );
	}

	my %ticket;
	if ( defined $ticket ) {
		$api->select_tickets(
			attributes => ['ticket_number'],
			query      => { merge_root => $ticket }
		)->foreach_row( sub { $ticket{ $_[0] } = 1 } );
		return undef, undef unless keys %ticket;
	}

	# Darf der das?
	my $ok = check_perm("perso",1);
	$ok ||= (defined $ticket);
	$ok ||= (defined $pid and $pid == current_user());
	$ok ||= (defined $kid and not ist_unterkunde($kid));
	$ok ||= check_vorgesetzter( undef, $pid ) if $pid;
	return problem "Du darfst diese Stunden nicht ansehen (kein perso-Flag)."
		unless $ok;

	unless(defined $beginn) {
		$beginn = select_date(0,"Zeitraum von",undef,qw(- heute));
		return undef unless defined $beginn;
	}
	unless(defined $ende) {
		$ende = select_date(16,"Zeitraum bis",undef,qw(- heute));
		return undef unless defined $ende;
	}
	$beginn = select_date(1|2048) if $beginn eq "-";
	$ende = select_date(32|2048) if $ende eq "-";

	line_printer;

	my($tagstunden);
	$tagstunden = DoFn("select tagstunden from perso where person = $pid")
		if $pid;

	if(defined $tagstunden) {
		$tagstunden /= 1000;
	} else {
		$tagstunden = 8;
	}

	my $no_category = '(keine Kategorisierung)';
	my($csv, $hdr, $res, %category, @no_category, %special);

	if ( $flags & 1 ) {
		require Text::CSV_XS;
		$csv = Text::CSV_XS->new( { binary => 1 } );
		binmodus($Db::pr_fh);
	}

	my %subject4ticket = ();
	my $subject4ticket = sub {
		my ($ticket) = @_;
		( $subject4ticket{$ticket} ) = $api->get_ticket( $ticket, ['title'] )
		  unless exists $subject4ticket{$ticket};
		$subject4ticket{$ticket};
	};

	($res = stunden_bereich($pid,$kid,$beginn,$ende,16|32, sub {
		my $subid = pop @_;
		my (
			$sid,  $beg,  $dau,  $person, $kunde, $faktor,
			$text, $tick, undef, $artflg, $artname,
		) = @_;
		return if $ticket and not $ticket{$tick||0};

		$dau *= $tagstunden/8 if test_flag("stunden_art","keine_arbeit",$artflg);

		$ssum += $faktor*$dau/100;
		$ksum += $faktor*$dau/100 if $kunde > 1;
		$psum += $dau unless $faktor;
		$sum += $dau;
		unless ( defined $hdr ) {
			$hdr = '    ID ';
			$hdr .= 'Kunde      ' unless defined $kid;
			$hdr .= 'Person      ' unless defined $pid;
			$hdr .= '  Ticket '    unless defined $ticket;
			$hdr .= 'Art      Start            Dauer Text';
			if ( $flags & 1 ) {
				$csv->combine( split ' ', $hdr )
				  or return problem( 'Kann Kopfzeile nicht als CSV ausgeben: ' . $csv->error_input );
				$hdr = $csv->string;
			}
			print $Db::pr_fh "$hdr\n";
		}

		(my $begs = isotime $beg,2) =~ s/\s+\+.*//;

		my $output;
		$text = '' unless defined $text;
		{
			if ( defined $tick ) {
				unless ( length $text ) {
					$text = '(' . $subject4ticket->($tick) . ')';
				}
				elsif ( $text =~ /^RT#\d+(?:-\d+)?\z/ ) {
					$text .= ' (' . $subject4ticket->($tick) . ')';
				}
			}
			my @output;
			push @output, [ '%6d '   => $sid                      ];
			push @output, [ '%-10s ' => name_kunde($kunde) || '-' ] unless defined $kid;
			push @output, [ '%-11s ' => kpersinfo($person) || '-' ] unless defined $pid;
			push @output, [ '%8d '   => $tick || 0                ] unless defined $ticket;
			push @output, [ '%-8s %-10s %-5s %s' =>
							$artname,
			                $begs,
			                iso_intervall($dau,4),
			                no_crlf($text) . ( $subid > 1 ? " ($subid)" : '' )
			              ];
			if ( $flags & 1 ) {
				$csv->combine( map @$_[ 1 .. $#$_ ], @output )
				  or return problem(' Kann kein CSV erzeugen: ' . $csv->error_input );
				$output = $csv->string;
			}
			else {
				$output = join '', map sprintf( $_->[0], @$_[ 1.. $#$_ ] ), @output;
			}
		}
		$output .= "\n";

		if ($faktor) {
			if ( $text =~ /^(\w+)(?:#\d+)?: / ) {
				$category{lc $1}[0] += $dau;
				$category{lc $1}[1] += $dau * $faktor / 100;
			} elsif ( test_flag("stunden_art","keine_arbeit",$artflg) ) {
				$special{uc $artname} += $dau;
			} else {
				$category{$no_category}[0] += $dau;
				$category{$no_category}[1] += $dau * $faktor / 100;
				push @no_category, $output;
			}
		}

		print $Db::pr_fh $output or goto out;
	})) ? do {
		unless ( $flags & 1 ) {
			print $Db::pr_fh "Summen: ";
			print $Db::pr_fh iso_intervall($ssum,4)." (gesamt), ";
			print $Db::pr_fh iso_intervall($ksum,4)." (an Kunden), ";
			print $Db::pr_fh iso_intervall($sum,4)." (Anwesenheit), ";
			print $Db::pr_fh iso_intervall($psum,4)." (Pause)\n";
		}
	} : print "(leer)\n";

	my( $tag , $monat , $jahr  ) = (localtime $beginn)[3,4,5];
	my( $tag_, $monat_, $jahr_ ) = (localtime $ende  )[3,4,5];
	if ( not $flags & 1 and $res and !$kid and $tag == 1 && $tag_ == 1 and !$ticket and !exists $category{$no_category} || $category{$no_category}[0]/$sum <= 0.5 ) {
		my $soll = 0;
		$soll -= $_ for values %special;
		for ( [ 0 => 'ohne' ], [ 1 => 'unter' ] ) {
			my($i, $description) = @$_;
			my $zeit = 0;
			$zeit    += $_->[$i] for values %category;
			my $trennlinie = '-' x length( my $headline = "Auswertung nach Kategorien $description Berücksichtigung von Faktoren:" );
			print $Db::pr_fh "\n$headline\n$trennlinie\n";
			printf $Db::pr_fh "%6s (%s %%)  %s\n",
			  iso_intervall( $category{$_}[$i], 4 ),
			  $zeit
			  ? sprintf( '%3.f', 100 * $category{$_}[$i] / $zeit )
			  : '???', ucfirst $_
			  for sort { $category{$b}[$i] <=> $category{$a}[$i] }
			  keys %category;
			{
				$_ ++      for $monat, $monat_;
				$_ += 1900 for $jahr,  $jahr_;
				while ( $jahr < $jahr_ || $jahr == $jahr_ && $monat < $monat_ ) {
					my $sql = sprintf "SELECT soll FROM persomonat WHERE monat = %d%02d ", $jahr, $monat;
					my $monatssoll = DoFn "$sql and person=$pid";
					   $monatssoll = DoFn "$sql and (person=0 or person is NULL)" unless defined $monatssoll;
					unless ( defined $monatssoll ) {
						warnung "Konnte Monatssoll für #$pid, $jahr/$monat nicht ermitteln.\n";
						undef $soll;
					} elsif ( defined $soll ) {
						$soll += $monatssoll;
					}
				} continue {
					if ( ++$monat > 12 ) {
						$jahr ++;
						$monat = 1;
					}
				}
			}
			printf $Db::pr_fh $soll
				? ( "%s\n%6s (%3.f %%)  Stunden anwesend (%s Stunden Soll)\n", $trennlinie, iso_intervall($zeit,4), 100*$zeit/$soll, iso_intervall($soll,4) )
				: ( "%s\n%6s          Stunden anwesend\n",                    $trennlinie, iso_intervall($zeit,4)                                          );
		}
		if ( keys %special ) {
			print $Db::pr_fh "\nAußerdem:\n";
			printf $Db::pr_fh "%s Stunden %s\n", iso_intervall($special{$_},4), $_ for sort keys %special;
		}
		@no_category < 10 || !-t $Db::pr_fh || line_in(@no_category.' nicht kategorisierte Einträge anzeigen? ') =~ /^\s*(?:ja?|y(?:es)?)\s*\z/i
			and print $Db::pr_fh "\nNicht kategorisierte" . (@no_category == 1 ? 'r Eintrag' : ' Einträge') . ":\n$hdr\n", @no_category
			if @no_category;
	}
out:
	$res;
}

1;
