From d5a98d0c54e3c5da17e3b2fd4b4e2961ba70c1a2 Mon Sep 17 00:00:00 2001 From: ragu Date: Tue, 7 May 2024 08:15:54 +0200 Subject: [PATCH] Booking calendar (beta intern) for calendar bike reservation --- copri4/main/js/rentalator.js | 84 +++ copri4/main/src/Mod/APIfunc.pm | 252 +++++-- copri4/main/src/Mod/APIjsonclient.pm | 10 +- copri4/main/src/Mod/APIjsonserver.pm | 46 +- copri4/main/src/Mod/DBtank.pm | 58 +- copri4/main/src/Mod/GBFSout.pm | 13 +- copri4/main/src/Mod/Indexsharee.pm | 19 +- copri4/main/src/Mod/MailTransport.pm | 2 + copri4/main/src/Mod/Payment.pm | 3 +- copri4/main/src/Mod/Prelib.pm | 7 +- copri4/main/src/Mod/Printpreview.pm | 2 +- copri4/main/src/Mod/Shareework.pm | 17 +- copri4/main/src/Tpl/BaseEdit.pm | 6 +- copri4/main/src/Tpl/Calorin.pm | 6 +- copri4/main/src/Tpl/Liste3.pm | 7 +- copri4/main/src/scripts/Ilockit_cloud.pl | 4 +- .../main/src/scripts/Ilockit_trackingcloud.pl | 4 +- copri4/main/src/scripts/mailTransportcms.pl | 2 +- copri4/main/src/scripts/sms_message.pl | 2 +- ...le20230105.css => local_style20240315.css} | 37 + copri4/shareeapp-operator/src/Lib/Mlogic.pm | 47 +- .../src/Tpl/AccountSubmenu.pm | 6 + .../shareeapp-operator/src/Tpl/CalReserv.pm | 634 ++++++++++++++++++ copri4/shareeapp-operator/src/Tpl/FormEdit.pm | 2 +- .../examples/sharee_operator.sql.gz | Bin 35925 -> 38235 bytes copri4/shareeweb-project/src/Lib/Config.pm | 1 + 26 files changed, 1127 insertions(+), 144 deletions(-) create mode 100644 copri4/main/js/rentalator.js rename copri4/shareeapp-operator/css/{local_style20230105.css => local_style20240315.css} (70%) create mode 100755 copri4/shareeapp-operator/src/Tpl/CalReserv.pm diff --git a/copri4/main/js/rentalator.js b/copri4/main/js/rentalator.js new file mode 100644 index 0000000..83d6aae --- /dev/null +++ b/copri4/main/js/rentalator.js @@ -0,0 +1,84 @@ + +function rental_request(url,bikeID,bookingID) { + console.log('getURL: ' + url); + console.log('bike: ' + bikeID); + console.log('pos_id: ' + bookingID); + if(bookingID){ + bikeID = bookingID; + console.log('Take bikeID from requesting bookinID: ' + bikeID); + } + const button_reserv = document.querySelector('#button_reserv_' + bikeID); + const button_cancel = document.querySelector('#button_cancel_' + bikeID); + const return_state = document.querySelector('#return_state_' + bikeID); + const reserved_bikes = document.querySelector('#reserved_bikes'); + + $.getJSON( url, function( data ) { + console.log('state=' + data.shareejson.state + '|bike: ' + data.shareejson.bike + '==' + bikeID + '| bookingID: ' + data.shareejson.pos_id + '==' + bookingID); + + var DID = data.shareejson.bike; + if(data.shareejson.pos_id && bookingID){ + DID = data.shareejson.pos_id; + console.log('Take DID from responsing pos_id: ' + DID); + } + + if(data.shareejson.response_state.match(/Failure/)){ + return_state.style = 'color:red'; + } + + if(data.shareejson.state == 'requested' && DID == bikeID){ + return_state.textContent = data.shareejson.response_text; + button_reserv.style.display = 'none'; + button_cancel.style.display = 'block'; + } + else if(data.shareejson.state == 'reserved' && DID == bikeID){ + return_state.textContent = data.shareejson.response_text; + button_reserv.style.display = 'none'; + button_cancel.style.display = 'block'; + } + else if(data.shareejson.state == 'available' && DID == bikeID){ + return_state.textContent = data.shareejson.response_text; + button_cancel.style.display = 'none'; + button_reserv.style.display = 'block'; + } + else { + return_state.style = 'color:red'; + return_state.textContent = data.shareejson.response_text; + } + + if(1 == 2){ + for (const key in data.shareejson.bikes_occupied) { + console.log(key + ':' + data.shareejson.bikes_occupied[key].bike); + reserved_bikes.textContent = data.shareejson.bikes_occupied[key].bike; + } + } + + }); +} +var acc = document.getElementsByClassName("accordion"); +var i; + +for (i = 0; i < acc.length; i++) { + acc[i].addEventListener("click", function() { + this.classList.toggle("active"); + var panel = this.nextElementSibling; + if (panel.style.maxHeight) { + panel.style.maxHeight = null; + } else { + panel.style.maxHeight = panel.scrollHeight + "px"; + } + }); +} + +var scrollToTopBtn = document.getElementById("scroll-top-wrapper"); +var rootElement = document.documentElement; + +function scrollToTop() { + // Scroll to top logic + rootElement.scrollTo({ + top: 0, + behavior: "smooth" + }); +} +scrollToTopBtn.addEventListener("click", scrollToTop); + + diff --git a/copri4/main/src/Mod/APIfunc.pm b/copri4/main/src/Mod/APIfunc.pm index 5dfd8dc..657759b 100755 --- a/copri4/main/src/Mod/APIfunc.pm +++ b/copri4/main/src/Mod/APIfunc.pm @@ -17,6 +17,7 @@ use Digest::MD5 qw(md5 md5_hex); use Digest::SHA qw(sha256_base64); use Scalar::Util qw(looks_like_number); use DateTime; +use DateTime::Format::Strptime; use DateTime::Format::Pg; use URI::Encode; use Config::General; @@ -50,6 +51,13 @@ sub new { my $now_dt = strftime "%Y-%m-%d %H:%M:%S", localtime; my $now_date = strftime "%Y-%m-%d", localtime; +my $strp = DateTime::Format::Strptime->new( + pattern => '%Y-%m-%dT%H:%M:%S', + locale => 'de_DE', + time_zone => 'Europe/Berlin', + on_error => 'croak', +); + my $lang="de"; my $owner=188;#via API my $dbh = ""; @@ -60,7 +68,7 @@ sub fetch_merchant { my $q = shift; my $varenv = shift; my $req_coo = shift || ""; - my $req_merchant_id = shift || ""; + my $req_merchant_id = shift || "$dbt->{appsframe}->{'sharee.bike'}->{merchant_id}";#defaults to sharee.bike my $return = { aowner => "", @@ -885,7 +893,7 @@ sub service_work { }#end service_work -#bike reservation +#bike adhock booking sub booking_request(){ my $self = shift; my $q = shift; @@ -900,9 +908,18 @@ sub booking_request(){ my $state = $q->escapeHTML($q->param('state')) || ""; my $lock_state = $q->escapeHTML($q->param('lock_state')) || ""; + my $now_dt = DateTime->now( time_zone => "Europe/Berlin" ); + my $reserv_starttime = $q->escapeHTML($q->param('reserv_starttime')) || $now_dt; + my $reserv_endtime = $q->escapeHTML($q->param('reserv_endtime')) || $now_dt; + my $calreserv = $q->escapeHTML($q->param('calreserv')) || 0; + + my $request_para = { + start_time => $reserv_starttime, + end_time => $reserv_endtime, + }; + my $dbh = ""; my $pos_id=""; - my $now_dt = strftime "%Y-%m-%d %H:%M", localtime; my $response_state = "OK"; my $response_text = ""; @@ -1001,21 +1018,61 @@ sub booking_request(){ #Rental is only permitted if no invoice with payment_fault or opos $bw->log("Rental is only permitted if no invoice with payment_fault or opos: $sum_balance <= 1 && !$ctt_opos->{c_id}, userID: $auth->{c_id} ",$sum_balance,""); if($sum_balance <= 1 && !$ctt_opos->{c_id}){ - #2 = "requested" - $pos_id = $dbt->insert_pos($dbh,$ctt->{c_id},$ct_bike,$ct_station,$auth,$ct_tariff,$now_dt,$bike,"2",$owner,$sig_book); - $bw->log("booking_request insert_pos:",$pos_id,""); + my $booking_state = 2; + $booking_state = 7 if($calreserv); + my $timestamp = DateTime->now( time_zone => "Europe/Berlin" ); + my $now_time = $strp->parse_datetime($timestamp); + + my $dtstart = $strp->parse_datetime($reserv_starttime); + my $dtend = $strp->parse_datetime($reserv_endtime); + + my $start_loc = $dtstart->strftime("%d.%m.%Y %H:%M"); + my $end_loc = $dtend->strftime("%d.%m.%Y %H:%M"); + + if($dtend >= $now_time){ + my $until_time = $now_time->add( weeks => 4 ); + #max allowed future rental + if($dtend > $until_time){ + $response_state = "Failure 4261, 4 weeks reserv timerange"; + $response_text = "Abbruch, Reservierungen sind maximal 4 Wochen im voraus erlaubt"; + }else{ + $pos_id = $dbt->insert_pos($dbh,$ctt->{c_id},$ct_bike,$ct_station,$auth,$ct_tariff,$request_para,$bike,$booking_state,$owner,$sig_book); + $bw->log("booking_request insert_pos:",$pos_id,""); + } + }else{ + $response_state = "Failure 4260, reserv timerange"; + $response_text = "Abbruch, die Mietzeit liegt in der Vergangenheit"; + } if($pos_id){ - $response_state = "OK, bike " . $bike . " succesfully requested"; - $response_text = "Rad $bike wurde erfolgreich für 15 Min. reserviert"; $bw->log("booking_request insert contenttranspos pos_id: $pos_id\n","",""); my $update_ctt = { table => "contenttrans", c_id => $ctt->{c_id}, + mtime => "now()", + owner => $owner, }; $dbt->update_one($dbh,$update_ctt,"start_time='$now_dt'"); + # state=7 meens reserved, prevents auto available after 15min + if($calreserv){ + $response_state = "OK, bike " . $bike . " succesfully reserved"; + $response_text = "Erfolgreiche Buchung: $start_loc - $end_loc."; + } + #update waren bike state only if adhock booking + elsif(!$calreserv){ + my $update_ct = { + table => "content", + c_id => $ct_bike->{c_id}, + mtime => "now()", + owner => $owner, + }; + $response_state = "OK, bike " . $bike . " succesfully adhock requested"; + $response_text = "Rad $bike wurde erfolgreich für 15 Min. adhock reserviert"; + } + + #BVB once auto-coupon until 2023-08-31 #disabled if(1==2 && $varenv->{dbname} eq "sharee_bvb"){ @@ -1048,7 +1105,7 @@ sub booking_request(){ } }#end BVB auto-coupon - }else{ + }elsif($response_state !~ /Failure/){ $response_state="Failure 1007: booking request fails"; $response_text="Entschuldigung, es ist ein Fehler aufgetreten. Bitte kontaktieren Sie unsere hotline damit wir das Problem lösen können"; } @@ -1089,7 +1146,7 @@ sub booking_request(){ } #booking_cancel changed to booking_update -sub booking_update(){ +sub booking_update { my $self = shift; my $q = shift; my $varenv = shift; @@ -1097,13 +1154,15 @@ sub booking_update(){ my $owner = shift || 0; my $sig_book = shift || {}; my $dbh = ""; + my $rows = 0; + my $user_agent = $q->user_agent(); my $state = $q->escapeHTML($q->param('state')) || ""; my $lock_state = $q->escapeHTML($q->param('lock_state')) || ""; - my $rows = 0; - my $user_agent = $q->user_agent(); - #my $bike = $q->param('bike'); - #my $bike_id = $1 if($q->escapeHTML($q->param('bike')) =~ /(\d+)/); + my $pos_id = $q->escapeHTML($q->param('pos_id')) || 0; + my $reserv_starttime = $q->escapeHTML($q->param('reserv_starttime')) || ""; + my $reserv_endtime = $q->escapeHTML($q->param('reserv_endtime')) || ""; + my $bike = $q->escapeHTML($q->param('bike')) || ""; my $bike_id = $bike; $bike_id =~ s/S[1-9]X/SX/; @@ -1137,9 +1196,26 @@ sub booking_update(){ fetch => "one", barcode => $bike_id, ca_id => $auth->{c_id}, - int10 => "IN::('3','2')", + int10 => "IN::('2','3')", }; + if($pos_id){ + $booking_pos->{c_id} = $pos_id; + $booking_pos->{int10} = 7; + }elsif($reserv_starttime && $reserv_endtime){ + $booking_pos->{start_time} = $reserv_starttime; + $booking_pos->{end_time} = $reserv_endtime; + $booking_pos->{int10} = 7; + } $record_pos = $dbt->fetch_tablerecord($dbh,$booking_pos) if($bike_id > 0 && $auth->{c_id} > 0); + #trying to get reserved in timerange + if($state eq "occupied" && !$record_pos->{c_id}){ + $booking_pos->{int10} = 7; + $booking_pos->{start_time} = "<=::now()"; + $booking_pos->{end_time} = ">=::now()"; + $record_pos = $dbt->fetch_tablerecord($dbh,$booking_pos) if($bike_id > 0 && $auth->{c_id} > 0); + } + $booking_values->{pos_id} = $record_pos->{c_id} || ""; + if(!$record_pos->{c_id}){ $booking_values->{response_state} = "Failure 758: Can not find bike " . $q->param('bike') . " rental or reservation on varenv-dbname: $varenv->{dbname}"; $booking_values->{response_text} = "Keine Miete oder Reservierung zu Rad " . $q->param('bike') . " gefunden."; @@ -1238,27 +1314,49 @@ sub booking_update(){ $update_pos->{txt27} = $q->escapeHTML($q->param('app_debug')) if($q->param('app_debug')); } - #if($state eq canceled && $record_pos.state eq requested && $record_pos.lock_state eq locked) - if($state_key == 6 && $record_pos->{int10} == 2 && $record_pos->{int20} == 1){ + #if($state eq canceled && $record_pos.lock_state eq locked && $record_pos.state eq requested) + if($state_key == 6 && $record_pos->{int20} == 1 && $record_pos->{int10} == 2){ $rows = $dbt->delete_content($dbh,"contenttranspos",$record_pos->{c_id}); if($rows > 0){ - $update_cc->{int10} = 1 if($record_cc->{int10} == 2);#only if still requested $booking_values->{response_state} = "OK: canceled bike " . $q->param('bike'); $booking_values->{response_text} = "Rad " . $q->param('bike') . " wurde erfolgreich storniert"; $booking_values->{state} = "available"; }else{ $booking_values->{response_state} = "Failure 2002: cancel bike " . $q->param('bike') . " fails, bike not requested"; - $booking_values->{response_text} = "Keine Reservierung zu Rad " . $q->param('bike') . " gefunden."; + $booking_values->{response_text} = "Keine adhock Reservierung zu Rad " . $q->param('bike') . " gefunden."; } - }else{ + } + #canceled reserved + elsif($state_key == 6 && $record_pos->{int20} == 1 && $record_pos->{int10} == 7){ + my $timestamp = DateTime->now( time_zone => "Europe/Berlin" ); + my $now_time = $strp->parse_datetime($timestamp); + my $reserv_starttime = DateTime::Format::Pg->parse_datetime($record_pos->{start_time}); + + #TODO define minmax hours before cancel reservation + if($now_time < $reserv_starttime){ + $rows = $dbt->delete_content($dbh,"contenttranspos",$record_pos->{c_id}); + if($rows > 0){ + $booking_values->{response_state} = "OK: canceled reserved bike " . $q->param('bike'); + $booking_values->{response_text} = "Gebuchtes Rad " . $q->param('bike') . " wurde erfolgreich storniert"; + $booking_values->{state} = "available"; + }else{ + $booking_values->{response_state} = "Failure 2082: cancel reserved bike " . $q->param('bike') . " fails, bike not reserved"; + $booking_values->{response_text} = "Keine Buchung zu Rad " . $q->param('bike') . " gefunden."; + } + }else{ + $booking_values->{response_state} = "Failure 2092: cancel reserved bike " . $q->param('bike') . " fails, too late"; + $booking_values->{response_text} = "Reservierung " . $q->param('bike') . " kann max. 1 Stunde vor Buchungsstart storniert werden."; + } + } + else{ #prevent reset occupied values OR only if genkey defined #if(($state eq "occupied" && $record_pos->{txt10} =~ /requested/) || ($state eq "occupied" && $record_pos->{txt10} =~ /occupied/ && $q->param('genkey') eq "1")) - if(($state_key == 3 && $record_pos->{int10} == 2) || ($state_key == 3 && $record_pos->{int10} == 3 && $q->param('genkey') eq "1")){ + if(($state_key == 3 && ($record_pos->{int10} == 2 || $record_pos->{int10} == 7)) || ($state_key == 3 && $record_pos->{int10} == 3 && $q->param('genkey') eq "1")){ $update_pos->{start_time} = "now()"; - $update_pos->{end_time} = "now()"; + #$update_pos->{end_time} = "now()";#don't set anymore because of revserv time $update_pos->{txt05} = "$record_cc->{txt06}";#pos start GPS from content end GPS $update_pos->{int06} = "$record_cc->{int04}";#pos start station from content station $update_pos->{txt12} = "$dbt->{operator}->{$varenv->{dbname}}->{oprefix}";#pos start station prefix @@ -1292,12 +1390,16 @@ sub booking_update(){ #client GPS must have. sigo ist done without client gps - if(($gps_data->{gps} && $gps_data->{gps_age_minutes} <= 3) || ($record_pos->{int11} == 3)){ + #if(($gps_data->{gps} && $gps_data->{gps_age_minutes} <= 3) || ($record_pos->{int11} == 3)){ + #2024-03-15 app secures gps_age, we don't need it + if($gps_data->{gps} || $record_pos->{int11} == 3){ #geofencing for Ilockit my $geo_distance_next = 100000; my $station_next = 0; my $geo_debug=""; my $rows_end = 0; + + #TODO stations_available on A-A out-of-geofence have to be checked my ($stations,$stations_raw) = $self->stations_available($q,$varenv,$auth,$record_pos,""); foreach my $id (sort { $stations_raw->{$a}->{barcode} <=> $stations_raw->{$b}->{barcode} } keys (%$stations_raw)){ @@ -1415,8 +1517,8 @@ sub booking_update(){ $booking_values->{response_state} = "Failure 2012: occupied bike " . $q->param('bike') . " cannot be state $state and $lock_state"; $booking_values->{response_text} = "Rad " . $q->param('bike') . " ist in Benutzung und kann somit nicht storniert werden."; }elsif($state_key == 3 && $record_pos->{int10} == 1){ - $booking_values->{response_state} = "Failure 2016: available bike " . $q->param('bike') . " have to be at first reserved, thats because cannot be $state"; - $booking_values->{response_text} = "Rad " . $q->param('bike') . " wurde nicht reserviert und kann somit nicht gemietet werden."; + $booking_values->{response_state} = "Failure 2016: available bike " . $q->param('bike') . " have to be at first requested, thats because cannot be $state"; + $booking_values->{response_text} = "Rad " . $q->param('bike') . " wurde nicht requested und kann somit nicht gemietet werden."; }else{ $booking_values->{response_state} = "Failure 2035: bike " . $q->param('bike') . " state change to state $state and $lock_state not possible."; @@ -1600,15 +1702,15 @@ sub smartlock { $lock_value = 1; $update_cc->{int20} = "$lock_value"; $update_pos->{int20} = "$lock_value"; - $update_pos->{end_time} = "now()"; + #$update_pos->{#end_time} = "now()"; $update_cc->{txt06} = $gps_data->{gps};#end content coordinates $update_pos->{txt06} = $gps_data->{gps};#end pos coordinates $update_pos->{int21} = $gps_data->{gps_age_minutes}; $update_pos->{int22} = $gps_data->{gps_accuracy}; $update_pos->{int23} = "null";# only computed on rental-end - $booking_values->{response_state} = "OK: bike " . $q->param('bike') . " locked confirmed. "; - $booking_values->{response_text} = "Schloss schließen von Rad " . $q->param('bike') . " bestätigt. "; + $booking_values->{response_state} = " OK: bike " . $q->param('bike') . " locked confirmed. "; + $booking_values->{response_text} .= " Schloss schließen von Rad " . $q->param('bike') . " bestätigt. "; }elsif($q->param('lock_state') eq "unlocked"){ $lock_value = 2; @@ -1810,9 +1912,43 @@ sub rental_to_feedback{ }#end rental_to_feedback +#blocked bikes, reserved from now until +4 weeks +sub bikes_reserved { + my $self = shift; + my $q = shift; + my $dbh = shift; + + my $timestamp = DateTime->now( time_zone => "Europe/Berlin" ); + my $start_time = $timestamp->strftime("%Y-%m-%d %H:%M"); + my $now_time = $strp->parse_datetime($timestamp); + my $until_time = $now_time->add( weeks => 4 ); + my $end_time = $until_time->strftime("%Y-%m-%d %H:%M"); + + if($q->param('request') eq "booking_request" && $q->param('reserv_starttime') && $q->param('reserv_endtime')){ + $start_time = $q->escapeHTML($q->param('reserv_starttime')); + $end_time = $q->escapeHTML($q->param('reserv_endtime')); + } + + my $pref = { + table_pos => "contenttranspos", + fetch => "all", + keyfield => "c_id", + int10 => "IN::('2','3','7')", + template_id => 205, + start_date_time => $start_time, + end_date_time => $end_time, + }; + + my $record = {}; + $record = $dbt->collect_transpos($dbh,$pref); + + return $record; +} + #user bikes occupied sub user_bikes_occupied { my $self = shift; + my $q = shift; my $dbh = shift; my $auth = shift; my $show_dialog = shift || ""; @@ -1825,9 +1961,20 @@ sub user_bikes_occupied { template_id => "218",#Faktura tpl_id keyfield => "c_id", ca_id => "$auth->{c_id}", - int10 => "IN::('3','2')", + int10 => "IN::('2','3')", "ct.close_time" => "is::null", }; + + if($q->param('calreserv')){ + $pref->{int10} = "IN::('2','3','7')"; + $pref->{end_time} = ">=::now()"; + } + + if($q->param('request') eq "booking_request"){ + $pref->{int10} = "IN::('2','3','7')"; + $pref->{start_time} = ">=::now()"; + } + $pref->{int06} = $station if($station);#pos start-station my $record = {}; @@ -1887,7 +2034,7 @@ sub rentals(){ #bikes_available -sub bikes_available(){ +sub bikes_available { my $self = shift; my $q = shift; my $varenv_prim = shift; @@ -1899,6 +2046,9 @@ sub bikes_available(){ my $station = $q->escapeHTML($q->param('station')) || ""; my $station_id = ""; $station_id = $1 if($station =~ /(\d+)$/); + my $station_gps = ""; + $station_gps = $q->escapeHTML($q->param('Lat')) || ""; + $station_gps .= ", " . $q->escapeHTML($q->param('Lng')) || "";#TOD check space after , #to get A-A key for text my $pref_st = { @@ -1910,6 +2060,7 @@ sub bikes_available(){ }; my $record_st = {}; $pref_st->{int04} = "$station_id" if($station_id); + $pref_st->{txt06} = "$station_gps" if($station_gps); $record_st = $dbt->fetch_record($dbh,$pref_st); my $bike = $q->escapeHTML($q->param('bike')) || ""; @@ -1926,6 +2077,8 @@ sub bikes_available(){ template_id => "205", int10 => "1",#1 = "available" }; + $pref->{int10} = "IN::('1','2','3')" if($q->param('calreserv'));#take all on calreserv + my $tariff_content = {}; my $adrtarif_hash = {}; $authed = 1 if(ref($auth) eq "HASH" && $auth->{c_id} > 0); @@ -1948,8 +2101,7 @@ sub bikes_available(){ my $record = {}; $record = $dbt->fetch_record($dbh,$pref) if(ref($bike_node) eq "ARRAY" && @{$bike_node}[0]); my $op_return = {}; - - my $td_template = $dbt->rental_description_template($varenv_prim); + my $td_template = $dbt->rental_description_template($varenv_prim); #return list of occupied/requested bikes my $adjust_freedtime = 0; @@ -1959,7 +2111,17 @@ sub bikes_available(){ $further_freedtime_available = $pri->count_freedrental("rentals by bikes_available",$varenv,$auth->{c_id},$cttpos->{$id},$adjust_freedtime); } + my $bikes_blocked = {}; + $bikes_blocked = $self->bikes_reserved($q,$dbh) if($R::calreserv); + foreach my $id (sort { $record->{$a}->{barcode} <=> $record->{$b}->{barcode} } keys (%$record)){ + #return all reserved bikes + foreach my $rid (sort { $bikes_blocked->{$a}->{barcode} <=> $bikes_blocked->{$b}->{barcode} } keys (%$bikes_blocked)){ + if($record->{$id}->{barcode} == $bikes_blocked->{$rid}->{barcode}){ + $return->{$id}->{blocked}->{$rid}->{start_time} = $bikes_blocked->{$rid}->{start_time}; + $return->{$id}->{blocked}->{$rid}->{end_time} = $bikes_blocked->{$rid}->{end_time}; + } + } $return->{$id}->{authed} = "$authed"; $return->{$id}->{station} = "$dbt->{operator}->{$varenv->{dbname}}->{oprefix}$record->{$id}->{int04}"; $return->{$id}->{aa_ride} = "0"; @@ -2113,10 +2275,7 @@ sub bikes_all(){ if(looks_like_number($bike_id)){ $pref->{ barcode} = "=::$bike_id"; #2022-12-02 not select occupied bikes in servicetool by direct select-bike - $pref->{int10} = "!=::3"; - #}else{ - #2023-05-23 not select requested and occupied bikes in servicetool by list-bikes? - #$pref->{int10} = "IN::(1,4,5,6)"; + #$pref->{int10} = "!=::3";#restriction disabled } my $record = {}; @@ -2201,10 +2360,11 @@ sub bikes_all(){ #if($q->param('authcookie') && $dbt->{merchant_ids}->{$varenv->{merchant_id}}->{id} && $dbt->{merchant_ids}->{$varenv->{merchant_id}}->{id} == 187 && scalar(@{$user_tour} >= 1)){ if($q->param('authcookie') && $varenv->{merchant_id} && $varenv->{merchant_id} eq $dbt->{appsframe}->{shareetool}->{merchant_id} && scalar(@{$user_tour} >= 1)){ #2023-01-18 temporarly for Konstanz deactivated - if(${$user_tour}[0] !~ /KN\d/){ + #2024-04-08 activated + #if(${$user_tour}[0] !~ /KN\d/){ my @service_code = split(/\s/,$record->{$id}->{txt23}); $return->{$id}->{service_code} = [@service_code]; - } + #} } }elsif($record->{$id}->{int11} eq "3"){ $return->{$id}->{system} = "sigo"; @@ -2368,12 +2528,14 @@ sub stations_available(){ if($record->{$id}->{int04} == $record_bikes->{$b_id}->{int04}){ push @bike_ids,$dbt->{operator}->{$varenv->{dbname}}->{oprefix} . $record_bikes->{$b_id}->{barcode}; $bike_count++; - $fleed = { + if($record_bikes->{$b_id}->{fleed_phone} && $record_bikes->{$b_id}->{fleed_email}){ + $fleed = { name => "$record_bikes->{$b_id}->{fleed_name}", hours => "$record_bikes->{$b_id}->{fleed_hours}", phone => "$record_bikes->{$b_id}->{fleed_phone}", email => "$record_bikes->{$b_id}->{fleed_email}", - }; + }; + } } } @@ -2436,6 +2598,8 @@ sub stations_available(){ operator_email => $hotline_data->{txt08}, }; } + #$return->{$id}->{operator_data}->{operator_name} = Encode::encode('utf-8', Encode::decode('iso-8859-1',$return->{$id}->{operator_data}->{operator_name})) if($return->{$id}->{operator_data}->{operator_name}); + #$return->{$id}->{operator_data}->{operator_hours} = Encode::encode('utf-8', Encode::decode('iso-8859-1',$return->{$id}->{operator_data}->{operator_hours})) if($return->{$id}->{operator_data}->{operator_hours}); #not desired 2024-02-15 if(1==2){ @@ -3183,7 +3347,7 @@ sub auth_verify(){ my $last_used_operator = $record->{txt19};#check if this is primary and/or useable #$bw->log("last_used_operator selected by txt19:",$last_used_operator,""); if($last_used_operator){ - my $dbh_operator = $dbt->dbconnect_extern("$last_used_operator","iso-8859-1"); + my $dbh_operator = $dbt->dbconnect_extern("$last_used_operator",""); my $hotline_hash = { table => "contentuser", fetch => "one", @@ -3198,8 +3362,10 @@ sub auth_verify(){ "operator_phone" => "", "operator_email" => "", }; - $return->{last_used_operator}->{operator_name} = Encode::encode('utf-8', Encode::decode('iso-8859-1',$hotline_data->{txt01})) if($hotline_data->{txt01}); - $return->{last_used_operator}->{operator_hours} = Encode::encode('utf-8', Encode::decode('iso-8859-1',$hotline_data->{txt84})) if($hotline_data->{txt84}); + #$return->{last_used_operator}->{operator_name} = Encode::encode('utf-8', Encode::decode('iso-8859-1',$hotline_data->{txt01})) if($hotline_data->{txt01}); + #$return->{last_used_operator}->{operator_hours} = Encode::encode('utf-8', Encode::decode('iso-8859-1',$hotline_data->{txt84})) if($hotline_data->{txt84}); + $return->{last_used_operator}->{operator_name} = $hotline_data->{txt01} if($hotline_data->{txt01}); + $return->{last_used_operator}->{operator_hours} = $hotline_data->{txt84} if($hotline_data->{txt84}); $return->{last_used_operator}->{operator_phone} = $hotline_data->{txt07} if($hotline_data->{txt07}); $return->{last_used_operator}->{operator_email} = $hotline_data->{txt08} if($hotline_data->{txt08}); } diff --git a/copri4/main/src/Mod/APIjsonclient.pm b/copri4/main/src/Mod/APIjsonclient.pm index e5ac995..2b31dbd 100755 --- a/copri4/main/src/Mod/APIjsonclient.pm +++ b/copri4/main/src/Mod/APIjsonclient.pm @@ -84,7 +84,10 @@ sub loop_sharees { $bw->log("shareetool select_users $return_merchant->{aowner} on $op_name->{database}->{dbname} $authraw->{c_id} $authraw->{txt17}",$users_serviceapp->{u_id},""); } #every sharee client must have a merchant_id which associate a project - if($op_name->{operatorApp} && ($users_serviceapp->{u_id} || $return_merchant->{merchant_id} eq $globalconf{website}->{'shareeweb-sharee'}->{merchant_id} || ($return_merchant->{project_id} eq $op_name->{project}))){ + #shareeweb gets all ($return_merchant->{merchant_id} eq $globalconf{website}->{'shareeweb-sharee'}->{merchant_id) + #sharee.bike gets also Bayern + #print $bw->log("Operator getter if($op_name->{operatorApp} && ($users_serviceapp->{u_id} || $return_merchant->{merchant_id} eq $globalconf{website}->{'shareeweb-sharee'}->{merchant_id} || ($return_merchant->{project_id} eq $op_name->{project}))){","",""); + if($op_name->{operatorApp} && ($users_serviceapp->{u_id} || $return_merchant->{merchant_id} eq $globalconf{website}->{'shareeweb-sharee'}->{merchant_id} || ($return_merchant->{project_id} eq $op_name->{project}) || ($return_merchant->{project_id} eq "Freiburg" && $op_name->{project} eq "Bayern"))){ $o++; #stations_available with optional caching if($rest_hash->{request} eq "stations_available"){ @@ -252,9 +255,10 @@ sub fetch_operator_json { my $self = shift; my $operator_server = shift || ""; my $rest = shift || ""; - my $operator_request = "$operator_server?$rest"; + my $operator_request = "$operator_server"; + $operator_request = "$operator_server?$rest" if($rest); - #$bw->log("fetch_operator_json >> $operator_request","$operator_request",""); + $bw->log("fetch_operator_json >> $operator_server","$operator_request",""); my $req = HTTP::Request->new(GET => "$operator_request"); $req->content_type('application/x-www-form-urlencoded'); $req->content($rest); diff --git a/copri4/main/src/Mod/APIjsonserver.pm b/copri4/main/src/Mod/APIjsonserver.pm index 63c84f5..6ccb982 100755 --- a/copri4/main/src/Mod/APIjsonserver.pm +++ b/copri4/main/src/Mod/APIjsonserver.pm @@ -71,7 +71,7 @@ my $response = { apiserver => "$apiserver", response => "$respreq", uri_primary => "$dbt->{primary}->{sharee_primary}->{primaryApp}", - copri_version => "4.1.23.29", + copri_version => "4.1.23.30", user_id => "", authcookie => "", new_authcoo => "0", @@ -243,18 +243,31 @@ elsif($q->param('request') eq "booking_request"){ $bike_id = $1 if($bike_id =~ /(\d+)/); #check count of occcupied/requested bikes - my $record = $apif->user_bikes_occupied($dbh,$authraw,""); + my $record = {}; my $max_rental_count = 3; $max_rental_count = 10 if($authraw->{int09}); my $rental_count=0; my $still_requested = 0; - foreach my $id (keys(%$record)){ + if($R::calreserv){ + $record = $apif->bikes_reserved($q,$dbh); + foreach my $id (keys(%$record)){ + if($bike_id && $bike_id == $record->{$id}->{barcode}){ + $still_requested = 1; + $response->{response_state} = "OK, bike " . $bike . " already in timerange reserved"; + $response->{response_text} = "Rad " . $bike . " ist für diesen Zeitraum nicht verfügbar."; + } + } + + }else{ + $record = $apif->user_bikes_occupied($q,$dbh,$authraw,""); + foreach my $id (keys(%$record)){ $rental_count++; if($bike_id && $bike_id == $record->{$id}->{barcode}){ $still_requested = 1; $response->{response_state} = "OK, bike " . $bike . " already requested or occupied"; - $response->{response_text} = "Fahrrad Nr. " . $bike . " ist bereits reserviert"; + $response->{response_text} = "Rad " . $bike . " ist bereits reserviert"; } + } } if(!$still_requested){ @@ -293,7 +306,7 @@ elsif($q->param('request') eq "booking_request"){ rentalId => "", }; - if(!$ct_bike->{barcode} || !$ct_bike->{int10} || $ct_bike->{int10} != 1){ + if(!$ct_bike->{barcode} || !$ct_bike->{int10}){ $response->{response_state} = "Failure 2001: booking bike $bike fails, bike not available"; $response->{response_text} = "Mietrad $bike ist nicht verfügbar. Ist das Mietrad für sie freigeschaltet? Bitte überprüfen Sie Ihre Profildaten auf Vollständigkeit"; }elsif(!$ct_tariff->{barcode}){#ist will not happen, because of prios tariff select in fetch_bike_tariff @@ -318,7 +331,7 @@ elsif($q->param('request') eq "booking_request"){ table => "contenttranspos", fetch => "one", barcode => "$bike_id", - int10 => "2", + int10 => "IN::('2','7')", ca_id => "$authraw->{c_id}", }; my $dbh = ""; @@ -342,7 +355,7 @@ elsif($q->param('request') eq "booking_request"){ } #return list of occupied/requested bikes - $record = $apif->user_bikes_occupied($dbh,$authraw,""); + $record = $apif->user_bikes_occupied($q,$dbh,$authraw,""); $response->{bikes_occupied} = $apif->rentals(\%varenv,$record,$authraw,"1");#returns JSON rental values }#end still_requested @@ -379,7 +392,7 @@ elsif($q->param('request') eq "booking_cancel" || $q->param('request') eq "booki $bike_id =~ s/S[1-9]X/SX/; $bike_id = $1 if($bike_id =~ /(\d+)/); - my $ctpos = {}; + my $ctpos = {};#can be removed after sig my $booking_pos = { table => "contenttranspos", fetch => "one", @@ -426,7 +439,7 @@ elsif($q->param('request') eq "booking_cancel" || $q->param('request') eq "booki ($rows, $booking_values) = $apif->booking_update($q,\%varenv,$authraw,$aowner,$sig_book); $response = {%$response, %$booking_values}; - my $record = $apif->user_bikes_occupied($dbh,$authraw,"show_dialog"); + my $record = $apif->user_bikes_occupied($q,$dbh,$authraw,"show_dialog"); $response->{bikes_occupied} = $apif->rentals(\%varenv,$record,$authraw,"1"); #update on Ilockit @@ -435,12 +448,12 @@ elsif($q->param('request') eq "booking_cancel" || $q->param('request') eq "booki $apif->stations_caching($q,\%varenv,$authraw); $response = {%$response, %$booking_values}; - my $record = $apif->user_bikes_occupied($dbh,$authraw,""); + my $record = $apif->user_bikes_occupied($q,$dbh,$authraw,""); $response->{bikes_occupied} = $apif->rentals(\%varenv,$record,$authraw,"1"); } }#end occupied|available else{ - my $record = $apif->user_bikes_occupied($dbh,$authraw,""); + my $record = $apif->user_bikes_occupied($q,$dbh,$authraw,""); $response->{bikes_occupied} = $apif->rentals(\%varenv,$record,$authraw,"1"); } @@ -478,7 +491,7 @@ elsif($q->param('request') eq "user_bikes_occupied"){ ($auth,$authraw) = $apif->auth_verify($q); if(ref($auth) eq "HASH" && $auth->{authcookie}){ $response = { %$response, %$auth }; - my $record = $apif->user_bikes_occupied($dbh,$authraw,"show_dialog"); + my $record = $apif->user_bikes_occupied($q,$dbh,$authraw,"show_dialog"); if($R::withoutkey){ $response->{bikes_occupied} = $apif->rentals(\%varenv,$record,$authraw,""); }else{ @@ -531,9 +544,12 @@ elsif($q->param('request') eq "bikes_available"){ my $station_id = ""; $station_id = $1 if($station =~ /(\d+)$/); - $record = $apif->user_bikes_occupied($dbh,$authraw,"",$station_id); + $record = $apif->user_bikes_occupied($q,$dbh,$authraw,"",$station_id); $response->{bikes_occupied} = $apif->rentals(\%varenv,$record,$authraw,"1");#returns JSON rental values } + + #$response->{bikes_blocked} = $apif->bikes_reserved($q,$dbh) if($R::calreserv); + } if(ref($response->{bikes}) ne "HASH"){ $response->{response_state} = "Failure 5003: cannot find any user defined bike tariff"; @@ -742,13 +758,15 @@ elsif($q->param('request') eq "stations_available"){ } #App merchant message + #if($user_agent =~ /konrad/i && $dbt->{copri_conf}->{betau_id}->{$authraw->{c_id}} && $varenv{cms}->{'App-merchant-message'}->{txt}){ if($user_agent =~ /konrad/i && $varenv{cms}->{'App-merchant-message'}->{txt}){ #if($epoch_now >= $epoch_start && $epoch_now <= $epoch_end){ $response->{merchant_message} = $varenv{cms}->{'App-merchant-message'}->{txt}; #} } #App merchant message bayern - if($user_agent =~ /bayern/i && $varenv{cms}->{'App-merchant-message-bayern'}->{txt}){ + #if($user_agent =~ /bayern/i && $varenv{cms}->{'App-merchant-message-bayern'}->{txt}){ + if($user_agent =~ /bayern/i && $user_agent_subversion <= 380 && $varenv{cms}->{'App-merchant-message-bayern'}->{txt}){ #if($epoch_now >= $epoch_start && $epoch_now <= $epoch_end){ $response->{merchant_message} = $varenv{cms}->{'App-merchant-message-bayern'}->{txt}; #} diff --git a/copri4/main/src/Mod/DBtank.pm b/copri4/main/src/Mod/DBtank.pm index 84d13d7..1609106 100755 --- a/copri4/main/src/Mod/DBtank.pm +++ b/copri4/main/src/Mod/DBtank.pm @@ -876,6 +876,8 @@ sub fetch_tablerecord(){ $where .= " and $key $op $value"; }elsif($op eq "IN"){ $where .= " and $key $op $value"; + }elsif($op eq "NOT IN"){ + $where .= " and $key $op $value"; }else{ $where .= " and $key $op '$value'"; } @@ -894,12 +896,10 @@ sub fetch_tablerecord(){ }elsif($key =~ /^(c_id|u_id|cc_id|ct_id|ca_id|barcode|int\d+|owner|template_id)$/ && (looks_like_number($value) || $value eq "null")){ if($value eq "null"){ $where .= " and ($key is null OR $key = 0)"; - }elsif($value eq "nullOR1"){ - $where .= " and ($key is null OR $key = 1)"; }else{ $where .= " and $key $op $value"; } - }elsif($key =~ /int\d+/ && $op eq "IN"){ + }elsif($key =~ /int\d+|template_id/ && $op eq "IN"){ $where .= " and $key $op $value"; } } @@ -917,7 +917,7 @@ sub fetch_tablerecord(){ my $rc = $sth->execute(); #debug $fetch->{table} eq "contenttranspos" on Failure 758 - #$bw->log("DBtank fetch_tablerecord source-dbname $source:",$sql,"") if($debug && $fetch->{table} eq "contenttranspos"); + $bw->log("DBtank fetch_tablerecord source-dbname $source:",$sql,"") if($debug && $fetch->{table} eq "contenttranspos"); my $record = { c_id => 0 }; if($fetch->{fetch} eq "all" && $fetch->{keyfield}){ @@ -1239,8 +1239,10 @@ sub collect_transpos { $search->{offset} = 0 if(!$search->{offset}); - my $where = "where cp.ct_id=ct.c_id"; + my $where = "where 1=1"; + $where .= " and cp.ct_id=ct.c_id" if($search->{table}); $where .= " and cp.int05 is null";#do not select sub workflow docs like storno + foreach my $key (keys (%$search)){ $search->{$key} =~ s/^\s//g; $search->{$key} =~ s/\s$//g; @@ -1256,6 +1258,7 @@ sub collect_transpos { $where .= " and cp.$key = $search->{$key}" if($key eq "int20" && looks_like_number($search->{$key})); $where .= " and cp.$key = $search->{$key}" if($key eq "barcode" && looks_like_number($search->{$key})); $where .= " and cp.$key = $search->{$key}" if($key eq "int12" && looks_like_number($search->{$key})); + $where .= " and cp.$key = $search->{$key}" if($key eq "template_id" && looks_like_number($search->{$key})); $where .= " and (cp.$key = $search->{$key} OR cp.owner_end = $search->{$key})" if($key eq "owner" && looks_like_number($search->{$key})); $where .= " and (cp.$key is $search->{$key} OR cp.$key = 0)" if($key eq "int34" && $search->{$key} eq "null"); $where .= " and cp.$key ilike '%$search->{$key}%'" if($key eq "txt23" && $search->{$key}); @@ -1267,24 +1270,31 @@ sub collect_transpos { #adding itime to get also not rental dependent entries if($search->{start_date_time} && $search->{end_date_time}){ - $where .= " and ((cp.end_time >= '$search->{start_date_time}' and cp.end_time <= '$search->{end_date_time}' and cp.start_time <= '$search->{end_date_time}') OR (cp.itime >= '$search->{start_date_time}' and cp.itime <= '$search->{end_date_time}'))"; + my $or_itime = ""; + #because of bikes_reserved, don't search others when bike is requested + if(!$search->{template_id} || $search->{template_id} != 205){ + $or_itime = "OR (cp.itime >= '$search->{start_date_time}' and cp.itime <= '$search->{end_date_time}')"; + } + $where .= " and ((cp.end_time >= '$search->{start_date_time}' and cp.end_time <= '$search->{end_date_time}' and cp.start_time <= '$search->{end_date_time}') $or_itime)"; + }elsif($search->{start_time_interval}){ $where .= " and cp.start_time <= $search->{start_time_interval}"; } $where .= " ORDER BY cp.c_id DESC LIMIT $search->{limit} OFFSET $search->{offset}" if($search->{limit}); - my $sql = "SELECT cp.* from $search->{table} cp, contenttrans ct $where"; - #2024-02-22 not used anymore - #for system messaging - #if($messaging){ - # $sql = "SELECT cp.*,ct.txt07 AS phone,ct.txt08 AS email,ct.txt11 AS lang from $search->{table} cp, contenttrans ct $where"; - #} + my $sql = ""; + if($search->{table}){ + $sql = "SELECT cp.* from $search->{table_pos} cp, $search->{table} ct $where"; + }else{ + $sql = "SELECT cp.* from $search->{table_pos} cp $where"; + } + my $sth = $dbh->prepare($sql); my $rc = $sth->execute(); my $ct = $sth->fetchall_hashref("c_id"); - #$bw->log("collect_transpos",$sql,"") if($debug); + $bw->log("collect_transpos",$sql,"") if($debug); return $ct; -} +}#end collect_transpos #collect contenttheftpos sub collect_theftpos { @@ -1534,13 +1544,14 @@ sub insert_pos(){ my $self = shift; my $dbh = shift || $dbh_intern; my $ctt_id = shift; - my $ct = shift || {}; + my $ct = shift || {};#bike or part content my $ct_station = shift || {}; my $ctadr = shift || {}; - my $ct_tariff = shift || ""; - my $endRental = shift || "0000-00-00"; + my $ct_tariff = shift || {}; + #my $endRental = shift || "0000-00-00"; + my $request_para = shift || {}; my $ct_name = shift || ""; - my $status = shift || ""; + my $booking_state = shift || ""; my $owner = shift || ""; my $sig_book = shift || {}; @@ -1601,7 +1612,7 @@ sub insert_pos(){ my $sth; #Verleihräder if($ct->{template_id} && $ct->{template_id} == 205){#Leihrad_list - $sth = $dbh->prepare("INSERT INTO contenttranspos (ct_id,cc_id,ca_id,ct_name,barcode,txt01,txt08,txt02,txt09,txt12,itime,start_time,end_time,int01,int02,int03,int06,txt05,txt06,txt07,int10,int12,template_id,int13,owner,int07,txt04,int09,int17,int15,int11,int18,int19,txt17,txt18,int20,int25,int29,int34,txt22,txt11,int35,int36,int37,int42,time01,time02) VALUES ('$ctt_id','$ct->{c_id}','$ctadr->{c_id}','$ct_name','$ct->{barcode}','$ct->{txt01}','$user_name','$ct->{txt02}','$ctadr->{txt09}','$prefix',now(),now(),'$endRental','1','$unit_price','$menge','$start_station','$ct->{txt06}','$ct->{txt06}','$ct->{txt07}','$status','$from_main_id','$from_template_id','$deviceId','$owner','$rabatt','$tariff_desc','$tariff_nr','$daymax_price','$abo_price','$ct->{int11}','$sharing_type','$bike_charge','$ct->{txt17}','$ct->{txt18}','1','$trackon','$bike_type_id','$staff','$sig_book->{bikeId}','$sig_book->{rentalId}','$unit_price1','$unit_price2','$start_price','$aa_station','$unit_time','$free_time') RETURNING c_id"); + $sth = $dbh->prepare("INSERT INTO contenttranspos (ct_id,cc_id,ca_id,ct_name,barcode,txt01,txt08,txt02,txt09,txt12,itime,start_time,end_time,int01,int02,int03,int06,txt05,txt06,txt07,int10,int12,template_id,int13,owner,int07,txt04,int09,int17,int15,int11,int18,int19,txt17,txt18,int20,int25,int29,int34,txt22,txt11,int35,int36,int37,int42,time01,time02) VALUES ('$ctt_id','$ct->{c_id}','$ctadr->{c_id}','$ct_name','$ct->{barcode}','$ct->{txt01}','$user_name','$ct->{txt02}','$ctadr->{txt09}','$prefix',now(),'$request_para->{start_time}','$request_para->{end_time}','1','$unit_price','$menge','$start_station','$ct->{txt06}','$ct->{txt06}','$ct->{txt07}','$booking_state','$from_main_id','$from_template_id','$deviceId','$owner','$rabatt','$tariff_desc','$tariff_nr','$daymax_price','$abo_price','$ct->{int11}','$sharing_type','$bike_charge','$ct->{txt17}','$ct->{txt18}','1','$trackon','$bike_type_id','$staff','$sig_book->{bikeId}','$sig_book->{rentalId}','$unit_price1','$unit_price2','$start_price','$aa_station','$unit_time','$free_time') RETURNING c_id"); }else{ $sth = $dbh->prepare("INSERT INTO contenttranspos (ct_id,cc_id,ca_id,ct_name,barcode,txt08,itime,int01,int02,int03,txt01,int10,int12,int16,template_id,owner) VALUES ('$ctt_id','$ct->{c_id}','$ctadr->{c_id}','$ct_name','$ct->{barcode}','$user_name',now(),'1','$unit_price','1','$ct->{txt01}','0','$from_main_id','$fibumark','$from_template_id','$owner') RETURNING c_id"); } @@ -1610,15 +1621,6 @@ sub insert_pos(){ $sth->bind_columns(\$last_id); my $c_id = $sth->fetchrow_array(); - if($c_id && $status){ - #update Leihräder in Stammdaten - my $set2 = "owner=$owner" if($owner); - $set2 .= ",int10='$status'" if($status); - $set2 .= ",mtime='now()'"; - my $sth2 = $dbh->prepare("UPDATE content SET $set2 where c_id='$ct->{c_id}'"); - my $rows2 = $sth2->execute(); - } - return $c_id; } diff --git a/copri4/main/src/Mod/GBFSout.pm b/copri4/main/src/Mod/GBFSout.pm index 92bdb2d..8352d13 100755 --- a/copri4/main/src/Mod/GBFSout.pm +++ b/copri4/main/src/Mod/GBFSout.pm @@ -39,6 +39,7 @@ sub handler { my %varenv = $cf->envonline(); my $coo = $q->cookie('domcookie') || $R::sessionid || ""; + my $station_select = $R::station || ""; my $users_sharee = { c_id => 0 }; my $api_return = { authcookie => '' }; @@ -63,14 +64,14 @@ sub handler { #request primary will jsonclient loop_sharees my $uri_request = $dbt->{primary}->{sharee_primary}->{primaryApp}; - #detect DMS, because this will do it directly without loop_sharees - $uri_request = $dbt->{operator}->{$varenv{dbname}}->{operatorApp} if($varenv{dbname} ne $dbt->{primary}->{sharee_primary}->{database}->{dbname} && $varenv{syshost} =~ /shareedms-/); + #detect operator, because this will do it directly without loop_sharees + $uri_request = $dbt->{operator}->{$varenv{dbname}}->{operatorApp} if($varenv{dbname} ne $dbt->{primary}->{sharee_primary}->{database}->{dbname}); if($R::request eq "stations_available"){ station_information($api_return,\%varenv,$users_sharee,$uri_request,$project); } if($R::request eq "bikes_available"){ - vehicle_status($api_return,\%varenv,$users_sharee,$uri_request,$project); + vehicle_status($api_return,\%varenv,$users_sharee,$uri_request,$project,$station_select); } return Apache2::Const::OK; @@ -103,6 +104,10 @@ sub station_information { $gbfs_station{$station}{bike_count} = $response_stations->{shareejson}->{stations}->{$station}->{bike_count}; $gbfs_station{$station}{lat} = $response_stations->{shareejson}->{stations}->{$station}->{gps}->{latitude}; $gbfs_station{$station}{lon} = $response_stations->{shareejson}->{stations}->{$station}->{gps}->{longitude}; + #view it only for bike reservation + if($R::calreserv){ + $gbfs_station{$station}{uri_operator} = $response_stations->{shareejson}->{stations}->{$station}->{uri_operator}; + } #$gbfs_station{$station}{is_charging_station} = 1 if(grep(/300102/, @{$response_stations->{shareejson}->{stations}->{$station}->{station_group}}));#E-L push (@gbfs_stations, $gbfs_station{$station}); } @@ -133,9 +138,11 @@ sub vehicle_status { my $users_sharee = shift || { c_id => 0 }; my $uri_request = shift || ""; my $project = shift || "all"; + my $station_select = shift || ""; my $json = JSON->new->allow_nonref; my $rest_bikes = "request=bikes_available&project=$project&authcookie=$authcookie->{authcookie}"; + $rest_bikes = "request=bikes_available&project=$project&station=$station_select&authcookie=$authcookie->{authcookie}" if($station_select); my $gbfs_resp = {}; my @gbfs_bikes = (); diff --git a/copri4/main/src/Mod/Indexsharee.pm b/copri4/main/src/Mod/Indexsharee.pm index c4cc057..9f4a2d9 100755 --- a/copri4/main/src/Mod/Indexsharee.pm +++ b/copri4/main/src/Mod/Indexsharee.pm @@ -568,12 +568,15 @@ sub handler { if($users_sharee->{c_id} && $R::sharee_edit && $R::sharee_edit =~ /save_account/){ ($returnwww,$feedb) = $shwo->save_account($dbh,$q,$users_sharee->{c_id},\%varenv,$aowner); } - if($users_sharee->{c_id} && $R::sharee_edit && $R::sharee_edit =~ /save_transact/){ + elsif($users_sharee->{c_id} && $R::sharee_edit && $R::sharee_edit =~ /save_transact/){ $returnwww = $shwo->save_transact($q,$users_sharee->{c_id},$coo,$aowner); } - if($users_sharee->{c_id} && $R::sharee_edit && $R::sharee_edit =~ /generate_payonelink/ && $R::prepaid_amount && $R::prepaid_amount =~ /\d+/){ + elsif($users_sharee->{c_id} && $R::sharee_edit && $R::sharee_edit =~ /generate_payonelink/ && $R::prepaid_amount && $R::prepaid_amount =~ /\d+/){ system("$dbt->{copri_conf}->{basedir}/$varenv{syshost}/src/scripts/payonelink.pl '$varenv{syshost}' '$users_sharee->{c_id}' '$R::prepaid_amount' '$aowner' &"); } + elsif($users_sharee->{c_id} && $R::sharee_edit && $R::sharee_edit =~ /bike_reserv/){ + $returnwww = $shwo->bike_reserv($q,\%varenv,$users_sharee->{c_id},$aowner); + } if($returnwww && $returnwww =~ /failure::(.*)/){ $returnwww =~ s/::/=/g; @@ -634,7 +637,7 @@ sub handler { $dbt->update_record($dbh,$update_adr,$users_sharee) if($users_sharee->{c_id} > 0); ($api_return,$users_sharee) = $apif->auth_verify($q,$coo,""); - #payment_ack + #payment_ack payAck system("$dbt->{copri_conf}->{basedir}/$varenv{syshost}/src/scripts/payment_ack.pl '$varenv{syshost}' 'payment_ackCC' '$users_sharee->{c_id}' '$aowner' &"); if(1==1){ @@ -726,6 +729,10 @@ sub handler { exit 0; } } + elsif(!$users_sharee->{c_id} && !$R::logedout && ($path !~ /$varenv{mandant}\/Anmelden|$varenv{mandant}\/Account/)){ + print redirect("$varenv{wwwhost}?logedout=1$session_and"); + exit 0; + } }#end web frame @@ -776,7 +783,6 @@ sub handler { }#end confirm - #Printpreview if($view =~ /Printpreview/){ require "Mod/Printpreview.pm"; @@ -803,8 +809,9 @@ sub handler { my $onload=""; my $local_style = "$varenv{metahost}/$dbt->{shareeapp_conf}->{local_style}"; - my $jquery = ""; - my $jquery_ui = ""; + #my $jquery = "$varenv{metahost}/$dbt->{copri_conf}->{jsscript}"; + my $jquery = "$varenv{metahost}/$dbt->{shareeapp_conf}->{jquery}";#used by CalReserv / rentalator.js + my $jquery_ui = "$varenv{metahost}/$dbt->{copri_conf}->{jsscript}"; my $style_jquery_ui = ""; my $js_bootstrap = "$varenv{metahost}/$dbt->{shareeapp_conf}->{js_bootstrap}"; my $style_bootstrap = "$varenv{metahost}/$dbt->{shareeapp_conf}->{style_bootstrap}"; diff --git a/copri4/main/src/Mod/MailTransport.pm b/copri4/main/src/Mod/MailTransport.pm index 839e63c..0b7e2a6 100755 --- a/copri4/main/src/Mod/MailTransport.pm +++ b/copri4/main/src/Mod/MailTransport.pm @@ -276,6 +276,8 @@ sub mail_hours_occupied { my $uadr = shift; my $contentpos = shift; my $sendref = $self->sendrefhash(); + $sendref->{mailxcfg} = "mailx_hotline"; + $sendref->{mailxcfg} = "mailx_konrad" if($varenv->{syshost} eq "shareedms-kn"); my $dbh_primary = $dbt->dbconnect_extern($dbt->{primary}->{sharee_primary}->{database}->{dbname},"iso-8859-1"); diff --git a/copri4/main/src/Mod/Payment.pm b/copri4/main/src/Mod/Payment.pm index 6960a67..1090941 100755 --- a/copri4/main/src/Mod/Payment.pm +++ b/copri4/main/src/Mod/Payment.pm @@ -927,7 +927,8 @@ sub rpc { $payoneret = $txidval; print FILE "payone CAPTURE $now_dt\n $todo: txid=$txidval && ctt.c_id=$ctt->{c_id} && ctadr.c_id=$ctadr->{c_id}\n" if($debug); - if($txidval && $ctt->{c_id} && $ctadr->{c_id} && $res->content =~ /settleaccount=/){ + #if($txidval && $ctt->{c_id} && $ctadr->{c_id} && $res->content =~ /settleaccount=/){ + if($txidval && $ctt->{c_id} && $ctadr->{c_id}){ $update_ctt->{int14} = "null"; $update_adr->{int12} = 0; }else{#because of Prelogic logic set it empty if no capture diff --git a/copri4/main/src/Mod/Prelib.pm b/copri4/main/src/Mod/Prelib.pm index 1d71ced..d062214 100755 --- a/copri4/main/src/Mod/Prelib.pm +++ b/copri4/main/src/Mod/Prelib.pm @@ -72,12 +72,11 @@ sub longterm_occupied { }; my $uadr = { c_id => 0 }; $uadr = $dbt->fetch_record($dbh,$pref_cc); - $uadr->{txt07} =~ s/\s//g; my $search = { - table => "contenttranspos", + table_pos => "contenttranspos", int10 => 3, - #int34 => "null",#if not staff TODO + int34 => "null",#if not staff start_time_interval => "(now() - interval '12 hours')", }; my $update_pos = { @@ -126,6 +125,7 @@ sub longterm_occupied { print FILE "if(!$cttpos->{$pid}->{int33} || $cttpos->{$pid}->{int33} < $h_id)\n"; print FILE "Sent message for > $h_id hour rental by $posting_sms->{$h_id}\n"; + $uadr->{txt07} =~ s/\s//g; my $contact_hotline = "Hotline $uadr->{txt01} $uadr->{txt08}, $uadr->{txt07}"; $smstrans->sms_message($h_id,$contact_hotline,$cttpos->{$pid}->{ca_id},"",$cttpos->{$pid}->{ct_name}); my $message_log = $cttpos->{$pid}->{txt20} . "\n- $log_stamp $posting_sms->{$h_id}"; @@ -963,6 +963,7 @@ sub delete_contenttranspos { table => "contenttrans", table_pos => "contenttranspos", fetch => "one", + catch => "content_contenttranspos", template_id => "219",#prepaid tpl_id c_id => $c_id, int02 => ">=::1", diff --git a/copri4/main/src/Mod/Printpreview.pm b/copri4/main/src/Mod/Printpreview.pm index 439ad36..f9c8973 100755 --- a/copri4/main/src/Mod/Printpreview.pm +++ b/copri4/main/src/Mod/Printpreview.pm @@ -276,7 +276,7 @@ td { $header_top = $header_px * ($site -1); $footer_top = $footer_px * $site + ($header_px - $footer_px -10); - $footer_top = $footer_px * $site + ($site * (0.70 * ($header_px - $footer_px))) if($site > 2); + $footer_top = $footer_px * $site + ($site * (0.75 * ($header_px - $footer_px))) if($site > 2); print PMA "2.$site) text_footer footer_top: $footer_top | $site/$site_all\n"; &text_footer(\%varenv,$ctf,$ctt,$footer_top,$site,$site_all); diff --git a/copri4/main/src/Mod/Shareework.pm b/copri4/main/src/Mod/Shareework.pm index 6ef9a71..984a158 100755 --- a/copri4/main/src/Mod/Shareework.pm +++ b/copri4/main/src/Mod/Shareework.pm @@ -52,6 +52,18 @@ my $i_rows=0; my $u_rows=0; my $d_rows=0; +#bike reservation by CalReserv +sub bike_reserv { + my $self = shift; + my $q = shift; + my $varenv = shift; + my $ca_id = shift; + my $aowner = shift; + + return; +} + + # #also done in src/Tpl/Anmelden.pm!? sub delete_account { @@ -286,10 +298,11 @@ sub save_account(){ } #Freischaltcode format can be "CA-Li-hsze789k" or "CA1234567" - if($valxx && ($valxx =~ /^(\w{2,3})-([\w\-]+)/i || $valxx =~ /^(\w{2,3})(\d+)/i)){ + if($valxx && ($valxx =~ /^(\w{2,3})-([\w\-]+)/i || $valxx =~ /^(\w{2,3})(\d+)/i || $valxx =~ /^(\w{2,3})-(\w+)/i)){ $valxx =~ s/\s//g; my $bonus_prefix = uc($1), my $bonusnr = $2; + $bonusnr = "$1-$2" if($valxx =~ /^(\w{2,3})-(\w+)/i);#TR Oberried my $operator_conf = $dbt->get_operator_conf($bonus_prefix); my @txt30_op = (); @@ -563,7 +576,7 @@ sub save_account(){ my $payone_mival = $payone->managemandate_main(\%varenv,$ctadr,"",$owner); if($payone_mival && $payone_mival =~ /\w{2}-\w+/){ - #payment_ack + #payment_ack payAck system("$dbt->{copri_conf}->{basedir}/$varenv{syshost}/src/scripts/payment_ack.pl '$varenv{syshost}' 'payment_ackSEPA' '$ctadr->{c_id}' '$owner' &"); $vde_on_fail = 0 if($vde_on_fail != 2); diff --git a/copri4/main/src/Tpl/BaseEdit.pm b/copri4/main/src/Tpl/BaseEdit.pm index a622772..6398a26 100755 --- a/copri4/main/src/Tpl/BaseEdit.pm +++ b/copri4/main/src/Tpl/BaseEdit.pm @@ -960,7 +960,7 @@ EOF table => "content", fetch => "one", template_id => "228", - ct_name => "$ctrel->{txt15}", + ct_name => "ilike::$ctrel->{txt15}", }; $bonus_record = $dbt->fetch_record($dbh,$pref_cc) if($ctrel->{txt15}); } @@ -974,11 +974,11 @@ EOF if($bonus_record->{int22} && $tf_id && $bonus_record->{int22} == $tf_id){ $postdes .= "*code aktiviert $sharing_type Tarif Nr. $bonus_record->{int22}. "; } - if((!$ctrel->{txt15} || $bonus_record->{ct_name} ne $ctrel->{txt15}) && $tf_id && $tariff_all->{$tf_id}->{int18} != 2){ + if((!$ctrel->{txt15} || $bonus_record->{ct_name} !~ /$ctrel->{txt15}/i) && $tf_id && $tariff_all->{$tf_id}->{int18} != 2){ $postdes .= $q->span({-style=>'color:red;padding-left:10px;'},"Achtung, $sharing_type Tarif $tf_id ohne *code aktiviert. "); } } - if($ctrel->{txt15} && $bonus_record->{ct_name} ne $ctrel->{txt15}){ + if($ctrel->{txt15} && $bonus_record->{ct_name} !~ /$ctrel->{txt15}/i){ $postdes .= $q->span({-style=>'color:red;padding-left:10px;'},"*code ist nicht vorhanden! "); }elsif($ctrel->{txt15} && $bonus_record->{ct_name} eq $ctrel->{txt15}){ my $sharing_type = "public-bonus"; diff --git a/copri4/main/src/Tpl/Calorin.pm b/copri4/main/src/Tpl/Calorin.pm index 245271a..e2ed674 100755 --- a/copri4/main/src/Tpl/Calorin.pm +++ b/copri4/main/src/Tpl/Calorin.pm @@ -85,6 +85,7 @@ sub tpl(){ parent_id => $dbt->{shareedms_conf}->{waren}, fetch => "all", keyfield => "main_id", + content_id => "null", }; my $part_nodes = $dbt->fetch_rel4tpl4nd($dbh,$bnode); @@ -178,7 +179,8 @@ sub tpl(){ my $cttpos = {}; $R::ct_ct_name =~ s/\#//; my $search = { - table => "$node_meta->{ct_table}", + table => "contenttrans", + table_pos => "$node_meta->{ct_table}", limit => $q->escapeHTML($limit), offset => $q->escapeHTML($offset), }; @@ -352,7 +354,7 @@ sub tpl(){ ($year_st,$mon_st,$day_st,$hh_st,$mm_st) = $lb->split_date($cttpos->{$pid}->{itime}); } if($cttpos->{$pid}->{end_time}){ - $cttpos->{$pid}->{end_time} = $now_dt if($node_meta->{ct_table} eq "contenttranspos" && $cttpos->{$pid}->{int10} == 3); + $cttpos->{$pid}->{end_time} = $now_dt if($node_meta->{ct_table} eq "contenttranspos" && $cttpos->{$pid}->{start_time} eq $cttpos->{$pid}->{end_time} ); ($year_en,$mon_en,$day_en,$hh_en,$mm_en) = $lb->split_date($cttpos->{$pid}->{end_time}); }else{ ($year_en,$mon_en,$day_en,$hh_en,$mm_en) = $lb->split_date($cttpos->{$pid}->{itime}); diff --git a/copri4/main/src/Tpl/Liste3.pm b/copri4/main/src/Tpl/Liste3.pm index 59706b7..b76ad5b 100755 --- a/copri4/main/src/Tpl/Liste3.pm +++ b/copri4/main/src/Tpl/Liste3.pm @@ -281,9 +281,10 @@ sub tpl(){ $tplids = "209,218"; } $searchref->{tplids} = "$tplids"; - }elsif($R::detail_search && $R::detail_search eq "search"){ + } + if($R::detail_search && $R::detail_search eq "search"){ $searchref->{offset} = 0; - $searchref->{limit} = 1000; + $searchref->{limit} = 1500; } my $main_ids .= $dbt->collect_noderec($dbh,$main_id); @@ -1118,7 +1119,7 @@ EOF if($cal_count <= 1){ my $scale_color = "#86cbd7;"; my $calement = "calement_86cbd7"; - if($ct4rel->{$id}->{int13} == $cttpos->{$ctid}->{int13} && $cttpos->{$ctid}->{int10} == 7){ + if($ct4rel->{$id}->{int13} == $cttpos->{$ctid}->{int13} && $cttpos->{$ctid}->{int05} == 7){ $scale_color = "#f0536e;"; $calement = "calement_f0536e"; } diff --git a/copri4/main/src/scripts/Ilockit_cloud.pl b/copri4/main/src/scripts/Ilockit_cloud.pl index 83bf9f1..0049c11 100755 --- a/copri4/main/src/scripts/Ilockit_cloud.pl +++ b/copri4/main/src/scripts/Ilockit_cloud.pl @@ -125,8 +125,8 @@ sub utctime { #'deviceTime' => '2021-10-14T08:19:35.000+0000', #'serverTime' => '2021-10-14T07:19:37.000+0000', - #$utc_epoch -= 2*60*60;# -2 std (Sommerzeit) - $utc_epoch -= 1*60*60;# -1 std (Winterzeit) + $utc_epoch -= 2*60*60;# -2 std (Sommerzeit) + #$utc_epoch -= 1*60*60;# -1 std (Winterzeit) $utc_epoch += $latency; $time = gmtime($utc_epoch);#epoch diff --git a/copri4/main/src/scripts/Ilockit_trackingcloud.pl b/copri4/main/src/scripts/Ilockit_trackingcloud.pl index 2f6ac23..5ecfa3e 100755 --- a/copri4/main/src/scripts/Ilockit_trackingcloud.pl +++ b/copri4/main/src/scripts/Ilockit_trackingcloud.pl @@ -87,8 +87,8 @@ sub utctime { my $time = Time::Piece->strptime($date, "%Y-%m-%dT%H:%M:%S"); print FILE "localtime: " . $time->datetime . "\n";#localtime my $utc_epoch = $time->epoch; - #$utc_epoch -= 2*60*60;# -2 std (Sommerzeit) - $utc_epoch -= 1*60*60;# -1 std (Winterzeit) + $utc_epoch -= 2*60*60;# -2 std (Sommerzeit) + #$utc_epoch -= 1*60*60;# -1 std (Winterzeit) $utc_epoch += $latency; $time = gmtime($utc_epoch);#epoch print FILE "utctime: " . $time->datetime . "\n";#utc zulu date time diff --git a/copri4/main/src/scripts/mailTransportcms.pl b/copri4/main/src/scripts/mailTransportcms.pl index 63136ec..2304986 100755 --- a/copri4/main/src/scripts/mailTransportcms.pl +++ b/copri4/main/src/scripts/mailTransportcms.pl @@ -431,7 +431,7 @@ sub send_occupied2hotline { $sendref->{mail_to} = $uadr->{txt08}; my $search = { - table => "contenttranspos", + table_pos => "contenttranspos", int10 => 3, #int33 => 3,#marker for sending 3. SMS int34 => "null",#not staff diff --git a/copri4/main/src/scripts/sms_message.pl b/copri4/main/src/scripts/sms_message.pl index b64a695..fcdacba 100755 --- a/copri4/main/src/scripts/sms_message.pl +++ b/copri4/main/src/scripts/sms_message.pl @@ -61,7 +61,7 @@ $contact_hotline = "Hotline $record_cc->{txt01} $record_cc->{txt08}, $record_cc- if($todo eq "24h_occupied"){ #select booking pos if state=occupied and start_time > 24h my $search = { - table => "contenttranspos", + table_pos => "contenttranspos", int10 => 3, int34 => "null",#if not staff start_time_interval => "(now() - interval '1 day')", diff --git a/copri4/shareeapp-operator/css/local_style20230105.css b/copri4/shareeapp-operator/css/local_style20240315.css similarity index 70% rename from copri4/shareeapp-operator/css/local_style20230105.css rename to copri4/shareeapp-operator/css/local_style20240315.css index 988e6c7..eb54121 100755 --- a/copri4/shareeapp-operator/css/local_style20230105.css +++ b/copri4/shareeapp-operator/css/local_style20240315.css @@ -15,6 +15,43 @@ html,body { text-align: center; } +/* Style the buttons that are used to open and close the accordion panel */ +.accordion { + text-align:left; + padding:0; + background-color: white; + border: none; + cursor: pointer; + text-decoration: none; + outline: none; + transition: 0.4s; +} + +.active, .accordion:hover { + color: #b2acac; +} + +/* Style the accordion panel. Note: hidden by default */ +.panel { + font-weight:normal; + padding:14px 0px 0 4px; + max-height: 0; + transition: max-height 0.5s ease-out; + overflow: hidden; +} + + +#scroll-top-wrapper a:hover { + color: gray; +} +#scroll-top-wrapper { + visibility:visible; + cursor:pointer; + opacity: 1.0; + text-decoration:underline; + padding: 10px; +} + .tdint { padding:0.3em 0.5em; vertical-align: top; diff --git a/copri4/shareeapp-operator/src/Lib/Mlogic.pm b/copri4/shareeapp-operator/src/Lib/Mlogic.pm index 771a5e9..96a7a71 100755 --- a/copri4/shareeapp-operator/src/Lib/Mlogic.pm +++ b/copri4/shareeapp-operator/src/Lib/Mlogic.pm @@ -8,10 +8,11 @@ use warnings; use CGI::Carp qw(fatalsToBrowser); use CGI ':standard'; use Mod::DBtank; - +use Tpl::CalReserv; use Data::Dumper; -my $dbt = new DBtank; +my $dbt = new DBtank; +my $calres = new CalReserv; sub new { my $class = shift; @@ -40,19 +41,11 @@ sub tpl(){ } my $bgcolor1 = $dbt->{primary}->{$varenv->{dbname}}->{bgcolor1}; - if($users_sharee->{c_id} && (!$R::sharee_edit || $R::sharee_edit ne "delete_account2") && ($users_sharee->{c_id} eq $varenv->{superu_id} || $dbt->{copri_conf}->{stage} eq "test" || $users_sharee->{txt08} eq "sigo\@sharee.bike")){ + #show it only in testmode + if($users_sharee->{c_id} && $node_meta->{tpl_id} == 302004 && ($users_sharee->{c_id} eq $varenv->{superu_id} || $dbt->{copri_conf}->{stage} eq "test")){ my $coo = $q->cookie('domcookie') || $q->param('sessionid') || ""; - - #my $api_test = "sharee_fr01"; my $bike="FR1538"; - #my $api_test = "sharee_fr01"; my $bike="FR1005";#E-Lastenrad (bike_group=300101, bike_node=300102) - my $api_test = "sharee_fr01"; my $bike="FR4781";#Tracking and BVB test - #my $api_test = "sharee_kn"; my $bike="KN1011"; - #my $api_test = "sharee_wue"; my $bike="WUE5525"; - #my $api_test = "sharee_sx"; my $bike="S3X1001"; - #my $api_test = "sharee_ren"; my $bike="REN2"; - + my $api_test = "sharee_fr01"; my $bike="FR4781";# test print $q->div({-style=>'float:right;text-align:right;height:25px;padding:6px 15px;background-color:white'},$q->a({-style=>"background-color:#ffffff;color:#$bgcolor1;", -href=>"$varenv->{metahost}/src/scripts/tests/index.pl?sessionid=$coo\&api_test=$api_test\&bike=$bike\&user_test=$users_sharee->{txt08}", -target=>'_blank'}," [ tests --> $api_test ] "),"$users_sharee->{txt08}",$q->a({-style=>"color:#$bgcolor1;", -href=>"logout_sharee$session"},"logout")),"\n"; - } print "
\n"; @@ -60,8 +53,6 @@ sub tpl(){ $self->tplselect($q,$node_meta,$users_dms,$mode,$varenv,$users_sharee,$feedb); print "
\n"; print "\n"; - #print "\n"; - } #2021-05-05 changed to Mlogic @@ -76,30 +67,36 @@ sub tplselect(){ my $feedb = shift || ""; $q->import_names('R'); - my $sort = ""; - my $lang = "de"; - my $tpl_id = $node_meta->{tpl_id}; if($node_meta->{main_id}){ - if($tpl_id == 2){ + if($node_meta->{tpl_id} == 2){ require "Tpl/Anmelden.pm"; &Anmelden::tpl($node_meta,$users_dms,$mode,$varenv,$users_sharee,$feedb); - }elsif($tpl_id == 302 || $tpl_id == 302008){ + }elsif($node_meta->{tpl_id} == 302 || $node_meta->{tpl_id} == 302008){ require "Tpl/FormEdit.pm"; &FormEdit::tpl($node_meta,$users_dms,$mode,$varenv,$users_sharee,$feedb); - }elsif($tpl_id == 302004){ + }elsif($node_meta->{tpl_id} == 302004){ require "Tpl/RentalData.pm"; &RentalData::tpl($node_meta,$users_dms,$varenv,$users_sharee,$feedb); - }elsif($tpl_id == 308){ + }elsif($node_meta->{tpl_id} == 308){ require "Tpl/PayoneSelect.pm"; &PayoneSelect::tpl($q,$node_meta,$users_dms,$varenv,$users_sharee,$feedb); - }elsif($tpl_id == 197){ + }elsif($node_meta->{tpl_id} == 314){ + #CalendarReserv CalReserv (ex. MapReserv_v2) + $calres->tpl($q,$node_meta,$users_dms,$varenv,$users_sharee,$feedb); + #}elsif($node_meta->{tpl_id} == 315){ + #CalendarBikes CalReserv iframe + #$calres->tpl($q,$node_meta,$users_dms,$varenv,$users_sharee,$feedb); + }elsif($node_meta->{tpl_id} == 197){ require "Tpl/Contact.pm"; &Contact::tpl($node_meta,$users_dms,$mode,$varenv,$users_sharee,$feedb); } + +print ""; + } - my $debug = "Mlogic --> (users_sharee->{c_id}: $users_sharee->{c_id} | ct_table: $node_meta->{ct_table} | parent_id: $node_meta->{parent_id} | main_id: $node_meta->{main_id} | tpl_id: $node_meta->{tpl_id} | u_id: $users_dms->{u_id} | mode: $mode)"; - print $q->div({-style=>'position:fixed;bottom:0%;right:2%;z-index:10;font-size:13px;'},"$debug"),"\n" if($users_sharee->{c_id} eq $varenv->{superu_id}); + my $debug = "Mlogic --> (users_sharee->{c_id}: $users_sharee->{c_id} | ct_table: $node_meta->{ct_table} | parent_id: $node_meta->{parent_id} | main_id: $node_meta->{main_id} | tpl_id: $node_meta->{tpl_id})"; + print $q->div({-style=>'position:fixed;bottom:0%;right:2%;z-index:10;font-size:13px;'},"$debug"),"\n" if($users_sharee->{c_id} eq $varenv->{superu_id} || $dbt->{copri_conf}->{stage} eq "test"); } diff --git a/copri4/shareeapp-operator/src/Tpl/AccountSubmenu.pm b/copri4/shareeapp-operator/src/Tpl/AccountSubmenu.pm index 0c3f2c9..bc0a799 100755 --- a/copri4/shareeapp-operator/src/Tpl/AccountSubmenu.pm +++ b/copri4/shareeapp-operator/src/Tpl/AccountSubmenu.pm @@ -134,12 +134,14 @@ if(1==1){ my $mstyle_1_5=""; my $mstyle_2=""; my $mstyle_3=""; + my $mstyle_4=""; if($node_meta->{main_id} == $node1->{$id1}->{main_id}){ $mstyle_1 .= "background-color: #$hgcolor1;" if("$node1->{$id1}->{node_name}" eq "$varenv->{accounting_1}"); $mstyle_1_5 .= "background-color: #$hgcolor1;" if("$node1->{$id1}->{node_name}" eq "$varenv->{accounting_1_5}"); $mstyle_2 .= "background-color: #$hgcolor1;" if("$node1->{$id1}->{node_name}" eq "$varenv->{accounting_2}"); $mstyle_3 .= "background-color: #$hgcolor1;" if("$node1->{$id1}->{node_name}" eq "$varenv->{accounting_3}"); + $mstyle_4 .= "background-color: #$hgcolor1;" if("$node1->{$id1}->{node_name}" eq "$varenv->{accounting_4}"); #sharee AGB if(!$users_sharee->{int14}){ @@ -154,6 +156,10 @@ if(1==1){ print $q->li($q->a({-style=>"$mstyle_1_5",-title=>"$varenv->{accounting_1_5}", -href=>"/$viewsel[0]/Account/$varenv->{accounting_1_5}$session"}, $q->img({-src=>"$varenv->{metahost}/img/Account_Zahlungsart.svg"}))),"\n"; print $q->li($q->a({-style=>"$mstyle_2",-title=>"$varenv->{accounting_2}", -href=>"/$viewsel[0]/Account/$varenv->{accounting_2}$session"}, $q->img({-src=>"$varenv->{metahost}/img/Account_Kontoverbindung.svg"}))),"\n"; print $q->li($q->a({-style=>"$mstyle_3",-title=>"$varenv->{accounting_3}", -href=>"/$viewsel[0]/Account/$varenv->{accounting_3}$session"}, $q->img({-src=>"$varenv->{metahost}/img/Account_Verleihdaten.svg"}))),"\n"; + #Calendar Reservation beta + if($users_sharee->{c_id} && $dbt->{copri_conf}->{betau_id}->{$users_sharee->{c_id}}){ + print $q->li($q->a({-style=>"$mstyle_4",-title=>"$varenv->{accounting_4}", -href=>"/$viewsel[0]/Account/$varenv->{accounting_4}$session"}, $q->span({-class=>"bi bi-calendar3", -style=>"font-size: 2rem;width:3rem;padding-top:0.4rem;"}))),"\n"; + } } else{ print $q->li($q->a({-style=>"$mstyle_1",-title=>"$varenv->{accounting_1}", -href=>"/$viewsel[0]/Account/$varenv->{accounting_1}$session"}, $q->img({-src=>"$varenv->{metahost}/img/Account_Kundendaten.svg"}))),"\n"; diff --git a/copri4/shareeapp-operator/src/Tpl/CalReserv.pm b/copri4/shareeapp-operator/src/Tpl/CalReserv.pm new file mode 100755 index 0000000..f8cf1b8 --- /dev/null +++ b/copri4/shareeapp-operator/src/Tpl/CalReserv.pm @@ -0,0 +1,634 @@ +package CalReserv; +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# Copyright (c) Rainer Gümpelein, TeilRad GmbH +# +#Bike calendar-reservation +# +use strict; +use warnings; +use POSIX; +use CGI ':standard'; +use Scalar::Util qw(looks_like_number); +use JSON; +use DateTime; +use DateTime::Format::Strptime; +use DateTime::Format::Pg; +use LWP::UserAgent; +use Mod::Basework; +use Mod::DBtank; +use Mod::Buttons; +use Tpl::AccountSubmenu; +use Data::Dumper; + +sub new { + my $class = shift; + my $self = {}; + bless($self,$class); + return $self; +} + +sub tpl { + my $self = shift; + my $q = shift; + my $node_meta = shift; + my $users_dms = shift || ""; + my $varenv = shift; + my $users_sharee = shift || ""; + my $feedb = shift || ""; + + my $bw = new Basework; + my $dbt = new DBtank; + my $but = new Buttons; + my $submenu = new AccountSubmenu; + my $path = $q->path_info(); + my $dbh = ""; + my $red = "#c83434"; + + my $coo = $q->cookie(-name=>'domcookie') || $R::sessionid; + my $session=""; + my $session_and=""; + if($R::sessionid && length($R::sessionid) > 20 && !$q->cookie(-name=>'domcookie')){ + $session = "?sessionid=$R::sessionid"; + $session_and = "&sessionid=$R::sessionid"; + } + my $bike = $q->escapeHTML($R::bike) || ""; + my $bgcolor1 = "009899";#sharee + $bgcolor1 = $dbt->{website}->{$varenv->{syshost}}->{bgcolor1} if($dbt->{website}->{$varenv->{syshost}}->{bgcolor1}); + $bgcolor1 = $dbt->{merchant_ids}->{$varenv->{merchant_id}}->{bgcolor1} if($dbt->{merchant_ids}->{$varenv->{merchant_id}}->{bgcolor1}); + my $initMap = "48.741246, 11.210390"; + my $map_zoom = 7; + my $project = "all"; + my $uri_server = $dbt->{primary}->{sharee_primary}->{primaryApp}; + + my $timestamp = DateTime->now( time_zone => "Europe/Berlin" ); + my $dt0 = DateTime->now( time_zone => "Europe/Berlin" ); + my $dt1 = DateTime->now( time_zone => "Europe/Berlin" ); + my $strp = DateTime::Format::Strptime->new( + pattern => '%Y-%m-%dT%H:%M', + locale => 'de_DE', + time_zone => 'Europe/Berlin', + on_error => 'croak', + ); + + my $reserv_starttime = $R::reserv_starttime || ""; + my $reserv_endtime = $R::reserv_endtime || ""; + + if($reserv_starttime && $reserv_starttime =~ /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/){ + $dt0 = $strp->parse_datetime($reserv_starttime); + $reserv_starttime = $dt0->datetime; + } + if($reserv_endtime && $reserv_endtime =~ /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/){ + $dt1 = $strp->parse_datetime($reserv_endtime); + $reserv_endtime = $dt1->datetime; + } + + if($dt0 < $timestamp){ + $dt0 = $strp->parse_datetime($timestamp); + $reserv_starttime = $dt0->datetime; + $reserv_starttime =~ s/:00$//; + $dt1 = $strp->parse_datetime($timestamp); + $dt1->add( hours => 4); + $reserv_endtime = $dt1->datetime; + $reserv_endtime =~ s/:00$//; + } + + if(!$reserv_starttime || $reserv_starttime !~ /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/){ + $dt0 = $strp->parse_datetime($timestamp); + $reserv_starttime = $dt0->datetime; + $reserv_starttime =~ s/:00$//; + } + + if(!$reserv_endtime || $reserv_endtime !~ /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/){ + $dt1 = $strp->parse_datetime($timestamp); + $dt1->add( hours => 3); + $reserv_endtime = $dt1->datetime; + $reserv_endtime =~ s/:00$//; + } + + print "
\n"; + + #subMenue-------- + $submenu->tpl($node_meta,$users_dms,$varenv,$users_sharee,$feedb); + #----------------- + + print $q->start_form(-name=>'calreservscreen', -action=>""),"\n"; + print $q->hidden(-name=>"sessionid",-override=>1,-value=>"$R::sessionid") if($R::sessionid); + print $q->hidden(-name=>"calreserv",-override=>1,-value=>"1");#to get uri_operator by GBFSout + + + #Station select by map + my $icon_green = "Open_Green.png"; + my $icon_red = "Open_Red.png"; + my $icon_blue = "Open_Blue.png"; + $initMap =~ s/\s//g; + my ($lat,$lng) = split(/,/,$initMap); + if($R::select_station && $R::select_station =~ /LatLng\((\d+\.\d+),\s(\d+\.\d+)\)/){ + $lat = $1; + $lng = $2; + } + +print< + + + + + +EOF +; + +print $q->div({-class=>'content_title3',-style=>''}, "$varenv->{cms}->{'iframe-calendar-reserv-intro'}->{txt}"),"\n"; +#print $q->div({-style=>'padding:10px 0;'},"Mietrad Reservierung. Radstation in Karte auswählen, Fahrtbeginn und Fahrtende einstellen und verfügbares Mietrad buchen."),"\n"; +print $q->div({-id=>"geolocatestate"},""),"\n"; + +print "
\n"; + + #print $q->div({-style=>'padding:15px 0;'},$q->b("Mietradstation: "),$q->span({-id=>"station_selected"},"")),"\n"; + #with Station select $stations_json + my $list_options = ""; + my %gbfs_station = (); + my %selected_station = (); + if(1==1){ + my $rest_stations = "request=stations_available&calreserv=1&authcookie=$coo"; + my $stations_json = fetchserver_json("",$uri_server,$rest_stations); + eval { + my $response_stations = {}; + $response_stations = decode_json($stations_json); + + foreach my $station (sort {$response_stations->{shareejson}->{stations}->{$a}->{station} cmp $response_stations->{shareejson}->{stations}->{$b}->{station} } keys (%{ $response_stations->{shareejson}->{stations} })) { + #print Dumper($response_stations->{shareejson}->{stations}->{$station}); + $response_stations->{shareejson}->{stations}->{$station}->{gps}->{latitude} =~ s/0$//g; + $response_stations->{shareejson}->{stations}->{$station}->{gps}->{longitude} =~ s/0$//g; + $gbfs_station{$station}{LatLng} = "LatLng($response_stations->{shareejson}->{stations}->{$station}->{gps}->{latitude}, $response_stations->{shareejson}->{stations}->{$station}->{gps}->{longitude})"; + $gbfs_station{$station}{name} = Encode::encode('utf-8', Encode::decode('iso-8859-1',$response_stations->{shareejson}->{stations}->{$station}->{description})); + $gbfs_station{$station}{station_id} = $response_stations->{shareejson}->{stations}->{$station}->{station}; + if($R::select_station && $R::select_station eq $gbfs_station{$station}{LatLng}){ + $selected_station{uri_operator} = $response_stations->{shareejson}->{stations}->{$station}->{uri_operator}; + $selected_station{name} = $gbfs_station{$station}{name}; + $selected_station{station_id} = $gbfs_station{$station}{station_id}; + $list_options .= "\n"; + }else{ + $list_options .= "\n"; + } + } + }; + if ($@){ + $bw->log("Failure, CalendarReserv station_information not valid","",""); + warn $@; + } + + print $q->label({-for=>'select_station', -class=>'form-label'},"Mietradstation"),"\n"; + print "
\n"; + #print $q->span({-class=>"input-group-text bi bi-record-circle", -style=>"font-size: 1.5rem;width:3rem;"},""),"\n"; + #print $q->input({-id=>'select_station', -name=>'select_station', -type=>'text', -list=>'list-stations', -class=>'form-control', -style=>'max-width:500px;font-weight:bold;', -placeholder=>'Ort oder Stationname', -override=>1},""),"\n"; + print ""; + #print $q->input({-type=>'reset',-value=>'Reset'},""),"\n"; + print "
\n"; + } + #end with Station select $stations_json + +print ""; + +print< + //document.getElementById('select_station').value = 'LatLng(47.976634, 7.825490)'; + + var map = L.map('map', { scrollWheelZoom: false }).setView([$lat, $lng], $map_zoom); + + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' +}).addTo(map); + +var icon_green = L.icon({ + iconUrl: '$varenv->{metahost}/img/$icon_green', + iconSize: [37, 37], + iconAnchor: [20, 37], + popupAnchor: [-2, -36] + //shadowUrl: 'marker-shadow.png', + //shadowSize: [68, 95], + //shadowAnchor: [22, 94] +}); +var icon_red = L.icon({ + iconUrl: '$varenv->{metahost}/img/$icon_red', + iconSize: [37, 37], + iconAnchor: [20, 37], + popupAnchor: [-2, -36] +}); + +//Only stations with bike_count +Promise.all([ + fetch( + "$uri_server/GBFSout?request=stations_available&calreserv=1&authcookie=$coo" + )]).then(async ([response1]) => { + const responseData1 = await response1.json(); + + const data1 = responseData1.data.stations; + + const layerGroup = L.featureGroup().addTo(map); + data1.forEach(({ lat, lon, name, bike_count, uri_operator, station_id: stationId }) => { + console.log('Station: ' + stationId , name, bike_count); + + layerGroup.addLayer( + //L.marker([lat, lon], { icon:icon_green }).bindPopup(`Station: \${name} \${stationId}
\${lat},\${lon}`).on('click', clickedGPS) + L.marker([lat, lon], { icon:icon_green }).bindPopup(`Station \${name} \${stationId}`).on('click', clickedGPS) + ); + + }); + + //disabled to setView on current location + //map.fitBounds(layerGroup.getBounds()); +}); + +function clickedGPS(e) { + const select_station = document.querySelector('#select_station'); + select_station.value = e.latlng + //station_selected.textContent = 'FR101' + console.log('clickedGPS:' + select_station.value); +} + +function onLocationFound(e) { + console.log('onLocationFound:' + e.latlng); + const geolocatestate = document.querySelector('#geolocatestate'); + var radius = e.accuracy; + + //L.marker(e.latlng).addTo(map).bindPopup("You are within " + radius + " meters from this point").openPopup(); + L.circle(e.latlng, radius).addTo(map); + geolocatestate.textContent = 'Stationen in Umgebung'; +} + + +function onLocationError(e) { + alert(e.message); +} + +function lastLocation() { + var latlng = L.latLng($lat, $lng); + console.log('lastLocation:' + latlng); + const geolocatestate = document.querySelector('#geolocatestate'); + var radius = 5000; + + L.marker(latlng, { icon:icon_green }).addTo(map).bindPopup(`Station $selected_station{name} | $selected_station{station_id}`).openPopup(); + L.circle(latlng, radius).addTo(map); + geolocatestate.textContent = 'Station nach Auswahl'; +} + + +if("$R::select_station"){ + map.on('locationfound', lastLocation); + map.locate({setView: false, maxZoom: 16}); + map.setView(L.latLng($lat, $lng), 11); +}else{ + map.on('locationfound', onLocationFound); + map.on('locationerror', onLocationError); + map.locate({setView: true, maxZoom: 16}); +} + + +EOF +; + + #end Station select by map + + #if(!$bike){ + print "
\n"; + print $q->label({-for=>'reserv_starttime', -class=>'form-label'},"Fahrtbeginn"),"\n"; + print $q->input({-id=>'reserv_starttime', -name=>'reserv_starttime',-type=>'datetime-local', -class=>'form-control', -style=>'max-width:230px;', -value=>"$reserv_starttime", -override=>1},""),"\n"; + print $q->label({-for=>'reserv_endtime', -class=>'form-label', -style=>'padding-top:1em;'},"Fahrtende"),"\n"; + print $q->input({-id=>'reserv_endtime', -name=>'reserv_endtime',-type=>'datetime-local', -class=>'form-control', -style=>'max-width:230px;', -value=>"$reserv_endtime", override=>1},""),"\n"; + print "
\n"; + #} + + if(1==2){ + print $q->div({-class=>"accordion bi bi-caret-down"}, "mehr Filter anzeigen"),"\n"; + print "
\n"; + + print "
\n"; + my $ecargo_checked = "checked"; + $ecargo_checked = "" if($R::sharee_edit && !$R::ecargo); + print "\n"; + print $q->label({-class=>"form-check-label", -for=>"ecargo",-style=>'padding-top:5px;'},"E-Lastenrad"),"\n"; + print "
\n"; + print "
\n"; + my $cargo_checked = "checked"; + $cargo_checked = "" if($R::sharee_edit && !$R::cargo); + print "\n"; + print $q->label({-class=>"form-check-label", -for=>"cargo",-style=>'padding-top:5px;'},"Lastenrad"),"\n"; + print "
\n"; + print "
\n"; + my $city_checked = "checked"; + $city_checked = "" if($R::sharee_edit && !$R::city); + print "\n"; + print $q->label({-class=>"form-check-label", -for=>"city",-style=>'padding-top:5px;'},"Stadtrad"),"\n"; + print "
\n"; + + print "
\n"; + } + + if($R::sharee_edit =~ /calendar_bikes_available/ && $bike){ + print $q->div({-style=>'margin:1em 0;text-align:center;clear:both;'},""),"\n"; + print $q->hidden(-name=>"bike",-override=>1,-value=>"$bike"); + } + my $but_search = "Suchen"; + $but_search = "Erneut suchen" if($R::sharee_edit =~ /calendar_bikes_available/); + print $q->div({-style=>'margin:1em 0;text-align:center;clear:both;'},""),"\n"; + + print $q->div({-id=>'reserved_bikes'},""),"\n";#not used + + #$bikes_json + if($R::sharee_edit =~ /calendar_bikes_available/){ + + my $rest_bikes = "request=bikes_available&calreserv=1&reserv_starttime=$R::reserv_starttime&reserv_endtime=$R::reserv_endtime&authcookie=$coo&lang=de"; + $rest_bikes .= "&station=$selected_station{station_id}" if($selected_station{station_id}); + + if($R::select_station && $R::select_station =~/LatLng\((\d+\.\d+), (\d+\.\d+)\)/){ + my $Lat = $1; + my $Lng = $2; + $rest_bikes .= "&Lat=$Lat&Lng=$Lng"; + $uri_server = $selected_station{uri_operator} if($selected_station{uri_operator}); + } + my $list_title = "Mieträder an Station:"; + if($R::sharee_edit ne "calendar_bikes_available_all" && $bike){ + $list_title = "Mietrad an Station:"; + $rest_bikes .= "&bike=$bike"; + } + + my $bikes_json = fetchserver_json("",$uri_server,$rest_bikes); + eval { + my $response_bikes = {}; + $response_bikes = decode_json($bikes_json); + + #bikes available + print $q->div({-class=>'content_title2',-style=>''},"$list_title $selected_station{name} | $selected_station{station_id}"),"\n" if(1 == 1); + foreach my $bike (sort {$response_bikes->{shareejson}->{bikes}->{$a}->{bike} cmp $response_bikes->{shareejson}->{bikes}->{$b}->{bike} } keys (%{ $response_bikes->{shareejson}->{bikes} })) { + + #print "
\n"; + print $q->div({-style=>'padding:0.1em 0;margin:0.5em 0;background-color:#b6b6b6;'},""),"\n"; + my $bike_image = "bike_Cargo_SoleHumanPowered_Two.png"; + $bike_image = "bike_Cargo_SoleHumanPowered_Trike.png" if($response_bikes->{shareejson}->{bikes}->{$bike}->{bike_type}->{category} eq "cargo" && $response_bikes->{shareejson}->{bikes}->{$bike}->{bike_type}->{wheels} eq "3"); + $bike_image = "bike_Cargo_Pedelec_Two.png" if($response_bikes->{shareejson}->{bikes}->{$bike}->{bike_type}->{category} eq "cargo" && $response_bikes->{shareejson}->{bikes}->{$bike}->{bike_type}->{engine}); + $bike_image = "bike_City_SoleHumanPowered_Two.png" if($response_bikes->{shareejson}->{bikes}->{$bike}->{bike_type}->{category} eq "city"); + + print $q->div({-style=>'padding:0.5em;float:left;'},$q->img({-style=>'width:100px;', -src=>"$varenv->{metahost}/img/$bike_image"})),"\n"; + print $q->div({-style=>'padding:0.5em;'},$q->span({-style=>'font-weight:bold;'},"$response_bikes->{shareejson}->{bikes}->{$bike}->{description} "),"$response_bikes->{shareejson}->{bikes}->{$bike}->{bike}"),"\n"; + + my $dts = $strp->parse_datetime($reserv_starttime); + my $start_loc = $dts->strftime("%d.%m.%Y %H:%M"); + my $dte = $strp->parse_datetime($reserv_endtime); + my $end_loc = $dte->strftime("%d.%m.%Y %H:%M"); + print $q->div({-style=>''},"Fahrtbeginn: $start_loc"),"\n"; + print $q->div({-style=>''},"Fahrtende: $end_loc"),"\n"; + + print $q->div({-id=>"return_state_$bike",-style=>'color:green;'},""),"\n"; + + #collect timerange + my $set_starttime = ""; + my $set_endtime = ""; + my $j = 0; + my $dt2 = DateTime->now(time_zone => "Europe/Berlin"); + my $dt3 = DateTime->now(time_zone => "Europe/Berlin"); + my $dtnow = $strp->parse_datetime($timestamp); + $dtnow->add( minutes => 1); + $set_starttime = $dtnow->datetime; + + my %blockedhash = (); + my %reservedhash = (); + my $sb = 100;#start blockedhash index . 1 + my $eb = 100;#end blockedhash index . 2 + my $sr = 100;#start reservedhash index . 1 + my $er = 100;#end reservedhash index . 2 + $blockedhash{$sb . '1'} = $set_starttime; + foreach my $rid (sort {$response_bikes->{shareejson}->{bikes}->{$bike}->{blocked}->{$a}->{start_time} cmp $response_bikes->{shareejson}->{bikes}->{$bike}->{blocked}->{$b}->{start_time} } keys (%{ $response_bikes->{shareejson}->{bikes}->{$bike}->{blocked} })) { + $j++; + $dt2 = DateTime::Format::Pg->parse_datetime($response_bikes->{shareejson}->{bikes}->{$bike}->{blocked}->{$rid}->{start_time}); + $sr++; + $er++; + + if($j==1){ + $dt3 = DateTime::Format::Pg->parse_datetime($response_bikes->{shareejson}->{bikes}->{$bike}->{blocked}->{$rid}->{end_time}); + } + + if($j==1 && $dtnow < $dt3){ + $set_endtime = $dt2->datetime; + $reservedhash{$sr . '1'} = $set_endtime;#reserv start_time + $reservedhash{$er . '2'} = $set_starttime;#reserv end_time + $dt3->add( minutes => 15); + $set_starttime = $dt3->datetime; + $blockedhash{$sb . '1'} = $set_starttime; + }else{ + my $dt0 = DateTime::Format::Pg->parse_datetime($response_bikes->{shareejson}->{bikes}->{$bike}->{blocked}->{$rid}->{start_time}); + my $dtend = $strp->parse_datetime($dt0); + $set_endtime = $dtend->datetime; + $reservedhash{$sr . '1'} = $set_endtime;#reserv start_time + $blockedhash{$eb . '2'} = $set_endtime;#blocked end_time + + my $dt1 = DateTime::Format::Pg->parse_datetime($response_bikes->{shareejson}->{bikes}->{$bike}->{blocked}->{$rid}->{end_time}); + my $dtstart = $strp->parse_datetime($dt1); + $set_starttime = $dtstart->datetime; + $reservedhash{$er . '2'} = $set_starttime;#reserv end_time + + $sb++; + $eb++; + $dt1->add( minutes => 15); + $dtstart = $strp->parse_datetime($dt1); + $set_starttime = $dtstart->datetime; + $blockedhash{$sb . '1'} = $set_starttime;#blocked start_time + } + } + $dtnow->add( weeks => 4); + $set_endtime = $dtnow->datetime; + $blockedhash{$eb . '2'} = $set_endtime; + #print Dumper(\%reservedhash) . "
\n"; + #print Dumper(\%blockedhash) . "
\n"; + #end collect timerange + + #view red flag if bike not available on selected rental timerange + my $reservindex = 0; + my $se = 0; + my $reservbut = 0; + foreach my $ri (sort keys %reservedhash){ + if($ri =~ /(\d{3})(\d{1})/){ + $reservindex = $1; + $se = $2; + } + if($ri =~ /^$reservindex/ && $se == 1){ + my $dtx = $strp->parse_datetime($reservedhash{$ri}); + my $end_ri = $ri + 1; + my $dty = $strp->parse_datetime($reservedhash{$end_ri}); + if($dty >= $dt0 && $dty <= $dt1 && $dtx <= $dt1){ + $reservbut = 1; + #print $q->div({-style=>'color:red;'},"Im gewähltem Zeitfenster nicht verfügbar"),"\n"; + #print "$ri Von: $reservedhash{$ri}
\n"; + #print "$end_ri Bis: $reservedhash{$end_ri}
\n"; + } + } + } + + #delete blockedhash with too small ranges + my $rangeindex = 0; + my $se = 0; + foreach my $ri (sort keys %blockedhash){ + if($ri =~ /(\d{3})(\d{1})/){ + $rangeindex = $1; + $se = $2; + } + if($ri =~ /^$rangeindex/ && $se == 1){ + my $dtx = $strp->parse_datetime($blockedhash{$ri}); + my $end_ri = $ri + 1; + my $dty = $strp->parse_datetime($blockedhash{$end_ri}); + if($dty <= $dtx){ + delete $blockedhash{$ri}; + delete $blockedhash{$end_ri}; + } + } + } + + my $availability = ""; + my $href = ""; + #build availability links + foreach my $ri (sort keys %blockedhash){ + if($ri =~ /(\d{3})(\d{1})/){ + $rangeindex = $1; + $se = $2; + } + if($ri =~ /^$rangeindex/ && $se == 1){ + $href = "/app/Account/CalendarReserv?sharee_edit=calendar_bikes_available&select_station=$R::select_station&bike=$bike&calreserv=1&authcookie=$coo&lang=de"; + #print "$ri Von: $blockedhash{$ri}
\n"; + my $dtx = $strp->parse_datetime($blockedhash{$ri}); + my $start_loc = $dtx->strftime("%d.%m.%Y %H:%M"); + $href .= "&reserv_starttime=$blockedhash{$ri}"; + $availability .= "
Von: $start_loc
\n"; + } + if($ri =~ /^$rangeindex/ && $se == 2){ + #print "$ri Bis: $blockedhash{$ri}
---
\n"; + my $dty = $strp->parse_datetime($blockedhash{$ri}); + my $end_loc = $dty->strftime("%d.%m.%Y %H:%M"); + $href .= "&reserv_endtime=$blockedhash{$ri}"; + $availability .= "\n +
Bis: $end_loc
\n +
\n"; + } + } + + +print < + +EOF +; + + if($reservbut){ + print $q->div({-style=>'color:red;'},"Im gewähltem Zeitfenster nicht verfügbar"),"\n"; + }else{ + #Mietrad buchen + print $q->div({-id=>"button_reserv_$bike",-style=>'clear:both;text-align:center;display:block;'},"Mietrad buchen"),"\n"; + + #Buchung stornieren + print $q->div({-id=>"button_cancel_$bike",-style=>'clear:both;text-align:center;display:none;'},"Buchung stornieren"),"\n"; + } + + #Weitere Verfügbarkeit anzeigen + print $q->div({-id=>"button_availablbility_$bike",-style=>'clear:both;text-align:center;display:block;'},"Weitere Verfügbarkeit anzeigen"),"\n"; + + + #print Dumper($response_bikes->{shareejson}->{bikes}->{$bike}->{rental_description}->{tarif_elements}); + foreach my $ele (%{ $response_bikes->{shareejson}->{bikes}->{$bike}->{rental_description}->{tarif_elements} }){ + if($response_bikes->{shareejson}->{bikes}->{$bike}->{rental_description}->{tarif_elements}->{$ele}[0] && $response_bikes->{shareejson}->{bikes}->{$bike}->{rental_description}->{tarif_elements}->{$ele}[1]){ + print $q->div({-style=>'padding:0.2em 0.5em;'},"$response_bikes->{shareejson}->{bikes}->{$bike}->{rental_description}->{tarif_elements}->{$ele}[0] : $response_bikes->{shareejson}->{bikes}->{$bike}->{rental_description}->{tarif_elements}->{$ele}[1]"),"\n"; + } + } + + #print "
\n"; + } + + #bikes_occupied + print $q->div({-style=>'padding:0.1em 0;margin:0.5em 0;background-color:#b6b6b6;'},""),"\n"; + my $i = 0; + foreach my $rid (sort {$response_bikes->{shareejson}->{bikes_occupied}->{$a}->{start_time} cmp $response_bikes->{shareejson}->{bikes_occupied}->{$b}->{start_time} } keys (%{ $response_bikes->{shareejson}->{bikes_occupied} })) { + my $reserv_starttime = DateTime::Format::Pg->parse_datetime($response_bikes->{shareejson}->{bikes_occupied}->{$rid}->{start_time}); + my $reserv_endtime = DateTime::Format::Pg->parse_datetime($response_bikes->{shareejson}->{bikes_occupied}->{$rid}->{end_time}); + my $start_loc = $reserv_starttime->strftime("%d.%m.%Y %H:%M"); + my $end_loc = $reserv_endtime->strftime("%d.%m.%Y %H:%M"); + $i++; + my $oprefix = ""; + $oprefix = $1 if($response_bikes->{shareejson}->{bikes_occupied}->{$rid}->{station} =~ /([A-Z]+)/i); + my $bike = $response_bikes->{shareejson}->{bikes_occupied}->{$rid}->{bike}; + print $q->div({-style=>'padding:1em 0.5em;font-weight:bold;'},"Deine $oprefix Buchungen:"),"\n" if($i == 1); + #selector by $rid pos_id + print $q->div({-id=>"return_state_$rid"},""),"\n"; + print $q->div({-id=>"button_cancel_$rid", -style=>'padding:0.2em 0.5em;color:green;display:block;'},"Buchung stornieren $start_loc - $end_loc: $response_bikes->{shareejson}->{bikes_occupied}->{$rid}->{description} $bike an Station $response_bikes->{shareejson}->{bikes_occupied}->{$rid}->{station}"),"\n"; + print $q->div({-id=>"button_reserv_$rid",-style=>'color:red;'},""),"\n";#dummy + } + + }; + if ($@){ + $bw->log("Failure, CalendarReserv bike_json not valid","",""); + warn $@; + } + }#end $bikes_json + + + + print $q->end_form,"\n"; + print $q->div({-id=>'scroll-top-wrapper'},"nach oben"),"\n" if($R::sharee_edit =~ /calendar_bikes_available/); + + print $q->div({-style=>'position:fixed;bottom:2%;right:2%;z-index:10;font-size:13px;'},"--> $varenv->{syshost} | $varenv->{merchant_id} | $bgcolor1 | template -> $node_meta->{tpl_name},$node_meta->{tpl_id}"),"\n" if($users_sharee->{c_id} eq $dbt->{copri_conf}->{superu_id} || $dbt->{copri_conf}->{stage} eq "test"); + + print "
\n"; +} + +#requestor +sub fetchserver_json { + my $self = shift; + my $uri_server = shift || ""; + my $rest = shift || ""; + my $server_request = "$uri_server/APIjsonserver?$rest"; + + my $ua = LWP::UserAgent->new; + $ua->agent("sharee CalReserv"); + + my $req = HTTP::Request->new(GET => "$server_request"); + $req->content_type('application/x-www-form-urlencoded'); + $req->content($rest); + + #Pass request to the user agent and get a response back + my $res = $ua->request($req); + #SSL certificate must be valid + #print Dumper($res); + # Check the outcome of the response + if ($res->is_success) { + #print $res->content; + return $res->content; + #print $res->status_line, "\n"; + }else { + return ""; + #print $res->status_line, "\n"; + } +} + +1; diff --git a/copri4/shareeapp-operator/src/Tpl/FormEdit.pm b/copri4/shareeapp-operator/src/Tpl/FormEdit.pm index a7fabb4..3e308b2 100755 --- a/copri4/shareeapp-operator/src/Tpl/FormEdit.pm +++ b/copri4/shareeapp-operator/src/Tpl/FormEdit.pm @@ -335,7 +335,7 @@ EOF print $q->div(" "); foreach my $opid (keys(%$ctadrcoupon)){ if($ctadrcoupon->{$opid}->{txt15}){ - print $q->div("$bonus_saved $ctadrcoupon->{$opid}->{oprefix}-$ctadrcoupon->{$opid}->{txt15}"),"\n"; + print $q->div("$bonus_saved $ctadrcoupon->{$opid}->{oprefix} $ctadrcoupon->{$opid}->{txt15}"),"\n"; } } diff --git a/copri4/shareeconf/examples/sharee_operator.sql.gz b/copri4/shareeconf/examples/sharee_operator.sql.gz index 5481a073e53c4c59d448252a16109458ce5d788d..8d7ef0d041fae8713030d9b4e9e838e4b1e1e50c 100755 GIT binary patch literal 38235 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
    YRrC&maXUHvIa0EJ!~ooXSj48UhcD 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 $globalconf{shareeapp_conf}->{accounting_1_5}, accounting_2 => $globalconf{shareeapp_conf}->{accounting_2}, accounting_3 => $globalconf{shareeapp_conf}->{accounting_3}, + accounting_4 => $globalconf{shareeapp_conf}->{accounting_4}, superu_id => $globalconf{copri_conf}->{superu_id}, debug => $globalconf{copri_conf}->{debug}, logdir => $globalconf{copri_conf}->{logdir},