sharee.bike/copri4/main/src/Mod/Pricing.pm
2023-04-12 16:12:19 +02:00

546 lines
20 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::Basework;
use Data::Dumper;
my $cf = new Config;
my $lb = new Libenz;
my $dbt = new DBtank;
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",
ca_id => "=::$ctpos->{ca_id}",
ct_id => "=::$ctpos->{ct_id}",
c_id => "!=::$ctpos->{c_id}",
#txt10 => "IN::('available','canceled')",
int10 => "IN::(1,6)",
};
$pref = { %$pref, time_range => "cp.start_time >= '$ctpos->{start_time}' and cp.start_time < '$ctpos->{end_time}' and cp.start_time != cp.end_time" };
my $record = { c_id => 0 };
$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);
my $computed_start_time = $ctpos->{start_time} || $today4db;#should be only used on parts
#main counting rental time and convert it to minute
my $dt0 = DateTime::Format::Pg->parse_datetime($computed_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);
#if end_station == start_station and rental minutes < 5 minutes, then 0
$rental_minute = 0 if($ctpos->{int04} == $ctpos->{int06} && $rental_minute && $rental_minute < 5);
my $rental_minute_all = $rental_minute;
#init with some defaults
my $total_price = 0;
my $ctpos_freed = { c_id => 0 };
$ctpos->{int35} = 0 if(!$ctpos->{int35});
#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
#freed_time depends on operator and will be set by rental end "count_freedrental"
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 = "";
#on time unit (int35 keeps unit_price1)
if($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";
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 .= "%";
}
}
$total_price = $total_price * $ctpos->{int01} if($ctpos->{int01});
$total_price = sprintf('%.2f', $total_price);
$return->{start_time} = "$computed_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}->{rental_minute_all} = "$rental_minute_all";
$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 $lang = "de";
my $td_template = $dbt->rental_description_template();
my $bike_group = "$dbt->{operator}->{$varenv->{dbname}}->{oprefix}$ctpos->{int29}" || "";
my $return = {};
$return->{bike_group} = ["$bike_group"];
#TOD save with prefix
$return->{station} = "$dbt->{operator}->{$varenv->{dbname}}->{oprefix}$ctpos->{int04}";
$return->{uri_operator} = "$varenv->{wwwhost}";#TOD, should be DB select
$return->{bike} = "$dbt->{operator}->{$varenv->{dbname}}->{oprefix}$ctpos->{barcode}";
#TOD save also sig prefix
if($dbt->{operator}->{$varenv->{dbname}}->{oprefix} eq "SX"){
$return->{bike} = "S3X$ctpos->{barcode}";
#$return->{station} = "S3X$ctpos->{int04}";
}
$return->{state} = "$dbt->{copri_conf}->{bike_state}->{$ctpos->{int10}}" || "";
$return->{lock_state} = "$dbt->{copri_conf}->{lock_state}->{$ctpos->{int20}}";
#defaults
$return->{bike_type}->{category} = "city";
$return->{bike_type}->{wheels} = "2";
#for station_type_id mapping
if($ctpos->{int29} && $ctpos->{int29} == 300101){
$return->{bike_type}->{category} = "cargo";
$return->{bike_type}->{wheels} = "2";
$return->{bike_type}->{wheels} = "3" if($ctpos->{txt01} =~ /drei|trike/i);
if($ctpos->{txt01} =~ /E-/i){
$return->{bike_type}->{engine}->{manufacturer} = "dummy";
my $max_bars = 5;
my $current_bars = 0;
$return->{bike_type}->{battery}->{charge_max_bars} = "$max_bars";
$return->{bike_type}->{battery}->{charge_current_bars} = "$current_bars";
$return->{bike_type}->{battery}->{charge_current_percent} = "0";
$return->{bike_type}->{battery}->{hidden} = "0";#1=hide charge view
if($ctpos->{int19}){
$current_bars = $bw->battery_bars($max_bars,$ctpos->{int19});
$return->{bike_type}->{battery}->{charge_current_bars} = "$current_bars";
$return->{bike_type}->{battery}->{charge_current_percent} = "$ctpos->{int19}";
}
}
}
$return->{Ilockit_ID} = "$ctpos->{txt18}" if($ctpos->{int11} == 2);
$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});
$return->{rental_description}->{name} = "$ctpos->{txt04}";
$return->{rental_description}->{id} = "$ctpos->{int09}";
$return->{rental_description}->{reserve_timerange} = "15";
$return->{rental_description}->{reserve_timerange} = "30" if($ctpos->{int11} == 3);
$return->{aa_ride} = "0";
if($ctpos->{int42}){
$return->{rental_description}->{rental_info}->{2} = ["AAFahrten","Dieses E-Lastenrad darf nur an der Station zurück gegeben werden an der es ausgeliehen wurde!"];
$return->{aa_ride} = "1";
}
foreach my $td (sort keys (%$td_template)){
my $time_unit = "";
if($td_template->{$td}->{int35} && $ctpos->{int35} && $ctpos->{int35} > 0){
$ctpos->{int35} =~ s/\./,/ if($lang eq "de");
$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/\./,/ if($lang eq "de");
$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/\./,/ if($lang eq "de");
$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"];
}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/\./,/ if($lang eq "de");
$return->{rental_description}->{tarif_elements}->{$td} = ["$td_template->{$td}->{xprice}","$returned_counting->{total_price} €"];
}
}#end new rental_description
}
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
#disabled
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/\./,/;
$sprit_price = "";
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;
}
$total = $total * $ctpos->{int01} if($ctpos->{int01});
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);
}
#computes operator invoices for accounting
sub operator_accounting2calc {
my $self = shift;
my $varenv = shift;
my $ctpos = shift;#client invoices
my $ctf = shift;#Operator-Faktura config
my $tplf = $dbt->get_tpl($dbh,"196");#Operator-Faktura
my @tplf_order = split /,/,$tplf->{tpl_order};
#returned values
my $oac = {
int01 => 0,
int02 => 0,
int93 => 0,
int94 => 0,
int95 => 0,
int96 => 0,
int97 => 0,
int98 => 0,
int99 => 0,
int100 => 0,
};
foreach(@tplf_order){
my ($key,$val,$size) = split /=/,$_;
if($key =~ /int/){
$oac->{int99} = $ctpos->{int01} if($key eq "int01" && $ctpos->{int01});#invoice capture brutto
$oac->{int01} = $ctpos->{int01} / 119 * 100 if($key eq "int01" && $ctpos->{int01});#invoice capture netto
$oac->{int01} = sprintf('%.2f', $oac->{int01});
if($ctpos->{state} =~ /Lastschrift/){
$oac->{int93} = $oac->{int01} / 100 * $ctf->{$key} if($key eq "int01");#7,5%
$oac->{int93} = sprintf('%.3f', $oac->{int93});
$oac->{int98} = $oac->{int93} * 0.19 if($oac->{int93});#UmSt auf 7,5%
$oac->{int98} = sprintf('%.3f', $oac->{int98});
$oac->{int94} = $oac->{int01} / 100 * $ctf->{$key} * -1 if($key eq "int02");#po Disagio %
$oac->{int94} = sprintf('%.3f', $oac->{int94});
$oac->{int95} = $ctf->{$key} * -1 if($key eq "int04");#po Trans
$oac->{int95} = sprintf('%.3f', $oac->{int95});
$oac->{int96} = $ctf->{$key} * -1 if($key eq "int06");#po Zahlungsmeldung
$oac->{int96} = sprintf('%.3f', $oac->{int96});
$oac->{int97} = 0;
}
if($ctpos->{state} =~ /Kreditkarte/){
$oac->{int93} = $oac->{int01} / 100 * $ctf->{$key} if($key eq "int01");#7,5%
$oac->{int93} = sprintf('%.3f', $oac->{int93});
$oac->{int98} = $oac->{int93} * 0.19 if($oac->{int93});#UmSt auf 7,5%
$oac->{int98} = sprintf('%.3f', $oac->{int98});
$oac->{int94} = $oac->{int01} / 100 * $ctf->{$key} * -1 if($key eq "int03");#po Disagio %
$oac->{int94} = sprintf('%.3f', $oac->{int94});
$oac->{int95} = $ctf->{$key} * -1 if($key eq "int05");#po Trans
$oac->{int95} = sprintf('%.3f', $oac->{int95});
$oac->{int96} = $ctf->{$key} * -1 if($key eq "int06");#po Zahlungsmeldung
$oac->{int96} = sprintf('%.3f', $oac->{int96});
$oac->{int97} = $ctf->{$key} * -1 if($key eq "int07");#po CC Zuordnung
$oac->{int97} = sprintf('%.3f', $oac->{int97});
}
#operator accounting
$oac->{int02} = $oac->{int01} + $oac->{int94} + $oac->{int95} + $oac->{int96} + $oac->{int97};
$oac->{int02} = sprintf('%.3f', $oac->{int02});
#operator invoice
#$oac->{int100} = $oac->{int93} + $oac->{int98};#brutto
$oac->{int100} = $oac->{int93};#netto
$oac->{int100} = sprintf('%.3f', $oac->{int100});
}
}
return $oac;
}
1;