+ # Load shipping address from database if shipto_id is set.
+ if ($self->{shipto_id}) {
+ my $shipto = SL::DB::Shipto->new(shipto_id => $self->{shipto_id})->load;
+ $self->{$_} = $shipto->$_ for grep { m{^shipto} } map { $_->name } @{ $shipto->meta->columns };
+ }
+
+ my $language = $self->{language} ? '_' . $self->{language} : '';
+
+ my ($language_tc, $output_numberformat, $output_dateformat, $output_longdates);
+ if ($self->{language_id}) {
+ ($language_tc, $output_numberformat, $output_dateformat, $output_longdates) = AM->get_language_details(\%::myconfig, $self, $self->{language_id});
+ }
+
+ $output_dateformat ||= $::myconfig{dateformat};
+ $output_numberformat ||= $::myconfig{numberformat};
+ $output_longdates //= 1;
+
+ $self->{myconfig_output_dateformat} = $output_dateformat // $::myconfig{dateformat};
+ $self->{myconfig_output_longdates} = $output_longdates // 1;
+ $self->{myconfig_output_numberformat} = $output_numberformat // $::myconfig{numberformat};
+
+ # Retrieve accounts for tax calculation.
+ IC->retrieve_accounts(\%::myconfig, $self, map { $_ => $self->{"id_$_"} } 1 .. $self->{rowcount});
+
+ if ($self->{type} =~ /_delivery_order$/) {
+ DO->order_details(\%::myconfig, $self);
+ } elsif ($self->{type} =~ /sales_order|sales_quotation|request_quotation|purchase_order/) {
+ OE->order_details(\%::myconfig, $self);
+ } else {
+ IS->invoice_details(\%::myconfig, $self, $::locale);
+ }
+
+ # Chose extension & set source file name
+ my $extension = 'html';
+ if ($self->{format} eq 'postscript') {
+ $self->{postscript} = 1;
+ $extension = 'tex';
+ } elsif ($self->{"format"} =~ /pdf/) {
+ $self->{pdf} = 1;
+ $extension = $self->{'format'} =~ m/opendocument/i ? 'odt' : 'tex';
+ } elsif ($self->{"format"} =~ /opendocument/) {
+ $self->{opendocument} = 1;
+ $extension = 'odt';
+ } elsif ($self->{"format"} =~ /excel/) {
+ $self->{excel} = 1;
+ $extension = 'xls';
+ }
+
+ my $printer_code = $self->{printer_code} ? '_' . $self->{printer_code} : '';
+ my $email_extension = $self->{media} eq 'email' && -f ($defaults->templates . "/$self->{formname}_email${language}.${extension}") ? '_email' : '';
+ $self->{IN} = "$self->{formname}${email_extension}${language}${printer_code}.${extension}";
+
+ # Format dates.
+ $self->format_dates($output_dateformat, $output_longdates,
+ qw(invdate orddate quodate pldate duedate reqdate transdate shippingdate deliverydate validitydate paymentdate datepaid
+ transdate_oe deliverydate_oe employee_startdate employee_enddate),
+ grep({ /^(?:datepaid|transdate_oe|reqdate|deliverydate|deliverydate_oe|transdate)_\d+$/ } keys(%{$self})));
+
+ $self->reformat_numbers($output_numberformat, 2,
+ qw(invtotal ordtotal quototal subtotal linetotal listprice sellprice netprice discount tax taxbase total paid),
+ grep({ /^(?:linetotal|listprice|sellprice|netprice|taxbase|discount|paid|subtotal|total|tax)_\d+$/ } keys(%{$self})));
+
+ $self->reformat_numbers($output_numberformat, undef, qw(qty price_factor), grep({ /^qty_\d+$/} keys(%{$self})));
+
+ my ($cvar_date_fields, $cvar_number_fields) = CVar->get_field_format_list('module' => 'CT', 'prefix' => 'vc_');
+
+ if (scalar @{ $cvar_date_fields }) {
+ $self->format_dates($output_dateformat, $output_longdates, @{ $cvar_date_fields });
+ }
+
+ while (my ($precision, $field_list) = each %{ $cvar_number_fields }) {
+ $self->reformat_numbers($output_numberformat, $precision, @{ $field_list });
+ }
+
+ $self->{template_meta} = {
+ formname => $self->{formname},
+ language => SL::DB::Manager::Language->find_by_or_create(id => $self->{language_id} || undef),
+ format => $self->{format},
+ media => $self->{media},
+ extension => $extension,
+ printer => SL::DB::Manager::Printer->find_by_or_create(id => $self->{printer_id} || undef),
+ today => DateTime->today,
+ };
+
+ return $self;
+}
+
+sub calculate_arap {
+ my ($self,$buysell,$taxincluded,$exchangerate,$roundplaces) = @_;
+
+ # this function is used to calculate netamount, total_tax and amount for AP and
+ # AR transactions (Kreditoren-/Debitorenbuchungen) by going over all lines
+ # (1..$rowcount)
+ # Thus it needs a fully prepared $form to work on.
+ # calculate_arap assumes $form->{amount_$i} entries still need to be parsed
+
+ # The calculated total values are all rounded (default is to 2 places) and
+ # returned as parameters rather than directly modifying form. The aim is to
+ # make the calculation of AP and AR behave identically. There is a test-case
+ # for this function in t/form/arap.t
+
+ # While calculating the totals $form->{amount_$i} and $form->{tax_$i} are
+ # modified and formatted and receive the correct sign for writing straight to
+ # acc_trans, depending on whether they are ar or ap.
+
+ # check parameters
+ die "taxincluded needed in Form->calculate_arap" unless defined $taxincluded;
+ die "exchangerate needed in Form->calculate_arap" unless defined $exchangerate;
+ die 'illegal buysell parameter, has to be \"buy\" or \"sell\" in Form->calculate_arap\n' unless $buysell =~ /^(buy|sell)$/;
+ $roundplaces = 2 unless $roundplaces;
+
+ my $sign = 1; # adjust final results for writing amount to acc_trans
+ $sign = -1 if $buysell eq 'buy';
+
+ my ($netamount,$total_tax,$amount);
+
+ my $tax;
+
+ # parse and round amounts, setting correct sign for writing to acc_trans
+ for my $i (1 .. $self->{rowcount}) {
+ $self->{"amount_$i"} = $self->round_amount($self->parse_amount(\%::myconfig, $self->{"amount_$i"}) * $exchangerate * $sign, $roundplaces);
+
+ $amount += $self->{"amount_$i"} * $sign;
+ }
+
+ for my $i (1 .. $self->{rowcount}) {
+ next unless $self->{"amount_$i"};
+ ($self->{"tax_id_$i"}) = split /--/, $self->{"taxchart_$i"};
+ my $tax_id = $self->{"tax_id_$i"};
+
+ my $selected_tax = SL::DB::Manager::Tax->find_by(id => "$tax_id");
+
+ if ( $selected_tax ) {
+
+ if ( $buysell eq 'sell' ) {
+ $self->{AR_amounts}{"tax_$i"} = $selected_tax->chart->accno if defined $selected_tax->chart;
+ } else {
+ $self->{AP_amounts}{"tax_$i"} = $selected_tax->chart->accno if defined $selected_tax->chart;
+ };
+
+ $self->{"taxkey_$i"} = $selected_tax->taxkey;
+ $self->{"taxrate_$i"} = $selected_tax->rate;
+ };
+
+ ($self->{"amount_$i"}, $self->{"tax_$i"}) = $self->calculate_tax($self->{"amount_$i"},$self->{"taxrate_$i"},$taxincluded,$roundplaces);
+
+ $netamount += $self->{"amount_$i"};
+ $total_tax += $self->{"tax_$i"};
+
+ }
+ $amount = $netamount + $total_tax;
+
+ # due to $sign amount_$i und tax_$i already have the right sign for acc_trans
+ # but reverse sign of totals for writing amounts to ar
+ if ( $buysell eq 'buy' ) {
+ $netamount *= -1;
+ $amount *= -1;
+ $total_tax *= -1;
+ };
+
+ return($netamount,$total_tax,$amount);
+}