package Pricing; # # SPDX-License-Identifier: AGPL-3.0-or-later # Copyright (c) Rainer Gümpelein, TeilRad GmbH # #Rental counting and pricing methods # #perl -cw use lib qw(/var/www/copri-bike/shareeapp-operator/src); # use strict; use warnings; use POSIX; use CGI; # only for debugging use Scalar::Util qw(looks_like_number); use DateTime; use DateTime::Format::Pg; use Lib::Config; use Mod::Libenz; use Mod::DBtank; use Mod::Callib; use Mod::Basework; use Data::Dumper; my $cf = new Config; my $lb = new Libenz; my $dbt = new DBtank; my $cal = new Callib; my $bw = new Basework; sub new { my $class = shift; my $self = {}; bless($self,$class); return $self; } my $dbh = ""; sub only_first_free(){ my $self = shift; my $ctpos = shift; my %varenv = $cf->envonline(); my $pref = { table => "contenttrans", table_pos => "contenttranspos", fetch => "one", template_id => "218",#Faktura tpl_id ca_id => "=::$ctpos->{ca_id}", c_id => "!=::$ctpos->{c_id}", #txt10 => "IN::('available','canceled')", int10 => "IN::('1','6')", "ct.close_time" => "is::null", }; $pref = { %$pref, time_range => "start_time >= '$ctpos->{start_time}' and start_time < '$ctpos->{end_time}' and start_time != end_time" }; my $record = $dbt->collect_post($dbh,$pref); return $record; } #converts clock-time to minutes sub clock_minutes { my $self = shift; my $clockfloat = shift;# like 12:35 my $day = 0; my $hour = 0; my $min = 0; if($clockfloat =~ /(\d+)\sday\s(\d+):(\d+)/){ $day = $1; $hour = $2; $min = $3; }elsif($clockfloat =~ /(\d+):(\d+)/){ $hour = $1; $min = $2; } my $hours = 0; $hours = $hour if($hour && $hour > 0); $hours += 24 * $day if($day && $day > 0); my $minutes = $min; $minutes = ($hours * 60) + $min if($hours && $hours > 0); return $minutes; } #new counting rental time in hours method (last sharee_pricing) sub counting_rental { my $self = shift; my $varenv = shift; my $ctpos = shift; my $todo = shift; my $today4db = strftime("%Y-%m-%d %H:%M:%S",localtime(time)); my $return = {}; my $computed_end_time = $ctpos->{end_time} || $today4db; $computed_end_time = $today4db if($ctpos->{int10} && $ctpos->{int10} == 3); my $dt0 = DateTime::Format::Pg->parse_datetime($ctpos->{start_time}); my $dt1 = DateTime::Format::Pg->parse_datetime($computed_end_time); my $dur10 = $dt1->subtract_datetime($dt0); my ($durdd,$durhh,$durmm) = $dur10->in_units( 'days', 'hours','minutes' ); $durhh = sprintf('%.2d',$durhh); $durmm = sprintf('%.2d',$durmm); my $real_clock = "$durhh:$durmm"; $real_clock = "$durdd day $durhh:$durmm" if($durdd); my $computed_clock = $real_clock; my $rental_minute = $self->clock_minutes($computed_clock); #init with some defaults my $total_price = 0; my $ctpos_freed = { c_id => 0 }; #Tariff unit by time my $tariff_unitbyminute = 60;#defaults to Stundentarif if($ctpos->{time01} && $ctpos->{time01} =~ /[1-9]/){ $tariff_unitbyminute = $self->clock_minutes($ctpos->{time01}); } #fee if ex. 00:30 Min/Gratis if($ctpos->{time02} && $ctpos->{time02} =~ /[1-9]/){ $ctpos_freed = $self->only_first_free($ctpos); if(!$ctpos_freed->{c_id}){ my ($dhh,$dmm) = split(/:/,$ctpos->{time02}); #adding free minutes to start_time $dt0->add( hours => $dhh, minutes => $dmm ) if(looks_like_number($dhh) && looks_like_number($dmm)); my $cdur10 = $dt1->subtract_datetime($dt0); my ($durdd,$durhh,$durmm) = $cdur10->in_units( 'days', 'hours','minutes' ); $durhh = sprintf('%.2d',$durhh); $durmm = sprintf('%.2d',$durmm); $computed_clock = "$durhh:$durmm"; $computed_clock = "$durdd day $durhh:$durmm" if($durdd); $rental_minute = $self->clock_minutes($computed_clock); } } my $hours = "";#old my $rental_units = 0; my $price_by_allunits = 0; my $max_daily_unit = 0;#how many rental_minute is one daily_unit my $fee_daily_minutes = 0; my $rental_days_price = 0; my $restal_minute = 0; my $rental_time_price = 0; my $rounded_time_factor = 0; #readonly if bike available then take saved $total_price if($ctpos->{int10} && $ctpos->{int10} == 1 && $todo eq "readonly"){ if($ctpos->{int03} && $ctpos->{int03} > 0 && $ctpos->{int02} && $ctpos->{int02} > 0){ $total_price = $ctpos->{int03} * $ctpos->{int02}; } } #on time unit elsif($rental_minute && $rental_minute > 0 && $ctpos->{int02} && $ctpos->{int02} > 0){ $rental_units = $rental_minute / $tariff_unitbyminute; $price_by_allunits = $rental_units * $ctpos->{int02}; #max fee per day (day = 1440 minutes) if($ctpos->{int17} && $price_by_allunits >= $ctpos->{int17}){ $max_daily_unit = $ctpos->{int17} / $ctpos->{int02}; $fee_daily_minutes = $max_daily_unit * $tariff_unitbyminute; my $days_dec = $rental_minute / 1440; my ($days,$ddec) = split(/\./, $days_dec) if($days_dec =~ /\.\d/); $rental_days_price = $days * $ctpos->{int17} if($days > 0); $restal_minute = $rental_minute - $days * 1440; if($restal_minute >= $fee_daily_minutes){ $rental_days_price += $ctpos->{int17}; }else{ my $rental_time_factor = $restal_minute / $tariff_unitbyminute; $rounded_time_factor = $rental_time_factor; if($rental_time_factor =~ /(\d+)\.(\d+)/){ $rounded_time_factor = $1 + 1; } $rental_time_price = $rounded_time_factor * $ctpos->{int02}; } }else{ my $rental_time_factor = $rental_minute / $tariff_unitbyminute; $rounded_time_factor = $rental_time_factor; if($rental_time_factor =~ /(\d+)\.(\d+)/){ $rounded_time_factor = $1 + 1; } $rental_time_price = $rounded_time_factor * $ctpos->{int02}; } } $total_price = $rental_days_price + $rental_time_price; $total_price = sprintf('%.2f', $total_price); $return->{start_time} = "$ctpos->{start_time}"; $return->{end_time} = "$computed_end_time"; $return->{real_hours} = "$real_clock"; $return->{computed_hours} = "$computed_clock"; $return->{unit_price} = "$ctpos->{int02}"; $return->{total_price} = "$total_price"; $return->{rentalog}->{real_hours} = "$real_clock"; $return->{rentalog}->{computed_hours} = "$computed_clock"; $return->{rentalog}->{rental_minute} = "$rental_minute"; $return->{rentalog}->{rental_units} = "$rental_units"; $return->{rentalog}->{tariff_unitbyminute} = "$tariff_unitbyminute"; $return->{rentalog}->{restal_minute} = "$restal_minute"; $return->{rentalog}->{rounded_time_factor} = "$rounded_time_factor"; $return->{rentalog}->{price_by_allunits} = "$price_by_allunits"; $return->{rentalog}->{rental_days_price} = "$rental_days_price"; $return->{rentalog}->{rental_time_price} = "$rental_time_price"; $return->{rentalog}->{ctpos_freed} = "$ctpos_freed->{c_id}"; $bw->log("Pricing counting_rental return:",$return,""); return $return; }#end counting_rental #all other values returned by user_bikes_occupied sub fetch_rentalfeed { my $self = shift; my $varenv = shift; my $ctpos = shift; my $bike_group = "$dbt->{operator}->{$varenv->{dbname}}->{oprefix}$ctpos->{int12}" || ""; my $return = {}; $return->{bike_group} = ["$bike_group"]; $return->{station} = "$dbt->{operator}->{$varenv->{dbname}}->{oprefix}$ctpos->{int04}";#TODO save with prefix $return->{uri_operator} = "$varenv->{wwwhost}";#TODO, should be DB select $return->{bike} = "$dbt->{operator}->{$varenv->{dbname}}->{oprefix}$ctpos->{barcode}"; $return->{state} = "$dbt->{copri_conf}->{bike_state}->{$ctpos->{int10}}" || ""; $return->{bike_charge} = "$ctpos->{int19}" if($ctpos->{int19}); $return->{description} = "$ctpos->{txt01}"; $return->{request_time} = "$ctpos->{itime}"; $return->{system} = "Ilockit" if($ctpos->{int11} && $ctpos->{int11} == 2); $return->{system} = "sigo" if($ctpos->{int11} && $ctpos->{int11} == 3); if($ctpos->{int11}){ ($return->{gps}->{latitude},$return->{gps}->{longitude}) = split(/,/,$ctpos->{txt06}); #if($ctpos->{txt10} =~ /requested|occupied/) if($ctpos->{int10} == 2 || $ctpos->{int10} == 3){ $return->{tariff_description}->{name} = "$ctpos->{txt04}"; $return->{tariff_description}->{number} = "$ctpos->{int09}"; $return->{tariff_description}->{eur_per_hour} = "$ctpos->{int02}" || "0"; $return->{tariff_description}->{max_eur_per_day} = "$ctpos->{int17}" || "0"; $return->{tariff_description}->{free_hours} = "$ctpos->{int16}" if($ctpos->{int16} && $ctpos->{int16} > 0); $return->{tariff_description}->{abo_eur_per_month} = "$ctpos->{int15}" if($ctpos->{int15} && $ctpos->{int15} > 0); $return->{tariff_description}->{track_info} = "Ich stimme der Speicherung (Tracking) meiner Fahrstrecke zwecks wissenschaftlicher Auswertung und Berechnung der CO2-Einsparung zu!" if($ctpos->{int25}); $return->{tariff_description}->{operator_agb} = "Mit der Mietrad Anmietung wird folgender Betreiber AGB zugestimmt (als Demo sharee AGB)." if($ctpos->{ca_id} == 1842 || $ctpos->{ca_id} == 5781); $return->{Ilockit_GUID} = "$ctpos->{txt17}" if($ctpos->{int11} == 2); $return->{Ilockit_ID} = "$ctpos->{txt18}" if($ctpos->{int11} == 2); ($return->{gps}->{latitude},$return->{gps}->{longitude}) = split(/,/,$ctpos->{txt06}); $return->{lock_state} = "locked" if($ctpos->{int20} == 1); $return->{lock_state} = "unlocked" if($ctpos->{int20} == 2); $return->{lock_state} = "locking" if($ctpos->{int20} == 3); $return->{lock_state} = "unlocking" if($ctpos->{int20} == 4); } } return $return; } # #last sub sharee_pricing(){ my $self = shift; my $ctpos = shift; my $todo = shift; my %varenv = $cf->envonline(); my $today4db = strftime("%Y-%m-%d %H:%M:%S",localtime(time)); my $return = {}; my $logging = {}; $logging->{ID} = "c_id:$ctpos->{c_id}/ct_id:$ctpos->{ct_id}/ca_id:$ctpos->{ca_id}"; my $computed_end_time = $ctpos->{end_time} || $today4db; $computed_end_time = $today4db if($ctpos->{int10} && $ctpos->{int10} == 3); #calculates rental time range in hours my ($start_datetime,$end_datetime,$hours) = $cal->contenttranspos_dating($ctpos->{c_id},$ctpos->{start_time},"$computed_end_time","$today4db"); $logging->{hours_input} = $hours; $logging->{tariff} = "$ctpos->{txt04} - $ctpos->{int09}"; my $bike_group = "$dbt->{operator}->{$varenv{dbname}}->{oprefix}$ctpos->{int12}" || ""; my $days_pricemax = $ctpos->{int17} || 9; $logging->{days_pricemax} = $days_pricemax; my $price = 2; #FIXME to real val. must be not 0 $price = sprintf('%.2f',$ctpos->{int02}) if($ctpos->{int02} && $ctpos->{int02} > 0); my $total = 0; #my $days_pricemax = "4.5";#TINK max 9,- € bike/day depends on 2,- €/hour #my $days_pricemax = 5; #KonRad max 15,- € bike/day depends on 3,- €/hour my $days_hour4price = $days_pricemax / $price; $logging->{days_hour4price} = $days_hour4price; my $real_hours = $hours; if($ctpos->{int16} && $ctpos->{int16} > 0){#z.b. 30 Min/Gratis --> 0.5 my $ctpos_freed = $self->only_first_free($ctpos); #Bsp 1h = 60min , 60*0,02 = 1,2min if(!$ctpos_freed->{c_id} || $real_hours <= 0.02){ $hours -= $ctpos->{int16}; $logging->{hours_freed} = $hours; }else{ $logging->{hours_freed} = "Not freed because of (!$ctpos_freed->{c_id} && $ctpos->{int16} || $real_hours <= 0.02)"; } } #If available then take saved hours if($ctpos->{int10} && $ctpos->{int10} == 1 && $todo eq "readonly"){ if($ctpos->{int03} && $ctpos->{int03} > 0){ $hours = $ctpos->{int03}; $total = $hours * $price if(looks_like_number($hours) && looks_like_number($price)); }else{ $hours = 0; $total = 0; } } #jede angebrochene Std. elsif(looks_like_number($hours) && $hours > 0){ if($days_hour4price > 0 && $hours >= $days_hour4price && $hours <= 24){ $logging->{_hours_lower24} = "$days_hour4price > 0 && $hours >= $days_hour4price && $hours <= 24"; $logging->{__hours_lower24} = $hours; $hours = $days_hour4price; $logging->{hours_lower24} = $hours; } elsif($days_hour4price > 0 && $hours >= 24){ $logging->{hours_greate24} = "$days_hour4price > 0 && $hours >= 24"; my $days = $hours / 24; my $days_int = $days; my $dez = 0; ($days_int,$dez) = split(/\./, $days) if($days =~ /\.\d/); my $days_hour = $days_int * 24; my $rest = $hours - $days_hour; $rest = $days_hour4price if($rest > $days_hour4price); $hours = ($days_int * $days_hour4price) + $rest; $logging->{hours_compute} = "$hours = ($days_int * $days_hour4price) + $rest"; } $logging->{hours_preround} = $hours; $hours = $lb->round_half($hours); $logging->{hours_postround} = $hours; $total = $hours * $price if(looks_like_number($hours) && looks_like_number($price)); }else{ $hours = 0; } $total = sprintf('%.2f', $total); #Bsp 1h = 60min , 60*0,02 = 1,2min $hours = "0" if($real_hours <= 0.02); $return->{real_hours} = "$real_hours"; $return->{computed_hours} = "$hours"; $return->{unit_price} = "$price"; $return->{total_price} = "$total"; $return->{bike_group} = ["$bike_group"]; $return->{station} = "$dbt->{operator}->{$varenv{dbname}}->{oprefix}$ctpos->{int04}";#TODO save with prefix $return->{uri_operator} = "$varenv{wwwhost}";#TODO, should be DB select $return->{bike} = "$dbt->{operator}->{$varenv{dbname}}->{oprefix}$ctpos->{barcode}"; $return->{state} = "$dbt->{copri_conf}->{bike_state}->{$ctpos->{int10}}" || ""; $return->{bike_charge} = "$ctpos->{int19}" if($ctpos->{int19}); $return->{description} = "$ctpos->{txt01}"; $return->{request_time} = "$ctpos->{itime}"; $return->{start_time} = "$ctpos->{start_time}"; $return->{end_time} = "$computed_end_time"; $return->{system} = "Ilockit" if($ctpos->{int11} && $ctpos->{int11} == 2); $return->{system} = "sigo" if($ctpos->{int11} && $ctpos->{int11} == 3); if($ctpos->{int11}){ #$return->{gps} = "$ctpos->{txt06}";#end_gps ($return->{gps}->{latitude},$return->{gps}->{longitude}) = split(/,/,$ctpos->{txt06}); #if($ctpos->{txt10} =~ /requested|occupied/) if($ctpos->{int10} == 2 || $ctpos->{int10} == 3){ $return->{tariff_description}->{name} = "$ctpos->{txt04}"; $return->{tariff_description}->{number} = "$ctpos->{int09}"; $return->{tariff_description}->{eur_per_hour} = "$ctpos->{int02}" || "0"; $return->{tariff_description}->{max_eur_per_day} = "$ctpos->{int17}" || "0"; $return->{tariff_description}->{free_hours} = "$ctpos->{int16}" if($ctpos->{int16} && $ctpos->{int16} > 0); $return->{tariff_description}->{abo_eur_per_month} = "$ctpos->{int15}" if($ctpos->{int15} && $ctpos->{int15} > 0); $return->{tariff_description}->{track_info} = "Ich stimme der Speicherung (Tracking) meiner Fahrstrecke zwecks wissenschaftlicher Auswertung und Berechnung der CO2-Einsparung zu!" if($ctpos->{int25}); $return->{tariff_description}->{operator_agb} = "Mit der Mietrad Anmietung wird folgender Betreiber AGB zugestimmt (als Demo sharee AGB)." if($ctpos->{ca_id} == 1842 || $ctpos->{ca_id} == 5781); $return->{Ilockit_GUID} = "$ctpos->{txt17}" if($ctpos->{int11} == 2); $return->{Ilockit_ID} = "$ctpos->{txt18}" if($ctpos->{int11} == 2); #$return->{gps} = "$ctpos->{txt06}";#start_gps ($return->{gps}->{latitude},$return->{gps}->{longitude}) = split(/,/,$ctpos->{txt06}); $return->{lock_state} = "locked" if($ctpos->{int20} == 1); $return->{lock_state} = "unlocked" if($ctpos->{int20} == 2); $return->{lock_state} = "locking" if($ctpos->{int20} == 3); $return->{lock_state} = "unlocking" if($ctpos->{int20} == 4); } } $bw->log("hour computed:",$logging,""); return $return; } #CO2 calculator #Bsp Berechnungen: # Pkw: # Distanz * CO2-Emission Pkw / 100 km # 8.760 km * 20 kg CO2 / 100 km = 1.752 kg CO2 # Pedelec: # Distanz * CO2-Emission Pedelec / 100 km # 10.950 km * 0,546 kg CO2 / 100 km = 62 kg CO2 # # Aus der Differenz zwischen der CO2-Emission Pkw und der CO2-Emission Pedelec ergibt sich das Einsparpotenzial: # 1.752 kg CO2 – 62 kg CO2 = 1.690 kg CO2, also rund 1,7 t CO2 pro Jahr # sub co2calc { my $self = shift; my $ctpos = shift; my $co2diff = 0; my $co2pkw = $ctpos->{int26} * 20 / 100; my $co2ped = $ctpos->{int26} * 0.546 / 100; $co2diff = $co2pkw - $co2ped; $co2diff = sprintf('%.2f',$co2diff); $co2diff =~ s/\./,/; return $co2diff; } #calculates sprit saving sub sprit2calc { my $self = shift; my $ctpos = shift; my $einzel = $ctpos->{int02}; my $menge = $ctpos->{int03}; my $rabatt_val = $ctpos->{int07} || 0; my $gesamt = 0; if($rabatt_val != 0 && $einzel && $menge){ my $rabatt_eur = $rabatt_val; #if int08 != 1 alias € $rabatt_eur = $einzel * $menge * $rabatt_val/100 if($ctpos->{int08} != 1); $gesamt = $einzel * $menge - $rabatt_eur; }elsif($einzel && $menge){ $gesamt = $einzel * $menge; } my $sprit_price = 0; $sprit_price = $ctpos->{int26} * 0.3 if($ctpos->{int26} != 0); $sprit_price -= $gesamt; $sprit_price = sprintf('%.2f',$sprit_price); $sprit_price =~ s/\./,/; return $sprit_price; } #computes position price and rabatt sub price2calc { my $self = shift; my $ctpos = shift; my $gesamt = 0; my $rabatt = ""; my $einzel = $ctpos->{int02}; my $menge = $ctpos->{int03}; my $rabatt_val = $ctpos->{int07} || 0; if($rabatt_val != 0 && $einzel && $menge){ my $rabatt_eur = $rabatt_val; #if int08 != 1 alias € $rabatt_eur = $einzel * $menge * $rabatt_val/100 if($ctpos->{int08} != 1); $gesamt = $einzel * $menge - $rabatt_eur; }elsif($einzel && $menge){ $gesamt = $einzel * $menge; } if($ctpos->{int07} && $ctpos->{int07} > 0 && $menge > 0){ $rabatt = "-" . $ctpos->{int07}; if($ctpos->{int08} == 1){ $rabatt .= " €"; }else{ $rabatt =~ s/\.00//; $rabatt .= " %"; } } return ($gesamt,$rabatt); } 1;