diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index 5812c54..1965da4 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -625,6 +625,91 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} } ); + # Returns (checkin id, checkout id, error) + # Must be called during a transaction. + # Must perform a rollback on error. + $self->helper( + 'add_journey' => sub { + my ( $self, %opt ) = @_; + + my $user_status = $self->get_user_status; + if ( $user_status->{checked_in} or $user_status->{cancelled} ) { + + # TODO: change database schema to one row per journey instead of two + return ( undef, undef, +'Während einer Zugfahrt können momentan keine manuellen Einträge vorgenommen werden. Klingt komisch, ist aber so.' + ); + } + + my $uid = $self->current_user->{id}; + my $dep_station = get_station( $opt{dep_station} ); + my $arr_station = get_station( $opt{arr_station} ); + + if ( not $dep_station ) { + return ( undef, undef, 'Unbekannter Startbahnhof' ); + } + if ( not $arr_station ) { + return ( undef, undef, 'Unbekannter Zielbahnhof' ); + } + + say $dep_station->[0]; + say $dep_station->[1]; + say $arr_station->[0]; + say $arr_station->[1]; + + my $success = $self->app->action_query->execute( + $uid, + $self->app->action_type->{checkin}, + $self->get_station_id( + ds100 => $dep_station->[0], + name => $dep_station->[1], + ), + DateTime->now( time_zone => 'Europe/Berlin' )->epoch, + 0x0f, + $opt{train_type}, + $opt{train_line}, + $opt{train_no}, + undef, + $opt{sched_departure}->epoch, + $opt{rt_departure} ? $opt{rt_departure}->epoch : undef, + undef, undef + ); + if ( not $success ) { + my $err = $self->app->action_query->errstr; + $self->app->log->error( + "add_journey($uid, checkin): INSERT failed: $err"); + return ( undef, undef, 'INSERT failed: ' . $err ); + } + my $checkin_id = $self->app->action_query->last_insert_id; + + $success = $self->app->action_query->execute( + $uid, + $self->app->action_type->{checkout}, + $self->get_station_id( + ds100 => $arr_station->[0], + name => $arr_station->[1], + ), + DateTime->now( time_zone => 'Europe/Berlin' )->epoch, + 0x0f, + $opt{train_type}, + $opt{train_line}, + $opt{train_no}, + undef, + $opt{sched_arrival}->epoch, + $opt{rt_arrival} ? $opt{rt_arrival}->epoch : undef, + undef, undef + ); + if ( not $success ) { + my $err = $self->app->action_query->errstr; + $self->app->log->error( + "add_journey($uid, checkout): INSERT failed: $err"); + return ( undef, undef, 'INSERT failed: ' . $err ); + } + my $checkout_id = $self->app->action_query->last_insert_id; + return ( $checkin_id, $checkout_id, undef ); + } + ); + $self->helper( 'checkin' => sub { my ( $self, $station, $train_id, $action_id ) = @_; @@ -1218,7 +1303,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} } elsif ( $opt{after} and $opt{before} ) { - # Each journey consists of at least two database entries: one for + # Each journey consists of exactly two database entries: one for # checkin, one for checkout. A simple query using e.g. # after = YYYY-01-01T00:00:00 and before YYYY-02-01T00:00:00 # will miss journeys where checkin and checkout take place in @@ -1602,6 +1687,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} $authed_r->get('/journey/add')->to('traveling#add_journey_form'); $authed_r->get('/journey/:id')->to('traveling#journey_details'); $authed_r->get('/s/*station')->to('traveling#station'); + $authed_r->post('/journey/add')->to('traveling#add_journey_form'); $authed_r->post('/journey/edit')->to('traveling#edit_journey'); $authed_r->post('/change_password')->to('account#change_password'); $authed_r->post('/delete')->to('account#delete'); diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm index ce7d1d4..0ace304 100755 --- a/lib/Travelynx/Controller/Traveling.pm +++ b/lib/Travelynx/Controller/Traveling.pm @@ -8,7 +8,11 @@ use Travel::Status::DE::IRIS::Stations; sub homepage { my ($self) = @_; if ( $self->is_user_authenticated ) { - $self->render( 'landingpage', with_geolocation => 1 ); + $self->render( + 'landingpage', + with_autocomplete => 1, + with_geolocation => 1 + ); } else { $self->render( 'landingpage', intro => 1 ); @@ -216,8 +220,9 @@ sub station { if ( $status->{errstr} ) { $self->render( 'landingpage', - with_geolocation => 1, - error => $status->{errstr} + with_autocomplete => 1, + with_geolocation => 1, + error => $status->{errstr} ); } else { @@ -517,7 +522,68 @@ sub edit_journey { sub add_journey_form { my ($self) = @_; - $self->render( 'add_journey', error => undef ); + if ( $self->param('action') and $self->param('action') eq 'save' ) { + my $parser = DateTime::Format::Strptime->new( + pattern => '%d.%m.%Y %H:%M', + locale => 'de_DE', + time_zone => 'Europe/Berlin' + ); + my %opt; + + my @parts = split( qr{\s+}, $self->param('train') ); + + if ( @parts == 2 ) { + @opt{ 'train_type', 'train_no' } = @parts; + } + elsif ( @parts == 3 ) { + @opt{ 'train_type', 'train_line', 'train_no' } = @parts; + } + else { + $self->render( + 'add_journey', + with_autocomplete => 1, + error => +'Zug muss als „Typ Nummer“ oder „Typ Linie Nummer“ eingegeben werden.' + ); + return; + } + + for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival)) + { + my $datetime = $parser->parse_datetime( $self->param($key) ); + if ( not $datetime ) { + $self->render( + 'add_journey', + with_autocomplete => 1, + error => "${key}: Ungültiges Datums-/Zeitformat" + ); + return; + } + $opt{$key} = $datetime; + } + + for my $key (qw(dep_station arr_station)) { + $opt{$key} = $self->param($key); + } + + $self->app->dbh->begin_work; + + my ( $checkin_id, $checkout_id, $error ) = $self->add_journey(%opt); + + $self->app->dbh->rollback; + $self->render( + 'add_journey', + with_autocomplete => 1, + error => $error + ); + return; + } + + $self->render( + 'add_journey', + with_autocomplete => 1, + error => undef + ); } 1; diff --git a/templates/add_journey.html.ep b/templates/add_journey.html.ep index 9ef00f5..e07f78f 100644 --- a/templates/add_journey.html.ep +++ b/templates/add_journey.html.ep @@ -29,6 +29,10 @@
+
+ %= text_field 'dep_station', id => 'dep_station', class => 'autocomplete validate', required => undef + +
%= text_field 'sched_departure', id => 'sched_departure', class => 'validate', required => undef, pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]' @@ -39,6 +43,10 @@
+
+ %= text_field 'arr_station', id => 'arr_station', class => 'autocomplete validate', required => undef + +
%= text_field 'sched_arrival', id => 'sched_arrival', class => 'validate', required => undef, pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]' diff --git a/templates/layouts/default.html.ep b/templates/layouts/default.html.ep index fa8dacf..b76f1ab 100644 --- a/templates/layouts/default.html.ep +++ b/templates/layouts/default.html.ep @@ -13,9 +13,11 @@ %= javascript "/static/${av}/js/materialize.min.js" %= javascript "/static/${av}/js/travelynx-actions.min.js" % if (stash('with_geolocation')) { - %= javascript "/static/${av}/js/autocomplete.min.js" %= javascript "/static/${av}/js/geolocation.min.js" % } + % if (stash('with_autocomplete')) { + %= javascript "/static/${av}/js/autocomplete.min.js" + % }