sharee.bike/copri4/main/src/Mod/Pricing.pm
2022-05-17 09:24:02 +02:00

447 lines
17 KiB
Perl
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 round(){
my $self = shift;
my ($amount) = @_;
$amount = $amount * (10**(2));
my $rounded = int($amount + .5 * ($amount<=> 0)) / (10**(2));
return $rounded;
}
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;
($hour,$min) = split(/:/, $clockfloat);
($day,$hour) = split(/\sday\s/, $hour) if($hour =~ /\sday\s/);
$hour += 24 * $day if($day && $day > 0);
my $minutes = $min;
$minutes = ($hour * 60) + $min if($hour && $hour > 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 $counting = { c_id => $ctpos->{c_id} };
my $computed_end_time = $ctpos->{end_time} || $today4db;
$computed_end_time = $today4db if($ctpos->{int10} && $ctpos->{int10} == 3);
#main counting rental time and convert it to minute
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 };
#convert tariff unit by minute time
my $tariff_unitbyminute = 60;#defaults to Stundentarif
if($ctpos->{time01} && $ctpos->{time01} =~ /[1-9]/){
$tariff_unitbyminute = $self->clock_minutes($ctpos->{time01});
}
#substract free time from rental time ex. 00:30 Min/Gratis
my $freed_time = "";
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});
$freed_time = "- $dhh:$dmm" if($dhh || $dmm);
#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);
#print "$computed_clock|$rental_minute";
}
}
my $rental_unit = 0;
my $price_by_allunit = 0;
my $max_daily_unit = 0;#how many rental_minute is one daily_unit
my $max_fee_daily_minute = 0;
my $rental_day_price = 0;
my $restal_minute = 0;
my $rental_time_price = 0;
my $rental_unit_rounded = 0;
my $used_max_fee = "off";
my $used_methode = "";
#readonly if bike available then take saved $total_price
if($ctpos->{int10} && $ctpos->{int10} == 1 && $todo eq "readonly"){
#if($ctpos->{int38} && $ctpos->{int38} > 0 && $ctpos->{int35} && $ctpos->{int35} > 0){
# $total_price = $ctpos->{int38} * $ctpos->{int35};
# $used_methode = "readonly $ctpos->{int38} pos.int38 * $ctpos->{int35} pos.int35";
#}
}
#on time unit (int35 keeps unit_price1)
elsif($rental_minute && $rental_minute > 0 && $ctpos->{int35} && $ctpos->{int35} > 0){
$rental_unit = $rental_minute / $tariff_unitbyminute;
$price_by_allunit = $rental_unit * $ctpos->{int35};
#max_fee/day (day = 1440 minutes)
#useless if time01 unit is set to 24:00
if($ctpos->{int17} && $ctpos->{int17} > 0 && $price_by_allunit >= $ctpos->{int17} && $tariff_unitbyminute < 1440){
$used_methode .= "max_fee/day";
$used_max_fee = "ON $ctpos->{int17}";
$max_daily_unit = $ctpos->{int17} / $ctpos->{int35};
$max_fee_daily_minute = $max_daily_unit * $tariff_unitbyminute;
my $days_dec = $rental_minute / 1440;
my $days = $days_dec;
($days,my $ddec) = split(/\./, $days_dec) if($days_dec =~ /\.\d/);
if($days > 0){
$rental_day_price = $days * $ctpos->{int17};
$counting->{int39} = $days;#by day
$used_methode .= " | rental_day_price: $days days * $ctpos->{int17} pos.int17";
}
$restal_minute = $rental_minute - $days * 1440;
if($restal_minute >= $max_fee_daily_minute){
$rental_day_price += $ctpos->{int17};
$counting->{int39} += 1;#by day
$used_methode .= " | += $ctpos->{int17} pos.int17";
}else{
$rental_unit = $restal_minute / $tariff_unitbyminute;
#
$rental_unit = sprintf('%.2f', $rental_unit);
$rental_unit_rounded = $rental_unit;
if($rental_unit =~ /(\d+)\.(\d+)$/){
$rental_unit_rounded = $1 + 1 if($2 > 0);
}
$rental_time_price = $rental_unit_rounded * $ctpos->{int35};
$counting->{int38} = $rental_unit_rounded;#by time
$used_methode .= " | $rental_unit_rounded rental_unit_rounded * $ctpos->{int35} int35";
}
#else if price by all unit < max_fee/day
}else{
$rental_unit = $rental_minute / $tariff_unitbyminute;
$rental_unit = sprintf('%.2f', $rental_unit);
$rental_unit_rounded = $rental_unit;
if($rental_unit =~ /(\d+)\.(\d+)/){
$rental_unit_rounded = $1 + 1 if($2 > 0);
}
if($ctpos->{int36} == 0 && $ctpos->{int17} && $ctpos->{int17} > 0){
$counting->{int38} = $rental_unit_rounded;#count by time
$rental_time_price = $rental_unit_rounded * $ctpos->{int35};
$used_methode .= " | $rental_unit_rounded rental_unit_rounded * $ctpos->{int35} int35";
}elsif($ctpos->{int36} > 0){#if second unit_price2 defined
$counting->{int40} = 1;#by day
$rental_time_price = 1 * $ctpos->{int35};
$used_methode .= " | 1 rental_unit_rounded * $ctpos->{int35} int35 with unit_price2 $ctpos->{int36} 36";
if($rental_unit_rounded >= 2 && $ctpos->{int36} && $ctpos->{int36} > 0){
my $rental_unit_rounded2 = $rental_unit_rounded - 1;#by day
$counting->{int41} = $rental_unit_rounded2;
$rental_time_price += $rental_unit_rounded2 * $ctpos->{int36};
$used_methode .= " | $rental_unit_rounded2 rental_unit_rounded * $ctpos->{int36} int36";
}
}
}
}
my $computed_hours = $rental_minute / 60;
$total_price = $rental_day_price + $rental_time_price;
$used_methode .= " --> $total_price total_price = $rental_day_price rental_day_price + $rental_time_price rental_time_price";
#$total_price = sprintf('%.2f', $total_price);
my $discount = "";
my $discount_val = $ctpos->{int07} || 0;
if($discount_val != 0 && $total_price){
my $discount_eur = $discount_val;
$discount_eur = $total_price * $discount_val/100 if(!$ctpos->{int08} || $ctpos->{int08} != 1);
$total_price -= $discount_eur;
$discount = "-" . $ctpos->{int07};
if($ctpos->{int08} && $ctpos->{int08} == 1){
$discount .= " €";
}else{
$discount =~ s/\.00//;
$discount .= "%";
}
}
$return->{start_time} = "$ctpos->{start_time}";
$return->{end_time} = "$computed_end_time";
$return->{freed_time} = "$freed_time";
$return->{computed_hours} = "$computed_hours";
$return->{unit_price} = "$ctpos->{int35}";
$return->{real_clock} = "$real_clock";
$return->{total_price} = "$total_price";
$return->{discount} = "$discount";
$return->{rentalog}->{real_clock} = "$real_clock";
$return->{rentalog}->{freed_time} = "$freed_time";
$return->{rentalog}->{computed_clock} = "$computed_clock";
$return->{rentalog}->{computed_hours} = "$computed_hours";
$return->{rentalog}->{rental_minute} = "$rental_minute";
$return->{rentalog}->{max_fee_daily_minute} = "$max_fee_daily_minute";
$return->{rentalog}->{tariff_unitbyminute} = "$tariff_unitbyminute";
$return->{rentalog}->{restal_minute} = "$restal_minute";
$return->{rentalog}->{rental_unit_rounded} = "$rental_unit_rounded";
$return->{rentalog}->{rental_unit} = "$rental_unit";
$return->{rentalog}->{price_by_allunit} = "$price_by_allunit";
$return->{rentalog}->{rental_day_price} = "$rental_day_price";
$return->{rentalog}->{total_price} = "$total_price";
$return->{rentalog}->{rental_time_price} = "$rental_time_price";
$return->{rentalog}->{ctpos_freed} = "$ctpos_freed->{c_id}";
$return->{rentalog}->{used_max_fee} = "$used_max_fee";
$return->{rentalog}->{used_methode} = "$used_methode";
$return->{rentalog}->{counting} = $counting;
$bw->log("Pricing counting_rental return:",$return,"");
return ($return,$counting);
}#end counting_rental
#all other values returned by user_bikes_occupied
sub fetch_rentalfeed {
my $self = shift;
my $varenv = shift;
my $ctpos = shift;
my $returned_counting = shift || {};
my $td_template = $dbt->rental_description_template();
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){
#2022-04-26 disabled condition
if(1==1){
#deprecated
$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);
#new rental_description
$return->{rental_description}->{name} = "$ctpos->{txt04}";
$return->{rental_description}->{id} = "$ctpos->{int09}";
foreach my $td (sort keys (%$td_template)){
my $time_unit = "";
if($td_template->{$td}->{int35} && $ctpos->{int35} && $ctpos->{int35} > 0){
$ctpos->{int35} =~ s/\./,/;
$time_unit = $dbt->time_format($ctpos->{time01});
$return->{rental_description}->{tarif_elements}->{$td} = ["$td_template->{$td}->{int35}","$ctpos->{int35} € / $time_unit"];
}elsif($td_template->{$td}->{int36} && $ctpos->{int36} && $ctpos->{int36} > 0){
$ctpos->{int36} =~ s/\./,/;
$time_unit = $dbt->time_format($ctpos->{time01});
$return->{rental_description}->{tarif_elements}->{$td} = ["$td_template->{$td}->{int36}", "$ctpos->{int36} € / $time_unit"];
}elsif($td_template->{$td}->{int17} && $ctpos->{int17} && $ctpos->{int17} > 0){
$ctpos->{int17} =~ s/\./,/;
$return->{rental_description}->{tarif_elements}->{$td} = ["$td_template->{$td}->{int17}","$ctpos->{int17} € / Tag"];
}elsif($td_template->{$td}->{time02} && $ctpos->{time02} =~ /[1-9]/){
$time_unit = $dbt->time_format($ctpos->{time02});
$return->{rental_description}->{tarif_elements}->{$td} = ["$td_template->{$td}->{time02}","$time_unit / Tag"];
}elsif($td_template->{$td}->{xduration} && $returned_counting->{real_clock} && $returned_counting->{real_clock} =~ /[1-9]/){
$time_unit = $dbt->time_format($returned_counting->{real_clock});
$return->{rental_description}->{tarif_elements}->{$td} = ["$td_template->{$td}->{xduration}","$time_unit"];
}elsif($td_template->{$td}->{xprice} && $returned_counting->{total_price} && $returned_counting->{total_price} > 0){
$returned_counting->{total_price} =~ s/\./,/;
$return->{rental_description}->{tarif_elements}->{$td} = ["$td_template->{$td}->{xprice}","$returned_counting->{total_price} €"];
}
}#end new rental_description
$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;
}
#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 $discount_val = $ctpos->{int07} || 0;
my $total = 0;
if($discount_val != 0 && $einzel && $menge){
my $discount_eur = $discount_val;
#if int08 != 1 alias €
$discount_eur = $einzel * $menge * $discount_val/100 if($ctpos->{int08} != 1);
$total = $einzel * $menge - $discount_eur;
}elsif($einzel && $menge){
$total = $einzel * $menge;
}
my $sprit_price = 0;
$sprit_price = $ctpos->{int26} * 0.3 if($ctpos->{int26} != 0);
$sprit_price -= $total;
$sprit_price = sprintf('%.2f',$sprit_price);
$sprit_price =~ s/\./,/;
return $sprit_price;
}
#computes article position price and rabatt
sub price2calc {
my $self = shift;
my $ctpos = shift;
my $total = 0;
my $discount = "";
my $einzel = $ctpos->{int02} || 0;
my $menge = $ctpos->{int03} || 0;
my $discount_val = $ctpos->{int07} || 0;
if($discount_val != 0 && $einzel && $menge){
my $discount_eur = $discount_val;
#if int08 != 1 alias €
$discount_eur = $einzel * $menge * $discount_val/100 if(!$ctpos->{int08} || $ctpos->{int08} != 1);
$total = $einzel * $menge - $discount_eur;
}elsif($einzel && $menge){
$total = $einzel * $menge;
}
if($ctpos->{int07} && $ctpos->{int07} > 0 && $menge > 0){
$discount = "-" . $ctpos->{int07};
if($ctpos->{int08} && $ctpos->{int08} == 1){
$discount .= " €";
}else{
$discount =~ s/\.00//;
$discount .= " %";
}
}
return ($total,$discount);
}
1;