App-InvestSim

 view release on metacpan or  search on metacpan

lib/App/InvestSim/Finance.pm  view on Meta::CPAN

}

sub notary_fees {
  return $values{invested} * $values{notary_fees} / 100;
}

sub total_invested {
  return $values{invested} + notary_fees();
}

# Takes a yearly loan interest rate as a percentage value (e.g. 2.0 for 2%) and
# returns the monthly interest rate.
sub monthly_rate {
  my ($yearly_rate) = @_;
  # Or should it be: (1 + yearly_rate) ** (1/12) - 1 ?
  # That calculation is more correct but it seems that real loans are using the
  # approximate value.
  my $ir = $yearly_rate / 100 / 12;  
}

# Payment par term for a loan.

lib/App/InvestSim/GUI.pm  view on Meta::CPAN

use File::Spec::Functions;
use Text::Wrap ();
use Tkx;

# All entry aligned in a column should have the same width (in number of
# characters), for simplicity we are using the same width everywhere.
use constant ENTRY_WIDTH => 10;

my $cldr = CLDR::Number->new(locale => 'fr-FR');
my $cldr_currency = $cldr->currency_formatter(currency_code => 'EUR', maximum_fraction_digits => 0);
my $cldr_percent = $cldr->percent_formatter(minimum_fraction_digits => 1);
my $cldr_decimal = $cldr->decimal_formatter();

sub format_euro {
  my ($val) = @_;
  $val = 0 unless $val;  # Cover the case where $val is undef or '' (also 0).
  return $cldr_currency->format($val);
}

sub format_percent {
  my ($val) = @_;
  $val = 0 unless $val;  # Cover the case where $val is undef or '' (also 0).
  return $cldr_percent->format($val / 100);
}

sub format_year {
  my ($val) = @_;
  $val = 0 unless $val;  # Cover the case where $val is undef or '' (also 0).
  return '1 an' if $val == 1;
  return "${val} ans";
}

sub format_surface {

lib/App/InvestSim/GUI.pm  view on Meta::CPAN

sub focus_out_field {
  my ($widget, $var, $right_justified, $refresh) = @_;
  $widget->m_configure(-validate => 'none');
  # Todo: check here that the widget is in a valid state before proceeding.
  $$var = $widget->m_get() =~ s/,/./r;
  $refresh->();
  $widget->m_configure(-justify => 'right') if $right_justified;
  calculate_all();
}

# Receives a ttk_entry widget, a var reference and a field type (euro, percent,
# or year) and setup the entry so that it edits that variable with a beautified
# display corresponding to the type. $textvar can be undef or a scalar
# reference that gets the beautified content of the field.
sub setup_entry {
  my ($widget, $var, $format, $validate, $textvar) = @_;
  my $right_justified = (Tkx::SplitList($widget->m_configure('-justify')))[-1] eq 'right';
  my $refresh = sub {
    $widget->m_delete(0, 'end');
    $widget->m_insert(0, $format->($$var));
    $$textvar = $format->($$var) if $textvar;

lib/App/InvestSim/GUI.pm  view on Meta::CPAN

  Tkx::font('configure', $treeview_total_font, Tkx::SplitList(Tkx::font('configure', $default_treeview_font)));
  Tkx::font('configure', $treeview_total_font, -weight => 'bold');
  
  # Build the left bar with various parameters.
  {
    my $frame = $root->new_ttk__frame(-padding => 3);
    $frame->g_grid(-column => 0, -row => 0, -rowspan => 3, -sticky => "we");
    
    my $row = 0;
    for my $c (['invested', "Valeur du bien", \&format_euro, "Prix d'achat du bien, hors frais de notaire."],
               ['tax_rate', "Taux d'imposition marginal", \&format_percent],
               ['base_rent', "Loyer brut initial", \&format_euro],
               ['rent_charges', "Charge et gestion locative", \&format_percent],
               ['rent_increase', "revalorisation loyer", \&format_percent],
               ['duration', "Durée d'investissement", \&format_year],
               ['notary_fees', "Frais de notaire", \&format_percent],
               ['loan_insurance', "Assurance décès du prêt", \&format_percent],
               ['other_rate', "Taux de placement autre", \&format_percent],
               ['surface', "Superficie (pondérée)", \&format_surface, "Surface totale habitable, additionnée, le cas échéant, de la moitié des surfaces annexes dans la limite de 8m² (utilisée seulement pour l'application des plafonds 'Pinel')."]) {
      add_input_entry($frame, \$row, @$c);
    }
  }

  # Build the top bar with the loan duration and rate values.
  my @loan_duration_texts;  # Re-used in the core data table.
  {
    my $frame = $root->new_ttk__frame(-padding => 3);
    $frame->g_grid(-column => 1, -row => 0, -sticky => "nwes");

lib/App/InvestSim/GUI.pm  view on Meta::CPAN

    my $loan_durations = $values{loan_durations};
    my $validate_duration = $values_config{loan_durations}[1];
    my $loan_rates = $values{loan_rates};
    my $validate_rate = $values_config{loan_rates}[1];
    for my $i (0..NUM_LOAN_DURATION-1) {
      my $d = $frame->new_ttk__entry(-width => ENTRY_WIDTH);
      $d->g_grid(-column => $i + 1, -row => 0, -sticky => "we");
      setup_entry($d, \$loan_durations->[$i], \&format_year, $validate_duration, \$loan_duration_texts[$i]);
      my $r = $frame->new_ttk__entry(-width => ENTRY_WIDTH);
      $r->g_grid(-column => $i + 1, -row => 1, -sticky => "we");
      setup_entry($r, \$loan_rates->[$i], \&format_percent, $validate_rate);
    }
  }

  # Build the combo-box with the list of possible values, in its own frame.
  my @modes;
  $modes[MONTHLY_PAYMENT] = "Mensualité de l'emprunt (assurance comprise)";
  $modes_format[MONTHLY_PAYMENT] = \&format_euro;
  $modes[LOAN_COST] = "Cout total de l'emprunt (assurance comprise)";
  $modes_format[LOAN_COST] = \&format_euro;
  $modes[YEARLY_RENT_AFTER_LOAN] = "Revenus locatif net déduit des remboursement, par an";
  $modes_format[YEARLY_RENT_AFTER_LOAN] = \&format_euro;
  $modes[MEAN_BALANCE_LOAN_DURATION] = "Balance mensuelle moyenne de l'opération sur la durée du prêt";
  $modes_format[MEAN_BALANCE_LOAN_DURATION] = \&format_euro;
  $modes[MEAN_BALANCE_OVERALL] = "Balance mensuelle moyenne de l'opération sur la durée de simulation";
  $modes_format[MEAN_BALANCE_OVERALL] = \&format_euro;
  $modes[NET_GAIN] = "Gain Net de l'opération";
  $modes_format[NET_GAIN] = \&format_euro;
  $modes[INVESTMENT_RETURN] = "Rendement de l'opération";
  $modes_format[INVESTMENT_RETURN] = \&format_percent;
  {
    my $frame = $root->new_ttk__frame(-padding => 5);
    $frame->g_grid(-column => 1, -row => 1, -sticky => "nwes");
    $frame->g_grid_columnconfigure(0, -weight => 1); # So that it extends to the whole width.

    $modes_combobox = $frame->new_ttk__combobox(-state => 'readonly', -values => \@modes, -justify => 'center');
    $modes_combobox->g_grid(-column => 0, -row => 0, -sticky => "we");
    $modes_combobox->m_current(MONTHLY_PAYMENT);
    $modes_combobox->g_bind('<<ComboboxSelected>>', \&update_displayed_mode);
  }

lib/App/InvestSim/GUI.pm  view on Meta::CPAN

  # depending on the loan parameters.
  {
    my $frame = $root->new_ttk__frame(-padding => 3);
    $frame->g_grid(-column => 2, -row => 0, -rowspan => 3, -sticky => "we");
    
    my $row = 0;
    
    for my $c (['rent_delay', "Delai de mise en location", \&format_year],
               ['loan_delay', "Durée de franchise de l'emprunt", \&format_year],
               ['application_fees', "Frais de dossier du prêt", \&format_euro],
               ['mortgage_fees', "Frais d'hypothèque", \&format_percent],
               ['social_tax', "CSG + CRDS + Solidarité", \&format_percent]) {
      add_input_entry($frame, \$row, @$c);
    }
    
    # Just some empty white-space between the inputs and the output fields.
    $frame->g_grid_rowconfigure($row++, -minsize => 10);
    
    $frame->new_ttk__label(-text => "Revenus total du loyer (net) :")
      ->g_grid(-column => 0, -row => $row, -sticky => "e", -padx => "0 2");
    $frame->new_ttk__entry(-width => ENTRY_WIDTH, -state => 'readonly', -textvariable => \$total_rent_text, -takefocus => 0)
      ->g_grid(-column => 1, -row => $row++, -sticky => "we", -pady => 2);



( run in 0.344 second using v1.01-cache-2.11-cpan-05162d3a2b1 )