qq|c_vendor_id = ?, | .
qq|klass = ?, | .
qq|currency_id = (SELECT id FROM currencies WHERE name = ?), | .
- qq|taxincluded_checked = ? | .
+ qq|taxincluded_checked = ?, | .
+ qq|delivery_term_id = ? | .
qq|WHERE id = ?|;
my @values = (
$form->{customernumber},
conv_i($form->{klass}),
$form->{currency},
$form->{taxincluded_checked} ne '' ? $form->{taxincluded_checked} : undef,
+ conv_i($form->{delivery_term_id}),
$form->{id}
);
do_query( $form, $dbh, $query, @values );
qq| username = ?, | .
qq| user_password = ?, | .
qq| v_customer_id = ?, | .
- qq| currency_id = (SELECT id FROM currencies WHERE name = ?) | .
+ qq| currency_id = (SELECT id FROM currencies WHERE name = ?), | .
+ qq| delivery_term_id = ? | .
qq|WHERE id = ?|;
my @values = (
$form->{vendornumber},
$form->{user_password},
$form->{v_customer_id},
$form->{currency},
+ conv_i($form->{delivery_term_id}),
$form->{id}
);
do_query($form, $dbh, $query, @values);
vc.*,
pt.description AS payment_terms,
b.description AS business,
- l.description AS language
+ l.description AS language,
+ dt.description AS delivery_terms
FROM ${vc} vc
LEFT JOIN payment_terms pt ON (vc.payment_id = pt.id)
LEFT JOIN business b ON (vc.business_id = b.id)
LEFT JOIN language l ON (vc.language_id = l.id)
+ LEFT JOIN delivery_terms dt ON (vc.delivery_term_id = dt.id)
WHERE vc.id = ?|;
my $ref = selectfirst_hashref_query($form, $dbh, $query, $vc_id);
use SL::Controller::CsvImport::Part;
use SL::Controller::CsvImport::Shipto;
use SL::Controller::CsvImport::Project;
+use SL::Controller::CsvImport::Order;
use SL::BackgroundJob::CsvImport;
use SL::System::TaskServer;
my $file = SL::SessionFile->new($file_name, mode => '>', encoding => $self->profile->get('charset'));
my $csv = Text::CSV_XS->new({ binary => 1, map { ( $_ => $self->profile->get($_) ) } qw(sep_char escape_char quote_char),});
- $csv->print($file->fh, [ map { $_->{name} } @{ $self->displayable_columns } ]);
- $file->fh->print("\r\n");
- $csv->print($file->fh, [ map { $_->{description} } @{ $self->displayable_columns } ]);
- $file->fh->print("\r\n");
+ if ($self->worker->is_multiplexed) {
+ foreach my $p (@{ $self->worker->profile }) {
+ $csv->print($file->fh, [ map { $_->{name} } @{ $self->displayable_columns->{$p->{row_ident}} } ]);
+ $file->fh->print("\r\n");
+ }
+ foreach my $p (@{ $self->worker->profile }) {
+ $csv->print($file->fh, [ map { $_->{description} } @{ $self->displayable_columns->{$p->{row_ident}} } ]);
+ $file->fh->print("\r\n");
+ }
+ } else {
+ $csv->print($file->fh, [ map { $_->{name} } @{ $self->displayable_columns } ]);
+ $file->fh->print("\r\n");
+ $csv->print($file->fh, [ map { $_->{description} } @{ $self->displayable_columns } ]);
+ $file->fh->print("\r\n");
+ }
$file->fh->close;
: $page;
$pages->{common} = [ grep { $_->{visible} } @{ SL::DB::Helper::Paginated::make_common_pages($pages->{cur}, $pages->{max}) } ];
+ $self->{report_numheaders} = $self->{report}->numheaders;
+ my $first_row_header = 0;
+ my $last_row_header = $self->{report_numheaders} - 1;
+ my $first_row_data = $pages->{per_page} * ($pages->{cur}-1) + $self->{report_numheaders};
+ my $last_row_data = min($pages->{per_page} * $pages->{cur}, $num_rows) + $self->{report_numheaders} - 1;
$self->{display_rows} = [
- 0,
- $pages->{per_page} * ($pages->{cur}-1) + 1
+ $first_row_header
+ ..
+ $last_row_header,
+ $first_row_data
..
- min($pages->{per_page} * $pages->{cur}, $num_rows)
+ $last_row_data
];
my @query = (
csv_import_report_id => $report_id,
or => [
- row => 0,
and => [
- row => { gt => $pages->{per_page} * ($pages->{cur}-1) },
- row => { le => $pages->{per_page} * $pages->{cur} },
+ row => { ge => $first_row_header },
+ row => { le => $last_row_header },
+ ],
+ and => [
+ row => { ge => $first_row_data },
+ row => { le => $last_row_data },
]
]
);
sub check_type {
my ($self) = @_;
- die "Invalid CSV import type" if none { $_ eq $::form->{profile}->{type} } qw(parts customers_vendors addresses contacts projects);
+ die "Invalid CSV import type" if none { $_ eq $::form->{profile}->{type} } qw(parts customers_vendors addresses contacts projects orders);
$self->type($::form->{profile}->{type});
}
: $self->type eq 'contacts' ? $::locale->text('CSV import: contacts')
: $self->type eq 'parts' ? $::locale->text('CSV import: parts and services')
: $self->type eq 'projects' ? $::locale->text('CSV import: projects')
+ : $self->type eq 'orders' ? $::locale->text('CSV import: orders')
: die;
if ($self->{type} eq 'parts') {
$::form->{settings}->{sellprice_adjustment} = $::form->parse_amount(\%::myconfig, $::form->{settings}->{sellprice_adjustment});
}
+ if ($self->type eq 'orders') {
+ $::form->{settings}->{max_amount_diff} = $::form->parse_amount(\%::myconfig, $::form->{settings}->{max_amount_diff});
+ }
+
delete $::form->{profile}->{id};
$self->profile($existing_profile || SL::DB::CsvImportProfile->new(login => $::myconfig{login}));
$self->profile->assign_attributes(%{ $::form->{profile} });
sub save_report {
my ($self, $report_id) = @_;
+ if ($self->worker->is_multiplexed) {
+ return $self->save_report_multi($report_id);
+ } else {
+ return $self->save_report_single($report_id);
+ }
+}
+
+sub save_report_single {
+ my ($self, $report_id) = @_;
+
$self->track_progress(phase => 'building report', progress => 0);
my $clone_profile = $self->profile->clone_and_reset_deep;
type => $self->type,
file => '',
numrows => scalar @{ $self->data },
+ numheaders => 1,
);
$report->save(cascade => 1) or die $report->db->error;
return $report->id;
}
+sub save_report_multi {
+ my ($self, $report_id) = @_;
+
+ $self->track_progress(phase => 'building report', progress => 0);
+
+ my $clone_profile = $self->profile->clone_and_reset_deep;
+ $clone_profile->save; # weird bug. if this isn't saved before adding it to the report, it will default back to the last profile.
+
+ my $report = SL::DB::CsvImportReport->new(
+ session_id => $::auth->create_or_refresh_session,
+ profile => $clone_profile,
+ type => $self->type,
+ file => '',
+ numrows => scalar @{ $self->data },
+ numheaders => scalar @{ $self->worker->profile },
+ );
+
+ $report->save(cascade => 1) or die $report->db->error;
+
+ my $dbh = $::form->get_standard_dbh;
+ $dbh->begin_work;
+
+ my $query = 'INSERT INTO csv_import_report_rows (csv_import_report_id, col, row, value) VALUES (?, ?, ?, ?)';
+ my $query2 = 'INSERT INTO csv_import_report_status (csv_import_report_id, row, type, value) VALUES (?, ?, ?, ?)';
+
+ my $sth = $dbh->prepare($query);
+ my $sth2 = $dbh->prepare($query2);
+
+ # save headers
+ my ($headers, $info_methods, $raw_methods, $methods);
+
+ for my $i (0 .. $#{ $self->worker->profile }) {
+ my $row_ident = $self->worker->profile->[$i]->{row_ident};
+
+ for my $i (0 .. $#{ $self->info_headers->{$row_ident}->{headers} }) {
+ next unless $self->info_headers->{$row_ident}->{used}->{ $self->info_headers->{$row_ident}->{methods}->[$i] };
+ push @{ $headers->{$row_ident} }, $self->info_headers->{$row_ident}->{headers}->[$i];
+ push @{ $info_methods->{$row_ident} }, $self->info_headers->{$row_ident}->{methods}->[$i];
+ }
+ for my $i (0 .. $#{ $self->headers->{$row_ident}->{headers} }) {
+ next unless $self->headers->{$row_ident}->{used}->{ $self->headers->{$row_ident}->{headers}->[$i] };
+ push @{ $headers->{$row_ident} }, $self->headers->{$row_ident}->{headers}->[$i];
+ push @{ $methods->{$row_ident} }, $self->headers->{$row_ident}->{methods}->[$i];
+ }
+
+ for my $i (0 .. $#{ $self->raw_data_headers->{$row_ident}->{headers} }) {
+ next unless $self->raw_data_headers->{$row_ident}->{used}->{ $self->raw_data_headers->{$row_ident}->{headers}->[$i] };
+ push @{ $headers->{$row_ident} }, $self->raw_data_headers->{$row_ident}->{headers}->[$i];
+ push @{ $raw_methods->{$row_ident} }, $self->raw_data_headers->{$row_ident}->{headers}->[$i];
+ }
+
+ }
+
+ for my $i (0 .. $#{ $self->worker->profile }) {
+ my $row_ident = $self->worker->profile->[$i]->{row_ident};
+ $sth->execute($report->id, $_, $i, $headers->{$row_ident}->[$_]) for 0 .. $#{ $headers->{$row_ident} };
+ }
+
+ # col offsets
+ my ($off1, $off2);
+ for my $i (0 .. $#{ $self->worker->profile }) {
+ my $row_ident = $self->worker->profile->[$i]->{row_ident};
+ my $n_info_methods = $info_methods->{$row_ident} ? scalar @{ $info_methods->{$row_ident} } : 0;
+ my $n_methods = $methods->{$row_ident} ? scalar @{ $methods->{$row_ident} } : 0;
+
+ $off1->{$row_ident} = $n_info_methods;
+ $off2->{$row_ident} = $off1->{$row_ident} + $n_methods;
+ }
+
+ my $n_header_rows = scalar @{ $self->worker->profile };
+
+ for my $row (0 .. $#{ $self->data }) {
+ $self->track_progress(progress => $row / @{ $self->data } * 100) if $row % 1000 == 0;
+ my $data_row = $self->{data}[$row];
+ my $row_ident = $data_row->{raw_data}{datatype};
+
+ my $o1 = $off1->{$row_ident};
+ my $o2 = $off2->{$row_ident};
+
+ $sth->execute($report->id, $_, $row + $n_header_rows, $data_row->{info_data}{ $info_methods->{$row_ident}->[$_] }) for 0 .. $#{ $info_methods->{$row_ident} };
+ $sth->execute($report->id, $o1 + $_, $row + $n_header_rows, $data_row->{object}->${ \ $methods->{$row_ident}->[$_] }) for 0 .. $#{ $methods->{$row_ident} };
+ $sth->execute($report->id, $o2 + $_, $row + $n_header_rows, $data_row->{raw_data}{ $raw_methods->{$row_ident}->[$_] }) for 0 .. $#{ $raw_methods->{$row_ident} };
+
+ $sth2->execute($report->id, $row + $n_header_rows, 'information', $_) for @{ $data_row->{information} || [] };
+ $sth2->execute($report->id, $row + $n_header_rows, 'errors', $_) for @{ $data_row->{errors} || [] };
+ }
+
+ $dbh->commit;
+
+ return $report->id;
+}
+
sub csv_file_name {
my ($self) = @_;
return "csv-import-" . $self->type . ".csv";
: $self->{type} eq 'addresses' ? SL::Controller::CsvImport::Shipto->new(@args)
: $self->{type} eq 'parts' ? SL::Controller::CsvImport::Part->new(@args)
: $self->{type} eq 'projects' ? SL::Controller::CsvImport::Project->new(@args)
+ : $self->{type} eq 'orders' ? SL::Controller::CsvImport::Order->new(@args)
: die "Program logic error";
}
use List::MoreUtils qw(pairwise any);
use SL::Helper::Csv;
-use SL::DB::Currency;
use SL::DB::Customer;
use SL::DB::Language;
use SL::DB::PaymentTerm;
+use SL::DB::DeliveryTerm;
use SL::DB::Vendor;
use SL::DB::Contact;
use SL::DB::History;
use Rose::Object::MakeMethods::Generic
(
scalar => [ qw(controller file csv test_run save_with_cascade) ],
- 'scalar --get_set_init' => [ qw(profile displayable_columns existing_objects class manager_class cvar_columns all_cvar_configs all_languages payment_terms_by all_currencies default_currency_id all_vc vc_by) ],
+ 'scalar --get_set_init' => [ qw(profile displayable_columns existing_objects class manager_class cvar_columns all_cvar_configs all_languages payment_terms_by delivery_terms_by all_vc vc_by) ],
);
sub run {
my $profile = $self->profile;
$self->csv(SL::Helper::Csv->new(file => $self->file->file_name,
encoding => $self->controller->profile->get('charset'),
- class => $self->class,
- profile => $profile,
+ profile => [{ profile => $profile, class => $self->class }],
ignore_unknown_columns => 1,
strict_profile => 1,
case_insensitive_header => 1,
return SL::DB::Manager::Language->get_all;
}
-sub init_all_currencies {
+sub init_payment_terms_by {
my ($self) = @_;
- return SL::DB::Manager::Currency->get_all;
+ my $all_payment_terms = SL::DB::Manager::PaymentTerm->get_all;
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_payment_terms } } ) } qw(id description) };
}
-sub init_default_currency_id {
+sub init_delivery_terms_by {
my ($self) = @_;
- return SL::DB::Default->get->currency_id;
-}
-
-sub init_payment_terms_by {
- my ($self) = @_;
-
- my $all_payment_terms = SL::DB::Manager::PaymentTerm->get_all;
- return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_payment_terms } } ) } qw(id description) };
+ my $all_delivery_terms = SL::DB::Manager::DeliveryTerm->get_all;
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_delivery_terms } } ) } qw(id description) };
}
sub init_all_vc {
$self->manager_class("SL::DB::Manager::" . $1);
}
+sub is_multiplexed { 0 }
+
sub check_objects {
}
return 1;
}
+sub check_delivery_term {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ # Check whether or not delivery term ID is valid.
+ if ($object->delivery_term_id && !$self->delivery_terms_by->{id}->{ $object->delivery_term_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid delivery terms');
+ return 0;
+ }
+
+ # Map name to ID if given.
+ if (!$object->delivery_term_id && $entry->{raw_data}->{delivery_term}) {
+ my $terms = $self->delivery_terms_by->{description}->{ $entry->{raw_data}->{delivery_term} };
+
+ if (!$terms) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid delivery terms');
+ return 0;
+ }
+
+ $object->delivery_term_id($terms->id);
+ }
+
+ return 1;
+}
+
sub save_objects {
my ($self, %params) = @_;
my $object = $entry->{object_to_save} || $entry->{object};
- if ( !$object->save(cascade => !!$self->save_with_cascade()) ) {
+ my $ret;
+ if (!eval { $ret = $object->save(cascade => !!$self->save_with_cascade()); 1 }) {
+ push @{ $entry->{errors} }, $::locale->text('Error when saving: #1', $@);
+ } elsif ( !$ret ) {
push @{ $entry->{errors} }, $::locale->text('Error when saving: #1', $entry->{object}->db->error);
} else {
$self->_save_history($object);
sub _save_history {
my ($self, $object) = @_;
- if (any { $_ eq $self->controller->{type} } qw(parts customers_vendors)) {
+ if (any { $_ eq $self->controller->{type} } qw(parts customers_vendors orders)) {
my $snumbers = $self->controller->{type} eq 'parts' ? 'partnumber_' . $object->partnumber
: $self->controller->{type} eq 'customers_vendors' ?
($self->table eq 'customer' ? 'customernumber_' . $object->customernumber : 'vendornumber_' . $object->vendornumber)
+ : $self->controller->{type} eq 'orders' ? 'ordnumber_' . $object->ordnumber
: '';
+ my $what_done = $self->controller->{type} eq 'orders' ? 'sales_order'
+ : '';
+
SL::DB::History->new(
trans_id => $object->id,
snumbers => $snumbers,
employee_id => $self->controller->{employee_id},
addition => 'SAVED',
+ what_done => $what_done,
)->save();
}
}
--- /dev/null
+package SL::Controller::CsvImport::BaseMulti;
+
+use strict;
+
+use List::MoreUtils qw(pairwise);
+
+use SL::Helper::Csv;
+
+use parent qw(SL::Controller::CsvImport::Base);
+
+use Rose::Object::MakeMethods::Generic
+(
+'scalar --get_set_init' => [ qw(cvar_configs_by cvar_columns_by) ],
+);
+
+sub run {
+ my ($self, %params) = @_;
+
+ $self->test_run($params{test_run});
+
+ $self->controller->track_progress(phase => 'parsing csv', progress => 0);
+
+ my $profile = $self->profile;
+
+ $self->csv(SL::Helper::Csv->new(file => $self->file->file_name,
+ encoding => $self->controller->profile->get('charset'),
+ profile => $profile,
+ ignore_unknown_columns => 1,
+ strict_profile => 1,
+ case_insensitive_header => 1,
+ map { ( $_ => $self->controller->profile->get($_) ) } qw(sep_char escape_char quote_char),
+ ));
+
+ $self->controller->track_progress(progress => 10);
+
+ my $old_numberformat = $::myconfig{numberformat};
+ $::myconfig{numberformat} = $self->controller->profile->get('numberformat');
+
+ $self->csv->parse;
+
+ $self->controller->track_progress(progress => 50);
+
+ $self->controller->errors([ $self->csv->errors ]) if $self->csv->errors;
+
+ return if ( !$self->csv->header || $self->csv->errors );
+
+ my $headers;
+ my $i = 0;
+ foreach my $header (@{ $self->csv->header }) {
+
+ my $profile = $self->csv->profile->[$i]->{profile};
+ my $row_ident = $self->csv->profile->[$i]->{row_ident};
+
+ my $h = { headers => [ grep { $profile->{$_} } @{ $header } ] };
+ $h->{methods} = [ map { $profile->{$_} } @{ $h->{headers} } ];
+ $h->{used} = { map { ($_ => 1) } @{ $h->{headers} } };
+
+ $headers->{$row_ident} = $h;
+ $i++;
+ }
+
+ $self->controller->headers($headers);
+
+ my $raw_data_headers;
+ my $info_headers;
+ foreach my $p (@{ $self->csv->profile }) {
+ $raw_data_headers->{ $p->{row_ident} } = { used => { }, headers => [ ] };
+ $info_headers->{ $p->{row_ident} } = { used => { }, headers => [ ] };
+ }
+ $self->controller->raw_data_headers($raw_data_headers);
+ $self->controller->info_headers($info_headers);
+
+ my @objects = $self->csv->get_objects;
+
+ $self->controller->track_progress(progress => 70);
+
+ my @raw_data = @{ $self->csv->get_data };
+
+ $self->controller->track_progress(progress => 80);
+
+ $self->controller->data([ pairwise { { object => $a, raw_data => $b, errors => [], information => [], info_data => {} } } @objects, @raw_data ]);
+
+ $self->controller->track_progress(progress => 90);
+
+ $self->check_objects;
+ if ( $self->controller->profile->get('duplicates', 'no_check') ne 'no_check' ) {
+ $self->check_std_duplicates();
+ $self->check_duplicates();
+ }
+ $self->fix_field_lengths;
+
+ $self->controller->track_progress(progress => 100);
+
+ $::myconfig{numberformat} = $old_numberformat;
+}
+
+sub add_columns {
+ my ($self, $row_ident, @columns) = @_;
+
+ my $h = $self->controller->headers->{$row_ident};
+
+ foreach my $column (grep { !$h->{used}->{$_} } @columns) {
+ $h->{used}->{$column} = 1;
+ push @{ $h->{methods} }, $column;
+ push @{ $h->{headers} }, $column;
+ }
+}
+
+sub add_info_columns {
+ my ($self, $row_ident, @columns) = @_;
+
+ my $h = $self->controller->info_headers->{$row_ident};
+
+ foreach my $column (grep { !$h->{used}->{ $_->{method} } } map { ref $_ eq 'HASH' ? $_ : { method => $_, header => $_ } } @columns) {
+ $h->{used}->{ $column->{method} } = 1;
+ push @{ $h->{methods} }, $column->{method};
+ push @{ $h->{headers} }, $column->{header};
+ }
+}
+
+sub add_raw_data_columns {
+ my ($self, $row_ident, @columns) = @_;
+
+ my $h = $self->controller->raw_data_headers->{$row_ident};
+
+ foreach my $column (grep { !$h->{used}->{$_} } @columns) {
+ $h->{used}->{$column} = 1;
+ push @{ $h->{headers} }, $column;
+ }
+}
+
+sub add_cvar_raw_data_columns {
+ my ($self) = @_;
+
+ foreach my $data (@{ $self->controller->data }) {
+ my $ri = $data->{raw_data}->{datatype};
+ map { $self->add_raw_data_columns($ri, $_) if exists $data->{raw_data}->{$_} } @{ $self->cvar_columns_by->{row_ident}->{$ri} };
+ }
+}
+
+sub init_cvar_configs_by {
+ # Must be overridden by derived specialized importer classes.
+ return {};
+}
+
+sub init_cvar_columns_by {
+ my ($self) = @_;
+
+ my $ccb;
+ foreach my $p (@{ $self->profile }) {
+ my $ri = $p->{row_ident};
+ $ccb->{row_ident}->{$ri} = [ map { "cvar_" . $_->name } (@{ $self->cvar_configs_by->{row_ident}->{$ri} }) ];
+ }
+
+ return $ccb;
+}
+
+sub handle_cvars {
+ my ($self, $entry, %params) = @_;
+
+ my %type_to_column = ( text => 'text_value',
+ textfield => 'text_value',
+ select => 'text_value',
+ date => 'timestamp_value_as_date',
+ timestamp => 'timestamp_value_as_date',
+ number => 'number_value_as_number',
+ bool => 'bool_value' );
+
+ $params{sub_module} ||= '';
+ my @cvars;
+ foreach my $config (@{ $self->cvar_configs_by->{row_ident}->{$entry->{raw_data}->{datatype}} }) {
+ next unless exists $entry->{raw_data}->{ "cvar_" . $config->name };
+ my $value = $entry->{raw_data}->{ "cvar_" . $config->name };
+ my $column = $type_to_column{ $config->type } || die "Program logic error: unknown custom variable storage type";
+
+ push @cvars, SL::DB::CustomVariable->new(config_id => $config->id, $column => $value, sub_module => $params{sub_module});
+ }
+
+ $entry->{object}->custom_variables(\@cvars) if @cvars;
+}
+
+sub init_profile {
+ my ($self) = @_;
+
+ my @profile;
+ foreach my $class (@{ $self->class }) {
+ eval "require " . $class;
+
+ my %unwanted = map { ( $_ => 1 ) } (qw(itime mtime), map { $_->name } @{ $class->meta->primary_key_columns });
+ my %prof;
+ $prof{datatype} = '';
+ for my $col ($class->meta->columns) {
+ next if $unwanted{$col};
+
+ my $name = $col->isa('Rose::DB::Object::Metadata::Column::Numeric') ? "$col\_as_number"
+ : $col->isa('Rose::DB::Object::Metadata::Column::Date') ? "$col\_as_date"
+ : $col->isa('Rose::DB::Object::Metadata::Column::Timestamp') ? "$col\_as_date"
+ : $col->name;
+
+ $prof{$col} = $name;
+ }
+
+ $prof{ 'cvar_' . $_->name } = '' for @{ $self->cvar_configs_by->{class}->{$class} };
+
+ $class =~ m/^SL::DB::(.+)/;
+ push @profile, {'profile' => \%prof, 'class' => $class, 'row_ident' => $::locale->text($1)};
+ }
+
+ \@profile;
+}
+
+sub add_displayable_columns {
+ my ($self, $row_ident, @columns) = @_;
+
+ my $dis_cols = $self->controller->displayable_columns || {};
+
+ my @cols = @{ $dis_cols->{$row_ident} || [] };
+ my %ex_col_map = map { $_->{name} => $_ } @cols;
+
+ foreach my $column (@columns) {
+ if ($ex_col_map{ $column->{name} }) {
+ @{ $ex_col_map{ $column->{name} } }{ keys %{ $column } } = @{ $column }{ keys %{ $column } };
+ } else {
+ push @cols, $column;
+ }
+ }
+
+ my $by_name_datatype_first = sub { 'datatype' eq $a->{name} ? -1 :
+ 'datatype' eq $b->{name} ? 1 :
+ $a->{name} cmp $b->{name} };
+ $dis_cols->{$row_ident} = [ sort $by_name_datatype_first @cols ];
+
+ $self->controller->displayable_columns($dis_cols);
+}
+
+sub setup_displayable_columns {
+ my ($self) = @_;
+
+ foreach my $p (@{ $self->profile }) {
+ $self->add_displayable_columns($p->{row_ident}, map { { name => $_ } } keys %{ $p->{profile} });
+ }
+}
+
+sub add_cvar_columns_to_displayable_columns {
+ my ($self, $row_ident) = @_;
+
+ $self->add_displayable_columns($row_ident,
+ map { { name => 'cvar_' . $_->name,
+ description => $::locale->text('#1 (custom variable)', $_->description) } }
+ @{ $self->cvar_configs_by->{row_ident}->{$row_ident} });
+}
+
+sub is_multiplexed { 1 }
+
+1;
+
use strict;
use SL::Helper::Csv;
+use SL::Controller::CsvImport::Helper::Consistency;
use SL::DB::Business;
use SL::DB::CustomVariable;
use SL::DB::CustomVariableConfig;
use Rose::Object::MakeMethods::Generic
(
- 'scalar --get_set_init' => [ qw(table languages_by businesses_by currencies_by) ],
+ 'scalar --get_set_init' => [ qw(table languages_by businesses_by) ],
);
sub init_table {
return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $self->all_languages } } ) } qw(id description article_code) };
}
-sub init_currencies_by {
- my ($self) = @_;
-
- return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $self->all_currencies } } ) } qw(id name) };
-}
-
sub check_objects {
my ($self) = @_;
$self->check_language($entry);
$self->check_business($entry);
$self->check_payment($entry);
- $self->check_currency($entry);
+ $self->check_delivery_term($entry);
+ $self->check_currency($entry, take_default => 1);
$self->handle_cvars($entry);
next if @{ $entry->{errors} };
$i++;
}
- $self->add_columns(map { "${_}_id" } grep { exists $self->controller->data->[0]->{raw_data}->{$_} } qw(language business payment));
+ $self->add_columns(map { "${_}_id" } grep { exists $self->controller->data->[0]->{raw_data}->{$_} } qw(language business payment delivery_term));
$self->add_cvar_raw_data_columns;
}
return 1;
}
-sub check_currency {
- my ($self, $entry) = @_;
-
- my $object = $entry->{object};
-
- # Check whether or not currency ID is valid.
- if ($object->currency_id && !$self->currencies_by->{id}->{ $object->currency_id }) {
- push @{ $entry->{errors} }, $::locale->text('Error: Invalid currency');
- return 0;
- }
-
- # Map name to ID if given.
- if (!$object->currency_id && $entry->{raw_data}->{currency}) {
- my $currency = $self->currencies_by->{name}->{ $entry->{raw_data}->{currency} };
- if (!$currency) {
- push @{ $entry->{errors} }, $::locale->text('Error: Invalid currency');
- return 0;
- }
-
- $object->currency_id($currency->id);
- }
-
- # Set default currency if none was given.
- $object->currency_id($self->default_currency_id) if !$object->currency_id;
-
- $entry->{raw_data}->{currency_id} = $object->currency_id;
-
- return 1;
-}
-
sub check_business {
my ($self, $entry) = @_;
my ($self) = @_;
my $profile = $self->SUPER::init_profile;
- delete @{$profile}{qw(business datevexport language payment salesman salesman_id taxincluded terms)};
+ delete @{$profile}{qw(business datevexport language payment delivery_term salesman salesman_id taxincluded terms)};
return $profile;
}
{ name => 'customernumber', description => $::locale->text('Customer Number') },
{ name => 'department_1', description => $::locale->text('Department 1') },
{ name => 'department_2', description => $::locale->text('Department 2') },
+ { name => 'delivery_term_id', description => $::locale->text('Delivery terms (database ID)') },
+ { name => 'delivery_term', description => $::locale->text('Delivery terms (name)') },
{ name => 'direct_debit', description => $::locale->text('direct debit') },
{ name => 'discount', description => $::locale->text('Discount') },
{ name => 'email', description => $::locale->text('E-mail') },
--- /dev/null
+package SL::Controller::CsvImport::Helper::Consistency;
+
+use strict;
+
+use SL::DB::Default;
+use SL::DB::Currency;
+
+use SL::Helper::Csv::Error;
+
+use parent qw(Exporter);
+our @EXPORT = qw(check_currency);
+
+#
+# public functions
+#
+
+sub check_currency {
+ my ($self, $entry, %params) = @_;
+
+ my $object = $entry->{object};
+
+ # Check whether or not currency ID is valid.
+ if ($object->currency_id && ! _currencies_by($self)->{id}->{ $object->currency_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid currency');
+ return 0;
+ }
+
+ # Map name to ID if given.
+ if (!$object->currency_id && $entry->{raw_data}->{currency}) {
+ my $currency = _currencies_by($self)->{name}->{ $entry->{raw_data}->{currency} };
+ if (!$currency) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid currency');
+ return 0;
+ }
+
+ $object->currency_id($currency->id);
+ }
+
+ # Set default currency if none was given and take_default is true.
+ $object->currency_id(_default_currency_id($self)) if !$object->currency_id and $params{take_default};
+
+ $entry->{raw_data}->{currency_id} = $object->currency_id;
+
+ return 1;
+}
+
+#
+# private functions
+#
+
+sub _currencies_by {
+ my ($self) = @_;
+
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ _all_currencies($self) } } ) } qw(id name) };
+}
+
+sub _all_currencies {
+ my ($self) = @_;
+
+ return SL::DB::Manager::Currency->get_all;
+}
+
+sub _default_currency_id {
+ my ($self) = @_;
+
+ return SL::DB::Default->get->currency_id;
+}
+
+1;
--- /dev/null
+package SL::Controller::CsvImport::Order;
+
+
+use strict;
+
+use List::MoreUtils qw(any);
+
+use SL::Helper::Csv;
+use SL::Controller::CsvImport::Helper::Consistency;
+use SL::DB::Order;
+use SL::DB::OrderItem;
+use SL::DB::Part;
+use SL::DB::PaymentTerm;
+use SL::DB::Contact;
+use SL::DB::Department;
+use SL::DB::PriceFactor;
+use SL::DB::Pricegroup;
+use SL::DB::Project;
+use SL::DB::Shipto;
+use SL::DB::TaxZone;
+use SL::TransNumber;
+
+use parent qw(SL::Controller::CsvImport::BaseMulti);
+
+
+use Rose::Object::MakeMethods::Generic
+(
+ 'scalar --get_set_init' => [ qw(settings languages_by parts_by contacts_by departments_by projects_by ct_shiptos_by taxzones_by price_factors_by pricegroups_by) ],
+);
+
+
+sub init_class {
+ my ($self) = @_;
+ $self->class(['SL::DB::Order', 'SL::DB::OrderItem']);
+}
+
+
+sub init_settings {
+ my ($self) = @_;
+
+ return { map { ( $_ => $self->controller->profile->get($_) ) } qw(order_column item_column max_amount_diff) };
+}
+
+
+sub init_cvar_configs_by {
+ my ($self) = @_;
+
+ my $item_cvar_configs = SL::DB::Manager::CustomVariableConfig->get_all(where => [ module => 'IC' ]);
+ $item_cvar_configs = [grep { $_->has_flag('editable') } @{ $item_cvar_configs }];
+
+ my $ccb;
+ $ccb->{class}->{'SL::DB::Order'} = [];
+ $ccb->{class}->{'SL::DB::OrderItem'} = $item_cvar_configs;
+ $ccb->{row_ident}->{$self->_order_column} = [];
+ $ccb->{row_ident}->{$self->_item_column} = $item_cvar_configs;
+
+ return $ccb;
+}
+
+
+sub init_profile {
+ my ($self) = @_;
+
+ my $profile = $self->SUPER::init_profile;
+
+ # SUPER::init_profile sets row_ident to the translated class name
+ # overwrite it with the user specified settings
+ foreach my $p (@{ $profile }) {
+ if ($p->{class} eq 'SL::DB::Order') {
+ $p->{row_ident} = $self->_order_column;
+ }
+ if ($p->{class} eq 'SL::DB::OrderItem') {
+ $p->{row_ident} = $self->_item_column;
+ }
+ }
+
+ foreach my $p (@{ $profile }) {
+ my $prof = $p->{profile};
+ if ($p->{row_ident} eq $self->_order_column) {
+ # no need to handle
+ delete @{$prof}{qw(delivery_customer_id delivery_vendor_id proforma quotation amount netamount)};
+ }
+ if ($p->{row_ident} eq $self->_item_column) {
+ # no need to handle
+ delete @{$prof}{qw(trans_id)};
+ }
+ }
+
+ return $profile;
+}
+
+
+sub setup_displayable_columns {
+ my ($self) = @_;
+
+ $self->SUPER::setup_displayable_columns;
+
+ $self->add_displayable_columns($self->_order_column,
+ { name => 'datatype', description => $self->_order_column . ' [1]' },
+ { name => 'closed', description => $::locale->text('Closed') },
+ { name => 'currency', description => $::locale->text('Currency') },
+ { name => 'currency_id', description => $::locale->text('Currency (database ID)') },
+ { name => 'cusordnumber', description => $::locale->text('Customer Order Number') },
+ { name => 'delivered', description => $::locale->text('Delivered') },
+ { name => 'delivery_term_id', description => $::locale->text('Delivery terms (database ID)') },
+ { name => 'delivery_term', description => $::locale->text('Delivery terms (name)') },
+ { name => 'employee_id', description => $::locale->text('Employee (database ID)') },
+ { name => 'intnotes', description => $::locale->text('Internal Notes') },
+ { name => 'marge_percent', description => $::locale->text('Margepercent') },
+ { name => 'marge_total', description => $::locale->text('Margetotal') },
+ { name => 'notes', description => $::locale->text('Notes') },
+ { name => 'ordnumber', description => $::locale->text('Order Number') },
+ { name => 'quonumber', description => $::locale->text('Quotation Number') },
+ { name => 'reqdate', description => $::locale->text('Reqdate') },
+ { name => 'salesman_id', description => $::locale->text('Salesman (database ID)') },
+ { name => 'shippingpoint', description => $::locale->text('Shipping Point') },
+ { name => 'shipvia', description => $::locale->text('Ship via') },
+ { name => 'transaction_description', description => $::locale->text('Transaction description') },
+ { name => 'transdate', description => $::locale->text('Order Date') },
+ { name => 'verify_amount', description => $::locale->text('Amount (for verification)') . ' [2]' },
+ { name => 'verify_netamount', description => $::locale->text('Net amount (for verification)') . ' [2]'},
+ { name => 'taxincluded', description => $::locale->text('Tax Included') },
+ { name => 'customer', description => $::locale->text('Customer (name)') },
+ { name => 'customernumber', description => $::locale->text('Customer Number') },
+ { name => 'customer_id', description => $::locale->text('Customer (database ID)') },
+ { name => 'vendor', description => $::locale->text('Vendor (name)') },
+ { name => 'vendornumber', description => $::locale->text('Vendor Number') },
+ { name => 'vendor_id', description => $::locale->text('Vendor (database ID)') },
+ { name => 'language_id', description => $::locale->text('Language (database ID)') },
+ { name => 'language', description => $::locale->text('Language (name)') },
+ { name => 'payment_id', description => $::locale->text('Payment terms (database ID)') },
+ { name => 'payment', description => $::locale->text('Payment terms (name)') },
+ { name => 'taxzone_id', description => $::locale->text('Tax zone (database ID)') },
+ { name => 'taxzone', description => $::locale->text('Tax zone (description)') },
+ { name => 'cp_id', description => $::locale->text('Contact Person (database ID)') },
+ { name => 'contact', description => $::locale->text('Contact Person (name)') },
+ { name => 'department_id', description => $::locale->text('Department (database ID)') },
+ { name => 'department', description => $::locale->text('Department (description)') },
+ { name => 'globalproject_id', description => $::locale->text('Document Project (database ID)') },
+ { name => 'globalprojectnumber', description => $::locale->text('Document Project (number)') },
+ { name => 'globalproject', description => $::locale->text('Document Project (description)') },
+ { name => 'shipto_id', description => $::locale->text('Ship to (database ID)') },
+ );
+
+ $self->add_cvar_columns_to_displayable_columns($self->_item_column);
+
+ $self->add_displayable_columns($self->_item_column,
+ { name => 'datatype', description => $self->_item_column . ' [1]' },
+ { name => 'cusordnumber', description => $::locale->text('Customer Order Number') },
+ { name => 'description', description => $::locale->text('Description') },
+ { name => 'discount', description => $::locale->text('Discount') },
+ { name => 'lastcost', description => $::locale->text('Lastcost') },
+ { name => 'longdescription', description => $::locale->text('Long Description') },
+ { name => 'marge_percent', description => $::locale->text('Margepercent') },
+ { name => 'marge_total', description => $::locale->text('Margetotal') },
+ { name => 'ordnumber', description => $::locale->text('Order Number') },
+ { name => 'parts_id', description => $::locale->text('Part (database ID)') },
+ { name => 'partnumber', description => $::locale->text('Part Number') },
+ { name => 'project_id', description => $::locale->text('Project (database ID)') },
+ { name => 'projectnumber', description => $::locale->text('Project (number)') },
+ { name => 'project', description => $::locale->text('Project (description)') },
+ { name => 'price_factor_id', description => $::locale->text('Price factor (database ID)') },
+ { name => 'price_factor', description => $::locale->text('Price factor (name)') },
+ { name => 'pricegroup_id', description => $::locale->text('Price group (database ID)') },
+ { name => 'pricegroup', description => $::locale->text('Price group (name)') },
+ { name => 'qty', description => $::locale->text('Quantity') },
+ { name => 'reqdate', description => $::locale->text('Reqdate') },
+ { name => 'sellprice', description => $::locale->text('Sellprice') },
+ { name => 'serialnumber', description => $::locale->text('Serial No.') },
+ { name => 'subtotal', description => $::locale->text('Subtotal') },
+ { name => 'unit', description => $::locale->text('Unit') },
+ );
+}
+
+
+sub init_languages_by {
+ my ($self) = @_;
+
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $self->all_languages } } ) } qw(id description article_code) };
+}
+
+sub init_parts_by {
+ my ($self) = @_;
+
+ my $all_parts = SL::DB::Manager::Part->get_all;
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_parts } } ) } qw(id partnumber ean description) };
+}
+
+sub init_contacts_by {
+ my ($self) = @_;
+
+ my $all_contacts = SL::DB::Manager::Contact->get_all;
+
+ my $cby;
+ # by customer/vendor id _and_ contact person id
+ $cby->{'cp_cv_id+cp_id'} = { map { ( $_->cp_cv_id . '+' . $_->cp_id => $_ ) } @{ $all_contacts } };
+ # by customer/vendor id _and_ contact person name
+ $cby->{'cp_cv_id+cp_name'} = { map { ( $_->cp_cv_id . '+' . $_->cp_name => $_ ) } @{ $all_contacts } };
+
+ return $cby;
+}
+
+sub init_departments_by {
+ my ($self) = @_;
+
+ my $all_departments = SL::DB::Manager::Department->get_all;
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_departments } } ) } qw(id description) };
+}
+
+sub init_projects_by {
+ my ($self) = @_;
+
+ my $all_projects = SL::DB::Manager::Project->get_all;
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_projects } } ) } qw(id projectnumber description) };
+}
+
+sub init_ct_shiptos_by {
+ my ($self) = @_;
+
+ my $all_ct_shiptos = SL::DB::Manager::Shipto->get_all(query => [module => 'CT']);
+
+ my $sby;
+ # by trans_id _and_ shipto_id
+ $sby->{'trans_id+shipto_id'} = { map { ( $_->trans_id . '+' . $_->shipto_id => $_ ) } @{ $all_ct_shiptos } };
+
+ return $sby;
+}
+
+sub init_taxzones_by {
+ my ($self) = @_;
+
+ my $all_taxzones = SL::DB::Manager::TaxZone->get_all;
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_taxzones } } ) } qw(id description) };
+}
+
+sub init_price_factors_by {
+ my ($self) = @_;
+
+ my $all_price_factors = SL::DB::Manager::PriceFactor->get_all;
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_price_factors } } ) } qw(id description) };
+}
+
+sub init_pricegroups_by {
+ my ($self) = @_;
+
+ my $all_pricegroups = SL::DB::Manager::Pricegroup->get_all;
+ return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_pricegroups } } ) } qw(id pricegroup) };
+}
+
+sub check_objects {
+ my ($self) = @_;
+
+ $self->controller->track_progress(phase => 'building data', progress => 0);
+
+ my $i = 0;
+ my $num_data = scalar @{ $self->controller->data };
+ foreach my $entry (@{ $self->controller->data }) {
+ $self->controller->track_progress(progress => $i/$num_data * 100) if $i % 100 == 0;
+
+ if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+ $self->handle_order($entry);
+ } elsif ($entry->{raw_data}->{datatype} eq $self->_item_column && $entry->{object}->can('part')) {
+ $self->handle_item($entry);
+ }
+ $self->handle_cvars($entry, sub_module => 'orderitems');
+
+ } continue {
+ $i++;
+ }
+
+ $self->add_info_columns($self->_order_column,
+ { header => $::locale->text('Customer/Vendor'), method => 'vc_name' });
+ # Todo: access via ->[0] ok? Better: search first order column and use this
+ $self->add_columns($self->_order_column,
+ map { "${_}_id" } grep { exists $self->controller->data->[0]->{raw_data}->{$_} } qw(payment delivery_term language department globalproject taxzone cp currency));
+ $self->add_columns($self->_order_column, 'globalproject_id') if exists $self->controller->data->[0]->{raw_data}->{globalprojectnumber};
+ $self->add_columns($self->_order_column, 'cp_id') if exists $self->controller->data->[0]->{raw_data}->{contact};
+
+ $self->add_info_columns($self->_item_column,
+ { header => $::locale->text('Part Number'), method => 'partnumber' });
+ # Todo: access via ->[1] ok? Better: search first item column and use this
+ $self->add_columns($self->_item_column,
+ map { "${_}_id" } grep { exists $self->controller->data->[1]->{raw_data}->{$_} } qw(project price_factor pricegroup));
+ $self->add_columns($self->_item_column, 'project_id') if exists $self->controller->data->[1]->{raw_data}->{projectnumber};
+
+ $self->add_cvar_raw_data_columns();
+
+ $self->add_items_to_order();
+ $self->handle_prices_and_taxes();
+
+
+ # If order has errors set error for orderitems as well
+ my $order_entry;
+ foreach my $entry (@{ $self->controller->data }) {
+ # Search first order
+ if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+ $order_entry = $entry;
+ } elsif ( defined $order_entry
+ && $entry->{raw_data}->{datatype} eq $self->_item_column
+ && scalar @{ $order_entry->{errors} } > 0 ) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid order for this order item');
+ }
+ }
+
+}
+
+sub handle_order {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ my $vc_obj;
+ if (any { $entry->{raw_data}->{$_} } qw(customer customernumber customer_id)) {
+ $self->check_vc($entry, 'customer_id');
+ $vc_obj = SL::DB::Customer->new(id => $object->customer_id)->load if $object->customer_id;
+ } elsif (any { $entry->{raw_data}->{$_} } qw(vendor vendornumber vendor_id)) {
+ $self->check_vc($entry, 'vendor_id');
+ $vc_obj = SL::DB::Vendor->new(id => $object->vendor_id)->load if $object->vendor_id;
+ } else {
+ push @{ $entry->{errors} }, $::locale->text('Error: Customer/vendor missing');
+ }
+
+ $self->check_contact($entry);
+ $self->check_language($entry);
+ $self->check_payment($entry);
+ $self->check_delivery_term($entry);
+ $self->check_department($entry);
+ $self->check_project($entry, global => 1);
+ $self->check_ct_shipto($entry);
+ $self->check_taxzone($entry);
+ $self->check_currency($entry, take_default => 0);
+
+ if ($vc_obj) {
+ # copy from customer if not given
+ foreach (qw(payment_id language_id taxzone_id currency_id)) {
+ $object->$_($vc_obj->$_) unless $object->$_;
+ }
+ }
+
+ $self->handle_salesman($entry);
+ $self->handle_employee($entry);
+}
+
+# ToDo: salesman by name
+sub handle_salesman {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+ my $vc_obj = SL::DB::Customer->new(id => $object->customer_id)->load if $object->customer_id;
+ $vc_obj = SL::DB::Vendor->new(id => $object->vendor_id)->load if (!$vc_obj && $object->vendor_id);
+
+ # salesman from customer/vendor or login if not given
+ if (!$object->salesman) {
+ if ($vc_obj && $vc_obj->salesman_id) {
+ $object->salesman(SL::DB::Manager::Employee->find_by(id => $vc_obj->salesman_id));
+ } else {
+ $object->salesman(SL::DB::Manager::Employee->find_by(login => $::myconfig{login}));
+ }
+ }
+}
+
+# ToDo: employee by name
+sub handle_employee {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ # employee from login if not given
+ if (!$object->employee_id) {
+ $object->employee_id(SL::DB::Manager::Employee->find_by(login => $::myconfig{login})->id);
+ }
+}
+
+sub check_language {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ # Check whether or not language ID is valid.
+ if ($object->language_id && !$self->languages_by->{id}->{ $object->language_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid language');
+ return 0;
+ }
+
+ # Map name to ID if given.
+ if (!$object->language_id && $entry->{raw_data}->{language}) {
+ my $language = $self->languages_by->{description}->{ $entry->{raw_data}->{language} }
+ || $self->languages_by->{article_code}->{ $entry->{raw_data}->{language} };
+
+ if (!$language) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid language');
+ return 0;
+ }
+
+ $object->language_id($language->id);
+ }
+
+ if ($object->language_id) {
+ $entry->{info_data}->{language} = $self->languages_by->{id}->{ $object->language_id }->description;
+ }
+
+ return 1;
+}
+
+sub handle_item {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+ return unless $self->check_part($entry);
+
+ my $part_obj = SL::DB::Part->new(id => $object->parts_id)->load;
+
+ # copy from part if not given
+ $object->description($part_obj->description) unless $object->description;
+ $object->longdescription($part_obj->notes) unless $object->longdescription;
+ $object->unit($part_obj->unit) unless $object->unit;
+ $object->sellprice($part_obj->sellprice) unless defined $object->sellprice;
+ $object->lastcost($part_obj->lastcost) unless defined $object->lastcost;
+
+ # set to 0 if not given
+ $object->discount(0) unless $object->discount;
+ $object->ship(0) unless $object->ship;
+
+ $self->check_project($entry, global => 0);
+ $self->check_price_factor($entry);
+ $self->check_pricegroup($entry);
+}
+
+sub check_part {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ # Check wether or non part ID is valid.
+ if ($object->parts_id && !$self->parts_by->{id}->{ $object->parts_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid part');
+ return 0;
+ }
+
+ # Map number to ID if given.
+ if (!$object->parts_id && $entry->{raw_data}->{partnumber}) {
+ my $part = $self->parts_by->{partnumber}->{ $entry->{raw_data}->{partnumber} };
+ if (!$part) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid part');
+ return 0;
+ }
+
+ $object->parts_id($part->id);
+ }
+
+ if ($object->parts_id) {
+ $entry->{info_data}->{partnumber} = $self->parts_by->{id}->{ $object->parts_id }->partnumber;
+ } else {
+ push @{ $entry->{errors} }, $::locale->text('Error: Part not found');
+ return 0;
+ }
+
+ return 1;
+}
+
+sub check_contact {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ my $cp_cv_id = $object->customer_id || $object->vendor_id;
+ return 0 unless $cp_cv_id;
+
+ # Check wether or not contact ID is valid.
+ if ($object->cp_id && !$self->contacts_by->{'cp_cv_id+cp_id'}->{ $cp_cv_id . '+' . $object->cp_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid contact');
+ return 0;
+ }
+
+ # Map name to ID if given.
+ if (!$object->cp_id && $entry->{raw_data}->{contact}) {
+ my $cp = $self->contacts_by->{'cp_cv_id+cp_name'}->{ $cp_cv_id . '+' . $entry->{raw_data}->{contact} };
+ if (!$cp) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid contact');
+ return 0;
+ }
+
+ $object->cp_id($cp->cp_id);
+ }
+
+ if ($object->cp_id) {
+ $entry->{info_data}->{contact} = $self->contacts_by->{'cp_cv_id+cp_id'}->{ $cp_cv_id . '+' . $object->cp_id }->cp_name;
+ }
+
+ return 1;
+}
+
+sub check_department {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ # Check wether or not department ID is valid.
+ if ($object->department_id && !$self->departments_by->{id}->{ $object->department_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid department');
+ return 0;
+ }
+
+ # Map description to ID if given.
+ if (!$object->department_id && $entry->{raw_data}->{department}) {
+ my $dep = $self->departments_by->{description}->{ $entry->{raw_data}->{department} };
+ if (!$dep) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid department');
+ return 0;
+ }
+
+ $object->department_id($dep->id);
+ }
+
+ return 1;
+}
+
+sub check_project {
+ my ($self, $entry, %params) = @_;
+
+ my $id_column = ($params{global} ? 'global' : '') . 'project_id';
+ my $number_column = ($params{global} ? 'global' : '') . 'projectnumber';
+ my $description_column = ($params{global} ? 'global' : '') . 'project';
+
+ my $object = $entry->{object};
+
+ # Check wether or not projetc ID is valid.
+ if ($object->$id_column && !$self->projects_by->{id}->{ $object->$id_column }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid project');
+ return 0;
+ }
+
+ # Map number to ID if given.
+ if (!$object->$id_column && $entry->{raw_data}->{$number_column}) {
+ my $proj = $self->projects_by->{projectnumber}->{ $entry->{raw_data}->{$number_column} };
+ if (!$proj) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid project');
+ return 0;
+ }
+
+ $object->$id_column($proj->id);
+ }
+
+ # Map description to ID if given.
+ if (!$object->$id_column && $entry->{raw_data}->{$description_column}) {
+ my $proj = $self->projects_by->{description}->{ $entry->{raw_data}->{$description_column} };
+ if (!$proj) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid project');
+ return 0;
+ }
+
+ $object->$id_column($proj->id);
+ }
+
+ return 1;
+}
+
+sub check_ct_shipto {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ my $trans_id = $object->customer_id || $object->vendor_id;
+ return 0 unless $trans_id;
+
+ # Check wether or not shipto ID is valid.
+ if ($object->shipto_id && !$self->ct_shiptos_by->{'trans_id+shipto_id'}->{ $trans_id . '+' . $object->shipto_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid shipto');
+ return 0;
+ }
+
+ return 1;
+}
+
+sub check_taxzone {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ # Check wether or not taxzone ID is valid.
+ if ($object->taxzone_id && !$self->taxzones_by->{id}->{ $object->taxzone_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid tax zone');
+ return 0;
+ }
+
+ # Map description to ID if given.
+ if (!$object->taxzone_id && $entry->{raw_data}->{taxzone}) {
+ my $taxzone = $self->taxzones_by->{description}->{ $entry->{raw_data}->{taxzone} };
+ if (!$taxzone) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid tax zone');
+ return 0;
+ }
+
+ $object->taxzone_id($taxzone->id);
+ }
+
+ return 1;
+}
+
+sub check_price_factor {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ # Check wether or not price_factor ID is valid.
+ if ($object->price_factor_id && !$self->price_factors_by->{id}->{ $object->price_factor_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid price factor');
+ return 0;
+ }
+
+ # Map description to ID if given.
+ if (!$object->price_factor_id && $entry->{raw_data}->{price_factor}) {
+ my $price_factor = $self->price_factors_by->{description}->{ $entry->{raw_data}->{price_factor} };
+ if (!$price_factor) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid price factor');
+ return 0;
+ }
+
+ $object->price_factor_id($price_factor->id);
+ }
+
+ return 1;
+}
+
+sub check_pricegroup {
+ my ($self, $entry) = @_;
+
+ my $object = $entry->{object};
+
+ # Check wether or not pricegroup ID is valid.
+ if ($object->pricegroup_id && !$self->pricegroups_by->{id}->{ $object->pricegroup_id }) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid price group');
+ return 0;
+ }
+
+ # Map pricegroup to ID if given.
+ if (!$object->pricegroup_id && $entry->{raw_data}->{pricegroup}) {
+ my $pricegroup = $self->pricegroups_by->{pricegroup}->{ $entry->{raw_data}->{pricegroup} };
+ if (!$pricegroup) {
+ push @{ $entry->{errors} }, $::locale->text('Error: Invalid price group');
+ return 0;
+ }
+
+ $object->pricegroup_id($pricegroup->id);
+ }
+
+ return 1;
+}
+
+sub add_items_to_order {
+ my ($self) = @_;
+
+ # add orderitems to order
+ my $order_entry;
+ my @orderitems;
+ foreach my $entry (@{ $self->controller->data }) {
+ # search first order
+ if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+
+ # new order entry: add collected orderitems to the previous one
+ if (defined $order_entry) {
+ $order_entry->{object}->orderitems(@orderitems);
+ @orderitems = ();
+ }
+
+ $order_entry = $entry;
+
+ } elsif ( defined $order_entry && $entry->{raw_data}->{datatype} eq $self->_item_column ) {
+ # collect orderitems to add to order (if they have no errors)
+ # ( add_orderitems does not work here if we want to call
+ # calculate_prices_and_taxes afterwards ...
+ # so collect orderitems and add them at once )
+ push @orderitems, $entry->{object} if (scalar @{ $entry->{errors} } == 0);
+ }
+ }
+ # add last collected orderitems to last order
+ $order_entry->{object}->orderitems(@orderitems) if $order_entry;
+}
+
+sub handle_prices_and_taxes() {
+ my ($self) = @_;
+
+ # calculate prices and taxes
+ foreach my $entry (@{ $self->controller->data }) {
+ next if @{ $entry->{errors} };
+
+ if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+
+ $entry->{object}->calculate_prices_and_taxes;
+
+ $entry->{info_data}->{calc_amount} = $entry->{object}->amount_as_number;
+ $entry->{info_data}->{calc_netamount} = $entry->{object}->netamount_as_number;
+ }
+ }
+
+ # If amounts are given, show calculated amounts as info and given amounts (verify_xxx).
+ # And throw an error if the differences are too big.
+ my @to_verify = ( { column => 'amount',
+ raw_column => 'verify_amount',
+ info_header => 'Calc. Amount',
+ info_method => 'calc_amount',
+ err_msg => 'Amounts differ too much',
+ },
+ { column => 'netamount',
+ raw_column => 'verify_netamount',
+ info_header => 'Calc. Net amount',
+ info_method => 'calc_netamount',
+ err_msg => 'Net amounts differ too much',
+ } );
+
+ foreach my $tv (@to_verify) {
+ # Todo: access via ->[0] ok? Better: search first order column and use this
+ if (exists $self->controller->data->[0]->{raw_data}->{ $tv->{raw_column} }) {
+ $self->add_raw_data_columns($self->_order_column, $tv->{raw_column});
+ $self->add_info_columns($self->_order_column,
+ { header => $::locale->text($tv->{info_header}), method => $tv->{info_method} });
+ }
+
+ # check differences
+ foreach my $entry (@{ $self->controller->data }) {
+ next if @{ $entry->{errors} };
+ if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+ next if !$entry->{raw_data}->{ $tv->{raw_column} };
+ my $parsed_value = $::form->parse_amount(\%::myconfig, $entry->{raw_data}->{ $tv->{raw_column} });
+ if (abs($entry->{object}->${ \$tv->{column} } - $parsed_value) > $self->settings->{'max_amount_diff'}) {
+ push @{ $entry->{errors} }, $::locale->text($tv->{err_msg});
+ }
+ }
+ }
+ }
+
+}
+
+sub save_objects {
+ my ($self, %params) = @_;
+
+ # set order number and collect to save
+ my $objects_to_save;
+ foreach my $entry (@{ $self->controller->data }) {
+ next if $entry->{raw_data}->{datatype} ne $self->_order_column;
+ next if @{ $entry->{errors} };
+
+ if (!$entry->{object}->ordnumber) {
+ my $number = SL::TransNumber->new(type => 'sales_order',
+ save => 1);
+ $entry->{object}->ordnumber($number->create_unique());
+ }
+
+ push @{ $objects_to_save }, $entry;
+ }
+
+ $self->SUPER::save_objects(data => $objects_to_save);
+}
+
+sub _order_column {
+ $_[0]->settings->{'order_column'}
+}
+
+sub _item_column {
+ $_[0]->settings->{'item_column'}
+}
+
+1;
employee_id => SL::DB::Manager::Employee->current->id,
addition => 'SAVED',
)->save();
+
+ if ( $::form->{delete_notes} ) {
+ foreach my $note_id (@{ $::form->{delete_notes} }) {
+ my $note = SL::DB::Note->new(id => $note_id)->load();
+ if ( $note->follow_up ) {
+ if ( $note->follow_up->follow_up_link ) {
+ $note->follow_up->follow_up_link->delete(cascade => 'delete');
+ }
+ $note->follow_up->delete(cascade => 'delete');
+ }
+ $note->delete(cascade => 'delete');
+ }
+ }
}) || die($db->error);
}
push(@redirect_params, shipto_id => $self->{shipto}->shipto_id);
}
- if ( $self->{note}->id ) {
- push(@redirect_params, note_id => $self->{note}->id);
- }
-
$self->redirect_to(@redirect_params);
}
sub action_get_delivery {
my ($self) = @_;
+ $::auth->assert('sales_all_edit');
+
my $dbh = $::form->get_standard_dbh();
my ($arap, $db, $qty_sign);
$qty_sign = '';
}
- my $where = ' WHERE 1=1 ';
+ my $where = ' WHERE 1=1';
my @values;
if ( !$self->is_vendor() && $::form->{shipto_id} && $::form->{shipto_id} ne 'all' ) {
- $where .= "AND ${arap}.shipto_id = ?";
+ $where .= " AND ${arap}.shipto_id = ?";
push(@values, $::form->{shipto_id});
+ } else {
+ $where .= " AND ${arap}.${db}_id = ?";
+ push(@values, $::form->{id});
}
if ( $::form->{delivery_from} ) {
- $where .= "AND ${arap}.transdate >= ?";
+ $where .= " AND ${arap}.transdate >= ?";
push(@values, conv_date($::form->{delivery_from}));
}
if ( $::form->{delivery_to} ) {
- $where .= "AND ${arap}.transdate <= ?";
+ $where .= " AND ${arap}.transdate <= ?";
push(@values, conv_date($::form->{delivery_to}));
}
if ( $::form->{note}->{id} ) {
$self->{note} = SL::DB::Note->new(id => $::form->{note}->{id})->load();
-
- $self->{note_followup_link} = SL::DB::Manager::FollowUpLink->get_all(
- query => [
- 'follow_up.note_id' => $self->{note}->id,
- trans_id => $self->{cv}->id,
- trans_type => ($self->is_vendor() ? 'vendor' : 'customer'),
- ],
- with_objects => ['follow_up'],
- )->[0];
-
- $self->{note_followup} = $self->{note_followup_link}->follow_up;
+ $self->{note_followup} = $self->{note}->follow_up;
+ $self->{note_followup_link} = $self->{note_followup}->follow_up_link;
} else {
$self->{note} = SL::DB::Note->new();
$self->{note_followup} = SL::DB::FollowUp->new();
if ( $::form->{note_id} ) {
$self->{note} = SL::DB::Note->new(id => $::form->{note_id})->load();
-
- $self->{note_followup_link} = SL::DB::Manager::FollowUpLink->get_all(
- query => [
- 'follow_up.note_id' => $self->{note}->id,
- trans_id => $self->{cv}->id,
- trans_type => ($self->is_vendor() ? 'vendor' : 'customer'),
- ],
- with_objects => ['follow_up'],
- )->[0];
-
- $self->{note_followup} = $self->{note_followup_link}->follow_up;
+ $self->{note_followup} = $self->{note}->follow_up;
+ $self->{note_followup_link} = $self->{note_followup}->follow_up_link;
} else {
$self->{note} = SL::DB::Note->new();
$self->{note_followup} = SL::DB::FollowUp->new();
$self->{all_payment_terms} = SL::DB::Manager::PaymentTerm->get_all();
+ $self->{all_delivery_terms} = SL::DB::Manager::DeliveryTerm->get_all();
+
$self->{all_pricegroups} = SL::DB::Manager::Pricegroup->get_all();
$query =
$self->{shiptos} = $self->{cv}->shipto;
$self->{shiptos} ||= [];
+ $self->{notes} = SL::DB::Manager::Note->get_all(
+ query => [
+ trans_id => $self->{cv}->id,
+ trans_module => 'ct',
+ ],
+ with_objects => ['follow_up'],
+ );
+
$self->{template_args} ||= {};
$::request->{layout}->add_javascripts('autocomplete_customer.js');
--- /dev/null
+package SL::Controller::DeliveryTerm;
+
+use strict;
+
+use parent qw(SL::Controller::Base);
+
+use SL::DB::DeliveryTerm;
+use SL::DB::Language;
+use SL::Helper::Flash;
+
+use Rose::Object::MakeMethods::Generic
+(
+ scalar => [ qw(delivery_term languages) ],
+);
+
+__PACKAGE__->run_before('check_auth');
+__PACKAGE__->run_before('load_delivery_term', only => [ qw(edit update destroy) ]);
+__PACKAGE__->run_before('load_languages', only => [ qw(new list edit create update) ]);
+
+
+#
+# actions
+#
+
+sub action_list {
+ my ($self) = @_;
+
+ $self->render('delivery_term/list',
+ title => $::locale->text('Delivery terms'),
+ DELIVERY_TERMS => SL::DB::Manager::DeliveryTerm->get_all_sorted);
+}
+
+sub action_new {
+ my ($self) = @_;
+
+ $self->{delivery_term} = SL::DB::DeliveryTerm->new;
+ $self->render('delivery_term/form', title => $::locale->text('Create a new delivery term'));
+}
+
+sub action_edit {
+ my ($self) = @_;
+ $self->render('delivery_term/form', title => $::locale->text('Edit delivery term'));
+}
+
+sub action_create {
+ my ($self) = @_;
+
+ $self->{delivery_term} = SL::DB::DeliveryTerm->new;
+ $self->create_or_update;
+}
+
+sub action_update {
+ my ($self) = @_;
+ $self->create_or_update;
+}
+
+sub action_destroy {
+ my ($self) = @_;
+
+ if (eval { $self->{delivery_term}->delete; 1; }) {
+ flash_later('info', $::locale->text('The delivery term has been deleted.'));
+ } else {
+ flash_later('error', $::locale->text('The delivery term is in use and cannot be deleted.'));
+ }
+
+ $self->redirect_to(action => 'list');
+}
+
+sub action_reorder {
+ my ($self) = @_;
+
+ SL::DB::DeliveryTerm->reorder_list(@{ $::form->{delivery_term_id} || [] });
+
+ $self->render(\'', { type => 'json' }); # ' make Emacs happy
+}
+
+#
+# filters
+#
+
+sub check_auth {
+ $::auth->assert('config');
+}
+
+#
+# helpers
+#
+
+sub create_or_update {
+ my $self = shift;
+ my $is_new = !$self->{delivery_term}->id;
+ my $params = delete($::form->{delivery_term}) || { };
+
+ $self->{delivery_term}->assign_attributes(%{ $params });
+
+ my @errors = $self->{delivery_term}->validate;
+
+ if (@errors) {
+ flash('error', @errors);
+ $self->render('delivery_term/form', title => $is_new ? $::locale->text('Create a new delivery term') : $::locale->text('Edit delivery term'));
+ return;
+ }
+
+ $self->{delivery_term}->save;
+ foreach my $language (@{ $self->{languages} }) {
+ $self->{delivery_term}->save_attribute_translation('description_long', $language, $::form->{"translation_" . $language->id});
+ }
+
+ flash_later('info', $is_new ? $::locale->text('The delivery term has been created.') : $::locale->text('The delivery term has been saved.'));
+ $self->redirect_to(action => 'list');
+}
+
+sub load_delivery_term {
+ my ($self) = @_;
+ $self->{delivery_term} = SL::DB::DeliveryTerm->new(id => $::form->{id})->load;
+}
+
+sub load_languages {
+ my ($self) = @_;
+ $self->{languages} = SL::DB::Manager::Language->get_all_sorted;
+}
+
+1;
default_buchungsgruppe => ($bugru ? $bugru->id : undef),
apply_buchungsgruppe => 'all',
);
+ } elsif ($self->type eq 'orders') {
+ $self->_set_defaults(order_column => $::locale->text('Order'),
+ item_column => $::locale->text('OrderItem'),
+ max_amount_diff => 0.02,
+ );
} else {
$self->_set_defaults(table => 'customer');
}
--- /dev/null
+# This file has been auto-generated only because it didn't exist.
+# Feel free to modify it at will; it will not be overwritten automatically.
+
+package SL::DB::DeliveryTerm;
+
+use strict;
+
+use SL::DB::MetaSetup::DeliveryTerm;
+use SL::DB::Manager::DeliveryTerm;
+use SL::DB::Helper::ActsAsList;
+use SL::DB::Helper::TranslatedAttributes;
+
+
+sub validate {
+ my ($self) = @_;
+
+ my @errors;
+ push @errors, $::locale->text('The description is missing.') if !$self->description;
+ push @errors, $::locale->text('The long description is missing.') if !$self->description_long;
+
+ return @errors;
+}
+
+1;
use SL::DB::MetaSetup::FollowUp;
+__PACKAGE__->meta->add_relationships(
+ follow_up_link => {
+ type => 'one to one',
+ class => 'SL::DB::FollowUpLink',
+ column_map => { id => 'follow_up_id' },
+ },
+);
+
__PACKAGE__->meta->initialize;
# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
use SL::DB::DeliveryOrder;
use SL::DB::DeliveryOrderItem;
use SL::DB::DeliveryOrderItemsStock;
+use SL::DB::DeliveryTerm;
use SL::DB::Department;
use SL::DB::Draft;
use SL::DB::Dunning;
_copy($self, $form, '', '', 0, qw(id type taxzone_id ordnumber quonumber invnumber donumber cusordnumber taxincluded shippingpoint shipvia notes intnotes cp_id
employee_id salesman_id closed department_id language_id payment_id delivery_customer_id delivery_vendor_id shipto_id proforma
globalproject_id delivered transaction_description container_type accepted_by_customer invoice terms storno storno_id dunning_config_id
- orddate quodate reqdate gldate duedate deliverydate datepaid transdate));
+ orddate quodate reqdate gldate duedate deliverydate datepaid transdate delivery_term_id));
$form->{currency} = $form->{curr} = $self->currency_id ? $self->currency->name || '' : '';
if (_has($self, 'transdate')) {
my %args = ( map({ ( $_ => $source->$_ ) } qw(customer_id taxincluded shippingpoint shipvia notes intnotes salesman_id cusordnumber ordnumber quonumber
department_id cp_id language_id payment_id delivery_customer_id delivery_vendor_id taxzone_id shipto_id
- globalproject_id transaction_description currency_id)),
+ globalproject_id transaction_description currency_id delivery_term_id)),
transdate => DateTime->today_local,
gldate => DateTime->today_local,
duedate => DateTime->today_local->add(days => $terms * 1),
--- /dev/null
+package SL::DB::Manager::DeliveryTerm;
+
+use strict;
+
+use SL::DB::Helper::Manager;
+use base qw(SL::DB::Helper::Manager);
+
+use SL::DB::Helper::Sorted;
+
+sub object_class { 'SL::DB::DeliveryTerm' }
+
+__PACKAGE__->make_manager_methods;
+
+sub _sort_spec {
+ return ( default => [ 'sortkey', 1 ],
+ columns => { SIMPLE => 'ALL',
+ map { ( $_ => "lower(delivery_terms.${_})" ) } qw(description description_long),
+ });
+}
+
+1;
__PACKAGE__->meta->columns(
file => { type => 'text', not_null => 1 },
id => { type => 'serial', not_null => 1 },
+ numheaders => { type => 'integer', not_null => 1 },
numrows => { type => 'integer', not_null => 1 },
profile_id => { type => 'integer', not_null => 1 },
session_id => { type => 'text', not_null => 1 },
creditlimit => { type => 'numeric', default => '0', precision => 5, scale => 15 },
currency_id => { type => 'integer', not_null => 1 },
customernumber => { type => 'text' },
+ delivery_term_id => { type => 'integer' },
department_1 => { type => 'varchar', length => 75 },
department_2 => { type => 'varchar', length => 75 },
direct_debit => { type => 'boolean', default => 'false' },
key_columns => { currency_id => 'id' },
},
+ delivery_term => {
+ class => 'SL::DB::DeliveryTerm',
+ key_columns => { delivery_term_id => 'id' },
+ },
+
language_obj => {
class => 'SL::DB::Language',
key_columns => { language_id => 'id' },
cusordnumber => { type => 'text' },
customer_id => { type => 'integer' },
delivered => { type => 'boolean', default => 'false' },
+ delivery_term_id => { type => 'integer' },
department_id => { type => 'integer' },
donumber => { type => 'text', not_null => 1 },
employee_id => { type => 'integer' },
key_columns => { customer_id => 'id' },
},
+ delivery_term => {
+ class => 'SL::DB::DeliveryTerm',
+ key_columns => { delivery_term_id => 'id' },
+ },
+
department => {
class => 'SL::DB::Department',
key_columns => { department_id => 'id' },
--- /dev/null
+# This file has been auto-generated. Do not modify it; it will be overwritten
+# by rose_auto_create_model.pl automatically.
+package SL::DB::DeliveryTerm;
+
+use strict;
+
+use base qw(SL::DB::Object);
+
+__PACKAGE__->meta->setup(
+ table => 'delivery_terms',
+
+ columns => [
+ id => { type => 'integer', not_null => 1, sequence => 'id' },
+ description => { type => 'text' },
+ description_long => { type => 'text' },
+ sortkey => { type => 'integer', not_null => 1 },
+ itime => { type => 'timestamp', default => 'now()' },
+ mtime => { type => 'timestamp' },
+ ],
+
+ primary_key_columns => [ 'id' ],
+
+ allow_inline_column_values => 1,
+);
+
+1;
+;
__PACKAGE__->meta->allow_inline_column_values(1);
__PACKAGE__->meta->foreign_keys(
- employee => {
+ created_for => {
class => 'SL::DB::Employee',
- key_columns => { created_by => 'id' },
+ key_columns => { created_for_user => 'id' },
},
- employee_obj => {
+ employee => {
class => 'SL::DB::Employee',
- key_columns => { created_for_user => 'id' },
+ key_columns => { created_by => 'id' },
},
note => {
key_columns => { department_id => 'id' },
},
+ employee => {
+ class => 'SL::DB::Employee',
+ key_columns => { employee_id => 'id' },
+ },
+
storno_obj => {
class => 'SL::DB::GLTransaction',
key_columns => { storno_id => 'id' },
customer_id => { type => 'integer' },
datepaid => { type => 'date' },
delivery_customer_id => { type => 'integer' },
+ delivery_term_id => { type => 'integer' },
delivery_vendor_id => { type => 'integer' },
deliverydate => { type => 'date' },
department_id => { type => 'integer' },
key_columns => { customer_id => 'id' },
},
+ delivery_term => {
+ class => 'SL::DB::DeliveryTerm',
+ key_columns => { delivery_term_id => 'id' },
+ },
+
department => {
class => 'SL::DB::Department',
key_columns => { department_id => 'id' },
customer_id => { type => 'integer' },
delivered => { type => 'boolean', default => 'false' },
delivery_customer_id => { type => 'integer' },
+ delivery_term_id => { type => 'integer' },
delivery_vendor_id => { type => 'integer' },
department_id => { type => 'integer' },
employee_id => { type => 'integer' },
key_columns => { delivery_customer_id => 'id' },
},
+ delivery_term => {
+ class => 'SL::DB::DeliveryTerm',
+ key_columns => { delivery_term_id => 'id' },
+ },
+
delivery_vendor => {
class => 'SL::DB::Vendor',
key_columns => { delivery_vendor_id => 'id' },
cp_id => { type => 'integer' },
currency_id => { type => 'integer', not_null => 1 },
datepaid => { type => 'date' },
+ delivery_term_id => { type => 'integer' },
deliverydate => { type => 'date' },
department_id => { type => 'integer' },
direct_debit => { type => 'boolean', default => 'false' },
key_columns => { currency_id => 'id' },
},
+ delivery_term => {
+ class => 'SL::DB::DeliveryTerm',
+ key_columns => { delivery_term_id => 'id' },
+ },
+
department => {
class => 'SL::DB::Department',
key_columns => { department_id => 'id' },
__PACKAGE__->meta->table('vendor');
__PACKAGE__->meta->columns(
- account_number => { type => 'varchar', length => 15 },
- bank => { type => 'text' },
- bank_code => { type => 'varchar', length => 10 },
- bcc => { type => 'text' },
- bic => { type => 'varchar', length => 100 },
- business_id => { type => 'integer' },
- cc => { type => 'text' },
- city => { type => 'varchar', length => 75 },
- contact => { type => 'varchar', length => 75 },
- country => { type => 'varchar', length => 75 },
- creditlimit => { type => 'numeric', precision => 5, scale => 15 },
- currency_id => { type => 'integer', not_null => 1 },
- department_1 => { type => 'varchar', length => 75 },
- department_2 => { type => 'varchar', length => 75 },
- direct_debit => { type => 'boolean', default => 'false' },
- discount => { type => 'float', precision => 4 },
- email => { type => 'text' },
- fax => { type => 'varchar', length => 30 },
- greeting => { type => 'text' },
- homepage => { type => 'text' },
- iban => { type => 'varchar', length => 100 },
- id => { type => 'integer', not_null => 1, sequence => 'id' },
- itime => { type => 'timestamp', default => 'now()' },
- language => { type => 'varchar', length => 5 },
- language_id => { type => 'integer' },
- mtime => { type => 'timestamp' },
- name => { type => 'varchar', length => 75, not_null => 1 },
- notes => { type => 'text' },
- obsolete => { type => 'boolean', default => 'false' },
- payment_id => { type => 'integer' },
- phone => { type => 'text' },
- salesman_id => { type => 'integer' },
- street => { type => 'varchar', length => 75 },
- taxincluded => { type => 'boolean' },
- taxnumber => { type => 'text' },
- taxzone_id => { type => 'integer', default => '0', not_null => 1 },
- terms => { type => 'integer', default => '0' },
- user_password => { type => 'varchar', length => 12 },
- username => { type => 'varchar', length => 50 },
- ustid => { type => 'varchar', length => 14 },
- v_customer_id => { type => 'text' },
- vendornumber => { type => 'text' },
- zipcode => { type => 'varchar', length => 10 },
+ account_number => { type => 'varchar', length => 15 },
+ bank => { type => 'text' },
+ bank_code => { type => 'varchar', length => 10 },
+ bcc => { type => 'text' },
+ bic => { type => 'varchar', length => 100 },
+ business_id => { type => 'integer' },
+ cc => { type => 'text' },
+ city => { type => 'varchar', length => 75 },
+ contact => { type => 'varchar', length => 75 },
+ country => { type => 'varchar', length => 75 },
+ creditlimit => { type => 'numeric', precision => 5, scale => 15 },
+ currency_id => { type => 'integer', not_null => 1 },
+ delivery_term_id => { type => 'integer' },
+ department_1 => { type => 'varchar', length => 75 },
+ department_2 => { type => 'varchar', length => 75 },
+ direct_debit => { type => 'boolean', default => 'false' },
+ discount => { type => 'float', precision => 4 },
+ email => { type => 'text' },
+ fax => { type => 'varchar', length => 30 },
+ greeting => { type => 'text' },
+ homepage => { type => 'text' },
+ iban => { type => 'varchar', length => 100 },
+ id => { type => 'integer', not_null => 1, sequence => 'id' },
+ itime => { type => 'timestamp', default => 'now()' },
+ language => { type => 'varchar', length => 5 },
+ language_id => { type => 'integer' },
+ mtime => { type => 'timestamp' },
+ name => { type => 'varchar', length => 75, not_null => 1 },
+ notes => { type => 'text' },
+ obsolete => { type => 'boolean', default => 'false' },
+ payment_id => { type => 'integer' },
+ phone => { type => 'text' },
+ salesman_id => { type => 'integer' },
+ street => { type => 'varchar', length => 75 },
+ taxincluded => { type => 'boolean' },
+ taxnumber => { type => 'text' },
+ taxzone_id => { type => 'integer', default => '0', not_null => 1 },
+ terms => { type => 'integer', default => '0' },
+ user_password => { type => 'varchar', length => 12 },
+ username => { type => 'varchar', length => 50 },
+ ustid => { type => 'varchar', length => 14 },
+ v_customer_id => { type => 'text' },
+ vendornumber => { type => 'text' },
+ zipcode => { type => 'varchar', length => 10 },
);
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
key_columns => { currency_id => 'id' },
},
+ delivery_term => {
+ class => 'SL::DB::DeliveryTerm',
+ key_columns => { delivery_term_id => 'id' },
+ },
+
language_obj => {
class => 'SL::DB::Language',
key_columns => { language_id => 'id' },
use SL::DB::MetaSetup::Note;
-__PACKAGE__->meta->initialize;
-
-# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
-__PACKAGE__->meta->make_manager_class;
__PACKAGE__->meta->add_relationships(
- followup => {
+ follow_up => {
type => 'one to one',
class => 'SL::DB::FollowUp',
- column_map => { id => 'id' },
+ column_map => { id => 'note_id' },
},
);
+__PACKAGE__->meta->initialize;
+
+# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
+__PACKAGE__->meta->make_manager_class;
+
+
1;
push @values, conv_date($form->{transdateto});
}
+ if($form->{reqdatefrom}) {
+ push @where, qq|dord.reqdate >= ?|;
+ push @values, conv_date($form->{reqdatefrom});
+ }
+
+ if($form->{reqdateto}) {
+ push @where, qq|dord.reqdate <= ?|;
+ push @values, conv_date($form->{reqdateto});
+ }
+
if (@where) {
$query .= " WHERE " . join(" AND ", map { "($_)" } @where);
}
shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, closed = ?,
delivered = ?, department_id = ?, language_id = ?, shipto_id = ?,
globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?,
- is_sales = ?, taxzone_id = ?, taxincluded = ?, terms = ?, currency_id = (SELECT id FROM currencies WHERE name = ?)
+ is_sales = ?, taxzone_id = ?, taxincluded = ?, terms = ?, currency_id = (SELECT id FROM currencies WHERE name = ?),
+ delivery_term_id = ?
WHERE id = ?|;
@values = ($form->{donumber}, $form->{ordnumber},
$form->{transaction_description},
$form->{type} =~ /^sales/ ? 't' : 'f',
conv_i($form->{taxzone_id}), $form->{taxincluded} ? 't' : 'f', conv_i($form->{terms}), $form->{currency},
+ conv_i($form->{delivery_term_id}),
conv_i($form->{id}));
do_query($form, $dbh, $query, @values);
d.description AS department, dord.language_id,
dord.shipto_id,
dord.globalproject_id, dord.delivered, dord.transaction_description,
- dord.taxzone_id, dord.taxincluded, dord.terms, (SELECT cu.name FROM currencies cu WHERE cu.id=dord.currency_id) AS currency
+ dord.taxzone_id, dord.taxincluded, dord.terms, (SELECT cu.name FROM currencies cu WHERE cu.id=dord.currency_id) AS currency,
+ dord.delivery_term_id
FROM delivery_orders dord
JOIN ${vc} cv ON (dord.${vc}_id = cv.id)
LEFT JOIN employee e ON (dord.employee_id = e.id)
$h_pg->finish();
$h_bin_wh->finish();
+ $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
+ $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
+
$form->{username} = $myconfig->{name};
$main::lxdebug->leave_sub();
}
sub get_tax_dropdown {
+ my ($self, $accno) = @_;
+
my $myconfig = \%main::myconfig;
my $form = $main::form;
my $dbh = $form->get_standard_dbh($myconfig);
my $query = qq|SELECT category FROM chart WHERE accno = ?|;
- my ($category) = selectrow_query($form, $dbh, $query, $form->{accno});
+ my ($category) = selectrow_query($form, $dbh, $query, $accno);
$query = qq|SELECT * FROM tax WHERE chart_categories like '%$category%' order by taxkey, rate|;
my $sth = prepare_execute_query($form, $dbh, $query);
- $form->{TAX_ACCOUNTS} = [];
+ my @tax_accounts = ();
while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
- push(@{ $form->{TAX_ACCOUNTS} }, $ref);
+ push(@tax_accounts, $ref);
}
+ return @tax_accounts;
}
1;
use Carp;
use IO::File;
use Params::Validate qw(:all);
+use List::MoreUtils qw(all pairwise);
use Text::CSV_XS;
use Rose::Object::MakeMethods::Generic scalar => [ qw(
- file encoding sep_char quote_char escape_char header profile class
- numberformat dateformat ignore_unknown_columns strict_profile _io _csv
- _objects _parsed _data _errors all_cvar_configs case_insensitive_header
+ file encoding sep_char quote_char escape_char header profile
+ numberformat dateformat ignore_unknown_columns strict_profile is_multiplexed
+ _row_header _io _csv _objects _parsed _data _errors all_cvar_configs case_insensitive_header
) ];
use SL::Helper::Csv::Dispatcher;
quote_char => { default => '"' },
escape_char => { default => '"' },
header => { type => ARRAYREF, optional => 1 },
- profile => { type => HASHREF, optional => 1 },
+ profile => { type => ARRAYREF, optional => 1 },
file => 1,
encoding => 0,
- class => 0,
numberformat => 0,
dateformat => 0,
ignore_unknown_columns => 0,
my ($self, %params) = @_;
$self->_open_file;
+ return if ! $self->_check_multiplexed;
return if ! $self->_check_header;
return if ! $self->dispatcher->parse_profile;
return if ! $self->_parse_data;
sub get_objects {
my ($self, %params) = @_;
- croak 'no class given' unless $self->class;
croak 'must parse first' unless $self->_parsed;
$self->_make_objects unless $self->_objects;
return $self->_io;
}
-sub _check_header {
+# check, if data is multiplexed and if all nessesary infos are given
+sub _check_multiplexed {
my ($self, %params) = @_;
- my $header = $self->header;
- if (! $header) {
- $header = $self->_csv->getline($self->_io);
+ $self->is_multiplexed(0);
+
+ # If more than one profile is given, it is multiplexed.
+ if ($self->profile) {
+ my @profile = @{ $self->profile };
+ if (scalar @profile > 1) {
+ # Each profile needs a class and a row_ident
+ my $info_ok = all { defined $_->{class} && defined $_->{row_ident} } @profile;
+ $self->_push_error([
+ 0,
+ "missing class or row_ident in one of the profiles for multiplexed data",
+ 0,
+ 0]) unless $info_ok;
+
+ # If header is given, there need to be a header for each profile
+ # and no empty headers.
+ if ($info_ok && $self->header) {
+ my @header = @{ $self->header };
+ my $t_ok = scalar @profile == scalar @header;
+ $self->_push_error([
+ 0,
+ "number of headers and number of profiles must be the same for multiplexed data",
+ 0,
+ 0]) unless $t_ok;
+ $info_ok = $info_ok && $t_ok;
+
+ $t_ok = all { scalar @$_ > 0} @header;
+ $self->_push_error([
+ 0,
+ "no empty headers are allowed for multiplexed data",
+ 0,
+ 0]) unless $t_ok;
+ $info_ok = $info_ok && $t_ok;
+ }
+ $self->is_multiplexed($info_ok);
+ return $info_ok;
+ }
+ }
+
+ # ok, if not multiplexed
+ return 1;
+}
- $self->_push_error([
- $self->_csv->error_input,
- $self->_csv->error_diag,
- 0,
- ]) unless $header;
+sub _check_header {
+ my ($self, %params) = @_;
+ my $header;
+
+ $header = $self->header;
+ if (!$header) {
+ my $n_header = ($self->is_multiplexed)? scalar @{ $self->profile } : 1;
+ foreach my $p_num (0..$n_header - 1) {
+ my $h = $self->_csv->getline($self->_io);
+
+ $self->_push_error([
+ $self->_csv->error_input,
+ $self->_csv->error_diag,
+ 0,
+ ]) unless $h;
+
+ if ($self->is_multiplexed) {
+ push @{ $header }, $h;
+ } else {
+ $header = $h;
+ }
+ }
}
# Special case: utf8 BOM.
# certain software (namely MS Office and notepad.exe insist on prefixing
# data with a discouraged but valid byte order mark
# if not removed, the first header field will not be recognized
- if ($header && $header->[0] && $self->encoding =~ /utf-?8/i) {
- $header->[0] =~ s/^\x{FEFF}//;
+ if ($header) {
+ my $h = ($self->is_multiplexed)? $header->[0] : $header;
+
+ if ($h && $h->[0] && $self->encoding =~ /utf-?8/i) {
+ $h->[0] =~ s/^\x{FEFF}//;
+ }
}
- return unless $header;
+ # check, if all header fields are parsed well
+ if ($self->is_multiplexed) {
+ return unless $header && all { $_ } @$header;
+ } else {
+ return unless $header;
+ }
# Special case: human stupidity
# people insist that case sensitivity doesn't exist and try to enter all
# mopst likely meant that field, so rewrite the header
if ($self->case_insensitive_header) {
die 'case_insensitive_header is only possible with profile' unless $self->profile;
- my @names = (
- keys %{ $self->profile || {} },
- );
- for my $name (@names) {
- for my $i (0..$#$header) {
- $header->[$i] = $name if lc $header->[$i] eq lc $name;
+ if ($header) {
+ my $h_aref = ($self->is_multiplexed)? $header : [ $header ];
+ my $p_num = 0;
+ foreach my $h (@{ $h_aref }) {
+ my @names = (
+ keys %{ $self->profile->[$p_num]->{profile} || {} },
+ );
+ for my $name (@names) {
+ for my $i (0..$#$h) {
+ $h->[$i] = $name if lc $h->[$i] eq lc $name;
+ }
+ }
+ $p_num++;
}
}
}
my ($self, %params) = @_;
my (@data, @errors);
- $self->_csv->column_names(@{ $self->header });
-
while (1) {
my $row = $self->_csv->getline($self->_io);
if ($row) {
+ my $header = $self->_header_by_row($row);
my %hr;
- @hr{@{ $self->header }} = @$row;
+ @hr{@{ $header }} = @$row;
push @data, \%hr;
} else {
last if $self->_csv->eof;
return ! @errors;
}
+sub _header_by_row {
+ my ($self, $row) = @_;
+
+ # initialize lookup hash if not already done
+ if ($self->is_multiplexed && ! defined $self->_row_header ) {
+ $self->_row_header({ pairwise { no warnings 'once'; $a->{row_ident} => $b } @{ $self->profile }, @{ $self->header } });
+ }
+
+ if ($self->is_multiplexed) {
+ return $self->_row_header->{$row->[0]}
+ } else {
+ return $self->header;
+ }
+}
+
sub _encode_layer {
':encoding(' . $_[0]->encoding . ')';
}
my ($self, %params) = @_;
my @objs;
- eval "require " . $self->class;
local $::myconfig{numberformat} = $self->numberformat if $self->numberformat;
local $::myconfig{dateformat} = $self->dateformat if $self->dateformat;
for my $line (@{ $self->_data }) {
- my $tmp_obj = $self->class->new;
- $self->dispatcher->dispatch($tmp_obj, $line);
+ my $tmp_obj = $self->dispatcher->dispatch($line);
push @objs, $tmp_obj;
}
sep_char => ',', # default ';'
quote_char => '\'', # default '"'
escape_char => '"', # default '"'
- header => [qw(id text sellprice word)], # see later
- profile => { sellprice => 'sellprice_as_number' },
- class => 'SL::DB::CsvLine', # if present, map lines to this
+ header => [ qw(id text sellprice word) ], # see later
+ profile => [ { profile => { sellprice => 'sellprice_as_number'},
+ class => 'SL::DB::Part' } ],
);
my $status = $csv->parse;
happen here. You will receive a plain mapping of the data into the class tree,
nothing more.
+=item Multiplex data
+
+This module can handle multiplexed data of different class types. In that case
+multiple profiles with classes and row identifiers must be given. Multiple
+headers may also be given or read from csv data. Data must contain the row
+identifier in the first column and it's field name must be 'datatype'.
+
=back
=head1 METHODS
Same as in L<Text::CSV>
-=item C<header> \@FIELDS
+=item C<header> \@HEADERS
+
+If given, it contains an ARRAY of the header fields for not multiplexed data.
+Or an ARRAYREF for each different class type for multiplexed data. These
+ARRAYREFS are the header fields which are an array of columns. In this case
+the first lines are not used as a header. Empty header fields will be ignored
+in objects.
-Can be an array of columns, in this case the first line is not used as a
-header. Empty header fields will be ignored in objects.
+If not given, headers are taken from the first n lines of data, where n is the
+number of different class types.
-=item C<profile> \%ACCESSORS
+In case of multiplexed data the first column must be named 'datatype'. This
+name must be given in the header.
-May be used to map header fields to custom accessors. Example:
+Examples:
- { listprice => listprice_as_number }
+ classic data of one type:
+ [ 'name', 'street', 'zipcode', 'city' ]
+
+ multiplexed data with two different types
+ [ [ 'datatype', 'ordernumber', 'customer', 'transdate' ],
+ [ 'datatype', 'partnumber', 'qty', 'sellprice' ] ]
+
+=item C<profile> [{profile => \%ACCESSORS, class => class, row_ident => ri},]
+
+This is an ARRAYREF to HASHREFs which may contain the keys C<profile>, C<class>
+and C<row_ident>.
+
+The C<profile> is a HASHREF which may be used to map header fields to custom
+accessors. Example:
+
+ [ {profile => { listprice => listprice_as_number }} ]
In this case C<listprice_as_number> will be used to read in values from the
C<listprice> column.
In case of a One-To-One relationsship these can also be set over
relationsships by sparating the steps with a dot (C<.>). This will work:
- { customer => 'customer.name' }
+ [ {profile => { customer => 'customer.name' }} ]
And will result in something like this:
C<strict_profile> and C<case_insensitive_header> checking, will be parsed into
C<get_data>, but will not be attempted to be dispatched into objects.
-=item C<class>
-
-If present, the line will be handed to the new sub of this class,
+If C<class> is present, the line will be handed to the new sub of this class,
and the return value used instead of the line itself.
+C<row_ident> is a string to recognize the right profile and class for each data
+line in multiplexed data. It must match the value in the column 'dataype' for
+each class.
+
+In case of multiplexed data, C<class> and C<row_ident> must be given.
+Example:
+ [ {
+ class => 'SL::DB::Order',
+ row_ident => 'O'
+ },
+ {
+ class => 'SL::DB::OrderItem',
+ row_ident => 'I',
+ profile => {sellprice => sellprice_as_number}
+ } ]
+
=item C<ignore_unknown_columns>
If set, the import will ignore unkown header columns. Useful for lazy imports,
Dispatch to child objects, like this:
$csv = SL::Helper::Csv->new(
- file => ...
- class => SL::DB::Part,
- profile => [
- makemodel => {
- make_1 => make,
- model_1 => model,
- },
- makemodel => {
- make_2 => make,
- model_2 => model,
- },
- ]
+ file => ...
+ profile => [ {
+ profile => [
+ makemodel => {
+ make_1 => make,
+ model_1 => model,
+ },
+ makemodel => {
+ make_2 => make,
+ model_2 => model,
+ },
+ ],
+ class => SL::DB::Part,
+ } ]
);
=head1 AUTHOR
use Data::Dumper;
use Carp;
use Scalar::Util qw(weaken);
+use List::MoreUtils qw(all pairwise);
use Rose::Object::MakeMethods::Generic scalar => [ qw(
- _specs _errors
+ _specs _row_class _row_spec _errors
) ];
use SL::Helper::Csv::Error;
}
sub dispatch {
- my ($self, $obj, $line) = @_;
+ my ($self, $line) = @_;
- for my $spec (@{ $self->_specs }) {
+ my $class = $self->_class_by_line($line);
+ croak 'no class given' unless $class;
+
+ eval "require " . $class;
+ my $obj = $class->new;
+
+ my $specs = $self->_specs_by_line($line);
+ for my $spec (@{ $specs }) {
$self->apply($obj, $spec, $line->{$spec->{key}});
}
+
+ return $obj;
+}
+
+sub _class_by_line {
+ my ($self, $line) = @_;
+
+ # initialize lookup hash if not already done
+ if ($self->_csv->is_multiplexed && ! defined $self->_row_class ) {
+ $self->_row_class({ map { $_->{row_ident} => $_->{class} } @{ $self->_csv->profile } });
+ }
+
+ if ($self->_csv->is_multiplexed) {
+ return $self->_row_class->{$line->{datatype}};
+ } else {
+ return $self->_csv->profile->[0]->{class};
+ }
}
+sub _specs_by_line {
+ my ($self, $line) = @_;
+
+ # initialize lookup hash if not already done
+ if ($self->_csv->is_multiplexed && ! defined $self->_row_spec ) {
+ $self->_row_spec({ pairwise { no warnings 'once'; $a->{row_ident} => $b } @{ $self->_csv->profile }, @{ $self->_specs } });
+ }
+
+ if ($self->_csv->is_multiplexed) {
+ return $self->_row_spec->{$line->{datatype}};
+ } else {
+ return $self->_specs->[0];
+ }
+}
+
+
sub apply {
my ($self, $obj, $spec, $value) = @_;
return unless $value;
sub parse_profile {
my ($self, %params) = @_;
- my $header = $self->_csv->header;
- my $profile = $self->_csv->profile;
+ my @specs;
+
+ my $csv_profile = $self->_csv->profile;
+ my $h_aref = ($self->_csv->is_multiplexed)? $self->_csv->header : [ $self->_csv->header ];
+ my $i = 0;
+ foreach my $header (@{ $h_aref }) {
+ my $spec = $self->_parse_profile(profile => $csv_profile->[$i]->{profile},
+ class => $csv_profile->[$i]->{class},
+ header => $header);
+ push @specs, $spec;
+ $i++;
+ }
+
+ $self->_specs(\@specs);
+
+ $self->_csv->_push_error($self->errors);
+
+ return ! $self->errors;
+}
+
+sub _parse_profile {
+ my ($self, %params) = @_;
+
+ my $profile = $params{profile};
+ my $class = $params{class};
+ my $header = $params{header};
+
my @specs;
for my $col (@$header) {
next unless $col;
if ($self->_csv->strict_profile) {
if (exists $profile->{$col}) {
- push @specs, $self->make_spec($col, $profile->{$col});
+ push @specs, $self->make_spec($col, $profile->{$col}, $class);
} else {
$self->unknown_column($col, undef);
}
} else {
if (exists $profile->{$col}) {
- push @specs, $self->make_spec($col, $profile->{$col});
+ push @specs, $self->make_spec($col, $profile->{$col}, $class);
} else {
- push @specs, $self->make_spec($col, $col);
+ push @specs, $self->make_spec($col, $col, $class);
}
}
}
- $self->_specs(\@specs);
- $self->_csv->_push_error($self->errors);
- return ! $self->errors;
+ return \@specs;
}
sub make_spec {
- my ($self, $col, $path) = @_;
+ my ($self, $col, $path, $cur_class) = @_;
my $spec = { key => $col, steps => [] };
return unless $path;
- my $cur_class = $self->_csv->class;
-
return unless $cur_class;
for my $step_index ( split /\.(?!\d)/, $path ) {
netamount = ?, paid = ?, duedate = ?,
invoice = ?, taxzone_id = ?, notes = ?, taxincluded = ?,
intnotes = ?, storno_id = ?, storno = ?,
- cp_id = ?, employee_id = ?, department_id = ?,
+ cp_id = ?, employee_id = ?, department_id = ?, delivery_term_id = ?,
globalproject_id = ?, direct_debit = ?
WHERE id = ?|;
@values = (
$netamount, $form->{paid}, conv_date($form->{duedate}),
'1', $taxzone_id, $form->{notes}, $form->{taxincluded} ? 't' : 'f',
$form->{intnotes}, conv_i($form->{storno_id}), $form->{storno} ? 't' : 'f',
- conv_i($form->{cp_id}), conv_i($form->{employee_id}), conv_i($form->{department_id}),
+ conv_i($form->{cp_id}), conv_i($form->{employee_id}), conv_i($form->{department_id}), conv_i($form->{delivery_term_id}),
conv_i($form->{globalproject_id}),
$form->{direct_debit} ? 't' : 'f',
conv_i($form->{id})
$query = qq|SELECT cp_id, invnumber, transdate AS invdate, duedate,
orddate, quodate, globalproject_id,
ordnumber, quonumber, paid, taxincluded, notes, taxzone_id, storno, gldate,
- intnotes, (SELECT cu.name FROM currencies cu WHERE cu.id=ap.currency_id) AS currency, direct_debit
+ intnotes, (SELECT cu.name FROM currencies cu WHERE cu.id=ap.currency_id) AS currency, direct_debit,
+ delivery_term_id
FROM ap
WHERE id = ?|;
$ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{id}));
qq|SELECT
v.id AS vendor_id, v.name AS vendor, v.discount as vendor_discount,
v.creditlimit, v.terms, v.notes AS intnotes,
- v.email, v.cc, v.bcc, v.language_id, v.payment_id,
+ v.email, v.cc, v.bcc, v.language_id, v.payment_id, v.delivery_term_id,
v.street, v.zipcode, v.city, v.country, v.taxzone_id, cu.name AS curr, v.direct_debit,
$duedate + COALESCE(pt.terms_netto, 0) AS duedate,
b.description AS business
$form->set_payment_options($myconfig, $form->{invdate});
+ $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
+ $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
+
$form->{username} = $myconfig->{name};
$main::lxdebug->leave_sub();
cp_id = ?, marge_total = ?, marge_percent = ?,
globalproject_id = ?, delivery_customer_id = ?,
transaction_description = ?, delivery_vendor_id = ?,
- donumber = ?, invnumber_for_credit_note = ?, direct_debit = ?
+ donumber = ?, invnumber_for_credit_note = ?, direct_debit = ?,
+ delivery_term_id = ?
WHERE id = ?|;
@values = ( $form->{"invnumber"}, $form->{"ordnumber"}, $form->{"quonumber"}, $form->{"cusordnumber"},
conv_date($form->{"invdate"}), conv_date($form->{"orddate"}), conv_date($form->{"quodate"}), conv_i($form->{"customer_id"}),
conv_i($form->{"globalproject_id"}), conv_i($form->{"delivery_customer_id"}),
$form->{transaction_description}, conv_i($form->{"delivery_vendor_id"}),
$form->{"donumber"}, $form->{"invnumber_for_credit_note"}, $form->{direct_debit} ? 't' : 'f',
+ conv_i($form->{delivery_term_id}),
conv_i($form->{"id"}));
do_query($form, $dbh, $query, @values);
a.employee_id, a.salesman_id, a.payment_id,
a.language_id, a.delivery_customer_id, a.delivery_vendor_id, a.type,
a.transaction_description, a.donumber, a.invnumber_for_credit_note,
- a.marge_total, a.marge_percent, a.direct_debit,
+ a.marge_total, a.marge_percent, a.direct_debit, a.delivery_term_id,
e.name AS employee
FROM ar a
LEFT JOIN employee e ON (e.id = a.employee_id)
$query =
qq|SELECT
c.id AS customer_id, c.name AS customer, c.discount as customer_discount, c.creditlimit, c.terms,
- c.email, c.cc, c.bcc, c.language_id, c.payment_id,
+ c.email, c.cc, c.bcc, c.language_id, c.payment_id, c.delivery_term_id,
c.street, c.zipcode, c.city, c.country,
c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id, cu.name AS curr,
c.taxincluded_checked, c.direct_debit,
qq| ex.$rate AS exchangerate, | .
qq| pr.projectnumber AS globalprojectnumber, | .
qq| e.name AS employee, s.name AS salesman, | .
- qq| ct.${vc}number AS vcnumber, ct.country, ct.ustid, ct.business_id | .
+ qq| ct.${vc}number AS vcnumber, ct.country, ct.ustid, ct.business_id, | .
+ qq| tz.description AS taxzone | .
$periodic_invoices_columns .
qq|FROM oe o | .
qq|JOIN $vc ct ON (o.${vc}_id = ct.id) | .
qq|LEFT JOIN exchangerate ex ON (ex.currency_id = o.currency_id | .
qq| AND ex.transdate = o.transdate) | .
qq|LEFT JOIN project pr ON (o.globalproject_id = pr.id) | .
+ qq|LEFT JOIN tax_zones tz ON (o.taxzone_id = tz.id) | .
qq|$periodic_invoices_joins | .
qq|WHERE (o.quotation = ?) |;
push(@values, $quotation);
push(@values, conv_date($form->{reqdateto}));
}
+ if ($form->{shippingpoint}) {
+ $query .= qq| AND o.shippingpoint ILIKE ?|;
+ push(@values, '%' . $form->{shippingpoint} . '%');
+ }
+
+ if ($form->{taxzone_id} ne '') { # taxzone_id could be 0
+ $query .= qq| AND tz.id = ?|;
+ push(@values, $form->{taxzone_id});
+ }
+
if ($form->{transaction_description}) {
$query .= qq| AND o.transaction_description ILIKE ?|;
push(@values, '%' . $form->{transaction_description} . '%');
"employee" => "e.name",
"salesman" => "s.name",
"shipvia" => "o.shipvia",
- "transaction_description" => "o.transaction_description"
+ "transaction_description" => "o.transaction_description",
+ "shippingpoint" => "o.shippingpoint",
+ "taxzone" => "tz.description",
);
if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
$sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}";
customer_id = ?, amount = ?, netamount = ?, reqdate = ?, taxincluded = ?,
shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, currency_id = (SELECT id FROM currencies WHERE name=?), closed = ?,
delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
- taxzone_id = ?, shipto_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,
+ taxzone_id = ?, shipto_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,delivery_term_id = ?,
globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
WHERE id = ?|;
conv_i($form->{shipto_id}), conv_i($form->{payment_id}),
conv_i($form->{delivery_vendor_id}),
conv_i($form->{delivery_customer_id}),
+ conv_i($form->{delivery_term_id}),
conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
$form->{transaction_description},
o.closed, o.reqdate, o.quonumber, o.department_id, o.cusordnumber,
d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id,
- o.globalproject_id, o.delivered, o.transaction_description
+ o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id
FROM oe o
JOIN ${vc} cv ON (o.${vc}_id = cv.id)
LEFT JOIN employee e ON (o.employee_id = e.id)
$dbh->disconnect;
+ $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
+ $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
+
$main::lxdebug->leave_sub();
}
use SL::DB::Part;
use Exporter qw(import);
-our @EXPORT = qw(part_picker);
+our @EXPORT = qw(part_picker part);
+
+use Carp;
+
+sub part {
+ my ($self, $part, %params) = @_;
+
+ $params{display} ||= 'inline';
+
+ croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
+
+ my $text = join '', (
+ $params{no_link} ? '' : '<a href="ic.pl?action=edit&id=' . $self->escape($part->id) . '">',
+ $self->escape($part->partnumber),
+ $params{no_link} ? '' : '</a>',
+ );
+ return $self->escaped_text($text);
+}
sub part_picker {
my ($self, $name, $value, %params) = @_;
=head1 NAME
-SL::Presenter::Part - Part lelated presenter stuff
+SL::Presenter::Part - Part related presenter stuff
=head1 SYNOPSIS
-see L<SL::Presenter>
+ # Create an html link for editing/opening a part/service/assembly
+ my $object = my $object = SL::DB::Manager::Part->get_first;
+ my $html = SL::Presenter->get->part($object, display => 'inline');
+
+see also L<SL::Presenter>
=head1 DESCRIPTION
=head1 FUNCTIONS
+=over 2
+
+=item C<part, $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the part object C<$object>
+
+C<%params> can include:
+
=over 4
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the part's name linked
+to the corresponding 'edit' action.
+
+=back
+
+=back
+
+=over 2
+
=item C<part_picker $name, $value, %params>
All-in-one picker widget for parts. The name will be both id and name
$locale->text("No vendor has been selected yet."));
Common->get_vc_details(\%myconfig, $form, $form->{vc}, $form->{vc_id});
+ $form->{discount_as_percent} = $form->format_amount(\%::myconfig, $form->parse_amount(\%::myconfig, $form->{discount}) * 100, 2);
$form->{title} = $form->{vc} eq "customer" ?
$locale->text("Customer details") : $locale->text("Vendor details");
use SL::Request qw(flatten);
use SL::DB::Business;
use SL::DB::Default;
+use SL::DB::DeliveryTerm;
use SL::Helper::Flash;
use SL::ReportGenerator;
+use SL::MoreCommon qw(uri_encode);
require "bin/mozilla/common.pl";
require "bin/mozilla/reportgenerator.pl";
# end of main
-sub add {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- $form->{title} = "Add";
- $form->{callback} = "$form->{script}?action=add&db=$form->{db}" unless $form->{callback};
-
- CT->populate_drop_down_boxes(\%myconfig, \%$form);
-
- &form_header;
- &form_footer;
-
- $main::lxdebug->leave_sub();
-}
-
sub search {
$main::lxdebug->enter_sub();
$::lxdebug->leave_sub;
}
-sub edit {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- # show history button
- $form->{javascript} = qq|<script type=text/javascript src=js/show_history.js></script>|;
- #/show hhistory button
-
- CT->get_tuple(\%myconfig, \%$form);
- CT->populate_drop_down_boxes(\%myconfig, \%$form);
-
- $form->{title} = "Edit";
-
- # format discount
- $form->{discount} *= 100;
- # format uri
- $form->{homepage} = 'http://' . $form->{homepage} unless ((!$form->{homepage}) || $form->{homepage} =~ m|^https?://|);
-
- &form_header;
- &form_footer;
-
- $main::lxdebug->leave_sub();
-}
-
-sub _shipto_label {
- my $s = shift(@_);
- join('; ', grep { $_ } map { $s->{"shipto$_"} } qw(name department_1 street city)) || ' '
-}
-
-sub _contacts_label {
- join ", ", grep { $_ } $_[0]->{cp_name}, $_[0]->{cp_givenname};
-}
-
-sub form_header {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
- my $locale = $main::locale;
-
- $form->get_lists(taxzones => "ALL_TAXZONES",
- currencies => "ALL_CURRENCIES");
- $form->get_pricegroup(\%myconfig, { all => 1 });
-
- $form->get_lists(customers => { key => "ALL_SALESMAN_CUSTOMERS", business_is_salesman => 1 }) if $::instance_conf->get_vertreter;
- $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{FU_created_for_user}, deleted => 0 ] ]);
- $form->{ALL_SALESMEN} = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{salesman_id}, deleted => 0 ] ]);
- $form->{USER} = SL::DB::Manager::Employee->current;
-
- $form->{taxincluded} = ($form->{taxincluded}) ? "checked" : "";
- $form->{is_customer} = $form->{db} eq 'customer';
- $form->{shipto_label} = \&_shipto_label;
- $form->{contacts_label} = \&_contacts_label;
- $form->{taxzone_id} = 0 if !$form->{id};
- $form->{SHIPTO_ALL} = [ +{ shipto_id => '0', shiptoname => $::locale->text('All') }, @{ $form->{SHIPTO} } ];
-
- $form->{title} = $form->{title_save}
- || $locale->text("$form->{title} " . ucfirst $form->{db}) . ($form->{title} eq "Edit" ? " $form->{name}" : '');
-
- CT->query_titles_and_greetings(\%myconfig, \%$form);
- map { $form->{"MB_$_"} = [ map +{ id => $_, description => $_ }, @{ $form->{$_} } ] } qw(COMPANY_GREETINGS);
-
- $form->{NOTES} ||= [ ];
-
- if (!$form->{'language_id'}) {
- my $l_id = SL::DB::Default->get->{'language_id'};
- if ($l_id) {
- $form->{'default_language_id'} = $l_id;
- }
- }
-
- if (!$form->{'id'}) {
- $form->{'currency'} = $form->get_default_currency(\%myconfig);
- } else {
- $form->{currency} = $form->{curr};
- }
-
- $::form->{CUSTOM_VARIABLES} = { };
- my %specs = ( CT => { field => 'id', name_prefix => '', },
- Contacts => { field => 'cp_id', name_prefix => 'cp', },
- );
-
- for my $module (keys %specs) {
- my $spec = $specs{$module};
-
- $::form->{CUSTOM_VARIABLES}->{$module} = CVar->get_custom_variables(module => $module, trans_id => $::form->{ $spec->{field} });
- CVar->render_inputs(variables => $::form->{CUSTOM_VARIABLES}->{$module}, name_prefix => $spec->{name_prefix})
- if scalar @{ $::form->{CUSTOM_VARIABLES}->{$module} };
- }
-
- $form->header;
- print $form->parse_html_template('ct/form_header');
-
- $main::lxdebug->leave_sub();
-}
-
-sub form_footer {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- my $form = $main::form;
-
- print $form->parse_html_template('ct/form_footer', { is_orphaned => $form->{status} eq 'orphaned',
- is_customer => $form->{db} eq 'customer' });
- $main::lxdebug->leave_sub();
-}
-
-sub _do_save {
- $main::auth->assert('customer_vendor_edit');
-
- $::form->isblank("name", $::locale->text("Name missing!"));
-
- if ($::form->{new_salesman_id} && $::instance_conf->get_vertreter) {
- $::form->{salesman_id} = $::form->{new_salesman_id};
- delete $::form->{new_salesman_id};
- }
-
- my $res = $::form->{db} eq 'customer' ? CT->save_customer(\%::myconfig, $::form) : CT->save_vendor(\%::myconfig, $::form);
-
- if (3 == $res) {
- if ($::form->{"db"} eq "customer") {
- $::form->error($::locale->text('This customer number is already in use.'));
- } else {
- $::form->error($::locale->text('This vendor number is already in use.'));
- }
- }
-}
-
-sub add_transaction {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit & ' .
- '(general_ledger | invoice_edit | vendor_invoice_edit | ' .
- ' request_quotation_edit | sales_quotation_edit | sales_order_edit | purchase_order_edit)');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
- my $locale = $main::locale;
-
-# # saving the history
-# if(!exists $form->{addition}) {
-# $form->{addition} = "ADD TRANSACTION";
-# $form->save_history;
-# }
-# # /saving the history
-
- _do_save();
-
- $form->{callback} = $form->escape($form->{callback}, 1);
- my $name = $form->escape("$form->{name}", 1);
-
- $form->{callback} =
- "$form->{script}?action=add&vc=$form->{db}&$form->{db}_id=$form->{id}&$form->{db}=$name&type=$form->{type}&callback=$form->{callback}";
- $form->redirect;
-
- $main::lxdebug->leave_sub();
-}
-
-sub save_and_ap_transaction {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit & general_ledger');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- $form->{script} = "ap.pl";
- # saving the history
- if(!exists $form->{addition}) {
- $form->{snumbers} = qq|invnumber_| . $form->{invnumber};
- $form->{addition} = "SAVED";
- $form->save_history;
- }
- # /saving the history
- &add_transaction;
- $main::lxdebug->leave_sub();
-}
-
-sub save_and_ar_transaction {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit & general_ledger');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- $form->{script} = "ar.pl";
- # saving the history
- if(!exists $form->{addition}) {
- $form->{snumbers} = qq|invnumber_| . $form->{invnumber};
- $form->{addition} = "SAVED";
- $form->save_history;
- }
- # /saving the history
- &add_transaction;
- $main::lxdebug->leave_sub();
-}
-
-sub save_and_invoice {
- $main::lxdebug->enter_sub();
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- if ($form->{db} eq 'customer') {
- $main::auth->assert('customer_vendor_edit & invoice_edit');
- } else {
- $main::auth->assert('customer_vendor_edit & vendor_invoice_edit');
- }
-
- $form->{script} = ($form->{db} eq 'customer') ? "is.pl" : "ir.pl";
- $form->{type} = "invoice";
- # saving the history
- if(!exists $form->{addition}) {
- $form->{snumbers} = qq|invnumber_| . $form->{invnumber};
- $form->{addition} = "SAVED";
- $form->save_history;
- }
- # /saving the history
- &add_transaction;
- $main::lxdebug->leave_sub();
-}
-
-sub save_and_rfq {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit & request_quotation_edit');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- $form->{script} = "oe.pl";
- $form->{type} = "request_quotation";
- # saving the history
- if(!exists $form->{addition}) {
- $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
- $form->{addition} = "SAVED";
- $form->save_history;
- }
- # /saving the history
- &add_transaction;
- $main::lxdebug->leave_sub();
-}
-
-sub save_and_quotation {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit & sales_quotation_edit');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- $form->{script} = "oe.pl";
- $form->{type} = "sales_quotation";
- # saving the history
- if(!exists $form->{addition}) {
- $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
- $form->{addition} = "SAVED";
- $form->save_history;
- }
- # /saving the history
- &add_transaction;
- $main::lxdebug->leave_sub();
-}
-
-sub save_and_order {
- $main::lxdebug->enter_sub();
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- if ($form->{db} eq 'customer') {
- $main::auth->assert('customer_vendor_edit & sales_order_edit');
- } else {
- $main::auth->assert('customer_vendor_edit & purchase_order_edit');
- }
-
- $form->{script} = "oe.pl";
- $form->{type} =
- ($form->{db} eq 'customer') ? "sales_order" : "purchase_order";
- # saving the history
- if(!exists $form->{addition}) {
- $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
- $form->{addition} = "SAVED";
- $form->save_history;
- }
- # /saving the history
- &add_transaction;
- $main::lxdebug->leave_sub();
-}
-
-sub save_and_close {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
- my $locale = $main::locale;
-
- my $msg = ucfirst $form->{db};
- $msg .= " saved!";
-
- _do_save();
-
- # saving the history
- if(!exists $form->{addition}) {
- $form->{snumbers} = ($form->{"db"} eq "customer" ? qq|customernumber_| . $form->{customernumber} : qq|vendornumber_| . $form->{vendornumber});
- $form->{addition} = "SAVED";
- $form->save_history;
- }
- # /saving the history
- $form->redirect($locale->text($msg));
-
- $main::lxdebug->leave_sub();
-}
-
-sub save {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
- my $locale = $main::locale;
-
- my $msg = ucfirst $form->{db};
- $msg .= " saved!";
-
- _do_save();
-
- # saving the history
- if(!exists $form->{addition}) {
- $form->{snumbers} = ($form->{"db"} eq "customer" ? qq|customernumber_| . $form->{customernumber} : qq|vendornumber_| . $form->{vendornumber});
- $form->{addition} = "SAVED";
- $form->save_history;
- }
- # /saving the history
- &edit;
-
- $main::lxdebug->leave_sub();
- ::end_of_request();
-}
-
-sub delete {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
- my $locale = $main::locale;
-
- CT->delete(\%myconfig, \%$form);
-
- my $msg = ucfirst $form->{db};
- $msg .= " deleted!";
- # saving the history
- if(!exists $form->{addition}) {
- $form->{snumbers} = ($form->{"db"} eq "customer" ? qq|customernumber_| . $form->{customernumber} : qq|vendornumber_| . $form->{vendornumber});
- $form->{addition} = "DELETED";
- $form->save_history;
- }
- # /saving the history
- $form->redirect($locale->text($msg));
-
- $main::lxdebug->leave_sub();
-}
-
-sub display {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- my $form = $main::form;
-
- &form_header();
- &form_footer();
-
- $main::lxdebug->leave_sub();
-}
-
-sub update {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- my $form = $main::form;
-
- &display();
- $main::lxdebug->leave_sub();
-}
-
-sub get_contact {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- CT->populate_drop_down_boxes(\%::myconfig, $::form);
- CT->query_titles_and_greetings(\%::myconfig, $::form);
- CT->get_contact(\%::myconfig, $::form) if $::form->{cp_id};
-
- $::form->{CUSTOM_VARIABLES}{Contacts} = CVar->get_custom_variables(module => 'Contacts', trans_id => $::form->{cp_id});
- CVar->render_inputs(variables => $::form->{CUSTOM_VARIABLES}{Contacts}, name_prefix => 'cp')
- if scalar @{ $::form->{CUSTOM_VARIABLES}->{Contacts} };
-
- $::form->{contacts_label} = \&_contacts_label;
-
- print $::form->ajax_response_header(), $::form->parse_html_template('ct/_contact');
-
- $main::lxdebug->leave_sub();
-}
-
-sub get_shipto {
- $main::lxdebug->enter_sub();
-
- $main::auth->assert('customer_vendor_edit');
-
- CT->populate_drop_down_boxes(\%::myconfig, $::form);
- CT->get_shipto(\%::myconfig, $::form) if $::form->{shipto_id};
-
- $::form->{shipto_label} = \&_shipto_label;
-
- print $::form->ajax_response_header(), $::form->parse_html_template('ct/_shipto');
-
- $main::lxdebug->leave_sub();
-}
-
-sub get_delivery {
- $::lxdebug->enter_sub;
-
- $::auth->assert('customer_vendor_edit');
- $::auth->assert('sales_all_edit');
-
- CT->get_delivery(\%::myconfig, $::form );
-
- print $::form->ajax_response_header,
- $::form->parse_html_template('ct/get_delivery', {
- is_customer => $::form->{db} eq 'customer',
- });
-
- $::lxdebug->leave_sub;
-}
-
-sub delete_shipto {
- $::lxdebug->enter_sub;
- $::auth->assert('customer_vendor_edit');
-
- if (!$::form->{shipto_id}) {
- flash('error', $::locale->text('No shipto selected to delete'));
- } else {
-
- CT->get_shipto(\%::myconfig, $::form);
-
- my $shipto = SL::DB::Manager::Shipto->find_by(shipto_id => $::form->{shipto_id});
-
- if ($shipto->used) {
- $shipto->detach->save;
- flash('info', $::locale->text('Shipto is in use and was flagged invalid.'));
- } else {
- $shipto->delete;
- flash('info', $::locale->text('Shipto deleted.'));
- }
- delete $::form->{$_} for grep /^shipto/, keys %$::form;
- }
-
- edit();
-
- $::lxdebug->leave_sub;
-}
-
-sub delete_contact {
- $::lxdebug->enter_sub;
- $::auth->assert('customer_vendor_edit');
-
- if (!$::form->{cp_id}) {
- flash('error', $::locale->text('No contact selected to delete'));
- } else {
-
- CT->get_contact(\%::myconfig, $::form);
-
- my $contact = SL::DB::Manager::Contact->find_by(cp_id => $::form->{cp_id});
-
- if ($contact->used) {
- $contact->detach->save;
- flash('info', $::locale->text('Contact is in use and was flagged invalid.'));
- } else {
- $contact->delete;
- flash('info', $::locale->text('Contact deleted.'));
- }
- delete $::form->{$_} for grep /^cp_/, keys %$::form;
- }
-
- edit();
-
- $::lxdebug->leave_sub;
-}
-
-sub ajax_autocomplete {
- $main::lxdebug->enter_sub();
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- $form->{column} = 'name' unless $form->{column} =~ /^name$/;
- $form->{vc} = 'customer' unless $form->{vc} =~ /^customer|vendor$/;
- $form->{db} = $form->{vc}; # CT expects this
- $form->{$form->{column}} = $form->{q} || '';
- $form->{limit} = ($form->{limit} * 1) || 10;
- $form->{searchitems} ||= '';
-
- CT->search(\%myconfig, $form);
-
- print $form->ajax_response_header(),
- $form->parse_html_template('ct/ajax_autocomplete');
-
- $main::lxdebug->leave_sub();
-}
-
sub continue { call_sub($main::form->{nextsub}); }
DO->retrieve('vc' => $form->{vc},
'ids' => $form->{id});
- $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes currency));
+ $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency));
$form->{shipto} = 1 if $form->{id} || $form->{convert_from_oe_ids};
# get customer / vendor
$form->{discount} = $form->{customer_discount};
}
- $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id));
+ $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id));
$form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids});
$form->restore_vars(qw(taxincluded)) if $form->{id};
$form->restore_vars(qw(salesman_id)) if $editing;
my $form = $main::form;
$form->{PRINT_OPTIONS} = print_options('inline' => 1);
+ $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
print $form->parse_html_template('do/form_footer',
{transfer_default => ($::instance_conf->get_transfer_default)});
my @hidden_variables = map { "l_${_}" } @columns;
push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber
- transaction_description transdatefrom transdateto type vc employee_id salesman_id project_id);
+ transaction_description transdatefrom transdateto reqdatefrom reqdateto
+ type vc employee_id salesman_id project_id);
my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
my %column_defs = (
'ids' => { 'text' => '', },
- 'transdate' => { 'text' => $locale->text('Date'), },
+ 'transdate' => { 'text' => $locale->text('Delivery Order Date'), },
'reqdate' => { 'text' => $locale->text('Reqdate'), },
'id' => { 'text' => $locale->text('ID'), },
'donumber' => { 'text' => $locale->text('Delivery Order'), },
if ($form->{transaction_description}) {
push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
}
- if ($form->{transdatefrom}) {
- push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1);
- }
- if ($form->{transdateto}) {
- push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1);
- }
+ if ( $form->{transdatefrom} or $form->{transdateto} ) {
+ push @options, $locale->text('Delivery Order Date');
+ push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1) if $form->{transdatefrom};
+ push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1) if $form->{transdateto};
+ };
+ if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
+ push @options, $locale->text('Reqdate');
+ push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1) if $form->{reqdatefrom};
+ push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{reqdateto}, 1) if $form->{reqdateto};
+ };
if ($form->{open}) {
push @options, $locale->text('Open');
}
$charts{$item->{accno}} = $item;
}
- my %taxchart_labels = ();
- my @taxchart_values = ();
- my %taxcharts = ();
- foreach my $item (@{ $form->{TAX_ACCOUNTS} }) {
- my $key = $item->{id} . "--" . $item->{rate};
- $taxchart_init = $key if ($taxchart_init == $item->{id});
- push(@taxchart_values, $key);
- $taxchart_labels{$key} = $item->{taxdescription} . " " . $item->{rate} * 100 . ' %';
- $taxcharts{$item->{id}} = $item;
- }
-
my ($source, $memo, $source_hidden, $memo_hidden);
for my $i (1 .. $form->{rowcount}) {
if ($form->{show_details}) {
my ($selected_accno, $selected_tax_id) = split(/--/, $selected_accno_full);
my ($previous_accno, $previous_tax_id) = split(/--/, $form->{"previous_accno_$i"});
+ my %taxchart_labels = ();
+ my @taxchart_values = ();
+ my %taxcharts = ();
+ my $filter_accno;
+ $filter_accno = $::form->{ALL_CHARTS}[0]->{accno};
+ $filter_accno = $selected_accno if (!$init and $i < $form->{rowcount});
+ foreach my $item ( GL->get_tax_dropdown($filter_accno) ) {
+ my $key = $item->{id} . "--" . $item->{rate};
+ $taxchart_init = $key if ($taxchart_init == $item->{id});
+ push(@taxchart_values, $key);
+ $taxchart_labels{$key} = $item->{taxdescription} . " " . $item->{rate} * 100 . ' %';
+ $taxcharts{$item->{id}} = $item;
+ }
+
if ($previous_accno &&
($previous_accno eq $selected_accno) &&
($previous_tax_id ne $selected_tax_id)) {
. qq|</td>|;
my $tax_ddbox = qq|<td>| .
NTI($cgi->popup_menu('-name' => "taxchart_$i",
- '-id' => "taxchart_$i",
- '-style' => 'width:200px',
- '-values' => \@taxchart_values,
- '-labels' => \%taxchart_labels,
- '-default' => $selected_taxchart))
+ '-id' => "taxchart_$i",
+ '-style' => 'width:200px',
+ '-values' => \@taxchart_values,
+ '-labels' => \%taxchart_labels,
+ '-default' => $selected_taxchart))
. qq|</td>|;
my ($fx_transaction, $checked);
"charts" => { "key" => "ALL_CHARTS",
"transdate" => $::form->{transdate} });
- $::form->{accno} = $::form->{ALL_CHARTS}[0]->{accno};
- GL->get_tax_dropdown();
-
GL->get_chart_balances('charts' => $::form->{ALL_CHARTS});
my $title = $::form->{title};
}
sub get_tax_dropdown {
+ $main::lxdebug->enter_sub();
my $form = $main::form;
- $main::lxdebug->enter_sub();
- GL->get_tax_dropdown();
+ my @tax_accounts = GL->get_tax_dropdown($form->{accno});
- foreach my $item (@{ $form->{TAX_ACCOUNTS} }) {
+ foreach my $item (@tax_accounts) {
$item->{taxdescription} = $::locale->{iconv_utf8}->convert($item->{taxdescription});
$item->{taxdescription} .= ' ' . $form->round_amount($item->{rate} * 100);
}
+ $form->{TAX_ACCOUNTS} = [ @tax_accounts ];
+
print $form->ajax_response_header, $form->parse_html_template("gl/update_tax_accounts");
$main::lxdebug->leave_sub();
my $language_saved = $form->{language_id};
my $payment_id_saved = $form->{payment_id};
+ my $delivery_term_id_saved = $form->{delivery_term_id};
my $salesman_id_saved = $form->{salesman_id};
my $cp_id_saved = $form->{cp_id};
my $taxzone_id_saved = $form->{taxzone_id};
$form->{language_id} = $language_saved;
$form->{payment_id} = $payment_id_saved;
+ $form->{delivery_term_id} = $delivery_term_id_saved;
$form->{taxzone_id} = $taxzone_id_saved;
$form->{currency} = $currency_saved;
}
}
- my ($payment_id, $language_id, $taxzone_id, $currency);
+ my ($payment_id, $language_id, $taxzone_id, $currency, $delivery_term_id);
if ($form->{payment_id}) {
$payment_id = $form->{payment_id};
}
if ($form->{currency}) {
$currency = $form->{currency};
}
+ if ($form->{delivery_term_id}) {
+ $delivery_term_id = $form->{delivery_term_id};
+ }
my $cp_id = $form->{cp_id};
IR->get_vendor(\%myconfig, \%$form);
if ($currency) {
$form->{currency} = $currency;
}
+ if ($delivery_term_id) {
+ $form->{delivery_term_id} = $delivery_term_id;
+ }
my @curr = $form->get_all_currencies();
map { $form->{selectcurrency} .= "<option>$_\n" } @curr;
$totalpaid += $form->{"paid_$i"};
}
+ $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
+
print $form->parse_html_template('ir/form_footer', {
is_type_credit_note => ($form->{type} eq "credit_note"),
totalpaid => $totalpaid,
my $editing = $form->{id};
- $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded currency cp_id intnotes id shipto_id));
+ $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id
+ taxincluded currency cp_id intnotes id shipto_id
+ delivery_term_id));
IS->get_customer(\%myconfig, \%$form);
$form->restore_vars(qw(id));
IS->retrieve_invoice(\%myconfig, \%$form);
- $form->restore_vars(qw(payment_id language_id taxzone_id currency intnotes cp_id shipto_id));
+ $form->restore_vars(qw(payment_id language_id taxzone_id currency intnotes
+ cp_id shipto_id delivery_term_id));
$form->restore_vars(qw(taxincluded)) if $form->{id};
$form->restore_vars(qw(salesman_id)) if $editing;
$form->{oldinvtotal} = $form->{invtotal};
+ $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
+
print $form->parse_html_template('is/form_footer', {
is_type_credit_note => ($form->{type} eq "credit_note"),
totalpaid => $totalpaid,
use Data::Dumper;
use SL::DB::Customer;
+use SL::DB::TaxZone;
require "bin/mozilla/io.pl";
require "bin/mozilla/arap.pl";
$form->{"$form->{vc}_id"} ||= $form->{"all_$form->{vc}"}->[0]->{id} if $form->{"all_$form->{vc}"};
- $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes shipto_id currency));
+ $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes shipto_id delivery_term_id currency));
$form->{shipto} = 1 if $form->{id} || $form->{convert_from_oe_ids};
# get customer / vendor
IR->get_vendor(\%myconfig, \%$form) if $form->{type} =~ /(purchase_order|request_quotation)/;
IS->get_customer(\%myconfig, \%$form) if $form->{type} =~ /sales_(order|quotation)/;
- $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id shipto_id));
+ $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id shipto_id delivery_term_id));
$form->restore_vars(qw(currency)) if $form->{id};
$form->restore_vars(qw(taxincluded)) if $form->{id};
$form->restore_vars(qw(salesman_id)) if $editing;
$form->{oldinvtotal} = $form->{invtotal};
+ $TMPL_VAR{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
+
print $form->parse_html_template("oe/form_footer", {
%TMPL_VAR,
webdav => $::instance_conf->get_webdav,
$form->get_lists("projects" => { "key" => "ALL_PROJECTS", "all" => 1 },
"departments" => "ALL_DEPARTMENTS",
"$form->{vc}s" => "ALL_VC",
- "business_types" => "ALL_BUSINESS_TYPES");
+ "taxzones" => "ALL_TAXZONES",
+ "business_types" => "ALL_BUSINESS_TYPES",);
$form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
# constants and subs for template
"delivered", "periodic_invoices",
"marge_total", "marge_percent",
"vcnumber", "ustid",
- "country",
+ "country", "shippingpoint",
+ "taxzone",
);
# only show checkboxes if gotten here via sales_order form.
push @hidden_variables, "l_subtotal", $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered ordnumber quonumber
transaction_description transdatefrom transdateto type vc employee_id salesman_id
reqdatefrom reqdateto projectnumber project_id periodic_invoices_active periodic_invoices_inactive
- business_id);
+ business_id shippingpoint taxzone_id);
+
+ my @keys_for_url = grep { $form->{$_} } @hidden_variables;
+ push @keys_for_url, 'taxzone_id' if $form->{taxzone_id} ne ''; # taxzone_id could be 0
- my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
+ my $href = build_std_url('action=orders', @keys_for_url);
my %column_defs = (
'ids' => { 'text' => '', },
'country' => { 'text' => $locale->text('Country'), },
'ustid' => { 'text' => $locale->text('USt-IdNr.'), },
'periodic_invoices' => { 'text' => $locale->text('Per. Inv.'), },
+ 'shippingpoint' => { 'text' => $locale->text('Shipping Point'), },
+ 'taxzone' => { 'text' => $locale->text('Steuersatz'), },
);
- foreach my $name (qw(id transdate reqdate quonumber ordnumber name employee salesman shipvia transaction_description)) {
+ foreach my $name (qw(id transdate reqdate quonumber ordnumber name employee salesman shipvia transaction_description shippingpoint taxzone)) {
my $sortdir = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
$column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
}
push @options, $locale->text('Order Number') . " : $form->{ordnumber}" if $form->{ordnumber};
push @options, $locale->text('Notes') . " : $form->{notes}" if $form->{notes};
push @options, $locale->text('Transaction description') . " : $form->{transaction_description}" if $form->{transaction_description};
+ push @options, $locale->text('Shipping Point') . " : $form->{shippingpoint}" if $form->{shippingpoint};
if ( $form->{transdatefrom} or $form->{transdateto} ) {
push @options, $locale->text('Order Date');
push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1) if $form->{transdatefrom};
my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
}
+ if ($form->{taxzone_id} ne '') { # taxzone_id could be 0
+ push @options, $locale->text('Steuersatz') . " : " . SL::DB::TaxZone->new(id => $form->{taxzone_id})->load->description;
+ }
$report->set_options('top_info_text' => join("\n", @options),
'raw_top_info_text' => $form->parse_html_template('oe/orders_top'),
WebDAV-Verzeichnis pro Mandant gibt. Die dafür notwendigen
Umstellungen werden zusammen mit dem Datenbankupgrade durchgeführt.
-
+- CSV-Import von Aufträgen
Kleinere neue Features und Detailverbesserungen:
+- Lieferbedingungen analog zu Zahlungsbedingungen eingeführt.
+ Sie können angelegt, beim Benutzer voreingestellt und in allen Ein- und Ver-
+ kaufsmasken gesetzt werden.
+
- Rechte für die Anzeige von Debitoren- und Kreditorenbuchungen in Berichten
__Es kann sinnvoll sein, den Standardeinkäufern und Verkäufern keinen Zugriff
__auf Debitoren- oder Kreditorenbuchungen zu geben. Debitorenbuchungen werden
</sect3>
<sect3 id="dokumentenvorlagen-und-variablen.allgemein-verkaeufer">
- <title>Informationen über den Bearbeiter</title>
+ <title>Informationen über den Verkäufer</title>
<variablelist>
<varlistentry>
</varlistentry>
</variablelist>
</sect3>
+
+ <sect3 id="dokumentenvorlagen-und-variablen.allgemein-lieferbedingungen">
+ <title>Variablen für Lieferbedingungen</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>delivery_term</varname></term>
+ <listitem><para>Datenbank-Objekt der Lieferbedingung</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>delivery_term.description</varname></term>
+ <listitem><para>Beschreibung der Lieferbedingung</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>delivery_term.long_description</varname></term>
+ <listitem><para>Langtext bzw. übersetzter Langtext der Lieferbedingung</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </sect3>
</sect2>
<sect2 id="dokumentenvorlagen-und-variablen.invoice">
<code class="varname">employee_taxnumber</code>
</span></dt><dd><p>Steuernummer</p></dd><dt><span class="term">
<code class="varname">employee_tel</code>
- </span></dt><dd><p>Telefonnummer</p></dd></dl></div></div><div class="sect3" title="3.2.7.4. Informationen über den Bearbeiter"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.allgemein-verkaeufer"></a>3.2.7.4. Informationen über den Bearbeiter</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
+ </span></dt><dd><p>Telefonnummer</p></dd></dl></div></div><div class="sect3" title="3.2.7.4. Informationen über den Verkäufer"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.allgemein-verkaeufer"></a>3.2.7.4. Informationen über den Verkäufer</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
<code class="varname">salesman_address</code>
</span></dt><dd><p>Adressfeld</p></dd><dt><span class="term">
<code class="varname">salesman_businessnumber</code>
<code class="varname">taxdescription</code>
</span></dt><dd><p>Name der Steuer</p></dd><dt><span class="term">
<code class="varname">taxrate</code>
- </span></dt><dd><p>Steuersatz</p></dd></dl></div></div></div><div class="sect2" title="3.2.8. Variablen in Rechnungen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.invoice"></a>3.2.8. Variablen in Rechnungen</h3></div></div></div><div class="sect3" title="3.2.8.1. Allgemeine Variablen"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.invoice-allgemein"></a>3.2.8.1. Allgemeine Variablen</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
+ </span></dt><dd><p>Steuersatz</p></dd></dl></div></div><div class="sect3" title="3.2.7.6. Variablen für Lieferbedingungen"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.allgemein-lieferbedingungen"></a>3.2.7.6. Variablen für Lieferbedingungen</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
+ <code class="varname">delivery_term</code>
+ </span></dt><dd><p>Datenbank-Objekt der Lieferbedingung</p></dd><dt><span class="term">
+ <code class="varname">delivery_term.description</code>
+ </span></dt><dd><p>Beschreibung der Lieferbedingung</p></dd><dt><span class="term">
+ <code class="varname">delivery_term.long_description</code>
+ </span></dt><dd><p>Langtext bzw. übersetzter Langtext der Lieferbedingung</p></dd></dl></div></div></div><div class="sect2" title="3.2.8. Variablen in Rechnungen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.invoice"></a>3.2.8. Variablen in Rechnungen</h3></div></div></div><div class="sect3" title="3.2.8.1. Allgemeine Variablen"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.invoice-allgemein"></a>3.2.8.1. Allgemeine Variablen</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
<code class="varname">creditremaining</code>
</span></dt><dd><p>Verbleibender Kredit</p></dd><dt><span class="term">
<code class="varname">currency</code>
<code class="varname">invdate</code>
</span></dt><dd><p>Rechnungsdatum</p></dd><dt><span class="term">
<code class="varname">invnumber</code>
- </span></dt><dd><p>Rechnungsnummer</p></dd></dl></div></div></div><div class="sect2" title="3.2.10. Variablen in anderen Vorlagen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.andere-vorlagen"></a>3.2.10. Variablen in anderen Vorlagen</h3></div></div></div><div class="sect3" title="3.2.10.1. Einführung"><div class="titlepage"><div><div><h4 class="title"><a name="d0e4654"></a>3.2.10.1. Einführung</h4></div></div></div><p>Die Variablen in anderen Vorlagen sind ähnlich wie in der
+ </span></dt><dd><p>Rechnungsnummer</p></dd></dl></div></div></div><div class="sect2" title="3.2.10. Variablen in anderen Vorlagen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.andere-vorlagen"></a>3.2.10. Variablen in anderen Vorlagen</h3></div></div></div><div class="sect3" title="3.2.10.1. Einführung"><div class="titlepage"><div><div><h4 class="title"><a name="d0e4685"></a>3.2.10.1. Einführung</h4></div></div></div><p>Die Variablen in anderen Vorlagen sind ähnlich wie in der
Rechnung. Allerdings heißen die Variablen, die mit
<code class="varname">inv</code> beginnen, jetzt anders. Bei den Angeboten
fangen sie mit <code class="varname">quo</code> für "quotation" an:
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Kapitel 4. Entwicklerdokumentation</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="up" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="prev" href="ch03s03.html" title="3.3. Excel-Vorlagen"><link rel="next" href="ch04s02.html" title="4.2. Entwicklung unter FastCGI"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Kapitel 4. Entwicklerdokumentation</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s03.html">Zurück</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="ch04s02.html">Weiter</a></td></tr></table><hr></div><div class="chapter" title="Kapitel 4. Entwicklerdokumentation"><div class="titlepage"><div><div><h2 class="title"><a name="d0e5261"></a>Kapitel 4. Entwicklerdokumentation</h2></div></div></div><div class="sect1" title="4.1. Globale Variablen"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="devel.globals"></a>4.1. Globale Variablen</h2></div></div></div><div class="sect2" title="4.1.1. Wie sehen globale Variablen in Perl aus?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5267"></a>4.1.1. Wie sehen globale Variablen in Perl aus?</h3></div></div></div><p>Globale Variablen liegen in einem speziellen namespace namens
+ <title>Kapitel 4. Entwicklerdokumentation</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="up" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="prev" href="ch03s03.html" title="3.3. Excel-Vorlagen"><link rel="next" href="ch04s02.html" title="4.2. Entwicklung unter FastCGI"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Kapitel 4. Entwicklerdokumentation</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s03.html">Zurück</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="ch04s02.html">Weiter</a></td></tr></table><hr></div><div class="chapter" title="Kapitel 4. Entwicklerdokumentation"><div class="titlepage"><div><div><h2 class="title"><a name="d0e5292"></a>Kapitel 4. Entwicklerdokumentation</h2></div></div></div><div class="sect1" title="4.1. Globale Variablen"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="devel.globals"></a>4.1. Globale Variablen</h2></div></div></div><div class="sect2" title="4.1.1. Wie sehen globale Variablen in Perl aus?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5298"></a>4.1.1. Wie sehen globale Variablen in Perl aus?</h3></div></div></div><p>Globale Variablen liegen in einem speziellen namespace namens
"main", der von überall erreichbar ist. Darüber hinaus sind bareword
globs global und die meisten speziellen Variablen sind...
speziell.</p><p>Daraus ergeben sich folgende Formen:</p><div class="variablelist"><dl><dt><span class="term">
<code class="varname">$PACKAGE::form</code>.</p></dd><dt><span class="term">
<code class="literal">local $form</code>
</span></dt><dd><p>Alle Änderungen an <code class="varname">$form</code> werden am Ende
- des scopes zurückgesetzt</p></dd></dl></div></div><div class="sect2" title="4.1.2. Warum sind globale Variablen ein Problem?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5368"></a>4.1.2. Warum sind globale Variablen ein Problem?</h3></div></div></div><p>Das erste Problem ist <span class="productname">FCGI</span>™.</p><p>
+ des scopes zurückgesetzt</p></dd></dl></div></div><div class="sect2" title="4.1.2. Warum sind globale Variablen ein Problem?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5399"></a>4.1.2. Warum sind globale Variablen ein Problem?</h3></div></div></div><p>Das erste Problem ist <span class="productname">FCGI</span>™.</p><p>
<span class="productname">SQL-Ledger</span>™ hat fast alles im globalen
namespace abgelegt, und erwartet, dass es da auch wiederzufinden ist.
Unter <span class="productname">FCGI</span>™ müssen diese Sachen aber wieder
dies hat, seit der Einführung, u.a. schon so manche langwierige
Bug-Suche verkürzt. Da globale Variablen aber implizit mit Package
angegeben werden, werden die nicht geprüft, und somit kann sich
- schnell ein Tippfehler einschleichen.</p></div><div class="sect2" title="4.1.3. Kanonische globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5401"></a>4.1.3. Kanonische globale Variablen</h3></div></div></div><p>Um dieses Problem im Griff zu halten gibt es einige wenige
+ schnell ein Tippfehler einschleichen.</p></div><div class="sect2" title="4.1.3. Kanonische globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5432"></a>4.1.3. Kanonische globale Variablen</h3></div></div></div><p>Um dieses Problem im Griff zu halten gibt es einige wenige
globale Variablen, die kanonisch sind, d.h. sie haben bestimmte
vorgegebenen Eigenschaften, und alles andere sollte anderweitig
umhergereicht werden.</p><p>Diese Variablen sind im Moment die folgenden neun:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
<code class="varname">$::request</code>
</p></li></ul></div><p>Damit diese nicht erneut als Müllhalde missbraucht werden, im
Folgenden eine kurze Erläuterung der bestimmten vorgegebenen
- Eigenschaften (Konventionen):</p><div class="sect3" title="4.1.3.1. $::form"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5465"></a>4.1.3.1. $::form</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Ist ein Objekt der Klasse
+ Eigenschaften (Konventionen):</p><div class="sect3" title="4.1.3.1. $::form"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5496"></a>4.1.3.1. $::form</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Ist ein Objekt der Klasse
"<code class="classname">Form</code>"</p></li><li class="listitem"><p>Wird nach jedem Request gelöscht</p></li><li class="listitem"><p>Muss auch in Tests und Konsolenscripts vorhanden
sein.</p></li><li class="listitem"><p>Enthält am Anfang eines Requests die Requestparameter vom
User</p></li><li class="listitem"><p>Kann zwar intern über Requestgrenzen ein Datenbankhandle
push @{ $form->{TEMPLATE_ARRAYS}{number} }, $form->{"partnumber_$i"};
push @{ $form->{TEMPLATE_ARRAYS}{description} }, $form->{"description_$i"};
# ...
-}</pre></div><div class="sect3" title="4.1.3.2. %::myconfig"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5549"></a>4.1.3.2. %::myconfig</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Das einzige Hash unter den globalen Variablen</p></li><li class="listitem"><p>Wird spätestens benötigt wenn auf die Datenbank
+}</pre></div><div class="sect3" title="4.1.3.2. %::myconfig"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5580"></a>4.1.3.2. %::myconfig</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Das einzige Hash unter den globalen Variablen</p></li><li class="listitem"><p>Wird spätestens benötigt wenn auf die Datenbank
zugegriffen wird</p></li><li class="listitem"><p>Wird bei jedem Request neu erstellt.</p></li><li class="listitem"><p>Enthält die Userdaten des aktuellen Logins</p></li><li class="listitem"><p>Sollte nicht ohne Filterung irgendwo gedumpt werden oder
extern serialisiert werden, weil da auch der Datenbankzugriff
für diesen user drinsteht.</p></li><li class="listitem"><p>Enthält unter anderem Listenbegrenzung vclimit,
überwiegend die Daten, die sich unter <span class="guimenu">Programm</span>
-> <span class="guimenuitem">Einstellungen</span> befinden, bzw. die
Informationen über den Benutzer die über die
- Administrator-Schnittstelle eingegeben wurden.</p></div><div class="sect3" title="4.1.3.3. $::locale"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5588"></a>4.1.3.3. $::locale</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "Locale"</p></li><li class="listitem"><p>Wird pro Request erstellt</p></li><li class="listitem"><p>Muss auch für Tests und Scripte immer verfügbar
+ Administrator-Schnittstelle eingegeben wurden.</p></div><div class="sect3" title="4.1.3.3. $::locale"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5619"></a>4.1.3.3. $::locale</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "Locale"</p></li><li class="listitem"><p>Wird pro Request erstellt</p></li><li class="listitem"><p>Muss auch für Tests und Scripte immer verfügbar
sein.</p></li><li class="listitem"><p>Cached intern über Requestgrenzen hinweg benutzte
Locales</p></li></ul></div><p>Lokalisierung für den aktuellen User. Alle Übersetzungen,
- Zahlen- und Datumsformatierungen laufen über dieses Objekt.</p></div><div class="sect3" title="4.1.3.4. $::lxdebug"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5606"></a>4.1.3.4. $::lxdebug</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "LXDebug"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Muss immer verfügbar sein, in nahezu allen
+ Zahlen- und Datumsformatierungen laufen über dieses Objekt.</p></div><div class="sect3" title="4.1.3.4. $::lxdebug"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5637"></a>4.1.3.4. $::lxdebug</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "LXDebug"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Muss immer verfügbar sein, in nahezu allen
Funktionen</p></li></ul></div><p>
<code class="varname">$::lxdebug</code> stellt Debuggingfunktionen
bereit, wie "<code class="function">enter_sub</code>" und
"<code class="function">message</code>" und "<code class="function">dump</code>" mit
denen man flott Informationen ins Log (tmp/kivitendo-debug.log)
packen kann.</p><p>Beispielsweise so:</p><pre class="programlisting">$main::lxdebug->message(0, 'Meine Konfig:' . Dumper (%::myconfig));
-$main::lxdebug->message(0, 'Wer bin ich? Kunde oder Lieferant:' . $form->{vc});</pre></div><div class="sect3" title="4.1.3.5. $::auth"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5643"></a>4.1.3.5. $::auth</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "SL::Auth"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Hat eine permanente DB Verbindung zur Authdatenbank</p></li><li class="listitem"><p>Wird nach jedem Request resettet.</p></li></ul></div><p>
+$main::lxdebug->message(0, 'Wer bin ich? Kunde oder Lieferant:' . $form->{vc});</pre></div><div class="sect3" title="4.1.3.5. $::auth"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5674"></a>4.1.3.5. $::auth</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "SL::Auth"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Hat eine permanente DB Verbindung zur Authdatenbank</p></li><li class="listitem"><p>Wird nach jedem Request resettet.</p></li></ul></div><p>
<code class="varname">$::auth</code> stellt Funktionen bereit um die
Rechte des aktuellen Users abzufragen. Obwohl diese Informationen
vom aktuellen User abhängen wird das Objekt aus
Geschwindigkeitsgründen nur einmal angelegt und dann nach jedem
Request kurz resettet.</p><p>Dieses Objekt kapselt auch den gerade aktiven Mandanten. Dessen Einstellungen können über
<code class="literal">$::auth->client</code> abgefragt werden; Rückgabewert ist ein Hash mit den Werten aus der Tabelle
- <code class="literal">auth.clients</code>.</p></div><div class="sect3" title="4.1.3.6. $::lx_office_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5672"></a>4.1.3.6. $::lx_office_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
+ <code class="literal">auth.clients</code>.</p></div><div class="sect3" title="4.1.3.6. $::lx_office_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5703"></a>4.1.3.6. $::lx_office_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
"<code class="classname">SL::LxOfficeConf</code>"</p></li><li class="listitem"><p>Global gecached</p></li><li class="listitem"><p>Repräsentation der
<code class="filename">config/kivitendo.conf[.default]</code>-Dateien</p></li></ul></div><p>Globale Konfiguration. Configdateien werden zum Start gelesen
und danach nicht mehr angefasst. Es ist derzeit nicht geplant, dass
file = /tmp/kivitendo-debug.log</pre><p>ist der Key <code class="varname">file</code> im Programm als
<code class="varname">$::lx_office_conf->{debug}{file}</code>
erreichbar.</p><div class="warning" title="Warnung" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warnung]" src="system/docbook-xsl/images/warning.png"></td><th align="left">Warnung</th></tr><tr><td align="left" valign="top"><p>Zugriff auf die Konfiguration erfolgt im Moment über
- Hashkeys, sind also nicht gegen Tippfehler abgesichert.</p></td></tr></table></div></div><div class="sect3" title="4.1.3.7. $::instance_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5708"></a>4.1.3.7. $::instance_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
+ Hashkeys, sind also nicht gegen Tippfehler abgesichert.</p></td></tr></table></div></div><div class="sect3" title="4.1.3.7. $::instance_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5739"></a>4.1.3.7. $::instance_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
"<code class="classname">SL::InstanceConfiguration</code>"</p></li><li class="listitem"><p>wird pro Request neu erstellt</p></li></ul></div><p>Funktioniert wie <code class="varname">$::lx_office_conf</code>,
speichert aber Daten die von der Instanz abhängig sind. Eine Instanz
ist hier eine Mandantendatenbank. Beispielsweise überprüft
</p><pre class="programlisting">$::instance_conf->get_inventory_system eq 'perpetual'</pre><p>
- ob die berüchtigte Bestandsmethode zur Anwendung kommt.</p></div><div class="sect3" title="4.1.3.8. $::dispatcher"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5729"></a>4.1.3.8. $::dispatcher</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
+ ob die berüchtigte Bestandsmethode zur Anwendung kommt.</p></div><div class="sect3" title="4.1.3.8. $::dispatcher"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5760"></a>4.1.3.8. $::dispatcher</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
"<code class="varname">SL::Dispatcher</code>"</p></li><li class="listitem"><p>wird pro Serverprozess erstellt.</p></li><li class="listitem"><p>enthält Informationen über die technische Verbindung zum
Server</p></li></ul></div><p>Der dritte Punkt ist auch der einzige Grund warum das Objekt
global gespeichert wird. Wird vermutlich irgendwann in einem anderen
- Objekt untergebracht.</p></div><div class="sect3" title="4.1.3.9. $::request"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5747"></a>4.1.3.9. $::request</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Hashref (evtl später Objekt)</p></li><li class="listitem"><p>Wird pro Request neu initialisiert.</p></li><li class="listitem"><p>Keine Unterstruktur garantiert.</p></li></ul></div><p>
+ Objekt untergebracht.</p></div><div class="sect3" title="4.1.3.9. $::request"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5778"></a>4.1.3.9. $::request</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Hashref (evtl später Objekt)</p></li><li class="listitem"><p>Wird pro Request neu initialisiert.</p></li><li class="listitem"><p>Keine Unterstruktur garantiert.</p></li></ul></div><p>
<code class="varname">$::request</code> ist ein generischer Platz um
Daten "für den aktuellen Request" abzulegen. Sollte nicht für action
at a distance benutzt werden, sondern um lokales memoizing zu
<code class="varname">$::request</code>
</p></li><li class="listitem"><p>Muss ich von anderen Teilen des Programms lesend drauf
zugreifen? Dann <code class="varname">$::request</code>, aber Zugriff über
- Wrappermethode</p></li></ul></div></div></div><div class="sect2" title="4.1.4. Ehemalige globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5789"></a>4.1.4. Ehemalige globale Variablen</h3></div></div></div><p>Die folgenden Variablen waren einmal im Programm, und wurden
- entfernt.</p><div class="sect3" title="4.1.4.1. $::cgi"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5794"></a>4.1.4.1. $::cgi</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil cookie Methoden nicht als
+ Wrappermethode</p></li></ul></div></div></div><div class="sect2" title="4.1.4. Ehemalige globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5820"></a>4.1.4. Ehemalige globale Variablen</h3></div></div></div><p>Die folgenden Variablen waren einmal im Programm, und wurden
+ entfernt.</p><div class="sect3" title="4.1.4.1. $::cgi"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5825"></a>4.1.4.1. $::cgi</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil cookie Methoden nicht als
Klassenfunktionen funktionieren</p></li><li class="listitem"><p>Aufruf als Klasse erzeugt Dummyobjekt was im
Klassennamespace gehalten wird und über Requestgrenzen
leaked</p></li><li class="listitem"><p>liegt jetzt unter
<code class="varname">$::request->{cgi}</code>
- </p></li></ul></div></div><div class="sect3" title="4.1.4.2. $::all_units"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5810"></a>4.1.4.2. $::all_units</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil einige Funktionen in Schleifen zum Teil
+ </p></li></ul></div></div><div class="sect3" title="4.1.4.2. $::all_units"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5841"></a>4.1.4.2. $::all_units</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil einige Funktionen in Schleifen zum Teil
ein paar hundert mal pro Request eine Liste der Einheiten
brauchen, und de als Parameter durch einen Riesenstack von
Funktionen geschleift werden müssten.</p></li><li class="listitem"><p>Liegt jetzt unter
<code class="varname">$::request->{cache}{all_units}</code>
</p></li><li class="listitem"><p>Wird nur in
<code class="function">AM->retrieve_all_units()</code> gesetzt oder
- gelesen.</p></li></ul></div></div><div class="sect3" title="4.1.4.3. %::called_subs"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5829"></a>4.1.4.3. %::called_subs</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>wurde benutzt um callsub deep recursions
+ gelesen.</p></li></ul></div></div><div class="sect3" title="4.1.4.3. %::called_subs"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5860"></a>4.1.4.3. %::called_subs</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>wurde benutzt um callsub deep recursions
abzufangen.</p></li><li class="listitem"><p>Wurde entfernt, weil callsub nur einen Bruchteil der
möglichen Rekursioenen darstellt, und da nie welche
auftreten.</p></li><li class="listitem"><p>komplette recursion protection wurde entfernt.</p></li></ul></div></div></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s03.html">Zurück</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="ch04s02.html">Weiter</a></td></tr><tr><td width="40%" align="left" valign="top">3.3. Excel-Vorlagen </td><td width="20%" align="center"><a accesskey="h" href="index.html">Zum Anfang</a></td><td width="40%" align="right" valign="top"> 4.2. Entwicklung unter FastCGI</td></tr></table></div></body></html>
\ No newline at end of file
<title>kivitendo 3.0.0: Installation, Konfiguration, Entwicklung</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="next" href="ch01.html" title="Kapitel 1. Aktuelle Hinweise"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">kivitendo 3.0.0: Installation, Konfiguration, Entwicklung</th></tr><tr><td width="20%" align="left"> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="ch01.html">Weiter</a></td></tr></table><hr></div><div lang="de" class="book" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><div class="titlepage"><div><div><h1 class="title"><a name="kivitendo-documentation"></a>kivitendo 3.0.0: Installation, Konfiguration, Entwicklung</h1></div></div><hr></div><div class="toc"><p><b>Inhaltsverzeichnis</b></p><dl><dt><span class="chapter"><a href="ch01.html">1. Aktuelle Hinweise</a></span></dt><dt><span class="chapter"><a href="ch02.html">2. Installation und Grundkonfiguration</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch02.html#Installation-%C3%9Cbersicht">2.1. Übersicht</a></span></dt><dt><span class="sect1"><a href="ch02s02.html">2.2. Benötigte Software und Pakete</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s02.html#Betriebssystem">2.2.1. Betriebssystem</a></span></dt><dt><span class="sect2"><a href="ch02s02.html#Pakete">2.2.2. Benötigte Perl-Pakete installieren</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s03.html">2.3. Manuelle Installation des Programmpaketes</a></span></dt><dt><span class="sect1"><a href="ch02s04.html">2.4. kivitendo-Konfigurationsdatei</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s04.html#config.config-file.introduction">2.4.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s04.html#config.config-file.sections-parameters">2.4.2. Abschnitte und Parameter</a></span></dt><dt><span class="sect2"><a href="ch02s04.html#config.config-file.prior-versions">2.4.3. Versionen vor 2.6.3</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s05.html">2.5. Anpassung der PostgreSQL-Konfiguration</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s05.html#Zeichens%C3%A4tze-die-Verwendung-von-UTF-8">2.5.1. Zeichensätze/die Verwendung von Unicode/UTF-8</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#%C3%84nderungen-an-Konfigurationsdateien">2.5.2. Änderungen an Konfigurationsdateien</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Erweiterung-f%C3%BCr-servergespeicherte-Prozeduren">2.5.3. Erweiterung für servergespeicherte Prozeduren</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Datenbankbenutzer-anlegen">2.5.4. Datenbankbenutzer anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s06.html">2.6. Webserver-Konfiguration</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s06.html#d0e697">2.6.1. Grundkonfiguration mittels CGI</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#Apache-Konfiguration.FCGI">2.6.2. Konfiguration für FastCGI/FCGI</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s07.html">2.7. Der Task-Server</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s07.html#Konfiguration-des-Task-Servers">2.7.1. Verfügbare und notwendige Konfigurationsoptionen</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Einbinden-in-den-Boot-Prozess">2.7.2. Automatisches Starten des Task-Servers beim Booten</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Prozesskontrolle">2.7.3. Wie der Task-Server gestartet und beendet wird</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Prozesskontrolle2">2.7.4. Task-Server mit mehreren Mandanten</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s08.html">2.8. Benutzerauthentifizierung und Administratorpasswort</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s08.html#Grundlagen-zur-Benutzerauthentifizierung">2.8.1. Grundlagen zur Benutzerauthentifizierung</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Administratorpasswort">2.8.2. Administratorpasswort</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Authentifizierungsdatenbank">2.8.3. Authentifizierungsdatenbank</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Passwort%C3%BCberpr%C3%BCfung">2.8.4. Passwortüberprüfung</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Name-des-Session-Cookies">2.8.5. Name des Session-Cookies</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Anlegen-der-Authentifizierungsdatenbank">2.8.6. Anlegen der Authentifizierungsdatenbank</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s09.html">2.9. Mandanten-, Benutzer- und Gruppenverwaltung</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s09.html#Zusammenh%C3%A4nge">2.9.1. Zusammenhänge</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Mandanten-Benutzer-Gruppen">2.9.2. Mandanten, Benutzer und Gruppen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Datenbanken-anlegen">2.9.3. Datenbanken anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Gruppen-anlegen">2.9.4. Gruppen anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Benutzer-anlegen">2.9.5. Benutzer anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Mandanten-anlegen">2.9.6. Mandanten anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s10.html">2.10. E-Mail-Versand aus kivitendo heraus</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s10.html#config.sending-email.sendmail">2.10.1. Versand über lokalen E-Mail-Server</a></span></dt><dt><span class="sect2"><a href="ch02s10.html#config.sending-email.smtp">2.10.2. Versand über einen SMTP-Server</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s11.html">2.11. Drucken mit kivitendo</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s11.html#Vorlagenverzeichnis-anlegen">2.11.1. Vorlagenverzeichnis anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#Vorlagen-Standard">2.11.2. Standard</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#f-tex">2.11.3. f-tex</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#Vorlagen-RB">2.11.4. RB</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#allgemeine-hinweise-zu-latex">2.11.5. Allgemeine Hinweise zu LaTeX Vorlagen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s12.html">2.12. OpenDocument-Vorlagen</a></span></dt><dt><span class="sect1"><a href="ch02s13.html">2.13. Konfiguration zur Einnahmenüberschussrechnung/Bilanzierung:
EUR</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s13.html#config.eur.introduction">2.13.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s13.html#config.eur.parameters">2.13.2. Konfigurationsparameter</a></span></dt><dt><span class="sect2"><a href="ch02s13.html#config.eur.setting-parameters">2.13.3. Festlegen der Parameter</a></span></dt><dt><span class="sect2"><a href="ch02s13.html#config.eur.inventory-system-perpetual">2.13.4. Bemerkungen zu Bestandsmethode</a></span></dt><dt><span class="sect2"><a href="ch02s13.html#config.eur.knonw-issues">2.13.5. Bekannte Probleme</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s14.html">2.14. SKR04 19% Umstellung für innergemeinschaftlichen Erwerb</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s14.html#config.skr04-update-3804.introduction">2.14.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s14.html#config.skr04-update-3804.create-chart">2.14.2. Konto 3804 manuell anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s15.html">2.15. Einstellungen pro Mandant</a></span></dt><dt><span class="sect1"><a href="ch02s16.html">2.16. kivitendo ERP verwenden</a></span></dt></dl></dd><dt><span class="chapter"><a href="ch03.html">3. Features und Funktionen</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch03.html#features.periodic-invoices">3.1. Wiederkehrende Rechnungen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.introduction">3.1.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.configuration">3.1.2. Konfiguration</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.variables">3.1.3. Spezielle Variablen</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.reports">3.1.4. Auflisten</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.task-server">3.1.5. Erzeugung der eigentlichen Rechnungen</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.create-for-current-month">3.1.6. Erste Rechnung für aktuellen Monat erstellen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s02.html">3.2. Dokumentenvorlagen und verfügbare Variablen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.einf%C3%BChrung">3.2.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.variablen-ausgeben">3.2.2. Variablen ausgeben</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.verwendung-in-druckbefehlen">3.2.3. Verwendung in Druckbefehlen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.tag-style">3.2.4. Anfang und Ende der Tags verändern</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.zuordnung-dateinamen">3.2.5. Zuordnung von den Dateinamen zu den Funktionen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.dateinamen-erweitert">3.2.6. Sprache, Drucker und E-Mail</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.allgemeine-variablen">3.2.7. Allgemeine Variablen, die in allen Vorlagen vorhanden
sind</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.invoice">3.2.8. Variablen in Rechnungen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.dunning">3.2.9. Variablen in Mahnungen und Rechnungen über Mahngebühren</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.andere-vorlagen">3.2.10. Variablen in anderen Vorlagen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.bloecke">3.2.11. Blöcke, bedingte Anweisungen und Schleifen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.markup">3.2.12. Markup-Code zur Textformatierung innerhalb von
- Formularen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s03.html">3.3. Excel-Vorlagen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s03.html#excel-templates.summary">3.3.1. Zusammenfassung</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.usage">3.3.2. Bedienung</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.syntax">3.3.3. Variablensyntax</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.limitations">3.3.4. Einschränkungen</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="ch04.html">4. Entwicklerdokumentation</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch04.html#devel.globals">4.1. Globale Variablen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04.html#d0e5267">4.1.1. Wie sehen globale Variablen in Perl aus?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5368">4.1.2. Warum sind globale Variablen ein Problem?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5401">4.1.3. Kanonische globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5789">4.1.4. Ehemalige globale Variablen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s02.html">4.2. Entwicklung unter FastCGI</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.general">4.2.1. Allgemeines</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.exiting">4.2.2. Programmende und Ausnahmen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.globals">4.2.3. Globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.performance">4.2.4. Performance und Statistiken</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s03.html">4.3. SQL-Upgradedateien</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.introduction">4.3.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.format">4.3.2. Format der Kontrollinformationen</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.format-perl-files">4.3.3. Format von in Perl geschriebenen Datenbankupgradescripten</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.dbupgrade-tool">4.3.4. Hilfsscript dbupgrade2_tool.pl</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s04.html">4.4. Translations and languages</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s04.html#translations-languages.introduction">4.4.1. Introduction</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#translations-languages.character-set">4.4.2. Character set</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#translations-languages.file-structure">4.4.3. File structure</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s05.html">4.5. Die kivitendo-Test-Suite</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.intro">4.5.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.prerequisites">4.5.2. Voraussetzungen</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.execution">4.5.3.
+ Formularen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s03.html">3.3. Excel-Vorlagen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s03.html#excel-templates.summary">3.3.1. Zusammenfassung</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.usage">3.3.2. Bedienung</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.syntax">3.3.3. Variablensyntax</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.limitations">3.3.4. Einschränkungen</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="ch04.html">4. Entwicklerdokumentation</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch04.html#devel.globals">4.1. Globale Variablen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04.html#d0e5298">4.1.1. Wie sehen globale Variablen in Perl aus?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5399">4.1.2. Warum sind globale Variablen ein Problem?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5432">4.1.3. Kanonische globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5820">4.1.4. Ehemalige globale Variablen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s02.html">4.2. Entwicklung unter FastCGI</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.general">4.2.1. Allgemeines</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.exiting">4.2.2. Programmende und Ausnahmen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.globals">4.2.3. Globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.performance">4.2.4. Performance und Statistiken</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s03.html">4.3. SQL-Upgradedateien</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.introduction">4.3.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.format">4.3.2. Format der Kontrollinformationen</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.format-perl-files">4.3.3. Format von in Perl geschriebenen Datenbankupgradescripten</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.dbupgrade-tool">4.3.4. Hilfsscript dbupgrade2_tool.pl</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s04.html">4.4. Translations and languages</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s04.html#translations-languages.introduction">4.4.1. Introduction</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#translations-languages.character-set">4.4.2. Character set</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#translations-languages.file-structure">4.4.3. File structure</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s05.html">4.5. Die kivitendo-Test-Suite</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.intro">4.5.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.prerequisites">4.5.2. Voraussetzungen</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.execution">4.5.3.
Existierende Tests ausführen
</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.meaning_of_scripts">4.5.4.
Bedeutung der verschiedenen Test-Scripte
'Amended Advance Turnover Tax Return' => 'Berichtigte Anmeldung',
'Amended Advance Turnover Tax Return (Nr. 10)' => 'Ist dies eine berichtigte Anmeldung? (Nr. 10/Zeile 15 Steuererklärung)',
'Amount' => 'Betrag',
+ 'Amount (for verification)' => 'Betrag (zur Überprüfung)',
'Amount Due' => 'Betrag fällig',
+ 'Amount and net amount are calculated by kivitendo. "verify_amount" and "verify_netamount" can be used for sanity checks.' => 'Betrag und Nettobetrag werden von kivitendo berechnet. "verify_amount" und "verify_netamount" können für Plausibilitätsprüfungen angegeben werden.',
'Amount payable' => 'Noch zu bezahlender Betrag',
'Amount payable less discount' => 'Noch zu bezahlender Betrag abzüglich Skonto',
'An exception occurred during execution.' => 'Während der Ausführung trat eine Ausnahme auf.',
'Are you sure you want to delete Transaction' => 'Buchung wirklich löschen?',
'Are you sure you want to delete this background job?' => 'Sind Sie sicher, dass Sie diesen Hintergrund-Job löschen möchten?',
'Are you sure you want to delete this business?' => 'Sind Sie sicher, dass Sie diesen Kunden-/Lieferantentyp löschen wollen?',
+ 'Are you sure you want to delete this delivery term?' => 'Wollen Sie diese Lieferbedingungen wirklich löschen?',
'Are you sure you want to delete this department?' => 'Sind Sie sicher, dass Sie diese Abteilung löschen wollen?',
'Are you sure you want to delete this payment term?' => 'Wollen Sie diese Zahlungsbedingungen wirklich löschen?',
'Are you sure you want to remove the marked entries from the queue?' => 'Sind Sie sicher, dass die markierten Einträge von der Warteschlange gelöscht werden sollen?',
'CSV export -- options' => 'CSV-Export -- Optionen',
'CSV import: contacts' => 'CSV-Import: Ansprechpersonen',
'CSV import: customers and vendors' => 'CSV-Import: Kunden und Lieferanten',
+ 'CSV import: orders' => 'CSV-Import: Aufträge',
'CSV import: parts and services' => 'CSV-Import: Waren und Dienstleistungen',
'CSV import: projects' => 'CSV-Import: Projekte',
'CSV import: shipping addresses' => 'CSV-Import: Lieferadressen',
'Confirmation' => 'Auftragsbestätigung',
'Contact' => 'Kontakt',
'Contact Person' => 'Ansprechperson',
+ 'Contact Person (database ID)' => 'Ansprechperson (Datenbank-ID)',
+ 'Contact Person (name)' => 'Ansprechperson (Name)',
'Contact deleted.' => 'Ansprechperson gelöscht.',
'Contact is in use and was flagged invalid.' => 'Die Ansprechperson ist noch in Verwendung und wurde deshalb nur als ungültig markiert.',
'Contact person (surname)' => 'Ansprechperson (Nachname)',
'Create a new background job' => 'Einen neuen Hintergrund-Job anlegen',
'Create a new business' => 'Einen neuen Kunden-/Lieferantentyp erfassen',
'Create a new client' => 'Einen neuen Mandanten anlegen',
+ 'Create a new delivery term' => 'Neue Lieferbedingungen anlegen',
'Create a new department' => 'Eine neue Abteilung erfassen',
'Create a new group' => 'Neue Benutzergruppe erfassen',
'Create a new payment term' => 'Neue Zahlungsbedingungen anlegen',
'Create new background job' => 'Neuen Hintergrund-Job anlegen',
'Create new business' => 'Kunden-/Lieferantentyp erfassen',
'Create new client #1' => 'Neuen Mandanten #1 anlegen',
+ 'Create new delivery term' => 'Neue Lieferbedingungen anlegen',
'Create new department' => 'Neue Abteilung erfassen',
'Create new payment term' => 'Neue Zahlungsbedingung anlegen',
'Create new templates from master templates' => 'Neue Druckvorlagen aus Vorlagensatz erstellen',
'Custom Variables' => 'Benutzerdefinierte Variablen',
'Custom variables for module' => 'Benutzerdefinierte Variablen für Modul',
'Customer' => 'Kunde',
+ 'Customer (database ID)' => 'Kunde (Datenbank-ID)',
'Customer (name)' => 'Kunde (Name)',
'Customer Master Data' => 'Kundenstammdaten',
'Customer Name' => 'Kundenname',
'Delivery Orders' => 'Lieferscheine',
'Delivery Plan' => 'Lieferplan',
'Delivery Plan for currently outstanding sales orders' => 'Lieferplan für offene Verkaufsaufträge',
+ 'Delivery Terms' => 'Lieferbedingungen',
+ 'Delivery terms' => 'Lieferbedingungen',
+ 'Delivery terms (database ID)' => 'Lieferbedingungen (Datenbank-ID)',
+ 'Delivery terms (name)' => 'Lieferbedingungen (Name)',
'Department' => 'Abteilung',
+ 'Department (database ID)' => 'Abeilung (Datenbank-ID)',
+ 'Department (description)' => 'Abteilung (Beschreibung)',
'Department 1' => 'Abteilung (1)',
'Department 2' => 'Abteilung (2)',
'Department Id' => 'Reservierung',
'Do you want to set the account number "#1" to "#2" and the name "#3" to "#4"?' => 'Soll die Kontonummer "#1" zu "#2" und den Name "#3" zu "#4" geändert werden?',
'Do you want to store the existing onhand values into a new warehouse?' => 'Möchten Sie die vorhandenen Mengendaten in ein Lager übertragen?',
'Document' => 'Dokument',
+ 'Document Project (database ID)' => 'Projektnummer des Belegs (Datenbank-ID)',
+ 'Document Project (description)' => 'Projektnummer des Belegs (Beschreibung)',
+ 'Document Project (number)' => 'Projektnummer des Belegs',
'Document Project Number' => 'Projektnummer des Belegs',
'Document Template' => 'Dokumentvorlage',
'Documentation' => 'Dokumentation',
'Edit bank account' => 'Bankkonto bearbeiten',
'Edit business' => 'Kunden-/Lieferantentyp bearbeiten',
'Edit custom variable' => 'Benutzerdefinierte Variable bearbeiten',
+ 'Edit delivery term' => 'Lieferbedingungen bearbeiten',
'Edit department' => 'Abteilung bearbeiten',
'Edit file' => 'Datei bearbeiten',
'Edit greetings' => 'Anreden bearbeiten',
'Element disabled' => 'Element deaktiviert',
'Employee' => 'Bearbeiter',
'Employee #1 saved!' => 'Benutzer #1 gespeichert!',
+ 'Employee (database ID)' => 'Bearbeiter (Datenbank-ID)',
'Employees' => 'Benutzer',
'Empty selection for warehouse will not be added, even if the old bin is still visible (use back and forth to edit again).' => 'Leere Lager-Auswahl wird ignoriert, selbst wenn noch ein Lagerplatz ausgewählt ist. Alle Daten können durch zurück und vorwärts korrigiert werden.',
'Empty transaction!' => 'Buchung ist leer!',
'Error when saving: #1' => 'Fehler beim Speichern: #1',
'Error!' => 'Fehler!',
'Error: Buchungsgruppe missing or invalid' => 'Fehler: Buchungsgruppe fehlt oder ungültig',
+ 'Error: Customer/vendor missing' => 'Fehler: Kunde/Lieferant fehlt',
'Error: Customer/vendor not found' => 'Fehler: Kunde/Lieferant nicht gefunden',
'Error: Gender (cp_gender) missing or invalid' => 'Fehler: Geschlecht (cp_gender) fehlt oder ungültig',
'Error: Invalid business' => 'Fehler: Kunden-/Lieferantentyp ungültig',
+ 'Error: Invalid contact' => 'Fehler: Ansprechperson ungültig',
'Error: Invalid currency' => 'Fehler: ungültige Währung',
+ 'Error: Invalid delivery terms' => 'Fehler: Lieferbedingungen ungültig',
+ 'Error: Invalid department' => 'Fehler: Abteilung ungültig',
'Error: Invalid language' => 'Fehler: Sprache ungültig',
+ 'Error: Invalid order for this order item' => 'Fehler: Auftrag für diese Position ungültig',
+ 'Error: Invalid part' => 'Fehler: Artikel ungültig',
'Error: Invalid part type' => 'Fehler: Artikeltyp ungültig',
'Error: Invalid parts group' => 'Fehler: Warengruppe ungültig',
'Error: Invalid payment terms' => 'Fehler: Zahlungsbedingungen ungültig',
'Error: Invalid price factor' => 'Fehler: Preisfaktor ungültig',
+ 'Error: Invalid price group' => 'Fehler: Preisgruppe ungültig',
+ 'Error: Invalid project' => 'Fehler: Projekt ungültig',
+ 'Error: Invalid shipto' => 'Fehler: Lieferadresse ungültig',
+ 'Error: Invalid tax zone' => 'Fehler: Steuerzone ungültig',
'Error: Invalid vendor in column make_#1' => 'Fehler: Lieferant ungültig in Spalte make_#1',
'Error: Name missing' => 'Fehler: Name fehlt',
+ 'Error: Part not found' => 'Fehler: Artikel nicht gefunden',
'Error: Unit missing or invalid' => 'Fehler: Einheit fehlt oder ungültig',
'Errors' => 'Fehler',
'Ertrag' => 'Ertrag',
'II' => 'II',
'III' => 'III',
'IV' => 'IV',
+ 'If amounts differ more than "Maximal amount difference" (see settings), this item is marked as invalid.' => 'Weichen die Beträge mehr als die "maximale Betragsabweichung" (siehe Einstellungen) ab, so wird diese Position als ungültig markiert.',
'If checked the taxkey will not be exported in the DATEV Export, but only IF chart taxkeys differ from general ledger taxkeys' => 'Falls angehakt wird der DATEV-Steuerschlüssel bei Buchungen auf dieses Konto nicht beim DATEV-Export mitexportiert, allerdings nur wenn zusätzlich der Konto-Steuerschlüssel vom Buchungs (Hauptbuch) Steuerschlüssel abweicht',
'If configured this bin will be preselected for all new parts. Also this bin will be used as the master default bin, if default transfer out with master bin is activated.' => 'Falls konfiguriert, wird dieses Lager mit Lagerplatz für neu angelegte Waren vorausgewählt.',
'If the article type is set to \'mixed\' then a column called \'type\' must be present.' => 'Falls der Artikeltyp auf \'gemischt\' gestellt wird, muss eine Spalte namens \'type\' vorhanden sein.',
'Last Vendor Number' => 'Letzte Lieferantennummer',
'Last command output' => 'Ausgabe des letzten Befehls',
'Last run at' => 'Letzte Ausführung um',
+ 'Lastcost' => 'Einkaufspreis',
'Lastcost (with X being a number)' => 'Einkaufspreis (X ist eine fortlaufende Zahl)',
'Lead' => 'Kundenquelle',
'Leads' => 'Leads',
'Master Data' => 'Stammdaten',
'Master Data Bin Text Deleted' => 'Gelöschte Stammdaten Freitext-Lagerplätze',
'Max. Dunning Level' => 'höchste Mahnstufe',
+ 'Maximal amount difference' => 'maximale Betragsabweichung',
'Maximum future booking interval' => 'Maximale Anzahl von Tagen an denen Buchungen in der Zukunft erlaubt sind.',
'May' => 'Mai',
'May ' => 'Mai',
'National Expenses' => 'Aufwand Inland',
'National Revenues' => 'Erlöse Inland',
'Net amount' => 'Nettobetrag',
+ 'Net amount (for verification)' => 'Nettobetrag (zur Überprüfung)',
'Netto Terms' => 'Zahlungsziel netto',
'New Password' => 'Neues Passwort',
'New assembly' => 'Neues Erzeugnis',
'No customer has been selected yet.' => 'Es wurde noch kein Kunde ausgewählt.',
'No data was found.' => 'Es wurden keine Daten gefunden.',
'No default currency' => 'Keine Standardwährung',
+ 'No delivery term has been created yet.' => 'Es wurden noch keine Lieferbedingungen angelegt',
'No department has been created yet.' => 'Es wurde noch keine Abteilung erfasst.',
'No dunnings have been selected for printing.' => 'Es wurden keine Mahnungen zum Drucken ausgewählt.',
'No file has been uploaded yet.' => 'Es wurde noch keine Datei hochgeladen.',
'Order Number' => 'Auftragsnummer',
'Order Number missing!' => 'Auftragsnummer fehlt!',
'Order deleted!' => 'Auftrag gelöscht!',
+ 'Order/Item row name' => 'Name der Auftrag-/Positions-Zeilen',
+ 'OrderItem' => 'Position',
'Ordered' => 'Von Kunden bestellt',
+ 'Orders' => 'Aufträge',
'Orders / Delivery Orders deleteable' => 'Aufträge / Lieferscheine löschbar',
'Orientation' => 'Seitenformat',
'Orphaned' => 'Nie benutzt',
'Page #1/#2' => 'Seite #1/#2',
'Paid' => 'bezahlt',
'Part' => 'Ware',
+ 'Part (database ID)' => 'Artikel (Datenbank-ID)',
'Part Description' => 'Artikelbeschreibung',
'Part Description missing!' => 'Artikelbezeichnung fehlt!',
'Part Notes' => 'Bemerkungen',
'Password' => 'Passwort',
'Payables' => 'Verbindlichkeiten',
'Payment' => 'Zahlungsausgang',
- 'Payment Options' => 'Zahlungsoptionen',
+ 'Payment / Delivery Options' => 'Zahlungs- und Lieferoptionen',
'Payment Reminder' => 'Zahlungserinnerung',
'Payment Terms' => 'Zahlungsbedingungen',
'Payment Terms missing in row ' => 'Zahlungsfrist fehlt in Zeile ',
'Price factor (name)' => 'Preisfaktor (Name)',
'Price factor deleted!' => 'Preisfaktor gelöscht.',
'Price factor saved!' => 'Preisfaktor gespeichert.',
+ 'Price group (database ID)' => 'Preisgruppe (Datenbank-ID)',
+ 'Price group (name)' => 'Preisgruppe (Name) ',
'Price information' => 'Preisinformation',
'Pricegroup' => 'Preisgruppe',
'Pricegroup deleted!' => 'Preisgruppe gelöscht!',
'Proforma Invoice' => 'Proformarechnung',
'Program' => 'Programm',
'Project' => 'Projekt',
+ 'Project (database ID)' => 'Projekt (Datenbank-ID)',
+ 'Project (description)' => 'Projekt (Beschreibung)',
+ 'Project (number)' => 'Projektnummer',
'Project Description' => 'Projektbeschreibung',
'Project Number' => 'Projektnummer',
'Project Numbers' => 'Projektnummern',
'Sales price total' => 'VK-Betrag',
'Sales quotation' => 'Angebot',
'Salesman' => 'Verkäufer/in',
+ 'Salesman (database ID)' => 'Verkäufer (Datenbank-ID)',
'Salesperson' => 'Verkäufer',
'Same as the quote character' => 'Wie Anführungszeichen',
'Sat. Fax' => 'Sat. Fax',
'Settings' => 'Einstellungen',
'Setup Menu' => 'Menü-Variante',
'Ship to' => 'Lieferadresse',
+ 'Ship to (database ID)' => 'Lieferadresse (Datenbank-ID)',
'Ship via' => 'Transportmittel',
'Shipping Address' => 'Lieferadresse',
'Shipping Point' => 'Versandort',
'Tax paid' => 'Vorsteuer',
'Tax rate' => 'Steuersatz',
'Tax saved!' => 'Steuer gespeichert!',
+ 'Tax zone (database ID)' => 'Steuerzone ((Datenbank-ID)',
+ 'Tax zone (description)' => 'Steuerzone (Beschreibung)',
'Tax-O-Matic' => 'Steuer',
'Tax-o-matic Account' => 'Automatikbuchung auf Konto',
'Taxaccount_coa' => 'Automatikkonto',
'The client has been created.' => 'Der Mandant wurde angelegt.',
'The client has been deleted.' => 'Der Mandant wurde gelöscht.',
'The client has been saved.' => 'Der Mandant wurde gespeichert.',
+ 'The column "datatype" must be present and must be the first column. The values must be the row names (see settings) for order and item data respectively.' => 'Die Spalte "datatype" muss vorhanden sein und sie muss die erste Spalte sein. Die Werte in dieser Spalte müssen die Namen der Auftrag-/Positions-Zeilen (siehe Einstellungen) sein.',
'The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.' => 'Die Spalte "make_X" can entweder die Datenbank-ID des Lieferanten, eine Lieferantennummer oder einen Lieferantennamen enthalten.',
'The column triplets can occur multiple times with different numbers "X" each time (e.g. "make_1", "model_1", "lastcost_1", "make_2", "model_2", "lastcost_2", "make_3", "model_3", "lastcost_3" etc).' => 'Die Spalten-Dreiergruppen können mehrfach auftreten, sofern sie unterschiedliche Nummern "X" verwenden (z.B. "make_1", "model_1", "lastcost_1", "make_2", "model_2", "lastcost_2", "make_3", "model_3", "lastcost_3" etc).',
'The columns "Dunning Duedate", "Total Fees" and "Interest" show data for the previous dunning created for this invoice.' => 'Die Spalten "Zahlbar bis", "Kumulierte Gebühren" und "Zinsen" zeigen Daten der letzten für diese Rechnung erzeugten Mahnung.',
'The deductible amount' => 'Der abziehbare Skontobetrag',
'The default value depends on the variable type:' => 'Die Bedeutung des Standardwertes hängt vom Variablentypen ab:',
'The delivery order has not been marked as delivered. The warehouse contents have not changed.' => 'Der Lieferschein wurde nicht als geliefert markiert. Der Lagerinhalt wurde nicht verändert.',
+ 'The delivery term has been created.' => 'Die Lieferbedingungen wurden angelegt.',
+ 'The delivery term has been deleted.' => 'Die Lieferbedingungen wurden gelöscht.',
+ 'The delivery term has been saved.' => 'Die Lieferbedingungen wurden gespeichert.',
+ 'The delivery term is in use and cannot be deleted.' => 'Die Lieferbedingungen werden bereits verwendet und können nicht gelöscht werden.',
'The department has been created.' => 'Die Abteilung wurde angelegt.',
'The department has been deleted.' => 'Die Abteiltung wurde gelöscht.',
'The department has been saved.' => 'Die abteilung wurde gespeichert.',
'Variable Description' => 'Datenfeldbezeichnung',
'Variable Name' => 'Datenfeldname (intern)',
'Vendor' => 'Lieferant',
+ 'Vendor (database ID)' => '(Datenbank-ID)',
'Vendor (name)' => 'Lieferant (Name)',
'Vendor Invoice' => 'Einkaufsrechnung',
'Vendor Invoices & AP Transactions' => 'Einkaufsrechnungen & Kreditorenbuchungen',
#!/usr/bin/perl
# -*- coding: utf-8; -*-
-# vim: fenc=UTF-8
+# vim: fenc=utf-8
use utf8;
' Part Number missing!' => '',
' missing!' => '',
'#1 (custom variable)' => '',
+ '#1 MD' => '',
+ '#1 h' => '',
'#1 of #2 importable objects were imported.' => '',
'#1 prices were updated.' => '',
- '* there are restrictions for the perpetual method, look at chapter "Bemerkungen zu Bestandsmethode" in' => '',
- '*) Since version 2.7 these parameters ares set in the client database and not in the lx-erp.conf / lx_office.conf file, details in chapter:' => '',
+ '(recommended) Insert the used currencies in the system. You can simply change the name of the currencies by editing the textfields above. Do not use a name of a currency that is already in use.' => '',
'*/' => '',
+ ', if set' => '',
'---please select---' => '',
'. Automatically generated.' => '',
'...after loggin in' => '',
'2. Quarter' => '',
'3. Quarter' => '',
'4. Quarter' => '',
+ '<b> I DO CARE!</b> Please check create warehouse and bins and define a name for the warehouse (Bins will be created automatically) and then continue' => '',
+ '<b> I DO CARE!</b> Please click back and cancel the update and come back after there has been at least one warehouse defined with bin(s).:' => '',
+ '<b> I DO NOT CARE</b> Please click continue and the following data (see list) will be deleted:' => '',
+ '<b>Automatically create new bins</b> in the following new warehouse ' => '',
+ '<b>Automatically create new bins</b> in the following warehouse if not selected in the list above' => '',
+ '<b>Default Bins Migration !READ CAREFULLY!</b>' => '',
'<b>What</b> do you want to look for?' => '',
- 'A Buchungsgruppe consists of a descriptive name and the account numbers for the income and expense accounts for those four tax zones as well as the inventory account number.' => '',
'A digit is required.' => '',
- 'A group named "Full Access" has been created.' => '',
- 'A group with that name does already exist.' => '',
+ 'A directory with the name for the new print templates exists already.' => '',
'A lot of the usability of kivitendo has been enhanced with javascript. Although it is currently possible to use every aspect of kivitendo without javascript, we strongly recommend it. In a future version this may change and javascript may be necessary to access advanced features.' => '',
'A lower-case character is required.' => '',
'A special character is required (valid characters: #1).' => '',
- 'A temporary directory could not be created:' => '',
- 'A temporary file could not be created. Please verify that the directory "#1" is writeable by the webserver.' => '',
- 'A temporary file could not be created:' => '',
'A unit with this name does already exist.' => '',
'A valid taxkey is missing!' => '',
'A variable marked as \'editable\' can be changed in each quotation, order, invoice etc.' => '',
'ASSETS' => '',
'ATTENTION! If you enabled this feature you can not simply turn it off again without taking care that best_before fields are emptied in the database.' => '',
'ATTENTION! You can not simply change it from periodic to perpetual once you started posting.' => '',
+ 'AUTOMATICALLY MATCH BINS' => '',
'Abort' => '',
'Abrechnungsnummer' => '',
'Abteilung' => '',
+ 'Access rights' => '',
+ 'Access to clients' => '',
'Account' => '',
'Account Category A' => '',
'Account Category C' => '',
'Account Link IC_taxpart' => '',
'Account Link IC_taxservice' => '',
'Account Number' => '',
- 'Account Number already used!' => '',
'Account Number missing!' => '',
'Account Nummer' => '',
'Account Type' => '',
'Account Type missing!' => '',
+ 'Account categories' => '',
'Account deleted!' => '',
'Account for fees' => '',
'Account for interest' => '',
'Account number' => '',
'Account number #1, bank code #2, #3' => '',
+ 'Account number not unique!' => '',
'Account saved!' => '',
'Accounting Group deleted!' => '',
'Accounting Group saved!' => '',
'Accounting method' => '',
'Accrual' => '',
+ 'Accrual accounting' => '',
'Active' => '',
'Active?' => '',
'Add' => '',
'Add Accounts Receivables Transaction' => 'Add Sales Transaction',
'Add Assembly' => '',
'Add Buchungsgruppe' => '',
- 'Add Business' => '',
+ 'Add Client' => '',
'Add Credit Note' => '',
'Add Customer' => '',
'Add Delivery Note' => '',
'Add Delivery Order' => '',
- 'Add Department' => '',
'Add Dunning' => '',
'Add Exchangerate' => '',
'Add Follow-Up' => '',
'Add Lead' => '',
'Add Machine' => '',
'Add Part' => '',
- 'Add Payment Terms' => '',
'Add Price Factor' => '',
'Add Pricegroup' => '',
'Add Printer' => '',
'Add Storno Credit Note' => '',
'Add Transaction' => '',
'Add User' => '',
+ 'Add User Group' => '',
'Add Vendor' => '',
'Add Vendor Invoice' => '',
'Add Warehouse' => '',
- 'Add a new group' => '',
'Add and edit units' => '',
'Add bank account' => '',
'Add custom variable' => '',
+ 'Add link: select records to link with' => '',
+ 'Add linked record' => '',
+ 'Add links' => '',
+ 'Add new currency' => '',
+ 'Add new custom variable' => '',
'Add note' => '',
'Add unit' => '',
'Address' => '',
+ 'Admin' => '',
'Administration' => '',
- 'Administration (Used to access instance administration from user logins)' => '',
'Administration area' => '',
'Advance turnover tax return' => '',
'Aktion' => '',
'All' => '',
'All Accounts' => '',
- 'All Datasets up to date!' => '',
'All changes in that file have been reverted.' => '',
- 'All database upgrades have been applied.' => '',
+ 'All clients' => '',
'All general ledger entries' => '',
+ 'All groups' => '',
'All of the exports you have selected were already closed.' => '',
'All reports' => '',
+ 'All the other clients will start with an empty set of WebDAV folders.' => '',
'All the selected exports have already been closed, or all of their items have already been executed.' => '',
'All units have either no or exactly one base unit of which they are multiples.' => '',
'All users' => '',
'Allow access' => '',
'Allow the following users access to my follow-ups:' => '',
'Alternatively you can create a new part which will then be selected.' => '',
- 'Alternatively you can skip this step and create groups yourself.' => '',
'Amended Advance Turnover Tax Return' => '',
'Amended Advance Turnover Tax Return (Nr. 10)' => '',
'Amount' => '',
+ 'Amount (for verification)' => '',
'Amount Due' => '',
- 'Amount has to be greater then zero! Wrong row number: ' => '',
+ 'Amount and net amount are calculated by kivitendo. "verify_amount" and "verify_netamount" can be used for sanity checks.' => '',
'Amount payable' => '',
'Amount payable less discount' => '',
+ 'An exception occurred during execution.' => '',
'An invalid character was used (invalid characters: #1).' => '',
'An invalid character was used (valid characters: #1).' => '',
'An upper-case character is required.' => '',
'Annotations' => '',
- 'Another user with the login #1 does already exist.' => '',
'Any stock contents containing a best before date will be impossible to stock out otherwise.' => '',
'Ap aging on %s' => '',
'Application Error. No Format given' => '',
'Apr' => '',
'April' => '',
'Ar aging on %s' => '',
- 'Are you sure you want to delete Delivery Order Number #1?' => '',
'Are you sure you want to delete Invoice Number' => '',
- 'Are you sure you want to delete Order Number' => '',
- 'Are you sure you want to delete Quotation Number' => '',
'Are you sure you want to delete Transaction' => '',
+ 'Are you sure you want to delete this background job?' => '',
'Are you sure you want to delete this business?' => '',
+ 'Are you sure you want to delete this delivery term?' => '',
'Are you sure you want to delete this department?' => '',
'Are you sure you want to delete this payment term?' => '',
'Are you sure you want to remove the marked entries from the queue?' => '',
'Are you sure you want to update the prices' => '',
+ 'Are you sure?' => '',
'Article Code' => '',
'Article Code missing!' => '',
'Article type (see below)' => '',
'Assembly Number missing!' => '',
'Asset' => '',
'Assets' => '',
- 'Assign new units' => '',
- 'Assign units' => '',
'Assistant for general ledger corrections' => '',
'Assume Tax Consultant Data in Tax Computation?' => '',
'At least' => '',
- 'At least one Perl module that Lx-Office ERP requires for running is not installed on your system.' => '',
+ 'At least one Perl module that kivitendo ERP requires for running is not installed on your system.' => '',
'At least one of the columns #1, customer, customernumber, vendor, vendornumber (depending on the target table) is required for matching the entry to an existing customer or vendor.' => '',
'At most' => '',
'At the moment the transaction looks like this:' => '',
'BWA' => '',
'Back' => '',
'Back to login' => '',
- 'Back to the login page' => '',
- 'Backup Dataset' => '',
- 'Backup file' => '',
- 'Backup of dataset' => '',
+ 'Background job history' => '',
+ 'Background jobs' => '',
+ 'Background jobs and task server' => '',
'Balance' => '',
'Balance Sheet' => '',
+ 'Balancing' => '',
'Bank' => '',
'Bank Code' => '',
'Bank Code (long)' => '',
'Basic Data' => '',
'Batch Printing' => '',
'Bcc' => '',
+ 'Bcc E-mail' => '',
+ 'Because the useability gets worse if one partnumber is used for several parts (for example if you are searching a position for an invoice), partnumbers should be unique.' => '',
'Belegnummer' => '',
'Beratername' => '',
'Beraternummer' => '',
'Bilanz' => '',
'Billing Address' => '',
'Billing/shipping address (city)' => '',
+ 'Billing/shipping address (country)' => '',
'Billing/shipping address (street)' => '',
'Billing/shipping address (zipcode)' => '',
'Bin' => '',
'Bin From' => '',
'Bin List' => '',
'Bin To' => '',
- 'Binding to the LDAP server as "#1" failed. Please check config/lx_office.conf.' => '',
+ 'Binding to the LDAP server as "#1" failed. Please check config/kivitendo.conf.' => '',
'Bins saved.' => '',
'Bins that have been used in the past cannot be deleted anymore. For these bins there\'s no checkbox in the "Delete" column.' => '',
'Birthday' => '',
+ 'Birthday (after conversion)' => '',
+ 'Birthday (before conversion)' => '',
'Bis' => '',
'Bis Konto: ' => '',
'Block' => '',
'Both' => '',
'Bottom' => '',
'Bought' => '',
+ 'Break up the update and contact a service provider.' => '',
'Buchungsdatum' => '',
'Buchungsgruppe' => '',
'Buchungsgruppe (database ID)' => '',
'CB Transaction' => '',
'CB Transactions' => '',
'CR' => '',
+ 'CRM' => '',
'CRM admin' => '',
'CRM create customers, vendors and contacts' => '',
'CRM follow up' => '',
'CRM status' => '',
'CRM termin' => '',
'CRM user' => '',
+ 'CSS style for pictures' => '',
'CSV export -- options' => '',
'CSV import: contacts' => '',
'CSV import: customers and vendors' => '',
+ 'CSV import: orders' => '',
'CSV import: parts and services' => '',
+ 'CSV import: projects' => '',
'CSV import: shipping addresses' => '',
'Calculate' => '',
- 'Calendar' => '',
'Can not create that quantity with current stock' => '',
'Cancel' => '',
'Cancel Accounts Payables Transaction' => '',
'Cancel Accounts Receivables Transaction' => '',
- 'Cannot create Lock!' => '',
+ 'Cannot check correct WebDAV folder' => '',
'Cannot delete account!' => '',
'Cannot delete customer!' => '',
'Cannot delete default account!' => '',
'Cannot post invoice!' => '',
'Cannot post payment for a closed period!' => '',
'Cannot post payment!' => '',
+ 'Cannot post storno for a closed period!' => '',
+ 'Cannot post transaction above the maximum future booking date!' => '',
'Cannot post transaction for a closed period!' => '',
'Cannot post transaction with a debit and credit entry for the same account!' => '',
'Cannot post transaction!' => '',
'Cannot save preferences!' => '',
'Cannot save quotation!' => '',
'Cannot storno storno invoice!' => '',
+ 'Cannot transfer. <br> Reason:<br>#1' => '',
'Carry over shipping address' => '',
'Cash' => '',
+ 'Cash accounting' => '',
+ 'Cash basis accounting' => '',
+ 'Catalog' => '',
'Cc' => '',
- 'Change Lx-Office installation settings (all menu entries beneath \'System\')' => '',
+ 'Cc E-mail' => '',
+ 'Change default bin for this parts' => '',
+ 'Change kivitendo installation settings (most entries in the \'System\' menu)' => '',
'Change representative to' => '',
'Changes in this block are only sensible if the account is NOT a summary account AND there exists one valid taxkey. To select both Receivables and Payables only make sense for Payment / Receipt (i.e. account cash).' => '',
'Changes to Receivables and Payables are only possible if no transactions to this account are posted yet.' => '',
'Chart Type' => '',
'Chart balance' => '',
'Chart of Accounts' => '',
- 'Chart of accounts' => '',
'Chartaccounts connected to this Tax:' => '',
'Check' => 'Cheque',
'Check Details' => '',
'City' => '',
'Cleared Balance' => '',
'Clearing Tax Received (No 71)' => '',
- 'Click on login name to edit!' => '',
+ 'Client' => '',
+ 'Client #1' => '',
'Client Configuration' => '',
'Client Configuration saved!' => '',
+ 'Client administration: configuration, editing templates, task server control, background jobs (remaining entries in the \'System\' menu)' => '',
+ 'Client list' => '',
+ 'Client name' => '',
+ 'Client to assign the existing WebDAV folders to' => '',
+ 'Client to configure the printers for' => '',
+ 'Clients this Group is valid for' => '',
+ 'Clients this user has access to' => '',
'Close' => '',
'Close Books up to' => '',
- 'Close Dialog' => '',
'Close Flash' => '',
'Close SEPA exports' => '',
'Close Window' => '',
+ 'Close window' => '',
'Closed' => '',
'Collective Orders only work for orders from one customer!' => '',
'Column name' => '',
'Comment' => '',
'Company' => '',
'Company Name' => '',
+ 'Company name' => '',
+ 'Company settings' => '',
'Compare to' => '',
'Configuration' => '',
'Configuration of individual TODO items' => '',
'Configure' => '',
- 'Confirm' => '',
'Confirm!' => '',
'Confirmation' => '',
'Contact' => '',
'Contact Person' => '',
+ 'Contact Person (database ID)' => '',
+ 'Contact Person (name)' => '',
'Contact deleted.' => '',
'Contact is in use and was flagged invalid.' => '',
'Contact person (surname)' => '',
'Contacts' => '',
'Continue' => '',
'Contra' => '',
+ 'Conversion of "birthday" contact person attribute' => '',
'Copies' => '',
+ 'Copy file from #1 to #2 failed: #3' => '',
'Correct taxkey' => '',
- 'Corrections' => '',
'Costs' => '',
- 'Could not copy %s to %s. Reason: %s' => '',
'Could not load class #1 (#2): "#3"' => '',
'Could not load class #1, #2' => '',
'Could not load employee' => '',
- 'Could not open the file users/members.' => '',
- 'Could not open the old memberfile.' => '',
'Could not print dunning.' => '',
- 'Could not rename %s to %s. Reason: %s' => '',
'Could not spawn ghostscript.' => '',
'Could not spawn the printer command.' => '',
'Could not update prices!' => '',
'Country' => '',
'Create Assembly' => '',
- 'Create Buchungsgruppen' => '',
'Create Chart of Accounts' => '',
'Create Dataset' => '',
'Create Date' => '',
+ 'Create a new background job' => '',
'Create a new business' => '',
+ 'Create a new client' => '',
+ 'Create a new delivery term' => '',
'Create a new department' => '',
+ 'Create a new group' => '',
'Create a new payment term' => '',
- 'Create a standard group' => '',
+ 'Create a new printer' => '',
+ 'Create a new project' => '',
+ 'Create a new user' => '',
+ 'Create a new user group' => '',
'Create and edit RFQs' => '',
'Create and edit dunnings' => '',
'Create and edit invoices and credit notes' => '',
'Create customers and vendors. Edit all vendors. Edit only customers where salesman equals employee (login)' => '',
'Create invoice?' => '',
'Create new' => '',
+ 'Create new background job' => '',
'Create new business' => '',
+ 'Create new client #1' => '',
'Create new department' => '',
'Create new payment term' => '',
+ 'Create new templates from master templates' => '',
'Create tables' => '',
'Created by' => '',
'Created for' => '',
'Curr' => '',
'Currencies' => '',
'Currency' => '',
+ 'Currency (database ID)' => '',
+ 'Currency name' => '',
+ 'Currency names must be unique.' => '',
+ 'Currency names must not be empty.' => '',
'Current / Next Level' => '',
'Current Earnings' => '',
'Current assets account' => '',
+ 'Current filter' => '',
'Current profile' => '',
- 'Current unit' => '',
+ 'Current status' => '',
'Current value:' => '',
'Custom Variables' => '',
'Custom variables for module' => '',
'Customer' => '',
+ 'Customer (database ID)' => '',
'Customer (name)' => '',
+ 'Customer Master Data' => '',
'Customer Name' => '',
'Customer Number' => '',
'Customer Order Number' => '',
'Customer missing!' => '',
'Customer not on file or locked!' => '',
'Customer not on file!' => '',
+ 'Customer saved' => '',
'Customer saved!' => '',
'Customer type' => '',
'Customer variables' => '',
'DATEX - Export Assistent' => '',
'DELETED' => '',
'DFV-Kennzeichen' => '',
+ 'DHL' => '',
'DR' => '',
'DUNNING STARTED' => '',
+ 'DUNS number' => '',
'DUNS-Nr' => '',
- 'Database' => '',
+ 'Data' => '',
'Database Administration' => '',
'Database Connection Test' => '',
'Database Host' => '',
+ 'Database ID' => '',
+ 'Database Management' => '',
'Database User' => '',
- 'Database User missing!' => '',
- 'Database backups and restorations are disabled in the configuration.' => '',
+ 'Database host and port' => '',
+ 'Database login (#1)' => '',
'Database name' => '',
+ 'Database settings' => '',
'Database template' => '',
'Database update error:' => '',
- 'Dataset' => '',
+ 'Database user and password' => '',
'Dataset missing!' => '',
- 'Dataset name' => '',
'Dataset upgrade' => '',
'Date' => '',
'Date Format' => '',
'Decrease' => '',
'Default (no language selected)' => '',
'Default Accounts' => '',
+ 'Default Bin' => '',
+ 'Default Bin with ignoring onhand' => '',
+ 'Default Client (unconfigured)' => '',
'Default Customer/Vendor Language' => '',
+ 'Default Transfer' => '',
+ 'Default Transfer Out always succeed. The current part onhand is ignored and the inventory can have negative stocks (not recommended).' => '',
+ 'Default Transfer Out with negative inventory' => '',
+ 'Default Transfer with Master Bin' => '',
+ 'Default Warehouse' => '',
+ 'Default Warehouse with ignoring on hand' => '',
'Default buchungsgruppe' => '',
+ 'Default client' => '',
+ 'Default currency' => '',
+ 'Default currency missing!' => '',
'Default output medium' => '',
'Default printer' => '',
'Default template format' => '',
'Default unit' => '',
'Default value' => '',
- 'Defaults saved.' => '',
'Delete' => '',
'Delete Account' => '',
'Delete Contact' => '',
'Delete Dataset' => '',
'Delete Shipto' => '',
- 'Delete delivery order' => '',
'Delete drafts' => '',
- 'Delete group' => '',
+ 'Delete links' => '',
'Delete profile' => '',
'Delete transaction' => '',
'Deleted' => '',
'Delivery Orders' => '',
'Delivery Plan' => '',
'Delivery Plan for currently outstanding sales orders' => '',
+ 'Delivery Terms' => '',
+ 'Delivery terms' => '',
+ 'Delivery terms (database ID)' => '',
+ 'Delivery terms (name)' => '',
'Department' => '',
+ 'Department (database ID)' => '',
+ 'Department (description)' => '',
'Department 1' => '',
'Department 2' => '',
'Department Id' => '',
'Destination warehouse and bin' => '',
'Details (one letter abbreviation)' => '',
'Difference' => '',
- 'Dimension unit' => '',
'Directory' => '',
'Discard duplicate entries in CSV file' => '',
'Discard entries with duplicates in database or CSV file' => '',
'Display' => '',
'Display file' => '',
'Display options' => '',
+ 'Do not change the tax rate of taxkey 0.' => '',
'Do not check for duplicates' => '',
'Do not set default buchungsgruppe' => '',
'Do you really want to close the following SEPA exports? No payment will be recorded for bank collections that haven\'t been marked as executed yet.' => '',
'Do you really want to delete AP transaction #1?' => '',
'Do you really want to delete AR transaction #1?' => '',
'Do you really want to delete GL transaction #1?' => '',
- 'Do you really want to delete this group?' => '',
+ 'Do you really want to delete the selected links?' => '',
'Do you really want to delete this object?' => '',
'Do you really want to delete this warehouse?' => '',
- 'Do you want kivitendo to create a group for access to all functions?' => '',
'Do you want to <b>limit</b> your search?' => '',
'Do you want to carry this shipping address over to the new purchase order so that the vendor can deliver the goods directly to your customer?' => '',
+ 'Do you want to set the account number "#1" to "#2" and the name "#3" to "#4"?' => '',
'Do you want to store the existing onhand values into a new warehouse?' => '',
'Document' => '',
+ 'Document Project (database ID)' => '',
+ 'Document Project (description)' => '',
+ 'Document Project (number)' => '',
+ 'Document Project Number' => '',
'Document Template' => '',
+ 'Documentation' => '',
+ 'Documentation (in German)' => '',
'Documents' => '',
- 'Document Project Number' => '',
'Documents in the WebDAV repository' => '',
'Done' => '',
+ 'Double partnumbers' => '',
'Download SEPA XML export file' => '',
'Download sample file' => '',
- 'Download the backup' => '',
'Draft saved.' => '',
'Drawing' => '',
- 'Driver' => '',
'Dropdown Limit' => '',
'Due' => '',
'Due Date' => '',
'Dunnings' => '',
'Duplicate in CSV file' => '',
'Duplicate in database' => '',
- 'During this user migration kivitendo can create such a group for you and grant all users access to all of kivitendo\'s functions.' => '',
+ 'During the next update a taxkey 0 with tax rate of 0 will automatically created.' => '',
'E-mail' => '',
'E-mail Statement to' => '',
'E-mail address missing!' => '',
'ELSTER Export nach Winston' => '',
'ELSTER Tax Number' => '',
'EQUITY' => '',
- 'EU with VAT ID' => '',
- 'EU without VAT ID' => '',
'EUER' => '',
- 'EUR' => '',
'Earlier versions of kivitendo contained bugs which might have led to wrong entries in the general ledger.' => '',
'Edit' => '',
'Edit Access Rights' => '',
'Edit Assembly' => '',
'Edit Bins' => '',
'Edit Buchungsgruppe' => '',
+ 'Edit Client' => '',
'Edit Credit Note' => '',
'Edit Customer' => '',
'Edit Dunning' => '',
'Edit Price Factor' => '',
'Edit Pricegroup' => '',
'Edit Printer' => '',
- 'Edit Project' => '',
'Edit Purchase Delivery Order' => '',
'Edit Purchase Order' => '',
'Edit Quotation' => '',
'Edit Storno Credit Note' => '',
'Edit Storno Invoice' => '',
'Edit User' => '',
+ 'Edit User Group' => '',
'Edit Vendor' => '',
'Edit Vendor Invoice' => '',
'Edit Warehouse' => '',
- 'Edit and delete a group' => '',
+ 'Edit background job' => '',
'Edit bank account' => '',
'Edit business' => '',
'Edit custom variable' => '',
+ 'Edit delivery term' => '',
'Edit department' => '',
'Edit file' => '',
'Edit greetings' => '',
- 'Edit group ' => '',
- 'Edit group membership' => '',
- 'Edit groups' => '',
- 'Edit membership' => '',
'Edit note' => '',
'Edit payment term' => '',
'Edit prices and discount (if not used, textfield is ONLY set readonly)' => '',
- 'Edit rights' => '',
+ 'Edit project' => '',
+ 'Edit project #1' => '',
'Edit templates' => 'Templates, edit',
'Edit the Delivery Order' => '',
'Edit the configuration for periodic invoices' => '',
- 'Edit the membership of all users in all groups:' => '',
+ 'Edit the currency names in order to rename them.' => '',
'Edit the purchase_order' => '',
'Edit the request_quotation' => '',
'Edit the sales_order' => '',
'Element disabled' => '',
'Employee' => '',
'Employee #1 saved!' => '',
+ 'Employee (database ID)' => '',
'Employees' => '',
+ 'Empty selection for warehouse will not be added, even if the old bin is still visible (use back and forth to edit again).' => '',
'Empty transaction!' => '',
'End date' => '',
'Enter a description for this new draft.' => '',
'Enter longdescription' => '',
'Enter the requested execution date or leave empty for the quickest possible execution:' => '',
- 'Enter up to 3 letters separated by a colon (i.e CAD:USD:EUR) for your native and foreign currencies' => '',
+ 'Entries for which automatic conversion failed:' => '',
+ 'Entries for which automatic conversion succeeded:' => '',
'Equity' => '',
'Error' => '',
'Error in database control file \'%s\': %s' => '',
'Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.' => '',
'Error in row #1: The quantity you entered is bigger than the stocked quantity.' => '',
'Error message from the database driver:' => '',
+ 'Error message from the database: #1' => '',
'Error when saving: #1' => '',
'Error!' => '',
'Error: Buchungsgruppe missing or invalid' => '',
+ 'Error: Customer/vendor missing' => '',
'Error: Customer/vendor not found' => '',
'Error: Gender (cp_gender) missing or invalid' => '',
'Error: Invalid business' => '',
+ 'Error: Invalid contact' => '',
+ 'Error: Invalid currency' => '',
+ 'Error: Invalid delivery terms' => '',
+ 'Error: Invalid department' => '',
'Error: Invalid language' => '',
+ 'Error: Invalid order for this order item' => '',
+ 'Error: Invalid part' => '',
'Error: Invalid part type' => '',
'Error: Invalid parts group' => '',
'Error: Invalid payment terms' => '',
'Error: Invalid price factor' => '',
+ 'Error: Invalid price group' => '',
+ 'Error: Invalid project' => '',
+ 'Error: Invalid shipto' => '',
+ 'Error: Invalid tax zone' => '',
'Error: Invalid vendor in column make_#1' => '',
'Error: Name missing' => '',
+ 'Error: Part not found' => '',
'Error: Unit missing or invalid' => '',
'Errors' => '',
'Ertrag' => '',
'Ertrag prozentual' => '',
'Escape character' => '',
+ 'EuR' => '',
+ 'Everyone can log in.' => '',
'Exact' => '',
'Example: http://kivitendo.de' => '',
'Excel' => '',
'Exchangerate Difference' => '',
'Exchangerate for payment missing!' => '',
'Exchangerate missing!' => '',
+ 'Execute now' => '',
'Executed' => '',
'Execution date' => '',
'Execution date from' => '',
'Execution date to' => '',
- 'Existing Buchungsgruppen' => '',
+ 'Execution schedule' => '',
+ 'Execution status' => '',
+ 'Execution type' => '',
'Existing Datasets' => '',
+ 'Existing contacts (with column \'cp_id\')' => '',
+ 'Existing customers/vendors with same customer/vendor number' => '',
'Existing file on server' => '',
'Existing pending follow-ups for this item' => '',
'Existing profiles' => '',
'Expected Tax' => '',
'Expense' => '',
'Expense Account' => '',
- 'Expense accno' => '',
'Expense/Asset' => '',
'Expenses EU with UStId' => '',
'Expenses EU without UStId' => '',
'Export Buchungsdaten' => '',
+ 'Export Number' => '',
'Export Stammdaten' => '',
'Export as CSV' => '',
'Export as PDF' => '',
'Factor missing!' => '',
'Falsches Datumsformat!' => '',
'Fax' => '',
+ 'Features' => '',
'Feb' => '',
'February' => '',
'Fee' => '',
'Field' => '',
'File' => '',
'File name' => '',
- 'Files created by kivitendo\'s "Backup Dataset" function are such files.' => '',
'Filter' => '',
'Filter date by' => '',
'Filter for customer variables' => '',
'Filter for item variables' => '',
+ 'Filter parts' => '',
'Finish' => '',
'First 20 Lines' => '',
'Fix transaction' => '',
'Font size' => '',
'For AP transactions it will replace the sales taxkeys with input taxkeys with the same tax rate.' => '',
'For AR transactions it will replace the input taxkeys with sales taxkeys with the same tax rate.' => '',
- 'For each unit there\'s either no or exactly one base unit. If you chose a base unit then you also have to chose a factor. That way the new unit will be defined as a multiple of the base unit. The base unit must be the "smaller" one. A factor may not be less than 1. Therefore you may define "kg" with the base unit "g" and a factor of "1", but not the other way round.' => '',
'For further information read this: ' => '',
'For type "customer" the perl module JSON is required. Please check this on system level: $ ./scripts/installation_check.pl' => '',
'Foreign Exchange Gain' => '',
'Fristsetzung' => '',
'From' => '',
'From Date' => '',
- 'From this version on the taxkey 0 is reserved for tax rate 0.' => '',
+ 'From this version on a new feature is available.' => '',
+ 'From this version on it is necessary to name a default value.' => '',
+ 'From this version on the partnumber of services, articles and assemblies have to be unique.' => '',
+ 'From this version on the taxkey 0 must have a tax rate of 0 (for DATEV compatibility).' => '',
'Full Access' => '',
'Full Preview' => '',
'Full access to all functions' => '',
- 'Furthermore you should define a taxkey for all accounts, because this update cannot be executed.' => '',
+ 'Function/position' => '',
'Fwd' => 'Forward',
'GL Transaction' => '',
'GL transactions changeable' => '',
'General Ledger Transaction' => '',
'General ledger and cash' => '',
'General ledger corrections' => '',
+ 'General settings' => '',
'Generic Tax Report' => '',
+ 'Git revision: #1, #2 #3' => '',
'Given Name' => '',
'Go one step back' => '',
'Go one step forward' => '',
'Group' => '',
'Group Invoices' => '',
'Group Items' => '',
+ 'Group assignment' => '',
'Group deleted!' => '',
+ 'Group list' => '',
'Group membership' => '',
'Group missing!' => '',
'Group saved!' => '',
'Groups' => '',
+ 'Groups that are valid for this client for access rights' => '',
+ 'Groups this user is a member in' => '',
+ 'Groups valid for this client' => '',
'HTML' => '',
'HTML Templates' => '',
+ 'Handling of WebDAV' => '',
'Hardcopy' => '',
'Has serial number' => '',
'Heading' => '',
- 'Help' => '',
'Help Template Variables' => '',
'Help on column names' => '',
+ 'Here' => '',
+ 'Here you only provide the credentials for logging into the database.' => '',
'Here\'s an example command line:' => '',
'Hide Filter' => '',
'Hide by default' => '',
'Hide help text' => '',
+ 'Hide settings' => '',
+ 'Hints' => '',
'History' => '',
'History Search' => '',
'History Search Engine' => '',
'ID-Nummer' => '',
'II' => '',
'III' => '',
- 'IMPORTANT NOTE: You cannot safely change currencies, IF you have already booking entries!' => '',
'IV' => '',
+ 'If amounts differ more than "Maximal amount difference" (see settings), this item is marked as invalid.' => '',
'If checked the taxkey will not be exported in the DATEV Export, but only IF chart taxkeys differ from general ledger taxkeys' => '',
+ 'If configured this bin will be preselected for all new parts. Also this bin will be used as the master default bin, if default transfer out with master bin is activated.' => '',
'If the article type is set to \'mixed\' then a column called \'type\' must be present.' => '',
'If the automatic creation of invoices for fees and interest is switched on for a dunning level then the following accounts will be used for the invoice.' => '',
'If the database user listed above does not have the right to create a database then enter the name and password of the superuser below:' => '',
- 'If you chose to let Lx-Office do the migration then Lx-Office will also remove the old member file after creating a backup copy of it in the directory "#1".' => '',
+ 'If the default transfer out always succeed use this bin for negative stock quantity.' => '',
'If you enter values for the part number and / or part description then only those bins containing parts whose part number or part description match your input will be shown.' => '',
+ 'If you have not chosen for example the category revenue for a tax and you choose an revenue account to create a transfer in the general ledger, this tax will not be displayed in the tax dropdown.' => '',
+ 'If you lock the system normal users won\'t be able to log in.' => '',
'If you see this message, you most likely just setup your LX-Office and haven\'t added any entry types. If this is the case, the option is accessible for administrators in the System menu.' => '',
'If you select a base unit then you also have to enter a factor.' => '',
- 'If you want to change any of these parameters then press the "Back" button, edit the file "config/lx_office.conf" and login into the admin module again.' => '',
- 'If you want to delete such a dataset you have to edit the user(s) that are using the dataset in question and have them use another dataset.' => '',
+ 'If you want to change any of these parameters then press the "Back" button, edit the file "config/kivitendo.conf" and login into the admin module again.' => '',
+ 'If you want to delete such a dataset you have to edit the client(s) that are using the dataset in question and have them use another dataset.' => '',
'If you want to set up the authentication database yourself then log in to the administration panel. kivitendo will then create the database and tables for you.' => '',
- 'If you yourself want to upgrade the installation then please read the file "doc/UPGRADE" and follow the steps outlined in this file.' => '',
+ 'If your old bins match exactly Bins in the Warehouse CLICK on <b>AUTOMATICALLY MATCH BINS</b>.' => '',
+ 'Illegal characters have been removed from the following fields: #1' => '',
'Image' => '',
'Import' => '',
'Import CSV' => '',
+ 'Import Status' => '',
'Import file' => '',
+ 'Import not started yet, please wait...' => '',
'Import preview' => '',
'Import profiles' => '',
'Import result' => '',
'Import summary' => '',
'In order to do that hit the button "Delete transaction".' => '',
- 'In the latter case the tables needed by Lx-Office will be created in that database.' => '',
- 'In version 2.4.0 the administrator has to enter a list of units in the administrative section.' => '',
+ 'In order to migrate the old folder structure into the new structure you have to chose which client the old structure will be assigned to.' => '',
+ 'In order to use kivitendo you have to create at least a client, a user and a group.' => '',
+ 'In the latter case the tables needed by kivitendo will be created in that database.' => '',
'In-line' => '',
'Inactive' => '',
'Include Exchangerate Difference' => '',
'Include in drop-down menus' => '',
'Include invalid warehouses ' => '',
'Includeable in reports' => '',
+ 'Included in reports by default' => '',
'Including' => '',
'Income Statement' => '',
- 'Income accno' => '',
'Incoming Payments' => '',
'Incoming invoice number' => '',
- 'Incorrect Password!' => '',
- 'Incorrect password!.' => '',
- 'Incorrect username or password!' => '',
+ 'Inconsistency in database' => '',
+ 'Incorrect password!' => '',
+ 'Incorrect username or password or no access to selected client!' => '',
'Increase' => '',
'Individual Items' => '',
'Information' => '',
+ 'Insert with new customer/vendor number' => '',
+ 'Insert with new database ID' => '',
'Insert with new part number' => '',
'Interest' => '',
'Interest Rate' => '',
'Internal Notes' => '',
- 'International' => '',
'Internet' => '',
- 'Introduction of Buchungsgruppen' => '',
- 'Introduction of units' => '',
+ 'Introduction of clients' => '',
'Inv. Duedate' => '',
'Invalid' => '',
'Invalid follow-up ID.' => '',
'Invalid quantity.' => '',
+ 'Invalid request type \'#1\'' => '',
+ 'Invalid transactions' => '',
'Invdate' => '',
'Invdate from' => '',
'Inventory' => '',
'Invoice total less discount' => '',
'Invoice with Storno (abbreviation)' => '',
'Invoices' => '',
+ 'Invoices, Credit Notes & AR Transactions' => '',
'Is Searchable' => '',
'Is this a summary account to record' => '',
+ 'It can be changed later but must be unique within the installation.' => '',
+ 'It is not allowed that a summary account occurs in a drop-down menu!' => '',
'It is possible that even after such a correction there is something wrong with this transaction (e.g. taxes that don\'t match the selected taxkey). Therefore you should re-run the general ledger analysis.' => '',
- 'It is possible to do this automatically for some Buchungsgruppen, but not for all.' => '',
- 'It is possible to do this automatically for some units, but for others the user has to chose the new unit.' => '',
'It is possible to make a quick DATEV export everytime you post a record to ensure things work nicely with their data requirements. This will result in a slight overhead though you can enable this for each type of record independantly.' => '',
- 'It may optionally be compressed with "gzip".' => '',
'It will simply set the taxkey to 0 (meaning "no taxes") which is the correct value for such inventory transactions.' => '',
'Item deleted!' => '',
'Item mode' => '',
'Jan' => '',
'January' => '',
'Journal' => '',
+ 'Journal of Last 10 Transfers' => '',
'Jul' => '',
'July' => '',
'Jump to' => '',
'KNE-Export erfolgreich!' => '',
'KNr. beim Kunden' => '',
'Keine Suchergebnisse gefunden!' => '',
- 'kivitendo needs to update the authentication database before you can proceed.' => '',
- 'kivitendo will then update the database automatically.' => '',
+ 'Knowledge' => '',
'Konten' => '',
'L' => '',
'LIABILITIES' => '',
'LP' => '',
- 'Label' => '',
'LaTeX Templates' => '',
+ 'Label' => '',
'Landscape' => '',
'Language' => '',
'Language (database ID)' => '',
'Language deleted!' => '',
'Language missing!' => '',
'Language saved!' => '',
+ 'Language settings' => '',
'Languages' => '',
- 'Last Action' => '',
+ 'Languages and translations' => '',
'Last Article Number' => '',
+ 'Last Assembly Number' => '',
'Last Cost' => '',
'Last Credit Note Number' => '',
'Last Customer Number' => '',
'Last Service Number' => '',
'Last Transaction' => '',
'Last Vendor Number' => '',
+ 'Last command output' => '',
+ 'Last run at' => '',
+ 'Lastcost' => '',
+ 'Lastcost (with X being a number)' => '',
'Lead' => '',
- 'Leave host and port field empty unless you want to make a remote connection.' => '',
+ 'Leads' => '',
'Left' => '',
'Liability' => '',
'Limit part selection' => '',
'Line Total' => '',
'Line and column' => '',
'Line endings' => '',
- 'List' => '',
- 'List Accounting Groups' => '',
+ 'Link direction' => '',
+ 'Link to' => '',
+ 'Linked Records' => '',
'List Accounts' => '',
- 'List Businesses' => '',
- 'List Departments' => '',
- 'List Groups' => '',
'List Languages' => '',
- 'List Lead' => '',
- 'List Payment Terms' => '',
'List Price' => '',
- 'List Price Factors' => '',
- 'List Pricegroups' => '',
+ 'List Printers' => '',
'List Transactions' => '',
- 'List Warehouses' => '',
- 'List bank accounts' => '',
+ 'List Users, Clients and User Groups' => '',
+ 'List current background jobs' => '',
'List export' => '',
'List of bank accounts' => '',
'List of bank collections' => '',
'List open SEPA exports' => '',
'Load draft' => '',
'Load profile' => '',
+ 'Loading...' => '',
'Local Tax Office Preferences' => '',
'Lock System' => '',
+ 'Lock and unlock installation' => '',
+ 'Lock file handling failed. Please verify that the directory "#1" is writeable by the webserver.' => '',
'Lockfile created!' => '',
'Lockfile removed!' => '',
'Login' => '',
'Login Name' => '',
- 'Login name missing!' => '',
'Login of User' => '',
'Logout' => '',
'Logout now' => '',
'Long Dates' => '',
'Long Description' => '',
'MAILED' => '',
+ 'MD' => '',
'Machine' => '',
- 'MSG_BROWSER_DOES_NOT_SUPPORT_IFRAMES' => '',
'Main Preferences' => '',
'Main sorting' => '',
'Make' => '',
- 'Make (with X being a number)' => '',
+ 'Make (vendor\'s database ID, number or name; with X being a number)' => '',
'Make compatible for import' => '',
'Make default profile' => '',
'Manage Custom Variables' => '',
'Mandantennummer' => '',
'Mandatory Departments' => '',
+ 'Map' => '',
'Mar' => '',
'March' => '',
'Margepercent' => '',
'Marked as paid' => '',
'Marked entries printed!' => '',
'Master Data' => '',
+ 'Master Data Bin Text Deleted' => '',
'Max. Dunning Level' => '',
+ 'Maximal amount difference' => '',
+ 'Maximum future booking interval' => '',
'May' => '',
'May ' => '',
'May set the BCC field when sending emails' => '',
'Missing amount' => '',
'Missing parameter #1 in call to sub #2.' => '',
'Missing parameter (at least one of #1) in call to sub #2.' => '',
- 'Missing qty' => '',
+ 'Missing parameter for WebDAV file copy' => '',
'Missing taxkeys in invoices with taxes.' => '',
- 'Missing user id!' => '',
'Mitarbeiter' => '',
'Mixed (requires column "type")' => '',
'Mobile' => '',
'More than one #1 found matching, please be more specific.' => '',
'More than one control file with the tag \'%s\' exist.' => '',
'Multi mode not supported.' => '',
- 'Multibyte Encoding' => '',
'MwSt. inkl.' => '',
'Name' => '',
+ 'Name and Street' => '',
'Name missing!' => '',
- 'National' => '',
'National Expenses' => '',
'National Revenues' => '',
'Net amount' => '',
+ 'Net amount (for verification)' => '',
'Netto Terms' => '',
- 'New Buchungsgruppe #1' => '',
- 'New Templates' => '',
- 'New Win/Tab' => '',
+ 'New Password' => '',
'New assembly' => '',
'New bank account' => '',
+ 'New client #1: The database configuration fields "host", "port", "name" and "user" must not be empty.' => '',
+ 'New client #1: The name must be unique and not empty.' => '',
'New contact' => '',
'New customer' => '',
+ 'New filter for tax accounts' => '',
'New invoice' => '',
+ 'New name' => '',
'New part' => '',
'New sales order' => '',
'New service' => '',
'New shipto' => '',
- 'New unit' => '',
'New vendor' => '',
+ 'New window/tab' => '',
'Next Dunning Level' => '',
+ 'Next run at' => '',
'No' => '',
'No %s was found matching the search parameters.' => '',
'No Company Address given' => '',
'No Company Name given' => '',
'No Customer was found matching the search parameters.' => '',
- 'No Database Drivers available!' => '',
- 'No Dataset selected!' => '',
'No Vendor was found matching the search parameters.' => '',
'No action defined.' => '',
- 'No backup file has been uploaded.' => '',
+ 'No background job has been created yet.' => '',
'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' => '',
'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' => '',
'No bins have been added to this warehouse yet.' => '',
'No business has been created yet.' => '',
+ 'No clients have been created yet.' => '',
'No contact selected to delete' => '',
'No customer has been selected yet.' => '',
'No data was found.' => '',
- 'No databases have been found on this server.' => '',
- 'No datasets have been selected.' => '',
+ 'No default currency' => '',
+ 'No delivery term has been created yet.' => '',
'No department has been created yet.' => '',
'No dunnings have been selected for printing.' => '',
- 'No entries were found which had no unit assigned to them.' => '',
'No file has been uploaded yet.' => '',
- 'No group has been selected, or the group does not exist anymore.' => '',
- 'No groups have been added yet.' => '',
- 'No or an unknown authenticantion module specified in "config/lx_office.conf".' => '',
+ 'No groups have been created yet.' => '',
+ 'No or an unknown authenticantion module specified in "config/kivitendo.conf".' => '',
'No part was found matching the search parameters.' => '',
'No payment term has been created yet.' => '',
'No prices will be updated because no prices have been entered.' => '',
+ 'No print templates have been created for this client yet. Please do so in the client configuration.' => '',
+ 'No printers have been created yet.' => '',
'No problems were recognized.' => '',
+ 'No report with id #1' => '',
'No shipto selected to delete' => '',
+ 'No summary account' => '',
'No transaction selected!' => '',
+ 'No transactions yet.' => '',
'No transfers were executed in this export.' => '',
- 'No unknown units where found.' => '',
+ 'No users have been created yet.' => '',
'No valid number entered for pricegroup "#1".' => '',
'No vendor has been selected yet.' => '',
'No warehouse has been created yet or the quantity of the bins is not configured yet.' => '',
'No.' => '',
- 'Non-taxable Purchases' => '',
- 'Non-taxable Sales' => '',
'None' => '',
+ 'Normal users cannot log in.' => '',
'Not Discountable' => '',
'Not delivered' => '',
'Not done yet' => '',
'Notes' => '',
'Notes (translation for #1)' => '',
'Notes (will appear on hard copy)' => '',
+ 'Notes for customer' => '',
+ 'Notes for vendor' => '',
'Nothing has been selected for removal.' => '',
'Nothing has been selected for transfer.' => '',
'Nothing selected!' => '',
- 'Nothing to delete!' => '',
+ 'Nothing stocked yet.' => '',
+ 'Nothing will be created or deleted at this stage!' => '',
'Nov' => '',
'November' => '',
- 'Now the user must select a single Buchungsgruppe for each part instead of three distinct accounts.' => '',
'Number' => '',
'Number Format' => '',
'Number missing in Row' => '',
'One or more Perl modules missing' => '',
'Only Warnings and Errors' => '',
'Only due follow-ups' => '',
+ 'Only groups that have been configured for the client the user logs in to will be considered.' => '',
'Only shown in item mode' => '',
- 'Oops. No valid action found to dispatch. Please report this case to the Lx-Office team.' => '',
+ 'Oops. No valid action found to dispatch. Please report this case to the kivitendo team.' => '',
'Open' => '',
'Open Amount' => '',
- 'Open a further kivitendo Window or Tab' => '',
+ 'Open a further kivitendo window or tab' => '',
'Open amount' => '',
'Open in new window' => '',
'Open this Website' => '',
'OpenDocument/OASIS' => '',
'Openings' => '',
- 'Optional comment' => '',
'Opportunity' => '',
+ 'Optional comment' => '',
'Options' => '',
'Or download the whole Installation Documentation as PDF (350kB) for off-line study (currently in German Language): ' => '',
'Order' => '',
'Order Number' => '',
'Order Number missing!' => '',
'Order deleted!' => '',
+ 'Order/Item row name' => '',
+ 'OrderItem' => '',
'Ordered' => '',
+ 'Orders' => '',
'Orders / Delivery Orders deleteable' => '',
'Orientation' => '',
'Orphaned' => '',
+ 'Orphaned currencies' => '',
+ 'Other' => '',
'Other users\' follow-ups' => '',
'Other values are ignored.' => '',
'Others' => '',
- 'Otherwise all users will only have access to their own settings.' => '',
'Otherwise the variable is only available for printing.' => '',
+ 'Otherwise you can simply check create warehouse and bins and define a name for the warehouse (Bins will be created automatically) and then continue' => '',
'Out of balance transaction!' => '',
'Out of balance!' => '',
'Output Number Format' => '',
'POSTED' => '',
'POSTED AS NEW' => '',
'PRINTED' => '',
+ 'Package name' => '',
'Packing Lists' => '',
'Page' => '',
'Page #1/#2' => '',
'Paid' => '',
'Part' => '',
+ 'Part (database ID)' => '',
'Part Description' => '',
'Part Description missing!' => '',
'Part Notes' => '',
'Part Number' => '',
'Part Number missing!' => '',
+ 'Part picker' => '',
+ 'Partnumber' => '',
'Partnumber must not be set to empty!' => '',
'Partnumber not unique!' => '',
'Parts' => '',
'Parts Inventory' => '',
+ 'Parts Master Data' => '',
'Parts must have an entry type.' => '',
'Parts with existing part numbers' => '',
'Parts, services and assemblies' => '',
'Password' => '',
'Payables' => '',
'Payment' => '',
+ 'Payment / Delivery Options' => '',
'Payment Reminder' => '',
'Payment Terms' => '',
'Payment Terms missing in row ' => '',
'Period' => '',
'Period:' => '',
'Periodic Invoices' => '',
+ 'Periodic inventory' => '',
'Periodic invoices active' => '',
'Periodic invoices inactive' => '',
'Periodicity' => '',
+ 'Perpetual inventory' => '',
+ 'Person' => '',
'Personal settings' => '',
- 'Pg Database Administration' => '',
'Phone' => '',
'Phone1' => '',
'Phone2' => '',
'Pick List' => '',
+ 'Pictures for parts' => '',
+ 'Pictures for search parts' => '',
'Please Check the bank information for each customer:' => '',
'Please Check the bank information for each vendor:' => '',
'Please ask your administrator to create warehouses and bins.' => '',
+ 'Please change the partnumber of the following parts and run the update again:' => '',
+ 'Please choose for which categories the taxes should be displayed (otherwise remove the ticks):' => '',
+ 'Please contact your administrator or a service provider.' => '',
'Please contact your administrator.' => '',
+ 'Please correct the settings and try again or deactivate that client.' => '',
+ 'Please define a taxkey for the following taxes and run the update again:' => '',
+ 'Please do so in the administration area.' => '',
'Please enter a profile name.' => '',
- 'Please enter the login for the new user.' => '',
+ 'Please enter the currency you are working with.' => '',
+ 'Please enter the name for the new client.' => '',
+ 'Please enter the name for the new group.' => '',
'Please enter the name of the database that will be used as the template for the new database:' => '',
- 'Please enter the name of the dataset you want to restore the backup in.' => '',
'Please enter the sales tax identification number.' => '',
- 'Please enter the taxnumber in the administration menu user preferences' => '',
+ 'Please enter the taxnumber in the client configuration.' => '',
'Please enter values' => '',
'Please insert object dimensions below.' => '',
'Please insert your language values below' => '',
'Please read the file' => '',
'Please select a customer from the list below.' => '',
'Please select a part from the list below.' => '',
- 'Please select a user' => '',
'Please select a vendor from the list below.' => '',
- 'Please select the chart of accounts this installation is using from the list below.' => '',
- 'Please select the database you want to backup' => '',
+ 'Please select the dataset you want to delete:' => '',
'Please select the destination bank account for the collections:' => '',
'Please select the source bank account for the transfers:' => '',
- 'Please seletct the dataset you want to delete:' => '',
+ 'Please select which client configurations you want to create.' => '',
+ 'Please set another taxnumber for the following taxes and run the update again:' => '',
'Please specify a description for the warehouse designated for these goods.' => '',
- 'Please wait...' => '',
'Plural' => '',
'Port' => '',
'Portrait' => '',
'Price factor (name)' => '',
'Price factor deleted!' => '',
'Price factor saved!' => '',
+ 'Price group (database ID)' => '',
+ 'Price group (name)' => '',
'Price information' => '',
'Pricegroup' => '',
'Pricegroup deleted!' => '',
'Print dunnings' => '',
'Print list' => '',
'Print options' => '',
+ 'Print templates' => '',
+ 'Print templates to use' => '',
'Printer' => '',
'Printer Command' => '',
- 'Printer Command missing!' => '',
+ 'Printer Description' => '',
'Printer Management' => '',
- 'Printers are created for a user database. Please select a user. The associated database will be edited.' => '',
+ 'Printer management' => '',
'Printing ... ' => '',
- 'Prior to version v2.4.0 the user could enter arbitrary strings as units for parts, services and in invoices, sales quotations etc.' => '',
- 'Prior to version v2.4.0 the user had to chose the accounts for each part and service.' => '',
'Private E-mail' => '',
'Private Phone' => '',
'Problem' => '',
'Proforma Invoice' => '',
'Program' => '',
'Project' => '',
+ 'Project (database ID)' => '',
+ 'Project (description)' => '',
+ 'Project (number)' => '',
'Project Description' => '',
'Project Number' => '',
- 'Project Number missing!' => '',
'Project Numbers' => '',
'Project Transactions' => '',
- 'Project deleted!' => '',
- 'Project not on file!' => '',
- 'Project saved!' => '',
'Projects' => '',
'Projecttransactions' => '',
'Prozentual/Absolut' => '',
+ 'Purchase Delivery Orders' => '',
'Purchase Delivery Orders deleteable' => '',
'Purchase Invoice' => '',
+ 'Purchase Invoices' => '',
'Purchase Order' => '',
'Purchase Orders' => '',
'Purchase Orders deleteable' => '',
'RFQs' => '',
'ROP' => '',
'Ranges of numbers' => '',
- 'Ranges of numbers and default accounts' => '',
'Re-run analysis' => '',
'Receipt' => '',
'Receipt posted!' => '',
'Recorded taxkey' => '',
'Reference' => '',
'Reference / Invoice Number' => '',
+ 'Reference day' => '',
'Reference missing!' => '',
'Release From Stock' => '',
'Remaining' => '',
'Remove draft when posting' => '',
'Removed spoolfiles!' => '',
'Removing marked entries from queue ...' => '',
- 'Rename the group' => '',
+ 'Replace the orphaned currencies by other not orphaned currencies. To do so, please delete the currency in the textfields above and replace it by another currency. You could loose or change unintentionally exchangerates. Go on very carefully since you could destroy transactions.' => '',
'Report Positions' => '',
'Report about warehouse contents' => '',
'Report about warehouse transactions' => '',
'Report for' => '',
'Reports' => '',
'Representative' => '',
+ 'Representative for Customer' => '',
'Reqdate' => '',
+ 'Request Quotations' => '',
'Request for Quotation' => '',
+ 'Request for Quotation Number' => '',
'Request for Quotations' => '',
'Request quotation' => '',
'Requested execution date' => '',
'Requested execution date to' => '',
'Required by' => '',
'Reset' => '',
- 'Restore Dataset' => '',
+ 'Result' => '',
'Revenue' => '',
'Revenue Account' => '',
'Revenues EU with UStId' => '',
'Revenues EU without UStId' => '',
'Review of Aging list' => '',
'Right' => '',
+ 'Row #1: amount has to be different from zero.' => '',
+ 'Row number' => '',
+ 'Row was created from current record' => '',
+ 'Row was source for current record' => '',
+ 'Run at' => '',
'SAVED' => '',
'SAVED FOR DUNNING' => '',
'SCREENED' => '',
'Saldo neu' => '',
'Saldo per' => '',
'Sale Prices' => '',
+ 'Sales Delivery Orders' => '',
'Sales Delivery Orders deleteable' => '',
'Sales Invoice' => '',
'Sales Invoices' => '',
'Sales Orders' => '',
'Sales Orders deleteable' => '',
'Sales Price information' => '',
+ 'Sales Quotations' => '',
'Sales Report' => '',
'Sales and purchase invoices with inventory transactions with taxkeys' => '',
'Sales delivery order' => '',
'Sales price total' => '',
'Sales quotation' => '',
'Salesman' => '',
+ 'Salesman (database ID)' => '',
'Salesperson' => '',
'Same as the quote character' => '',
'Sat. Fax' => '',
'Satz %' => '',
'Save' => '',
'Save Draft' => '',
- 'Save account first to insert taxkeys' => '',
'Save and AP Transaction' => '',
'Save and AR Transaction' => '',
'Save and Close' => '',
'Save and Quotation' => '',
'Save and RFQ' => '',
'Save and close' => '',
+ 'Save and execute' => '',
'Save as new' => '',
+ 'Save document in WebDAV repository' => '',
'Save draft' => '',
'Save profile' => '',
'Save settings as' => '',
'Saving the file \'%s\' failed. OS error message: %s' => '',
'Screen' => '',
+ 'Search' => '',
'Search AP Aging' => '',
'Search AR Aging' => '',
'Search contacts' => '',
+ 'Search projects' => '',
'Search term' => '',
'Searchable' => '',
'Secondary sorting' => '',
+ 'Section "#1"' => '',
'Select' => '',
'Select a Customer' => '',
'Select a customer' => '',
'Select from one of the projects below' => '',
'Select postscript or PDF!' => '',
'Select tax office...' => '',
- 'Select the chart of accounts in use' => '',
- 'Select the checkboxes that match users to the groups they should belong to.' => '',
'Select type of removal' => '',
'Select type of transfer' => '',
'Selected' => '',
'Sellprice for price group \'#1\'' => '',
'Sellprice significant places' => '',
'Semicolon' => '',
- 'Send the backup via Email' => '',
'Sep' => '',
'Separator' => '',
'Separator chararacter' => '',
'Service Contract' => '',
'Service Items' => '',
'Service Number missing!' => '',
- 'Service unit' => '',
+ 'Service, assembly or part' => '',
'Services' => '',
'Set Language Values' => '',
'Set eMail text' => '',
'Settings' => '',
'Setup Menu' => '',
- 'Setup Templates' => '',
'Ship to' => '',
+ 'Ship to (database ID)' => '',
'Ship via' => '',
'Shipping Address' => '',
'Shipping Point' => '',
'Show "mark as paid" in ar transactions' => '',
'Show "mark as paid" in purchase invoices' => '',
'Show "mark as paid" in sales invoices' => '',
+ 'Show AP transactions as part of AP invoice report' => '',
+ 'Show AR transactions as part of AR invoice report' => '',
'Show Bestbefore' => '',
'Show Filter' => '',
'Show Salesman' => '',
'Show TODO list' => '',
+ 'Show Transfer via default' => '',
+ 'Show administration link' => '',
+ 'Show all parts' => '',
'Show by default' => '',
'Show custom variable search inputs' => '',
'Show delete button in purchase delivery orders?' => '',
'Show delete button in sales delivery orders?' => '',
'Show delete button in sales orders?' => '',
'Show details' => '',
+ 'Show details and reports of parts, services, assemblies' => '',
'Show fields used for the best before date?' => '',
'Show follow ups...' => '',
'Show help text' => '',
'Show items from invoices individually' => '',
'Show old dunnings' => '',
'Show overdue sales quotations and requests for quotations...' => '',
+ 'Show parts' => '',
+ 'Show settings' => '',
+ 'Show the picture in the part form' => '',
+ 'Show the pictures in the result for search parts' => '',
+ 'Show the weights of articles and the total weight in orders, invoices and delivery notes?' => '',
+ 'Show weights' => '',
'Show your TODO list after loggin in' => '',
'Signature' => '',
'Since bin is not enforced in the parts data, please specify a bin where goods without a specified bin will be put.' => '',
'Single quotes' => '',
'Single values in item mode, cumulated values in invoice mode' => '',
'Skip' => '',
+ 'Skip entry' => '',
+ 'Skipping due to existing entry in database' => '',
'Skonto' => '',
'Skonto Terms' => '',
+ 'So far you could use one partnumber for severel parts, for example a service and an article.' => '',
'Sold' => '',
'Solution' => '',
'Sort By' => '',
'Source bank account' => '',
'Source bin' => '',
'Space' => '',
- 'Split entry detected. The values you have entered will result in an entry with more than one position on both debit and credit. Due to known problems involving accounting software Lx-Office does not allow these.' => '',
+ 'Split entry detected. The values you have entered will result in an entry with more than one position on both debit and credit. Due to known problems involving accounting software kivitendo does not allow these.' => '',
'Spoolfile' => '',
'Start Dunning Process' => '',
'Start analysis' => '',
'Start date' => '',
+ 'Start task server' => '',
'Start the correction assistant' => '',
'Startdate_coa' => '',
'Starting Balance' => '',
+ 'Starting the task server failed.' => '',
'Starting with version 2.6.3 the configuration files in "config" have been consolidated.' => '',
'Statement' => '',
'Statement Balance' => '',
'Statement sent to' => '',
'Statements sent to printer!' => '',
'Status' => '',
- 'Step 1 of 3: Parts' => '',
'Step 2' => '',
- 'Step 2 of 3: Services' => '',
- 'Step 3 of 3: Assemblies' => '',
- 'Step 3 of 3: Default units' => '',
'Steuersatz' => '',
'Stock' => '',
'Stock Qty for Date' => '',
+ 'Stock for part #1' => '',
'Stock value' => '',
'Stocked Qty' => '',
+ 'Stop task server' => '',
+ 'Stopping the task server failed. Output:' => '',
'Storno' => '',
'Storno (one letter abbreviation)' => '',
'Storno Invoice' => '',
'Street' => '',
+ 'Style the picture with the following CSS code' => '',
'Stylesheet' => '',
'Subject' => '',
'Subject:' => '',
'Tab' => '',
'Target bank account' => '',
'Target table' => '',
+ 'Task Server is not running, starting it now. If this does not change, please check your task server config' => '',
+ 'Task server control' => '',
+ 'Task server status' => '',
'Tax' => '',
'Tax Consultant' => '',
+ 'Tax ID number' => '',
'Tax Included' => '',
'Tax Number' => '',
'Tax Number / SSN' => '',
'Tax deleted!' => '',
'Tax number' => '',
'Tax paid' => '',
+ 'Tax rate' => '',
'Tax saved!' => '',
+ 'Tax zone (database ID)' => '',
+ 'Tax zone (description)' => '',
'Tax-O-Matic' => '',
'Tax-o-matic Account' => '',
'Taxaccount_coa' => '',
'Templates' => '',
'Terms missing in row ' => '',
'Test and preview' => '',
- 'Test connection' => '',
+ 'Test database connectivity' => '',
'Text field' => '',
'Text field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the text field. They default to 30 and 5 respectively.' => '',
'Text variables: \'MAXLENGTH=n\' sets the maximum entry length to \'n\'.' => '',
'Text, text field and number variables: The default value will be used as-is.' => '',
'That export does not exist.' => '',
+ 'That is why kivitendo could not find a default currency.' => '',
+ 'The \'name\' is the field shown to the user during login.' => '',
'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => '',
'The AP transaction #1 has been deleted.' => '',
'The AR transaction #1 has been deleted.' => '',
+ 'The Bins in Inventory were only a information text field.' => '',
+ 'The Bins in master data were only a information text field.' => '',
'The GL transaction #1 has been deleted.' => '',
- 'The LDAP server "#1:#2" is unreachable. Please check config/lx_office.conf.' => '',
+ 'The LDAP server "#1:#2" is unreachable. Please check config/kivitendo.conf.' => '',
'The SEPA export has been created.' => '',
'The SEPA strings have been saved.' => '',
+ 'The WebDAV feature has been used.' => '',
+ 'The access rights a user has within a client instance is still governed by his group membership.' => '',
'The access rights have been saved.' => '',
'The account 3804 already exists, the update will be skipped.' => '',
'The account 3804 will not be added automatically.' => '',
+ 'The action you\'ve chosen has not been executed because the document does not contain any item yet.' => '',
+ 'The administration area is always accessible.' => '',
'The application "#1" was not found on the system.' => '',
'The assembly has been created.' => '',
'The assistant could not find anything wrong with #1. Maybe the problem has been solved in the meantime.' => '',
- 'The authentication configuration file "config/lx_office.conf" does not exist. This kivitendo installation has probably not been updated correctly yet. Please contact your administrator.' => '',
'The authentication database is not reachable at the moment. Either it hasn\'t been set up yet or the database server might be down. Please contact your administrator.' => '',
'The available options depend on the varibale type:' => '',
- 'The backup you upload here has to be a file created with "pg_dump -o -Ft".' => '',
+ 'The background job could not be destroyed.' => '',
+ 'The background job has been created.' => '',
+ 'The background job has been deleted.' => '',
+ 'The background job has been saved.' => '',
+ 'The background job was executed successfully.' => '',
'The bank information must not be empty.' => '',
'The base unit does not exist or it is about to be deleted in row %d.' => '',
'The base unit does not exist.' => '',
'The base unit relations must not contain loops (e.g. by saying that unit A\'s base unit is B, B\'s base unit is C and C\'s base unit is A) in row %d.' => '',
+ 'The basic client tables have not been created for this client\'s database yet.' => '',
'The business has been created.' => '',
'The business has been deleted.' => '',
'The business has been saved.' => '',
'The business is in use and cannot be deleted.' => '',
'The changing of tax-o-matic account is NOT recommended, but if you do so please also (re)configure buchungsgruppen and reconfigure ALL charts which point to this tax-o-matic account. ' => '',
+ 'The client could not be deleted.' => '',
+ 'The client has been created.' => '',
+ 'The client has been deleted.' => '',
+ 'The client has been saved.' => '',
+ 'The column "datatype" must be present and must be the first column. The values must be the row names (see settings) for order and item data respectively.' => '',
+ 'The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.' => '',
+ 'The column triplets can occur multiple times with different numbers "X" each time (e.g. "make_1", "model_1", "lastcost_1", "make_2", "model_2", "lastcost_2", "make_3", "model_3", "lastcost_3" etc).' => '',
'The columns "Dunning Duedate", "Total Fees" and "Interest" show data for the previous dunning created for this invoice.' => '',
- 'The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/lx_office.conf.' => '',
+ 'The combination of database host, port and name is not unique.' => '',
+ 'The command is missing.' => '',
+ 'The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/kivitendo.conf.' => '',
'The connection to the authentication database failed:' => '',
+ 'The connection to the configured client database "#1" on host "#2:#3" failed.' => '',
'The connection to the database could not be established.' => '',
'The connection to the template database failed:' => '',
'The connection was established successfully.' => '',
+ 'The contact person attribute "birthday" is converted from a free-form text field into a date field.' => '',
'The creation of the authentication database failed:' => '',
+ 'The custom variable has been created.' => '',
'The custom variable has been deleted.' => '',
'The custom variable has been saved.' => '',
- 'The database #1 has been successfully deleted.' => '',
+ 'The custom variable is in use and cannot be deleted.' => '',
'The database for user management and authentication does not exist. You can create let kivitendo create it with the following parameters:' => '',
+ 'The database host is missing.' => '',
+ 'The database name is missing.' => '',
+ 'The database port is missing.' => '',
'The database update/creation did not succeed. The file #1 contained the following error:' => '',
- 'The database upgrade for the introduction of Buchungsgruppen is now complete.' => '',
- 'The database upgrade for the introduction of units is now complete.' => '',
- 'The dataset #1 has been successfully created.' => '',
- 'The dataset backup has been sent via email to #1.' => '',
- 'The dataset has to exist before a restoration can be started.' => '',
- 'The dataset name is missing.' => '',
+ 'The database user is missing.' => '',
+ 'The dataset #1 has been created.' => '',
+ 'The dataset #1 has been deleted.' => '',
'The deductible amount' => '',
'The default value depends on the variable type:' => '',
'The delivery order has not been marked as delivered. The warehouse contents have not changed.' => '',
+ 'The delivery term has been created.' => '',
+ 'The delivery term has been deleted.' => '',
+ 'The delivery term has been saved.' => '',
+ 'The delivery term is in use and cannot be deleted.' => '',
'The department has been created.' => '',
'The department has been deleted.' => '',
'The department has been saved.' => '',
'The department is in use and cannot be deleted.' => '',
'The description is missing.' => '',
+ 'The description is not unique.' => '',
'The description is shown on the form. Chose something short and descriptive.' => '',
- 'The directory "%s" could not be created:\n%s' => '',
'The directory %s does not exist.' => '',
'The discount in percent' => '',
'The discount must be less than 100%.' => '',
'The discount must not be negative.' => '',
'The dunning process started' => '',
'The dunnings have been printed.' => '',
- 'The email address is missing.' => '',
'The end date is the last day for which invoices will possibly be created.' => '',
+ 'The execution schedule is invalid.' => '',
+ 'The execution type is invalid.' => '',
+ 'The existing record has been created from the link target to add.' => '',
'The factor is missing in row %d.' => '',
'The factor is missing.' => '',
'The first reason is that kivitendo contained a bug which resulted in the wrong taxkeys being recorded for transactions in which two entries are posted for the same chart with different taxkeys.' => '',
'The follow-up date is missing.' => '',
- 'The following Buchungsgruppen have already been created:' => '',
- 'The following Datasets need to be updated' => '',
+ 'The following currencies have been used, but they are not defined:' => '',
'The following drafts have been saved and can be loaded.' => '',
- 'The following old files whose settings have to be merged manually into the new configuration file "config/lx_office.conf" still exist:' => '',
+ 'The following groups are valid for this client' => '',
+ 'The following list has been generated automatically from existing users collapsing users with identical settings into a single entry.' => '',
+ 'The following old files whose settings have to be merged manually into the new configuration file "config/kivitendo.conf" still exist:' => '',
'The following transaction contains wrong taxes:' => '',
'The following transaction contains wrong taxkeys:' => '',
- 'The following units are unknown.' => '',
- 'The following units exist already:' => '',
- 'The following users have been migrated into the authentication database:' => '',
- 'The following warnings occured during an upgrade to the document templates:' => '',
+ 'The following transactions are concerned:' => '',
+ 'The following users are a member of this group' => '',
+ 'The following users will have access to this client' => '',
'The formula needs the following syntax:<br>For regular article:<br>Variablename= Variable Unit;<br>Variablename2= Variable2 Unit2;<br>...<br>###<br>Variable + ( Variable2 / Variable )<br><b>Please be beware of the spaces in the formula</b><br>' => '',
'The greetings have been saved.' => '',
- 'The group has been added.' => '',
- 'The group has been deleted.' => '',
- 'The group has been saved.' => '',
- 'The group memberships have been saved.' => '',
- 'The group name is missing.' => '',
+ 'The installation is currently locked.' => '',
+ 'The installation is currently unlocked.' => '',
+ 'The items are imported accoring do their number "X" regardless of the column order inside the file.' => '',
+ 'The link target to add has been created from the existing record.' => '',
'The list has been printed.' => '',
+ 'The login is missing.' => '',
+ 'The login is not unique.' => '',
'The long description is missing.' => '',
+ 'The master templates where not found.' => '',
'The name in row %d has already been used before.' => '',
'The name is missing in row %d.' => '',
'The name is missing.' => '',
+ 'The name is not unique.' => '',
'The name must only consist of letters, numbers and underscores and start with a letter.' => '',
'The number of days for full payment' => '',
- 'The old file containing the user information is still present ("#1"). Do you want to migrate these users into the database? If not then you will not be able to log in with any of the users present in the old file.' => '',
'The option field is empty.' => '',
+ 'The package name is invalid.' => '',
'The parts for this delivery order have already been transferred in.' => '',
'The parts for this delivery order have already been transferred out.' => '',
'The parts have been removed.' => '',
'The payment term has been saved.' => '',
'The payment term is in use and cannot be deleted.' => '',
'The payments have been posted.' => '',
- 'The pg_dump process could not be started.' => '',
- 'The pg_restore process could not be started.' => '',
'The preferred one is to install packages provided by your operating system distribution (e.g. Debian or RPM packages).' => '',
+ 'The printer could not be deleted.' => '',
+ 'The printer has been created.' => '',
+ 'The printer has been deleted.' => '',
+ 'The printer has been saved.' => '',
'The profile \'#1\' has been deleted.' => '',
'The profile has been saved under the name \'#1\'.' => '',
- 'The program\'s exit code was #1 ("0" usually means that everything went OK).' => '',
- 'The project has been added.' => '',
+ 'The project has been created.' => '',
+ 'The project has been deleted.' => '',
'The project has been saved.' => '',
- 'The restoration process has started. Here\'s the output of the "pg_restore" command:' => '',
- 'The restoration process is complete. Please review "pg_restore"\'s output to find out if the restoration was successful.' => '',
+ 'The project is in use and cannot be deleted.' => '',
+ 'The project number is already in use.' => '',
+ 'The project number is missing.' => '',
'The second reason is that kivitendo allowed the user to enter the tax amount manually regardless of the taxkey used.' => '',
'The second way is to use Perl\'s CPAN module and let it download and install the module for you.' => '',
- 'The selected PostgreSQL installation uses UTF-8 as its encoding. Therefore you have to configure Lx-Office to use UTF-8 as well.' => '',
'The selected bank account does not exist anymore.' => '',
'The selected bin does not exist.' => '',
'The selected currency' => '',
+ 'The selected database is still configured for client "#1". If you delete the database that client will stop working until you re-configure it. Do you still want to delete the database?' => '',
'The selected exports have been closed.' => '',
'The selected warehouse does not exist.' => '',
'The selected warehouse is empty, or no stocked items where found that match the filter settings.' => '',
+ 'The session has expired. Please log in again.' => '',
'The session is invalid or has expired.' => '',
'The settings were saved, but the password was not changed.' => '',
'The source warehouse does not contain any bins.' => '',
'The subject is missing.' => '',
'The tables for user management and authentication do not exist. They will be created in the next step in the following database:' => '',
'The tabulator character' => '',
+ 'The task server does not appear to be running.' => '',
+ 'The task server is already running.' => '',
+ 'The task server is not running at the moment but needed for this module' => '',
+ 'The task server is not running.' => '',
+ 'The task server was started successfully.' => '',
+ 'The task server was stopped successfully.' => '',
'The third way is to download the module from the above mentioned URL and to install the module manually following the installations instructions contained in the source archive.' => '',
+ 'The three columns "make_X", "model_X" and "lastcost_X" with the same number "X" are used to import vendor part numbers and vendor prices.' => '',
'The transaction is shown below in its current state.' => '',
+ 'The type is missing.' => '',
'The unit has been saved.' => '',
'The unit in row %d has been deleted in the meantime.' => '',
'The unit in row %d has been used in the meantime and cannot be changed anymore.' => '',
'The units have been saved.' => '',
- 'The user is a member in the following group(s):' => '',
- 'The user migration process is complete.' => '',
+ 'The user can chose which client to connect to during login.' => '',
+ 'The user could not be deleted.' => '',
+ 'The user group could not be deleted.' => '',
+ 'The user group has been created.' => '',
+ 'The user group has been deleted.' => '',
+ 'The user group has been saved.' => '',
+ 'The user has been created.' => '',
+ 'The user has been deleted.' => '',
+ 'The user has been saved.' => '',
'The variable name must only consist of letters, numbers and underscores. It must begin with a letter. Example: send_christmas_present' => '',
'The warehouse could not be deleted because it has already been used.' => '',
'The warehouse does not contain any bins.' => '',
'The wrong taxkeys for AP and AR transactions have been fixed.' => '',
'The wrong taxkeys for inventory transactions for sales and purchase invoices have been fixed.' => '',
'The wrong taxkeys have been fixed.' => '',
+ 'Then go to the database administration and chose "create database".' => '',
'There are #1 more open invoices for this customer with other currencies.' => '',
'There are #1 more open invoices from this vendor with other currencies.' => '',
'There are #1 unfinished follow-ups of which #2 are due.' => '',
+ 'There are Bins defined in your Inventory.' => '',
+ 'There are Bins defined in your master data.' => '',
'There are bookings to the account 3803 after 01.01.2007. If you didn\'t change this account manually to 19% the bookings are probably incorrect.' => '',
+ 'There are double partnumbers in your database.' => '',
'There are entries in tax where taxkey is NULL.' => '',
- 'There are four tax zones.' => '',
'There are invalid taxnumbers in use.' => '',
+ 'There are invalid transactions in your database.' => '',
'There are no entries in the background job history.' => '',
'There are no items in stock.' => '',
'There are no items on your TODO list at the moment.' => '',
- 'There are still entries in the database for which no unit has been assigned.' => '',
+ 'There are several options you can handle this problem, please select one:' => '',
'There are still transfers not matching the qty of the delivery order. Stock operations can not be changed later. Do you really want to proceed?' => '',
+ 'There are undefined currencies in your system.' => '',
'There are usually three ways to install Perl modules.' => '',
'There is already a taxkey 0 with tax rate not 0.' => '',
'There is an inconsistancy in your database.' => '',
'There is not enough available of \'#1\' at warehouse \'#2\', bin \'#3\', #4, #5, for the transfer of #6.' => '',
'There is not enough available of \'#1\' at warehouse \'#2\', bin \'#3\', #4, for the transfer of #5.' => '',
'There is not enough left of \'#1\' in bin \'#2\' for the removal of #3.' => '',
- 'There is nothing to do in this step.' => '',
+ 'There was an error executing the background job.' => '',
+ 'There was an error parsing the csv file: #1 in line #2.' => '',
+ 'There you can let kivitendo create the basic tables for you, even in an already existing database.' => '',
+ 'Therefore several settings that had to be made for each user in the past have been consolidated into the client configuration.' => '',
'Therefore the definition of "kg" with the base unit "g" and a factor of 1000 is valid while defining "g" with a base unit of "kg" and a factor of "0.001" is not.' => '',
- 'Therefore there\'s no need to create the same article more than once if it is sold or bought in/from another tax zone.' => '',
- 'These units can be based on other units so that kivitendo can convert prices when the user switches from one unit to another.' => '',
'These wrong entries cannot be fixed automatically.' => '',
'This can be done with the following query:' => '',
- 'This corresponds to kivitendo\'s behavior prior to version 2.4.4.' => '',
'This could have happened for two reasons:' => '',
'This customer number is already in use.' => '',
- 'This group will be called "Full Access".' => '',
- 'This installation uses an unknown chart of accounts ("#1"). This database upgrade cannot create standard buchungsgruppen automatically.' => '',
- 'This is a preliminary check for existing sources. Nothing will be created or deleted at this stage!' => '',
+ 'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => '',
+ 'This group is valid for the following clients' => '',
+ 'This has been changed in this version, therefore please change the "old" bins to some real warehouse bins.' => '',
+ 'This has been changed in this version.' => '',
+ 'This is a very critical problem.' => '',
+ 'This is the client to be selected by default on the login screen.' => '',
+ 'This is the default bin for ignoring onhand' => '',
+ 'This is the default bin for parts' => '',
'This list is capped at 15 items to keep it fast. If you need a full list, please use reports.' => '',
'This means that the user has created an AP transaction and chosen a taxkey for sales taxes, or that he has created an AR transaction and chosen a taxkey for input taxes.' => '',
'This module can help you identify and correct such entries by analyzing the general ledger and presenting you likely solutions but also allowing you to fix problems yourself.' => '',
'This option controls the inventory system.' => '',
'This option controls the method used for profit determination.' => '',
'This option controls the posting and calculation behavior for the accounting method.' => '',
+ 'This partnumber is not unique. You should change it.' => '',
+ 'This requires you to manually correct entries for which an automatic conversion failed and to check those for which it succeeded.' => '',
'This transaction has to be split into several transactions manually.' => '',
'This update will change the nature the onhand of goods is tracked.' => '',
- 'This upgrade script tries to map all existing parts in the database to the newly created Buchungsgruppen.' => '',
- 'This upgrade script tries to map all existing units in the database to the newly created units.' => '',
+ 'This user is a member in the following groups' => '',
+ 'This user will have access to the following clients' => '',
'This vendor number is already in use.' => '',
+ 'Three Options:' => '',
+ 'Time Format' => '',
+ 'Time Tracking' => '',
'Time period for the analysis:' => '',
- 'Time Track' => '',
'Timestamp' => '',
'Title' => '',
'To' => '',
'To (email)' => '',
'To (time)' => '',
'To Date' => '',
- 'To add a user to a group edit a name, change the login name and save. A new user with the same variables will then be saved under the new login name.' => '',
+ 'To continue please change the taxkey 0 to another value.' => '',
+ 'To user login' => '',
'Top' => '',
'Top (CSS)' => '',
- 'Top (CSS) new' => '',
'Top (Javascript)' => '',
'Top 100' => '',
'Top 100 hinzufuegen' => '',
- 'Top Level' => '',
+ 'Top Level Designation only' => '',
'Total' => '',
'Total Fees' => '',
'Total stock value' => '',
'Total sum' => '',
+ 'Total weight' => '',
'Totals' => '',
'Trade Discount' => '',
'Trans Id' => '',
'Transaction has already been cancelled!' => '',
'Transaction has been split on both the credit and the debit side' => '',
'Transaction posted!' => '',
+ 'Transactions without account:' => '',
+ 'Transactions without reference:' => '',
'Transactions, AR transactions, AP transactions' => '',
'Transdate' => '',
'Transfer' => '',
'Transfer To Stock' => '',
'Transfer from warehouse' => '',
'Transfer in' => '',
+ 'Transfer in via default' => '',
'Transfer out' => '',
+ 'Transfer out via default' => '',
'Transfer qty' => '',
+ 'Transfer successful' => '',
'Translation' => '',
'Trial Balance' => '',
'Trial balance between %s and %s' => '',
'Type of Business' => '',
'Type of Customer' => '',
'Type of Vendor' => '',
+ 'Types of Business' => '',
'USTVA' => '',
'USTVA 2004' => '',
'USTVA 2005' => '',
'Unbalanced Ledger' => '',
'Unchecked custom variables will not appear in orders and invoices.' => '',
'Unfinished follow-ups' => '',
+ 'Unfortunately you have no warehouse defined.' => '',
'Unit' => '',
'Unit (if missing or empty default unit will be used)' => '',
'Unit missing.' => '',
'Units that have already been used (e.g. for parts and services or in invoices or warehouse transactions) cannot be changed.' => '',
'Unknown Category' => '',
'Unknown Link' => '',
- 'Unknown chart of accounts' => '',
'Unknown dependency \'%s\'.' => '',
'Unknown problem type.' => '',
'Unlock System' => '',
'Until' => '',
'Update' => '',
- 'Update Dataset' => '',
'Update Prices' => '',
'Update SKR04: new tax account 3804 (19%)' => '',
- 'Update complete' => '',
'Update prices' => '',
'Update prices of existing entries' => '',
- 'Update?' => '',
+ 'Update properties of existing entries' => '',
'Updated' => '',
+ 'Updating existing entry in database' => '',
'Updating prices of existing entry in database' => '',
+ 'Updating the client fields in the database "#1" on host "#2:#3" failed.' => '',
'Uploaded on #1, size #2 kB' => '',
- 'Use As Template' => '',
- 'Use Templates' => '',
+ 'Use As New' => '',
+ 'Use WebDAV Repository' => '',
+ 'Use existing templates' => '',
+ 'Use master default bin for Default Transfer, if no default bin for the part is configured' => '',
'User' => '',
'User Config' => '',
'User Groups' => '',
- 'User Login' => '',
- 'User data migration' => '',
- 'User deleted!' => '',
- 'User migration complete' => '',
+ 'User Preferences' => '',
+ 'User access' => '',
+ 'User list' => '',
+ 'User login' => '',
'User name' => '',
- 'User saved!' => '',
'Username' => '',
- 'Users in this group' => '',
- 'Ust-IDNr' => '',
+ 'Users that are a member in this group' => '',
+ 'Users that have access to this client' => '',
+ 'Users with access' => '',
+ 'Users with access to this client' => '',
+ 'Users, Clients and User Groups' => '',
+ 'VAT ID' => '',
+ 'Valid' => '',
'Valid from' => '',
'Valid until' => '',
+ 'Valid/Obsolete' => '',
'Value' => '',
'Variable' => '',
'Variable Description' => '',
'Variable Name' => '',
'Vendor' => '',
+ 'Vendor (database ID)' => '',
'Vendor (name)' => '',
'Vendor Invoice' => '',
- 'Vendor Invoices' => '',
+ 'Vendor Invoices & AP Transactions' => '',
'Vendor Name' => '',
'Vendor Number' => '',
'Vendor Order Number' => '',
'Vendor missing!' => '',
'Vendor not on file or locked!' => '',
'Vendor not on file!' => '',
+ 'Vendor saved' => '',
'Vendor saved!' => '',
'Vendor type' => '',
'Vendors' => '',
'Verrechnungseinheit' => '',
'Version' => '',
- 'Version 2.4.0 introduces two new concepts: tax zones and Buchungsgruppen.' => '',
'View SEPA export' => '',
+ 'View background job execution result' => '',
+ 'View background job history' => '',
+ 'View background jobs' => '',
'View warehouse content' => '',
'View/edit all employees sales documents' => '',
'Von Konto: ' => '',
'Warehouse Migration' => '',
'Warehouse To' => '',
'Warehouse content' => '',
+ 'Warehouse correction' => '',
'Warehouse deleted.' => '',
+ 'Warehouse list' => '',
'Warehouse management' => '',
'Warehouse saved.' => '',
'Warehouses' => '',
'Warning' => '',
- 'Warnings during template upgrade' => '',
+ 'WebDAV' => '',
'WebDAV link' => '',
+ 'WebDAV save documents' => '',
'Webserver interface' => '',
'Weight' => '',
'Weight unit' => '',
'What type of item is this?' => '',
'Which is located at doc/kivitendo-Dokumentation.pdf. Click here: ' => '',
'With Extension Of Time' => '',
+ 'With the introduction of clients each client gets its own WebDAV folder.' => '',
'Workflow Delivery Order' => '',
'Workflow purchase_order' => '',
'Workflow request_quotation' => '',
'Workflow sales_order' => '',
'Workflow sales_quotation' => '',
+ 'Write bin to default bin in part?' => '',
'Wrong Period' => '',
- 'Wrong date format!' => '',
'Wrong tax keys recorded' => '',
'Wrong taxes recorded' => '',
'YYYY' => '',
'Yes, included by default' => '',
'Yes/No (Checkbox)' => '',
'You are logged out!' => '',
- 'You can also create new units now.' => '',
'You can also delete this transaction and re-enter it manually.' => '',
+ 'You can choose account categories for taxes. Depending on these categories taxes will be displayed for transfers in the general ledger or not.' => '',
'You can correct this transaction by chosing the correct taxkeys from the drop down boxes and hitting the button "Fix transaction" afterwards.' => '',
- 'You can create a missing dataset by going back and chosing "Create Dataset".' => '',
'You can create warehouses and bins via the menu "System -> Warehouses".' => '',
'You can declare different translations for singular and plural for each unit (e.g. "day" and "days).' => '',
'You can either create a new database or chose an existing database.' => '',
'You can find information on the migration in the upgrade chapter of the documentation.' => '',
'You can only delete datasets that are not in use.' => '',
+ 'You can update existing contacts by providing the \'cp_id\' column with their database IDs. Otherwise: ' => '',
'You can use the following strings in the long description and all translations. They will be replaced by their actual values by kivitendo before they\'re output.' => '',
'You cannot adjust the price for pricegroup "#1" by a negative percentage.' => '',
'You cannot continue before all required modules are installed.' => '',
- 'You cannot continue until all unknown units have been mapped to known ones.' => '',
'You cannot create an invoice for delivery orders for different customers.' => '',
'You cannot create an invoice for delivery orders from different vendors.' => '',
- 'You did not enter a name!' => '',
'You do not have the permissions to access this function.' => '',
'You have entered or selected the following shipping address for this customer:' => '',
+ 'You have never worked with currencies.' => '',
'You have not added bank accounts yet.' => '',
'You have not selected any delivery order.' => '',
'You have not selected any export.' => '',
'You have not selected any item.' => '',
'You have selected none of the invoices.' => '',
- 'You have to chose a dimension unit and a service unit which will then be assigned to those entries.' => '',
- 'You have to chose which unit to save for each of them.' => '',
- 'You have to create at least one group, grant it access to kivitendo\'s functions and assign users to it.' => '',
- 'You have to create new Buchungsgruppen for all the combinations of inventory, income and expense accounts that have been used already.' => '',
'You have to define a unit as a multiple of a smaller unit.' => '',
- 'You have to enter a company name in your user preferences (see the "Program" menu, "Preferences").' => '',
- 'You have to enter the SEPA creditor ID in your user preferences (see the "Program" menu, "Preferences").' => '',
+ 'You have to enter a company name in the client configuration.' => '',
+ 'You have to enter the SEPA creditor ID in the client configuration.' => '',
'You have to fill in at least an account number, the bank code, the IBAN and the BIC.' => '',
+ 'You have to grant users access to one or more clients.' => '',
'You have to specify a department.' => '',
'You have to specify an execution date for each antry.' => '',
'You must chose a user.' => '',
+ 'You must enter a name for your new print templates.' => '',
+ 'You must select existing print templates or create a new set.' => '',
'You should create a backup of the database before proceeding because the backup might not be reversible.' => '',
- 'You will now be forwarded to the administration panel.' => '',
'You\'re not editing a file.' => '',
'You\'ve already chosen the following limitations:' => '',
- 'Your PostgreSQL installationen uses UTF-8 as its encoding. Therefore you have to configure Lx-Office to use UTF-8 as well.' => '',
+ 'Your PostgreSQL installationen does not use Unicode as its encoding. This is not supported anymore.' => '',
'Your TODO list' => '',
'Your account number' => '',
'Your bank' => '',
'Your bank code' => '',
'Your browser does not currently support Javascript.' => '',
'Your download does not exist anymore. Please re-run the DATEV export assistant.' => '',
+ 'Your import is beig processed.' => '',
+ 'ZM' => '',
'Zeitpunkt' => '',
'Zeitraum' => '',
'Zero amount posting!' => '',
'action= not defined!' => '',
'active' => '',
'all entries' => '',
+ 'and' => '',
'ap_aging_list' => '',
'ar_aging_list' => '',
'as at' => '',
+ 'assembly' => '',
'assembly_list' => '',
'averaged values, in invoice mode only useful when filtered by a part' => '',
'back' => '',
'bestbefore #1' => '',
'bin_list' => '',
'bis' => '',
- 'button' => '',
+ 'building data' => '',
+ 'building report' => '',
'cash' => '',
'chargenumber #1' => '',
'chart_of_accounts' => '',
'close' => '',
'closed' => '',
'companylogo_subtitle' => '',
- 'config/lx_office.conf: Key "DB_config" is missing.' => '',
- 'config/lx_office.conf: Key "authentication/ldap" is missing.' => '',
- 'config/lx_office.conf: Missing parameters in "authentication/database". Required parameters are "host", "db" and "user".' => '',
- 'config/lx_office.conf: Missing parameters in "authentication/ldap". Required parameters are "host", "attribute" and "base_dn".' => '',
+ 'config/kivitendo.conf: Key "DB_config" is missing.' => '',
+ 'config/kivitendo.conf: Key "authentication/ldap" is missing.' => '',
+ 'config/kivitendo.conf: Missing parameters in "authentication/database". Required parameters are "host", "db" and "user".' => '',
+ 'config/kivitendo.conf: Missing parameters in "authentication/ldap". Required parameters are "host", "attribute" and "base_dn".' => '',
'contact_list' => '',
'continue' => '',
'correction' => '',
'done' => '',
'down' => '',
'dunning_list' => '',
+ 'eBayImporter' => '',
'eMail Send?' => '',
'eMail?' => '',
'ea' => '',
'emailed to' => '',
'empty' => '',
+ 'every third month' => '',
'every time' => '',
'executed' => '',
+ 'failed' => '',
'female' => '',
'follow_up_list' => '',
'for' => '',
'for Period' => '',
+ 'for date' => '',
'found' => '',
'from (time)' => '',
'general_ledger_list' => '',
+ 'h' => '',
'history' => '',
'history search engine' => '',
'inactive' => '',
'kivitendo' => '',
'kivitendo Homepage' => '',
'kivitendo can fix these problems automatically.' => '',
- 'kivitendo has been switched to group-based access restrictions.' => '',
+ 'kivitendo has been extended to handle multiple clients within a single installation.' => '',
'kivitendo has found one or more problems in the general ledger.' => '',
'kivitendo is about to update the database [ #1 ].' => '',
'kivitendo is now able to manage warehouses instead of just tracking the amount of goods in your system.' => '',
- 'Knowledge' => '',
+ 'kivitendo needs to update the authentication database before you can proceed.' => '',
+ 'kivitendo v#1' => '',
+ 'kivitendo v#1 administration' => '',
+ 'kivitendo website (external)' => '',
+ 'kivitendo will then update the database automatically.' => '',
'lead deleted!' => '',
'lead saved!' => '',
'list' => '',
'list_of_payments' => '',
'list_of_receipts' => '',
'list_of_transactions' => '',
- 'loading' => '',
'logout' => '',
'male' => '',
'mark as paid' => '',
'not configured' => '',
'not delivered' => '',
'not executed' => '',
- 'not logged in' => '',
+ 'not running' => '',
+ 'not set' => '',
+ 'not shipped' => '',
'not transferred in yet' => '',
'not transferred out yet' => '',
'not yet executed' => '',
'number' => '',
'oe.pl::search called with unknown type' => '',
'on the same day' => '',
+ 'one-time execution' => '',
'only OB Transactions' => '',
'open' => '',
'order' => '',
'our vendor number at customer' => '',
+ 'parsing csv' => '',
+ 'part' => '',
'part_list' => '',
'percental' => '',
'periodic' => '',
'prev' => '',
'print' => '',
'proforma' => '',
- 'project_list' => '',
'purchase_delivery_order_list' => '',
'purchase_order' => '',
'purchase_order_list' => '',
'quarter' => '',
- 'quarterly' => '',
'quotation_list' => '',
'release_material' => '',
'reorder item' => '',
+ 'repeated execution' => '',
'report_generator_dispatch_to is not defined.' => '',
'report_generator_nextsub is not defined.' => '',
'request_quotation' => '',
'reset' => '',
'return_material' => '',
'rfq_list' => '',
+ 'running' => '',
'sales tax identification number' => '',
'sales_delivery_order_list' => '',
'sales_order' => '',
'sales_quotation' => '',
'saved' => '',
'saved!' => '',
+ 'saving data' => '',
'sent' => '',
'sent to printer' => '',
+ 'service' => '',
'service_list' => '',
'shipped' => '',
'singular first char' => '',
'soldtotal' => '',
'stock' => '',
'submit' => '',
+ 'succeeded' => '',
'tax_chartaccno' => '',
'tax_percent' => '',
'tax_rate' => '',
'tax_taxdescription' => '',
'tax_taxkey' => '',
+ 'taxincluded checked' => '',
+ 'taxkey 0 is already allocated.' => '',
+ 'taxkey 0 with taxrate 0 was created.' => '',
'taxnumber' => '',
'terminated' => '',
'to (date)' => '',
'transferred in' => '',
'transferred out' => '',
'trial_balance' => '',
+ 'unconfigured' => '',
'up' => '',
'use program settings' => '',
+ 'use user config' => '',
'used' => '',
'valid from' => '',
'vendor' => '',
'wrongformat' => '',
'yearly' => '',
'yes' => '',
+ 'you can find professional help.' => '',
};
1;
module=controller.pl
action=PaymentTerm/list
+[System--Delivery Terms]
+module=controller.pl
+action=DeliveryTerm/list
+
[System--Manage Custom Variables]
module=controller.pl
action=CustomVariableConfig/list
action=CsvImport/new
profile.type=projects
+[System--Import CSV--Orders]
+module=controller.pl
+action=CsvImport/new
+profile.type=orders
+
[System--Templates]
ACCESS=admin
module=menu.pl
if ($remaining_header_rows) {
$remaining_header_rows--;
} else {
- $row_cnt++;
+ $row_cnt++ unless $do_leftovers;
}
}# End of while(scalar(@{$data}) and $cur_y-$row_h > $bot_marg)
orderitems => { parts => 'part', trans => 'order', },
delivery_order_items => { parts => 'part' },
invoice => { parts => 'part' },
+ follow_ups => { 'employee_obj' => 'created_for' },
periodic_invoices_configs => { oe => 'order' },
);
--- /dev/null
+-- @tag: csv_import_reports_add_numheaders
+-- @description: Anzahl der Header-Zeilen in Csv Import Report speichern
+-- @depends: csv_import_report_cache
+-- @encoding: utf-8
+
+ALTER TABLE csv_import_reports ADD COLUMN numheaders INTEGER;
+UPDATE csv_import_reports SET numheaders = 1;
+ALTER TABLE csv_import_reports ALTER COLUMN numheaders SET NOT NULL;
--- /dev/null
+-- @tag: delivery_terms
+-- @description: Neue Tabelle und Spalten für Lieferbedingungen
+-- @depends: release_3_0_0
+-- @encoding: utf-8
+
+CREATE TABLE delivery_terms (
+ id integer NOT NULL DEFAULT nextval('id'),
+ description text,
+ description_long text,
+ sortkey integer NOT NULL,
+ itime timestamp DEFAULT now(),
+ mtime timestamp,
+
+ PRIMARY KEY (id)
+);
+
+CREATE TRIGGER mtime_delivery_terms
+ BEFORE UPDATE ON delivery_terms
+ FOR EACH ROW
+ EXECUTE PROCEDURE set_mtime();
+
+
+ALTER TABLE oe ADD COLUMN delivery_term_id integer;
+ALTER TABLE oe ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE delivery_orders ADD COLUMN delivery_term_id integer;
+ALTER TABLE delivery_orders ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE ar ADD COLUMN delivery_term_id integer;
+ALTER TABLE ar ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE ap ADD COLUMN delivery_term_id integer;
+ALTER TABLE ap ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE customer ADD COLUMN delivery_term_id integer;
+ALTER TABLE customer ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE vendor ADD COLUMN delivery_term_id integer;
+ALTER TABLE vendor ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
--- /dev/null
+-- @tag: gl_add_employee_foreign_key
+-- @description: Dialogbuchungen mit Bearbeiter verknüpfen
+-- @depends: release_3_0_0
+-- @ignore: 0
+ALTER TABLE gl ADD FOREIGN KEY (employee_id) REFERENCES employee(id);
-use Test::More tests => 47;
+use Test::More tests => 71;
use lib 't';
use utf8;
Support::TestSetup::login();
my $csv = SL::Helper::Csv->new(
- file => \"Kaffee\n",
- header => [ 'description' ],
- class => 'SL::DB::Part',
+ file => \"Kaffee\n", # " # make emacs happy
+ header => [ 'description' ],
+ profile => [{ class => 'SL::DB::Part', }],
);
isa_ok $csv->_csv, 'Text::CSV_XS';
$::myconfig{dateformat} = 'dd.mm.yyyy';
$csv = SL::Helper::Csv->new(
- file => \"Kaffee;0.12;12,2;1,5234\n",
- header => [ 'description', 'sellprice', 'lastcost_as_number', 'listprice' ],
- profile => { listprice => 'listprice_as_number' },
- class => 'SL::DB::Part',
+ file => \"Kaffee;0.12;12,2;1,5234\n", # " # make emacs happy
+ header => [ 'description', 'sellprice', 'lastcost_as_number', 'listprice' ],
+ profile => [{profile => { listprice => 'listprice_as_number' },
+ class => 'SL::DB::Part',}],
);
$csv->parse;
EOL
sep_char => ',',
quote_char => "'",
- profile => { listprice => 'listprice_as_number' },
- class => 'SL::DB::Part',
+ profile => [{profile => { listprice => 'listprice_as_number' },
+ class => 'SL::DB::Part',}]
);
$csv->parse;
is scalar @{ $csv->get_objects }, 1, 'auto header works';
;;description;sellprice;lastcost_as_number;
#####;Puppy;Kaffee;0.12;12,2;1,5234
EOL
- class => 'SL::DB::Part',
+ profile => [{class => 'SL::DB::Part'}],
);
$csv->parse;
is scalar @{ $csv->get_objects }, 1, 'bozo header doesn\'t blow things up';
Kaffee;;0.12;12,2;1,5234
Beer;1123245;0.12;12,2;1,5234
EOL
- class => 'SL::DB::Part',
+ profile => [{class => 'SL::DB::Part'}],
);
$csv->parse;
is scalar @{ $csv->get_objects }, 2, 'multiple objects work';
Beer;1123245;0.12;1.5234
EOL
numberformat => '1,000.00',
- class => 'SL::DB::Part',
+ profile => [{class => 'SL::DB::Part'}],
);
$csv->parse;
is $csv->get_objects->[0]->lastcost, '1221.52', 'formatnumber';
Kaffee;;0.12;1,221.52
Beer;1123245;0.12;1.5234
EOL
+# " # make emacs happy
numberformat => '1,000.00',
- class => 'SL::DB::Part',
+ profile => [{class => 'SL::DB::Part'}],
);
is $csv->parse, undef, 'broken csv header won\'t get parsed';
"Kaf"fee";;0.12;1,221.52
Beer;1123245;0.12;1.5234
EOL
+# " # make emacs happy
numberformat => '1,000.00',
- class => 'SL::DB::Part',
+ profile => [{class => 'SL::DB::Part'}],
);
is $csv->parse, undef, 'broken csv content won\'t get parsed';
is_deeply $csv->errors, [ '"Kaf"fee";;0.12;1,221.52'."\n", 2023, 'EIQ - QUO character not allowed', 5, 2 ], 'error';
EOL
numberformat => '1,000.00',
ignore_unknown_columns => 1,
- class => 'SL::DB::Part',
+ profile => [{class => 'SL::DB::Part'}],
);
$csv->parse;
is $csv->get_objects->[0]->lastcost, '1221.52', 'ignore_unkown_columns works';
Beer;1123245;0.12;1.5234;16 %
EOL
numberformat => '1,000.00',
- class => 'SL::DB::Part',
- profile => {
- buchungsgruppe => "buchungsgruppen.description",
- }
+ profile => [{
+ profile => {buchungsgruppe => "buchungsgruppen.description"},
+ class => 'SL::DB::Part',
+ }]
);
$csv->parse;
isa_ok $csv->get_objects->[0]->buchungsgruppe, 'SL::DB::Buchungsgruppe', 'deep dispatch auto vivify works';
Beer;1123245;0.12;1.5234;
EOL
numberformat => '1,000.00',
- class => 'SL::DB::Part',
- profile => {
- make_1 => "makemodels.0.make",
- model_1 => "makemodels.0.model",
- }
+ profile => [{
+ profile => {
+ make_1 => "makemodels.0.make",
+ model_1 => "makemodels.0.model",
+ },
+ class => 'SL::DB::Part',
+ }],
);
$csv->parse;
my @mm = $csv->get_objects->[0]->makemodel;
Kaffee;;0.12;1,221.52;213;Chair 0815;523;Table 15
EOL
numberformat => '1,000.00',
- class => 'SL::DB::Part',
- profile => {
- make_1 => "makemodels.0.make",
- model_1 => "makemodels.0.model",
- make_2 => "makemodels.1.make",
- model_2 => "makemodels.1.model",
- }
+ profile => [{
+ profile => {
+ make_1 => "makemodels.0.make",
+ model_1 => "makemodels.0.model",
+ make_2 => "makemodels.1.make",
+ model_2 => "makemodels.1.model",
+ },
+ class => 'SL::DB::Part',
+ }]
);
$csv->parse;
description;partnumber;sellprice;lastcost_as_number;buchungsgruppe;
EOL
numberformat => '1,000.00',
- class => 'SL::DB::Part',
- profile => {
- buchungsgruppe => "buchungsgruppen.1.description",
- }
+ profile => [{
+ profile => {buchungsgruppe => "buchungsgruppen.1.description"},
+ class => 'SL::DB::Part',
+ }]
);
is $csv->parse, undef, 'wrong profile gets rejected';
is_deeply $csv->errors, [ 'buchungsgruppen.1.description', undef, "Profile path error. Indexed relationship is not OneToMany around here: 'buchungsgruppen.1'", undef ,0 ], 'error indicates wrong header';
numberformat => '1,000.00',
ignore_unknown_columns => 1,
strict_profile => 1,
- class => 'SL::DB::Part',
- profile => {
- lastcost => 'lastcost_as_number',
- }
+ profile => [{
+ profile => {lastcost => 'lastcost_as_number'},
+ class => 'SL::DB::Part',
+ }]
);
$csv->parse;
is $csv->get_objects->[0]->lastcost, '1221.52', 'strict_profile with ignore';
EOL
numberformat => '1,000.00',
strict_profile => 1,
- class => 'SL::DB::Part',
- profile => {
- lastcost => 'lastcost_as_number',
- }
+ profile => [{
+ profile => {lastcost => 'lastcost_as_number'},
+ class => 'SL::DB::Part',
+ }]
);
$csv->parse;
#####
$csv = SL::Helper::Csv->new(
- file => \"Kaffee",
- header => [ 'description' ],
- class => 'SL::DB::Part',
+ file => \"Kaffee", # " # make emacs happy
+ header => [ 'description' ],
+ profile => [{class => 'SL::DB::Part'}],
);
$csv->parse;
is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'eol bug at the end of files';
#####
$csv = SL::Helper::Csv->new(
- file => \"Description\nKaffee",
- class => 'SL::DB::Part',
+ file => \"Description\nKaffee", # " # make emacs happy
case_insensitive_header => 1,
- profile => { description => 'description' },
+ profile => [{
+ profile => { description => 'description' },
+ class => 'SL::DB::Part'
+ }],
);
$csv->parse;
is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive header from csv works';
#####
$csv = SL::Helper::Csv->new(
- file => \"Kaffee",
- header => [ 'Description' ],
- class => 'SL::DB::Part',
+ file => \"Kaffee", # " # make emacs happy
+ header => [ 'Description' ],
case_insensitive_header => 1,
- profile => { description => 'description' },
+ profile => [{
+ profile => { description => 'description' },
+ class => 'SL::DB::Part'
+ }],
);
$csv->parse;
is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive header as param works';
#####
$csv = SL::Helper::Csv->new(
- file => \"\x{EF}\x{BB}\x{BF}description\nKaffee",
- class => 'SL::DB::Part',
+ file => \"\x{EF}\x{BB}\x{BF}description\nKaffee", # " # make emacs happy
+ profile => [{class => 'SL::DB::Part'}],
encoding => 'utf8',
);
$csv->parse;
#####
$csv = SL::Helper::Csv->new(
- file => \"Kaffee",
+ file => \"Kaffee", # " # make emacs happy
header => [ 'Description' ],
- class => 'SL::DB::Part',
+ profile => [{class => 'SL::DB::Part'}],
);
$csv->parse;
is_deeply $csv->get_data, undef, 'case insensitive header without flag ignores';
#####
$csv = SL::Helper::Csv->new(
- file => \"Kaffee",
+ file => \"Kaffee", # " # make emacs happy
header => [ 'foo' ],
- class => 'SL::DB::Part',
- profile => { foo => '' },
+ profile => [{
+ profile => { foo => '' },
+ class => 'SL::DB::Part',
+ }],
);
$csv->parse;
#####
$csv = SL::Helper::Csv->new(
- file => \"Kaffee",
+ file => \"Kaffee", # " # make emacs happy
header => [ 'foo' ],
- class => 'SL::DB::Part',
strict_profile => 1,
- profile => { foo => '' },
+ profile => [{
+ profile => { foo => '' },
+ class => 'SL::DB::Part',
+ }],
);
$csv->parse;
ok $csv->get_objects->[0], 'empty path gets ignored in object creation (strict profile)';
$csv = SL::Helper::Csv->new(
- file => \"Phil",
+ file => \"Phil", # " # make emacs happy
header => [ 'CVAR_grOUnDHog' ],
- class => 'SL::DB::Part',
strict_profile => 1,
case_insensitive_header => 1,
- profile => { cvar_Groundhog => '' },
+ profile => [{
+ profile => { cvar_Groundhog => '' },
+ class => 'SL::DB::Part',
+ }],
+
);
$csv->parse;
is_deeply $csv->get_data, [ { cvar_Groundhog => 'Phil' } ], 'using empty path to get cvars working';
ok $csv->get_objects->[0], '...and not destorying the objects';
+#####
+
+$csv = SL::Helper::Csv->new(
+ file => \"description\nKaffee", # " # make emacs happy
+);
+$csv->parse;
+is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'without profile and class works';
+
+#####
+$csv = SL::Helper::Csv->new(
+ file => \"Kaffee;1,50\nSchoke;0,89\n",
+ header => [
+ [ 'datatype', 'description', 'sellprice' ],
+ ],
+ profile => [
+ { profile => { sellprice => 'sellprice_as_number' },
+ class => 'SL::DB::Part',}
+ ],
+);
+
+ok $csv->_check_multiplexed, 'multiplex check works on not-multiplexed data';
+ok !$csv->is_multiplexed, 'not-multiplexed data is recognized';
+
+#####
+$csv = SL::Helper::Csv->new(
+ file => \"P;Kaffee;1,50\nC;Meier\n",
+ header => [
+ [ 'datatype', 'description', 'listprice' ],
+ [ 'datatype', 'name' ],
+ ],
+ profile => [
+ { profile => { listprice => 'listprice_as_number' },
+ class => 'SL::DB::Part',
+ row_ident => 'P' },
+ { class => 'SL::DB::Customer',
+ row_ident => 'C' }
+ ],
+);
+
+ok $csv->_check_multiplexed, 'multiplex check works on multiplexed data';
+ok $csv->is_multiplexed, 'multiplexed data is recognized';
+
+#####
+$csv = SL::Helper::Csv->new(
+ file => \"P;Kaffee;1,50\nC;Meier\n",
+ header => [
+ [ 'datatype', 'description', 'listprice' ],
+ [ 'datatype', 'name' ],
+ ],
+ profile => [
+ { profile => { listprice => 'listprice_as_number' },
+ class => 'SL::DB::Part', },
+ { class => 'SL::DB::Customer',
+ row_ident => 'C' }
+ ],
+);
+
+ok !$csv->_check_multiplexed, 'multiplex check works on multiplexed data and detects missing row_ident';
+
+#####
+$csv = SL::Helper::Csv->new(
+ file => \"P;Kaffee;1,50\nC;Meier\n",
+ header => [
+ [ 'datatype', 'description', 'listprice' ],
+ [ 'datatype', 'name' ],
+ ],
+ profile => [
+ { profile => { listprice => 'listprice_as_number' },
+ row_ident => 'P' },
+ { class => 'SL::DB::Customer',
+ row_ident => 'C' }
+ ],
+);
+
+ok !$csv->_check_multiplexed, 'multiplex check works on multiplexed data and detects missing class';
+
+#####
+$csv = SL::Helper::Csv->new(
+ file => \"P;Kaffee;1,50\nC;Meier\n", # " # make emacs happy
+ header => [
+ [ 'datatype', 'description', 'listprice' ],
+ ],
+ profile => [
+ { profile => { listprice => 'listprice_as_number' },
+ class => 'SL::DB::Part',
+ row_ident => 'P' },
+ { class => 'SL::DB::Customer',
+ row_ident => 'C' }
+ ],
+);
+
+ok !$csv->_check_multiplexed, 'multiplex check works on multiplexed data and detects missing header';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+ file => \"P;Kaffee;1,50\nC;Meier\n", # " # make emacs happy
+ header => [
+ [ 'datatype', 'description', 'listprice' ],
+ [ 'datatype', 'name' ],
+ ],
+ profile => [
+ { profile => { listprice => 'listprice_as_number' },
+ class => 'SL::DB::Part',
+ row_ident => 'P' },
+ { class => 'SL::DB::Customer',
+ row_ident => 'C' }
+ ],
+ ignore_unknown_columns => 1,
+);
+
+$csv->parse;
+is_deeply $csv->get_data,
+ [ { datatype => 'P', description => 'Kaffee', listprice => '1,50' }, { datatype => 'C', name => 'Meier' } ],
+ 'multiplex: simple case works';
+is scalar @{ $csv->get_objects }, 2, 'multiplex: multiple objects work';
+is $csv->get_objects->[0]->description, 'Kaffee', 'multiplex: first object';
+is $csv->get_objects->[1]->name, 'Meier', 'multiplex: second object';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+ file => \"datatype;description;listprice\ndatatype;name\nP;Kaffee;1,50\nC;Meier\n", # " # make emacs happy
+ profile => [
+ { profile => { listprice => 'listprice_as_number' },
+ class => 'SL::DB::Part',
+ row_ident => 'P' },
+ { class => 'SL::DB::Customer',
+ row_ident => 'C' }
+ ],
+ ignore_unknown_columns => 1,
+);
+
+$csv->parse;
+is scalar @{ $csv->get_objects }, 2, 'multiplex: auto header works';
+is $csv->get_objects->[0]->description, 'Kaffee', 'multiplex: auto header first object';
+is $csv->get_objects->[1]->name, 'Meier', 'multiplex: auto header second object';
+
+######
+
+$csv = SL::Helper::Csv->new(
+ file => \<<EOL,
+datatype;description
+"datatype;name
+P;Kaffee
+C;Meier
+P;Beer
+EOL
+# " # make emacs happy
+ profile => [
+ {class => 'SL::DB::Part', row_ident => 'P'},
+ {class => 'SL::DB::Customer', row_ident => 'C'},
+ ],
+ ignore_unknown_columns => 1,
+);
+is $csv->parse, undef, 'multiplex: broken csv header won\'t get parsed';
+
+######
+
+$csv = SL::Helper::Csv->new(
+ file => \<<EOL,
+datatype;description
+P;Kaffee
+C;Meier
+P;Beer
+EOL
+# " # make emacs happy
+ profile => [
+ {class => 'SL::DB::Part', row_ident => 'P'},
+ {class => 'SL::DB::Customer', row_ident => 'C'},
+ ],
+ header => [ [], ['name'] ],
+ ignore_unknown_columns => 1,
+);
+ok !$csv->_check_multiplexed, 'multiplex check detects empty header';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+ file => \"Datatype;Description\nDatatype;Name\nP;Kaffee\nC;Meier", # " # make emacs happy
+ case_insensitive_header => 1,
+ ignore_unknown_columns => 1,
+ profile => [
+ {
+ profile => { datatype => 'datatype', description => 'description' },
+ class => 'SL::DB::Part',
+ row_ident => 'P'
+ },
+ {
+ profile => { datatype => 'datatype', name => 'name' },
+ class => 'SL::DB::Customer',
+ row_ident => 'C'
+ }
+ ],
+);
+$csv->parse;
+is_deeply $csv->get_data, [ { datatype => 'P', description => 'Kaffee' },
+ { datatype => 'C', name => 'Meier'} ],
+ 'multiplex: case insensitive header from csv works';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+ file => \"P;Kaffee\nC;Meier", # " # make emacs happy
+ header => [[ 'Datatype', 'Description' ], [ 'Datatype', 'Name']],
+ case_insensitive_header => 1,
+ ignore_unknown_columns => 1,
+ profile => [
+ {
+ profile => { datatype => 'datatype', description => 'description' },
+ class => 'SL::DB::Part',
+ row_ident => 'P'
+ },
+ {
+ profile => { datatype => 'datatype', name => 'name' },
+ class => 'SL::DB::Customer',
+ row_ident => 'C'
+ }
+ ],
+);
+$csv->parse;
+is_deeply $csv->get_data, [ { datatype => 'P', description => 'Kaffee' },
+ { datatype => 'C', name => 'Meier' } ],
+ 'multiplex: case insensitive header as param works';
+
+
+#####
+
+$csv = SL::Helper::Csv->new(
+ file => \"P;Kaffee\nC;Meier", # " # make emacs happy
+ header => [[ 'Datatype', 'Description' ], [ 'Datatype', 'Name']],
+ profile => [
+ {
+ profile => { datatype => 'datatype', description => 'description' },
+ class => 'SL::DB::Part',
+ row_ident => 'P'
+ },
+ {
+ profile => { datatype => 'datatype', name => 'name' },
+ class => 'SL::DB::Customer',
+ row_ident => 'C'
+ }
+ ],
+);
+$csv->parse;
+is_deeply $csv->get_data, undef, 'multiplex: case insensitive header without flag ignores';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+ file => \<<EOL,
+P;Kaffee;lecker
+C;Meier;froh
+EOL
+# " # make emacs happy
+ header => [[ 'datatype', 'Afoo', 'Abar' ], [ 'datatype', 'Bfoo', 'Bbar']],
+ profile => [{
+ profile => { datatype => '', Afoo => '', Abar => '' },
+ class => 'SL::DB::Part',
+ row_ident => 'P'
+ },
+ {
+ profile => { datatype => '', Bfoo => '', Bbar => '' },
+ class => 'SL::DB::Customer',
+ row_ident => 'C'
+ }],
+);
+$csv->parse;
+
+is_deeply $csv->get_data,
+ [ { datatype => 'P', Afoo => 'Kaffee', Abar => 'lecker' }, { datatype => 'C', Bfoo => 'Meier', Bbar => 'froh' } ],
+ 'multiplex: empty path still gets parsed into data';
+ok $csv->get_objects->[0], 'multiplex: empty path gets ignored in object creation';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+ file => \<<EOL,
+P;Kaffee;lecker
+C;Meier;froh
+EOL
+# " # make emacs happy
+ header => [[ 'datatype', 'Afoo', 'Abar' ], [ 'datatype', 'Bfoo', 'Bbar']],
+ strict_profile => 1,
+ profile => [{
+ profile => { datatype => '', Afoo => '', Abar => '' },
+ class => 'SL::DB::Part',
+ row_ident => 'P'
+ },
+ {
+ profile => { datatype => '', Bfoo => '', Bbar => '' },
+ class => 'SL::DB::Customer',
+ row_ident => 'C'
+ }],
+);
+$csv->parse;
+
+is_deeply $csv->get_data,
+ [ { datatype => 'P', Afoo => 'Kaffee', Abar => 'lecker' }, { datatype => 'C', Bfoo => 'Meier', Bbar => 'froh' } ],
+ 'multiplex: empty path still gets parsed into data (strict profile)';
+ok $csv->get_objects->[0], 'multiplex: empty path gets ignored in object creation (strict profile)';
+
+#####
+
+
# vim: ft=perl
+# set emacs to perl mode
+# Local Variables:
+# mode: perl
+# End:
+
\newcommand{\weiteraufnaechsterseite} {weiter auf der nächsten Seite ...}
\newcommand{\zahlung} {Zahlungsbedingungen:}
+\newcommand{\lieferung} {Lieferbedingungen:}
\newcommand{\textTelefon} {Tel.:}
\newcommand{\textFax} {Fax:}
\newcommand{\weiteraufnaechsterseite} {to be continued on next page ..}
\newcommand{\zahlung} {Payment terms:}
+\newcommand{\lieferung} {Delivery terms:}
\newcommand{\textTelefon} {Tel.:}
\newcommand{\textFax} {Fax:}
\zahlung ~<%payment_terms%>\\
<%end payment_terms%>
+<%if delivery_term%>
+ \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
<%if ustid%>\ihreustid ~<%ustid%>.\\<%end if%>
\ifthenelse{\equal{<%taxzone_id%>}{1}}
\vspace{5mm}
<%end if%>
+<%if delivery_term%>
+ \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
\end{document}
\vspace{5mm}
<%end if%>
+<%if delivery_term%>
+ \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
\gruesse \\ \\ \\
<%employee_name%>
\vspace{5mm}
<%end if%>
+<%if delivery_term%>
+ \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
<%if reqdate%>
\anfrageBenoetigtBis~<%reqdate%>.
<%end if%>
\vspace{5mm}
<%end if%>
+<%if delivery_term%>
+ \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
\end{document}
\vspace{5mm}
<%end if%>
+<%if delivery_term%>
+ \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
<%if reqdate%>
\lieferungErfolgtAm ~<%reqdate%>. \\
<%end if%>
\vspace{5mm}
<%end if%>
+<%if delivery_term%>
+ \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
\angebotdanke\\
<%if reqdate%>
\angebotgueltig~<%reqdate%>.
<tr>
<td align="right">[% 'Discount' | $T8 %]</td>
- <td>[% HTML.escape(discount) %]%</td>
+ <td>[% HTML.escape(discount_as_percent) %]%</td>
</tr>
[% IF is_customer %]
<td>[% HTML.escape(payment_terms) %]</td>
</tr>
+ <tr>
+ <td align="right">[% 'Delivery Terms' | $T8 %]</td>
+ <td>[% HTML.escape(delivery_terms) %]</td>
+ </tr>
+
<tr>
<td align="right">[% 'Tax Number' | $T8 %]</td>
<td>[% HTML.escape(taxnumber) %]</td>
--- /dev/null
+[% USE LxERP %]
+[% USE L %]
+<tr>
+ <th align="right">[%- LxERP.t8('Order/Item row name') %]:</th>
+ <td colspan="10">
+ [% L.input_tag('settings.order_column', SELF.profile.get('order_column'), size => "10") %]
+ [% L.input_tag('settings.item_column', SELF.profile.get('item_column'), size => "10") %]
+ </td>
+</tr>
+
+<tr>
+ <th align="right">[%- LxERP.t8('Maximal amount difference') %]:</th>
+ <td colspan="10">
+ [% L.input_tag('settings.max_amount_diff', LxERP.format_amount(SELF.profile.get('max_amount_diff')), size => "5") %]
+ </td>
+</tr>
<div class="help_toggle" style="display:none">
<p><a href="#" onClick="javascript:$('.help_toggle').toggle()">[% LxERP.t8("Hide help text") %]</a></p>
- <table>
- <tr class="listheading">
- <th>[%- LxERP.t8('Column name') %]</th>
- <th>[%- LxERP.t8('Meaning') %]</th>
- </tr>
-
- [%- FOREACH row = SELF.displayable_columns %]
- <tr class="listrow[% loop.count % 2 %]">
- <td>[%- HTML.escape(row.name) %]</td>
- <td>[%- HTML.escape(row.description) %]</td>
- </tr>
- [%- END %]
- </table>
+ [%- IF SELF.worker.is_multiplexed %]
+ <table>
+ <tr class="listheading">
+ [%- FOREACH p = SELF.worker.profile %]
+ <th>[%- p.row_ident %]</th>
+ [%- END %]
+ </tr>
+ <tr class="listrow[% loop.count % 2 %]">
+ [%- FOREACH p = SELF.worker.profile %]
+ [% SET ri = p.row_ident %]
+ <td>
+ <table>
+ <tr class="listheading">
+ <th>[%- LxERP.t8('Column name') %]</th>
+ <th>[%- LxERP.t8('Meaning') %]</th>
+ </tr>
+
+ [%- FOREACH row = SELF.displayable_columns.$ri %]
+ <tr class="listrow[% loop.count % 2 %]">
+ <td>[%- HTML.escape(row.name) %]</td>
+ <td>[%- HTML.escape(row.description) %]</td>
+ </tr>
+ [%- END %]
+ </table>
+ </td>
+ [%- END %]
+ </tr>
+ </table>
+ [%- ELSE %]
+ <table>
+ <tr class="listheading">
+ <th>[%- LxERP.t8('Column name') %]</th>
+ <th>[%- LxERP.t8('Meaning') %]</th>
+ </tr>
+
+ [%- FOREACH row = SELF.displayable_columns %]
+ <tr class="listrow[% loop.count % 2 %]">
+ <td>[%- HTML.escape(row.name) %]</td>
+ <td>[%- HTML.escape(row.description) %]</td>
+ </tr>
+ [%- END %]
+ </table>
+ [%- END %]
[%- IF SELF.type == 'contacts' %]
<p>
[% LxERP.t8('The items are imported accoring do their number "X" regardless of the column order inside the file.') %]
[% LxERP.t8('The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.') %]
</p>
+
+[%- ELSIF SELF.type == 'orders' %]
+ <p>
+ [1]:
+ [% LxERP.t8('The column "datatype" must be present and must be the first column. The values must be the row names (see settings) for order and item data respectively.') %]
+ </p>
+ <p>
+ [2]:
+ [%- LxERP.t8('Amount and net amount are calculated by kivitendo. "verify_amount" and "verify_netamount" can be used for sanity checks.') %]<br>
+ [%- LxERP.t8('If amounts differ more than "Maximal amount difference" (see settings), this item is marked as invalid.') %]<br>
+ </p>
[%- END %]
<p>
[%- INCLUDE 'csv_import/_form_customers_vendors.html' %]
[%- ELSIF SELF.type == 'contacts' %]
[%- INCLUDE 'csv_import/_form_contacts.html' %]
+[%- ELSIF SELF.type == 'orders' %]
+ [%- INCLUDE 'csv_import/_form_orders.html' %]
[%- END %]
<tr>
[%- PROCESS 'common/paginate.html' pages=SELF.pages, base_url = SELF.base_url %]
<table>
[%- FOREACH rownum = SELF.display_rows %]
- [%- IF loop.first %]
+ [%- IF rownum < SELF.report_numheaders %]
<tr class="listheading">
[%- FOREACH value = SELF.report_rows.${rownum} %]
<th>[% value | html %]</th>
[%- END %]
<td>
[%- FOREACH error = csv_import_report_errors %][%- error | html %][% UNLESS loop.last %]<br>[%- END %][%- END %]
- [%- FOREACH info = SELF.report_status.${rownum}.information %][% IF !loop.first || csv_import_report_errors.size %]<br>[%- END %][%- info | html %][%- END %]
+ [%- FOREACH info = SELF.report_status.${rownum}.information %][% IF rownum >= SELF.report_numheaders || csv_import_report_errors.size %]<br>[%- END %][%- info | html %][%- END %]
</td>
</tr>
[%- END %]
+++ /dev/null
-[% USE L %][% USE HTML %][% USE T8 %][% USE LxERP %]
- <table>
- <input type="hidden" name="cp_id" value="[% HTML.escape(cp_id) %]">
- <tr>
- <th align="left">[% 'Contacts' | $T8 %]</th>
- <td>
- [%- L.select_tag('cp_id', CONTACTS, default = cp_id, with_empty = 1, empty_title = LxERP.t8('New contact'), value_key = 'cp_id', title_sub = \contacts_label,
- onchange = "\$('#contacts').load('ct.pl?action=get_contact&id=' + \$('#cvid').val() + '&db=' + \$('#db').val() + '&cp_id=' + \$('#cp_id').val())") %]
- </td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Gender' | $T8 %]</th>
- <td>
- <select id="cp_gender" name="cp_gender">
- <option value="m"[% IF cp_gender == 'm' %] selected[% END %]>[% 'male' | $T8 %]</option>
- <option value="f"[% IF cp_gender == 'f' %] selected[% END %]>[% 'female' | $T8 %]</option>
- </select>
- </td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Title' | $T8 %]</th>
- <td>
- <input id="cp_title" name="cp_title" size="40" maxlength="75" value="[% HTML.escape(cp_title) %]">
- [% L.select_tag('selected_cp_title', TITLES, with_empty = 1) %]
- </td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Department' | $T8 %]</th>
- <td>
- <input id="cp_abteilung" name="cp_abteilung" size="40" value="[% HTML.escape(cp_abteilung) %]">
- [% L.select_tag('selected_cp_abteilung', DEPARTMENT, with_empty = 1) %]
- </td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Function/position' | $T8 %]</th>
- <td>[% L.input_tag('cp_position', cp_position, size=40, maxlength=75) %]</td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Given Name' | $T8 %]</th>
- <td><input id="cp_givenname" name="cp_givenname" size="40" maxlength="75" value="[% HTML.escape(cp_givenname) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Name' | $T8 %]</th>
- <td><input id="cp_name" name="cp_name" size="40" maxlength="75" value="[% HTML.escape(cp_name) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'E-mail' | $T8 %]</th>
- <td><input id="cp_email" name="cp_email" size="40" value="[% HTML.escape(cp_email) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Phone1' | $T8 %]</th>
- <td><input id="cp_phone1" name="cp_phone1" size="40" maxlength="75" value="[% HTML.escape(cp_phone1) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Phone2' | $T8 %]</th>
- <td><input id="cp_phone2" name="cp_phone2" size="40" maxlength="75" value="[% HTML.escape(cp_phone2) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Fax' | $T8 %]</th>
- <td><input id="cp_fax" name="cp_fax" size="40" value="[% HTML.escape(cp_fax) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Mobile1' | $T8 %]</th>
- <td><input id="cp_mobile1" name="cp_mobile1" size="40" value="[% HTML.escape(cp_mobile1) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Mobile2' | $T8 %]</th>
- <td><input id="cp_mobile2" name="cp_mobile2" size="40" value="[% HTML.escape(cp_mobile2) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Sat. Phone' | $T8 %]</th>
- <td><input id="cp_satphone" name="cp_satphone" size="40" value="[% HTML.escape(cp_satphone) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Sat. Fax' | $T8 %]</th>
- <td><input id="cp_satfax" name="cp_satfax" size="40" value="[% HTML.escape(cp_satfax) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Project' | $T8 %]</th>
- <td><input id="cp_project" name="cp_project" size="40" value="[% HTML.escape(cp_project) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Street' | $T8 %]</th>
- <td><input id="cp_street" name="cp_street" size="40" maxlength="75" value="[% HTML.escape(cp_street) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Zip, City' | $T8 %]</th>
- <td>
- <input id="cp_zipcode" name="cp_zipcode" size="5" maxlength="10" value="[% HTML.escape(cp_zipcode) %]">
- <input id="cp_city" name="cp_city" size="25" maxlength="75" value="[% HTML.escape(cp_city) %]">
- </td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Private Phone' | $T8 %]</th>
- <td><input id="cp_privatphone" name="cp_privatphone" size="40" value="[% HTML.escape(cp_privatphone) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Private E-mail' | $T8 %]</th>
- <td><input id="cp_privatemail" name="cp_privatemail" size="40" value="[% HTML.escape(cp_privatemail) %]"></td>
- </tr>
-
- <tr>
- <th align="left" nowrap>[% 'Birthday' | $T8 %]</th>
- <td>
- [% L.date_tag('cp_birthday', cp_birthday) %]
- </td>
- </tr>
-
- [% IF CUSTOM_VARIABLES.Contacts.size %]
- <tr>
- <td colspan="2"><hr></td>
- </tr>
-
- [%- FOREACH var = CUSTOM_VARIABLES.Contacts %]
- <tr>
- <th align="left" valign="top" nowrap>[% HTML.escape(var.description) %]</th>
- <td valign="top">[% var.HTML_CODE %]</td>
- </tr>
- [%- END %]
- [%- END %]
-
- </table>
-
- [% IF cp_id %]
- <input type="button" id="delete_contact" onclick="submitInputButton(this);" name="action" value="[% 'Delete Contact' | $T8 %]">
- [% END %]
+++ /dev/null
-[% USE L %][% USE HTML %][% USE T8 %][% USE LxERP %]
- <table width="100%" id="shipto_table">
- <tr>
- <th align="right">[% 'Shipping Address' | $T8 %]</th>
- <td>
- [% L.select_tag('shipto_id', SHIPTO, default = shipto_id, value_key = 'shipto_id', title_sub = \shipto_label, with_empty = 1, empty_title = LxERP.t8('New shipto'),
- onchange = "\$('#shipto').load('ct.pl?action=get_shipto&id=' + \$('#cvid').val() + '&db=' + \$('#db').val() + '&shipto_id=' + this.value)") %]
- </td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Name' | $T8 %]</th>
- <td><input id="shiptoname" name="shiptoname" size="35" maxlength="75" value="[% HTML.escape(shiptoname) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Abteilung' | $T8 %]</th>
- <td>
- <input id="shiptodepartment_1" name="shiptodepartment_1" size="16" maxlength="75" value="[% HTML.escape(shiptodepartment_1) %]">
- <input id="shiptodepartment_2" name="shiptodepartment_2" size="16" maxlength="75" value="[% HTML.escape(shiptodepartment_2) %]">
- </td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Street' | $T8 %]</th>
- <td><input id="shiptostreet" name="shiptostreet" size="35" maxlength="75" value="[% HTML.escape(shiptostreet) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Zipcode' | $T8 %]/[% 'City' | $T8 %]</th>
- <td>
- <input id="shiptozipcode" name="shiptozipcode" size="5" maxlength="75" value="[% HTML.escape(shiptozipcode) %]">
- <input id="shiptocity" name="shiptocity" size="30" maxlength="75" value="[% HTML.escape(shiptocity) %]">
- </td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Country' | $T8 %]</th>
- <td><input id="shiptocountry" name="shiptocountry" size="35" maxlength="75" value="[% HTML.escape(shiptocountry) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Contact' | $T8 %]</th>
- <td><input id="shiptocontact" name="shiptocontact" size="30" maxlength="75" value="[% HTML.escape(shiptocontact) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Phone' | $T8 %]</th>
- <td><input id="shiptophone" name="shiptophone" size="30" maxlength="30" value="[% HTML.escape(shiptophone) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Fax' | $T8 %]</th>
- <td><input id="shiptofax" name="shiptofax" size="30" maxlength="30" value="[% HTML.escape(shiptofax) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'E-mail' | $T8 %]</th>
- <td><input id="shiptoemail" name="shiptoemail" size="45" value="[% HTML.escape(shiptoemail) %]"></td>
- </tr>
- </table>
-
-[%- IF shipto_id %]
- <input type="submit" id="delete_shipto" name="action" value="[% 'Delete Shipto' | $T8 %]">
-[%- END %]
+++ /dev/null
-[%- USE HTML %]
-[%- FOREACH vc = CT %]
-[%- IF loop.count < limit %]
-[% vc.$column %]
-[%- END %]
-[%- END %]
+++ /dev/null
-[%- USE T8 %]
-[% USE HTML %]
-[% USE LxERP %]
-[% USE L %]
-<input name="id" type="hidden" id="cvid" value="[% HTML.escape(id) %]">
-<input name="business_save" type="hidden" value="[% HTML.escape(selectbusiness) %]">
-<input name="title_save" type="hidden" value="[% HTML.escape(title) %]">
-
-<input type="hidden" name="callback" value="[% HTML.escape(callback) %]">
-<input type="hidden" name="db" id="db" value="[% HTML.escape(db) %]">
-
-<br>
- <input class="submit" type="submit" name="action" accesskey="s" value="[% 'Save' | $T8 %]" onclick="return check_taxzone_and_ustid()">
- <input class="submit" type="submit" name="action" accesskey="s" value="[% 'Save and Close' | $T8 %]" onclick="return check_taxzone_and_ustid()">
- [%- IF is_customer %]
- <input class="submit" type="submit" name="action" value="[% 'Save and AR Transaction' | $T8 %]" onclick="return check_taxzone_and_ustid()">
- [%- ELSE %]
- <input class="submit" type="submit" name="action" value="[% 'Save and AP Transaction' | $T8 %]" onclick="return check_taxzone_and_ustid()">
- [%- END %]
- <input class="submit" type="submit" name="action" value="[% 'Save and Invoice' | $T8 %]" onclick="return check_taxzone_and_ustid()">
- <input class="submit" type="submit" name="action" value="[% 'Save and Order' | $T8 %]" onclick="return check_taxzone_and_ustid()">
- [%- IF is_customer %]
- <input class="submit" type="submit" name="action" value="[% 'Save and Quotation' | $T8 %]" onclick="return check_taxzone_and_ustid()">
- [%- ELSE %]
- <input class="submit" type="submit" name="action" value="[% 'Save and RFQ' | $T8 %]" onclick="return check_taxzone_and_ustid()">
- [%- END %]
-[%- IF id AND is_orphaned %]
- [% L.submit_tag('action', LxERP.t8('Delete'), id => 'action_delete', confirm => LxERP.t8('Do you really want to delete this object?')) %]
-[%- END %]
-[%- IF id %]
- <input type="button" class="submit" onclick="set_history_window([% HTML.escape(id) %]);" name="history" id="history" value="[% 'history' | $T8 %]">
-[%- END %]
-
- </form>
-
- <script type="text/javascript">
- <!--
- function enable_delete_shipto(used) { var s=document.getElementById('delete_shipto'); if (s) s.disabled = (used > 0 ? true : false); }
- function enable_delete_contact(used){ var s=document.getElementById('delete_contact'); if (s) s.disabled = (used > 0 ? true : false); }
-
- function submitInputButton(button)
- {
- var hidden = document.createElement("input");
- hidden.setAttribute("type", "hidden");
-
- if( button.hasAttribute("name") )
- hidden.setAttribute("name", button.getAttribute("name"));
-
- if( button.hasAttribute("value") )
- hidden.setAttribute("value", button.getAttribute("value"));
-
-
- button.form.appendChild(hidden);
-
- button.disabled = true;
-
- button.form.submit();
- }
-
- function check_taxzone_and_ustid() {
- if (($('#taxzone_id').val() == '1') && ($('#ustid').val() == '')) {
- alert('[% LxERP.t8('Please enter the sales tax identification number.') %]');
- return false;
- }
- return true;
- }
-
- -->
- </script>
+++ /dev/null
-[%- USE T8 %]
-[% USE HTML %][% USE LxERP %]
-[% USE L %]
-
- <h1>[% title %]</h1>
-
- [% PROCESS 'common/flash.html' %]
-
- <form method="post" name="ct" action="ct.pl" >
-
- <div class="tabwidget">
- <ul>
- <li><a href="#billing">[% 'Billing Address' | $T8 %]</a></li>
- <li><a href="#shipto">[% 'Shipping Address' | $T8 %]</a></li>
- <li><a href="#contacts">[% 'Contacts' | $T8 %]</a></li>
-[%- IF id %]
- <li><a href="#deliveries">[% 'Supplies' | $T8 %]</a></li>
-[%- END %]
- <li><a href="#vcnotes">[% 'Notes' | $T8 %]</a></li>
-[%- IF CUSTOM_VARIABLES.CT.size %]
- <li><a href="#custom_variables">[% 'Custom Variables' | $T8 %]</a></li>
-[%- END %]
- </ul>
-
- <div id="billing">
-
- <table width="100%">
- <tr height="5"></tr>
- [% IF INSTANCE_CONF.get_vertreter %]
- <tr>
- <th align="right">[% IF is_customer %][% 'Type of Customer' | $T8 %][%- ELSE %][% 'Type of Vendor' | $T8 %][%- END %]</th>
- <td>
- [%- INCLUDE generic/multibox.html
- name = 'business',
- DATA = all_business,
- show_empty = 1,
- id_key = 'id',
- label_key = 'description',
- -%]
- </td>
- </tr>
- [%- IF id %]
- <tr>
- <th align="right">[% 'Representative' | $T8 %]</th>
- <td>
- <input type="hidden" name="salesman_id" value="[%- HTML.escape(salesman_id) %]"%>
- [%- HTML.escape(salesman) %]
- </td>
- </tr>
- [%- END %]
- <tr>
- <th align="right">
- [%- IF !id %]
- [% 'Representative' | $T8 %]
- [%- ELSE %]
- [% 'Change representative to' | $T8 %]
- [%- END %]
- </th>
- <td>
- [%- INCLUDE generic/multibox.html
- name = 'new_salesman_id',
- DATA = ALL_SALESMAN_CUSTOMERS,
- id_key = 'id',
- label_key = 'name',
- show_empty = id,
- -%]
- </td>
- </tr>
- [%- END %]
- <tr>
- [%- IF is_customer %]
- <th align="right" nowrap>[% 'Customer Number' | $T8 %]</th>
- <td><input name="customernumber" size="35" value="[% HTML.escape(customernumber) %]"></td>
- [%- ELSE %]
- <th align="right" nowrap>[% 'Vendor Number' | $T8 %]</th>
- <td><input name="vendornumber" size="35" value="[% HTML.escape(vendornumber) %]"></td>
- [%- END %]
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Greeting' | $T8 %]</th>
- <td>
- <input id="greeting" name="greeting" size="30" value="[% HTML.escape(greeting) %]" class="initial_focus">
- [%- INCLUDE generic/multibox.html
- name = 'selected_company_greeting',
- DATA = MB_COMPANY_GREETINGS,
- show_empty = 1,
- id_key = 'id',
- label_key = 'description',
- -%]
- </td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% IF is_customer %][% 'Customer Name' | $T8 %][%- ELSE %][% 'Vendor Name' | $T8 %][%- END %]</th>
- <td><input name="name" size="35" maxlength="75" value="[% HTML.escape(name) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Department' | $T8 %]</th>
- <td>
- <input name="department_1" size="16" maxlength="75" value="[% HTML.escape(department_1) %]">
- <input name="department_2" size="16" maxlength="75" value="[% HTML.escape(department_2) %]">
- </td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Street' | $T8 %]</th>
- <td>
- <input name="street" size="35" maxlength="75" value="[% HTML.escape(street) %]">
- <a
- href="#"
- onclick="window.open('https://maps.google.com/maps?q='+ encodeURIComponent($('#billing input[name=street]').val() +', '+ $('#billing input[name=zipcode]').val() +' '+ $('#billing input[name=city]').val() +', '+ $('#billing input[name=country]').val()), '_blank'); window.focus();"
- title="[% 'Map' | $T8 %]"
- >
- <img src="image/map.png" alt="[% 'Map' | $T8 %]" />
- </a>
- </td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Zipcode' | $T8 %]/[% 'City' | $T8 %]</th>
- <td>
- <input name="zipcode" size="5" maxlength="10" value="[% HTML.escape(zipcode) %]">
- <input name="city" size="30" maxlength="75" value="[% HTML.escape(city) %]">
- </td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Country' | $T8 %]</th>
- <td><input name="country" size="35" maxlength="75" value="[% HTML.escape(country) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Contact' | $T8 %]</th>
- <td><input name="contact" size="28" maxlength="75" value="[% HTML.escape(contact) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Phone' | $T8 %]</th>
- <td><input name="phone" size="30" value="[% HTML.escape(phone) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Fax' | $T8 %]</th>
- <td><input name="fax" size="30" maxlength="30" value="[% HTML.escape(fax) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'E-mail' | $T8 %]</th>
- <td><input name="email" size="45" value="[% HTML.escape(email) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Cc E-mail' | $T8 %]</th>
- <td><input name="cc" size="45" value="[% HTML.escape(cc) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Bcc E-mail' | $T8 %]</th>
- <td><input name="bcc" size="45" value="[% HTML.escape(bcc) %]"></td>
- </tr>
-
-
- <tr>
- <th align="right" nowrap>
- [% IF homepage %]<a href="[% HTML.escape(homepage) %]" title="[% 'Open this Website' | $T8 %]" target="_blank">[% 'Homepage' | $T8 %]</a>
- [% ELSE %][% 'Homepage' | $T8 %]
- [% END %]
- </th>
- <td><input name="homepage" size="45" title="[% 'Example: http://kivitendo.de' | $T8 %]" value="[% HTML.escape(homepage) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Username' | $T8 %]</th>
- <td><input name="username" size="45" value="[% HTML.escape(username) %]"></td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'Password' | $T8 %]</th>
- <td><input name="user_password" size="45" value="[% HTML.escape(user_password) %]"></td>
- </tr>
- </table>
-
- <table>
- <tr>
- <th align="right">[% 'Credit Limit' | $T8 %]</th>
- <td><input name="creditlimit" size="9" value="[% LxERP.format_amount(creditlimit, 0) %]"></td>
- <input type="hidden" name="terms" value="[% HTML.escape(terms) %]">
- <th align="right">[% 'Payment Terms' | $T8 %]</th>
- <td>
- [%- INCLUDE generic/multibox.html
- name = 'payment_id',
- DATA = payment_terms,
- show_empty = 1,
- id_key = 'id',
- label_key = 'description',
- -%]
- </td>
-
- <th align="right">[% 'Discount' | $T8 %]</th>
- <td><input name="discount" size="4" value="[% LxERP.format_amount(discount) %]"> %</td>
- </tr>
-
- <tr>
- <th align="right">[% 'Tax Number / SSN' | $T8 %]</th>
- <td><input name="taxnumber" size="20" value="[% HTML.escape(taxnumber) %]"></td>
- <!-- Anm.: R&B 15.11.2008 VAT Reg No ist Ust-ID in GB, aber generell sollte es laut Richardson die sales tax id sein -->
- <th align="right">[% 'sales tax identification number' | $T8 %]</th>
- <td>[% L.input_tag('ustid', ustid, maxlength=14, size=30) %]</td>
- [%- IF is_customer %]
- <th align="right">[% 'our vendor number at customer' | $T8 %]</th>
- <td>[% L.input_tag('c_vendor_id', c_vendor_id, size=30) %]</td>
- [%- ELSE %]
- <th align="right">[% 'Customer Number' | $T8 %]</th>
- <td>[% L.input_tag('v_customer_id', v_customer_id, size=30) %]</td>
- [%- END %]
- </tr>
-
- <tr>
- <th align="right">[% 'Account Number' | $T8 %]</th>
- <td>[% L.input_tag('account_number', account_number, size=30) %]</td>
- <th align="right">[% 'Bank Code Number' | $T8 %]</th>
- <td>[% L.input_tag('bank_code', bank_code, size=30) %]</td>
- <th align="right">[% 'Bank' | $T8 %]</th>
- <td>[% L.input_tag('bank', bank, size=30) %]</td>
- </tr>
-
- <tr>
- <th align="right">[% 'IBAN' | $T8 %]</th>
- <td>[% L.input_tag('iban', iban, maxlength=100, size=30) %]</td>
- <th align="right">[% 'BIC' | $T8 %]</th>
- <td>[% L.input_tag('bic', bic, maxlength=100, size=30) %]</td>
- [%- IF ALL_CURRENCIES.size %]
- <th align="right">[% 'Currency' | $T8 %]</th>
- <td>[% L.select_tag('currency', ALL_CURRENCIES, default = currency) %]</td>
- [%- END %]
- </tr>
-
- <tr>
- [% UNLESS INSTANCE_CONF.get_vertreter %]
- <th align="right">[% IF is_customer %][% 'Type of Customer' | $T8 %][% ELSE %][% 'Type of Vendor' | $T8 %][%- END %]</th>
- <td>
- [%- INCLUDE generic/multibox.html
- name = 'business',
- DATA = all_business,
- show_empty = 1,
- id_key = 'id',
- label_key = 'description',
- -%]
- </td>
- [%- END %]
- <th align="right">[% 'Language' | $T8 %]</th>
- <td>
- [%- INCLUDE generic/multibox.html
- name = 'language_id',
- default = default_language_id,
- DATA = languages,
- show_empty = 1,
- id_key = 'id',
- label_key = 'description',
- -%]
- </td>
-
- [%- IF is_customer %]
- <th align="right">[% 'Preisklasse' | $T8 %]</th>
- <td>
- [%- INCLUDE generic/multibox.html
- name = 'klass',
- DATA = all_pricegroup,
- show_empty = 1,
- id_key = 'id',
- label_key = 'pricegroup',
- -%]
- </td>
- [%- END %]
- </tr>
-
- <tr>
- <td align="right"><label for="obsolete">[% 'Obsolete' | $T8 %]</label></td>
- <td><input name="obsolete" id="obsolete" class="checkbox" type="checkbox" value="1" [% IF obsolete %]checked[% END %]></td>
- <td align="right"><label for="direct_debit">[% 'direct debit' | $T8 %]</label></td>
- <td><input name="direct_debit" id="direct_debit" class="checkbox" type="checkbox" value="1" [% IF direct_debit %]checked[% END %]></td>
- </tr>
-
- <tr>
- <th align="right">[% 'Steuersatz' | $T8 %]</th>
- <td>
- [%- INCLUDE generic/multibox.html
- name = 'taxzone_id',
- id = 'taxzone_id',
- DATA = ALL_TAXZONES,
- show_empty = 0,
- id_key = 'id',
- label_key = 'description',
- -%]
- </td>
- [%- IF is_customer && !INSTANCE_CONF.get_vertreter %]
- <th align="right">[% 'Salesman' | $T8 %]</th>
- <td>[% L.select_tag('salesman_id', ALL_SALESMEN, default = salesman_id, with_empty = 1, title_key = 'safe_name') %]</td>
- [%- END %]
-
- <td>[% 'taxincluded checked' | $T8 %]</td>
- <td>[% L.select_tag('taxincluded_checked', [[undef, LxERP.t8('use user config')], ['1', LxERP.t8('Yes')], ['0', LxERP.t8('No')]], default = taxincluded_checked) %]</td>
- </tr>
- </table>
-
- <table>
- <tr>
- <th align="left" nowrap>[% 'Internal Notes' | $T8 %]</th>
- </tr>
-
- <tr>
- <td><textarea name="notes" rows="3" cols="60" wrap="soft">[% HTML.escape(notes) %]</textarea></td>
- </tr>
- </table>
- </div>
-
- <div id="shipto">
- [% INCLUDE 'ct/_shipto.html' %]
- </div>
-
- <div id="contacts">
- [% INCLUDE 'ct/_contact.html' %]
- </div>
-
- <div id="deliveries">
- <table>
- <tr>
- <th align="right">[% 'Shipping Address' | $T8 %]</th>
- <td colspan="3">
- [% L.select_tag('delivery_id', SHIPTO_ALL, value_key = 'shipto_id', title_sub = \shipto_label, with_empty = 1,
- onchange = "\$('#delivery').load('ct.pl?action=get_delivery&id=' + \$('#cvid').val() + '&db=' + \$('#db').val() + '&shipto_id=' + this.value)") %]
- </td>
- </tr>
-
- <tr>
- <th align="right" nowrap>[% 'From' | $T8 %]</th>
- <td>
- [% L.date_tag('from',
- from,
- onchange => "\$('#delivery').load('ct.pl?action=get_delivery&shipto_id='+ \$('#delivery_id').val() +'&from='+ \$('#from').val() +'&to='+ \$('#to').val() +'&id='+ \$('#cvid').val() +'&db='+ \$('#db').val())")
- %]
- </td>
- <th align="right" nowrap>[% 'To (time)' | $T8 %]</th>
- <td>
- [% L.date_tag('to',
- to,
- onchange => "\$('#delivery').load('ct.pl?action=get_delivery&shipto_id='+ \$('#delivery_id').val() +'&from='+ \$('#from').val() +'&to='+ \$('#to').val() +'&id='+ \$('#cvid').val() +'&db='+ \$('#db').val())")
- %]
- </td>
- </tr>
-
- <tr>
- <td colspan="4">
- <div id="delivery">
- </div>
- </td>
- </tr>
- </table>
- </div>
-
- <div id="vcnotes">
-
- [%- IF NOTES && NOTES.size %]
- <p>
- <table>
- <tr>
- <th class="listheading">[% 'Delete' | $T8 %]</th>
- <th class="listheading">[% 'Subject' | $T8 %]</th>
- <th class="listheading">[% 'Created on' | $T8 %]</th>
- <th class="listheading">[% 'Created by' | $T8 %]</th>
- <th class="listheading">[% 'Follow-Up Date' | $T8 %]</th>
- <th class="listheading">[% 'Follow-Up for' | $T8 %]</th>
- <th class="listheading">[% 'Follow-Up done' | $T8 %]</th>
- </tr>
-
- [%- FOREACH row = NOTES %]
- <tr class="listrow[% loop.count % 2 %]">
- <input type="hidden" name="NOTE_id_[% loop.count %]" value="[% HTML.escape(row.id) %]">
- <td>[% UNLESS NOTE_id && (NOTE_id == row.id) %]<input type="checkbox" name="NOTE_delete_[% loop.count %]" value="1">[% END %]</td>
- <td><a href="ct.pl?action=edit&db=[% HTML.url(db) %]&id=[% HTML.url(id) %]&edit_note_id=[% HTML.url(row.id) %]">[% HTML.escape(row.subject) %]</a></td>
- <td>[% HTML.escape(row.created_on) %]</td>
- <td>[% IF row.created_by_name %][% HTML.escape(row.created_by_name) %][% ELSE %][% HTML.escape(row.created_by_login) %][% END %]</td>
- <td>[% HTML.escape(row.follow_up_date) %]</td>
- <td>[% IF row.created_for_name %][% HTML.escape(row.created_for_name) %][% ELSE %][% HTML.escape(row.created_for_login) %][% END %]</td>
- <td>[% IF row.follow_up_date %][% IF row.follow_up_done %][% 'Yes' | $T8 %][% ELSE %][% 'No' | $T8 %][% END %][% END %]</td>
- </tr>
- [%- END %]
-
- <input type="hidden" name="NOTES_rowcount" value="[% NOTES.size %]">
-
- </table>
- </p>
- [%- END %]
-
- <div class="listtop">[% IF NOTE_id %][% 'Edit note' | $T8 %][% ELSE %][% 'Add note' | $T8 %][% END %]</div>
-
- <input type="hidden" name="NOTE_id" value="[% HTML.escape(NOTE_id) %]">
- <input type="hidden" name="FU_id" value="[% HTML.escape(FU_id) %]">
-
- <p>
- <table>
- <tr>
- <td valign="right">[% 'Subject' | $T8 %]</td>
- <td><input name="NOTE_subject" value="[% HTML.escape(NOTE_subject) %]" size="50"></td>
- </tr>
-
- <tr>
- <td valign="right" align="top">[% 'Body' | $T8 %]</td>
- <td align="top"><textarea cols="50" rows="10" name="NOTE_body">[% HTML.escape(NOTE_body) %]</textarea></td>
- </tr>
-
- <tr>
- <td valign="right">[% 'Follow-Up On' | $T8 %]</td>
- <td>
- [% L.date_tag('FU_date', FU_date) %]
- [% 'for' | $T8 %]
- [% L.select_tag('FU_created_for_user', ALL_EMPLOYEES, default = (FU_created_for_user ? FU_created_for_user : USER.id), title_key='safe_name') %]
- </td>
- </tr>
-
- <tr>
- <td> </td>
- <td>
- <input type="checkbox" name="FU_done" id="FU_done" value="1"[% IF FU_done %] checked[% END %]>
- <label for="FU_done">[% 'Follow-Up done' | $T8 %]</label>
- </td>
- </tr>
- </table>
- </p>
- </div>
-
- [%- IF CUSTOM_VARIABLES.CT.size %]
- <div id="custom_variables">
-
- <p>
- <table>
- [%- FOREACH var = CUSTOM_VARIABLES.CT %]
- <tr>
- <td align="right" valign="top">[% HTML.escape(var.description) %]</td>
- <td valign="top">[% var.HTML_CODE %]</td>
- </tr>
- [%- END %]
- </table>
- </p>
- </div>
- [%- END %]
-
- </div>
-
- <script type="text/javascript">
- <!--
- function set_gender(gender) {
- var s = document.getElementById('cp_gender');
- if (s) {
- s.selectedIndex = (gender == 'f') ? 1 : 0;
- }
- }
- -->
-
- </script>
+++ /dev/null
-[%- USE T8 %]
-[% USE HTML %][% USE LxERP %]
-<div id="delivery">
-<table width="100%">
- <tr>
- <td>
- <table width="100%">
- <tr class="listheading">
- <th class="listheading">[% 'Shipping Address' | $T8 %]</th>
- <th class="listheading">[% 'Invoice' | $T8 %]</th>
- <th class="listheading">[% 'Order' | $T8 %]</th>
- <th class="listheading">[% 'Invdate' | $T8 %]</th>
- <th class="listheading">[% 'Description' | $T8 %]</th>
- <th class="listheading">[% 'Qty' | $T8 %]</th>
- <th class="listheading">[% 'Unit' | $T8 %]</th>
-[%- IF is_customer %]
- <th class="listheading">[% 'Sell Price' | $T8 %]</th>
-[%- ELSE %]
- <th class="listheading">[% 'Last Cost' | $T8 %]</th>
-[%- END %]
- </tr>
-[%- FOREACH row = DELIVERY %]
-[%-
- row.script = is_customer ? ( row.invoice ? 'is' : 'ar' )
- : ( row.invoice ? 'ir' : 'ap' )
--%]
- <tr class="listrow[% loop.count % 2 %]">
- <td>[% HTML.escape(row.shiptoname) UNLESS loop.prev.shiptoname == row.shiptoname %] </td>
- <td>[% IF row.id %]<a href='[% row.script %].pl?action=edit&id=[% HTML.escape(row.id) %]'>[% END %][% HTML.escape(row.invnumber) || ' ' %][% IF row.id %]</a>[% END %]</td>
- <td>[% IF row.oe_id %]<a href='oe.pl?action=edit&type=[% IF is_customer %]sales_order[% ELSE %]purchase_order[% END %]&vc=customer&id=[% HTML.escape(row.oe_id) %]'>[% END %][% HTML.escape(row.ordnumber) || ' ' %][% IF row.oe_id %]</a>[% END %]</td>
- <td>[% HTML.escape(row.transdate) || ' ' %]</td>
- <td>[% HTML.escape(row.description) || ' ' %]</td>
- <td>[% HTML.escape(row.qty) || ' ' %]</td>
- <td>[% HTML.escape(row.unit) || ' ' %]</td>
- <td>[% LxERP.format_amount(row.sellprice, 2) || ' ' %]</td>
- </tr>
-[%- END %]
-
- </table>
-[%- IF DELIVERY.size == 15 %]
- <p>[% 'This list is capped at 15 items to keep it fast. If you need a full list, please use reports.' | $T8 %]</p>
-[%- END %]
-</div>
-[% USE T8 %][% USE HTML %]<form method="post" action="dispatcher.pl?M=ct">
+[% USE T8 %][% USE HTML %]
- <input name="callback" type="hidden" value="[% HTML.escape(callback) %]">
- <input name="db" type="hidden" value="[% HTML.escape(db) %]">
+<form method="post" action="controller.pl?action=CustomerVendor/add">
+ <input name="callback" type="hidden" value="[% HTML.escape(callback) %]">
+ <input name="db" type="hidden" value="[% HTML.escape(db) %]">
- [% IF IS_CUSTOMER %][% 'New customer' | $T8 %][% ELSE %][% 'New vendor' | $T8 %][% END %]<br>
-
- <input class="submit" type="submit" name="A_add" value="[%- 'Add' | $T8 %]">
+ [% IF IS_CUSTOMER %][% 'New customer' | $T8 %][% ELSE %][% 'New vendor' | $T8 %][% END %]<br>
+ <input class="submit" type="submit" value="[%- 'Add' | $T8 %]">
</form>
+++ /dev/null
-[% USE L %]
-[% USE T8 %]
-[% USE LxERP %]
-[% L.javascript_tag('jquery-ui') %]
-<link rel="stylesheet" href="css/ui-lightness/jquery-ui-1.8.12.custom.css" type="text/css" />
-
-<p>Pick a customer</p>
-id: [% L.input_tag('customer_id', '') %]
-nr: [% L.input_tag('customer_customernumber', '') %]
-desc: [% L.input_tag('customer_name', '') %]
-
-<script type='text/javascript'>
-function autocomplete_customer (selector, column) {
- $(function(){ $(selector).autocomplete({
- source: function(req, rsp) {
- $.ajax({
- url: 'controller.pl?action=Customer/ajax_autocomplete',
- dataType: "json",
- data: {
- column: column,
- term: req.term,
- current: function() { $('#customer_id').val() },
- obsolete: 0,
- },
- success: function (data){ rsp(data) }
- });
- },
- limit: 20,
- delay: 50,
- select: function(event, ui) {
- $('#customer_id').val(ui.item.id);
- $('#customer_customernumber').val(ui.item.customernumber);
- $('#customer_name').val(ui.item.name);
- },
- })});
-}
-//autocomplete_customer('#customer_customernumber', 'customernumber');
-autocomplete_customer('#customer_name', '');
-</script>
-
<li><a href="#billing">[% 'Billing Address' | $T8 %]</a></li>
<li><a href="#shipto">[% 'Shipping Address' | $T8 %]</a></li>
<li><a href="#contacts">[% 'Contacts' | $T8 %]</a></li>
- [% IF ( SELF.cv.id ) %]
+ [% IF ( SELF.cv.id && AUTH.assert('sales_all_edit', 1) ) %]
<li><a href="#deliveries">[% 'Supplies' | $T8 %]</a></li>
[% END %]
<li><a href="#vcnotes">[% 'Notes' | $T8 %]</a></li>
[% PROCESS "customer_vendor/tabs/billing.html" %]
[% PROCESS "customer_vendor/tabs/shipto.html" %]
[% PROCESS "customer_vendor/tabs/contacts.html" %]
- [% IF ( SELF.cv.id ) %]
+ [% IF ( SELF.cv.id && AUTH.assert('sales_all_edit', 1) ) %]
[% PROCESS "customer_vendor/tabs/deliveries.html" %]
[% END %]
[% PROCESS "customer_vendor/tabs/vcnotes.html" %]
<th align="right">[% 'Discount' | $T8 %]</th>
<td>
- [% L.input_tag('cv.discount_as_percent', SELF.cv.discount_as_percent, size = 4) %]
+ [% L.input_tag('cv.discount_as_percent', SELF.cv.discount_as_percent, size = 4) %]%
</td>
</tr>
[% L.select_tag('cv.language_id', SELF.all_languages, default = SELF.cv.language_id, value_key = 'id', title_key = 'description', with_empty = 1) %]
</td>
- [% IF ( SELF.is_customer() ) %]
- <th align="right">[% 'Preisklasse' | $T8 %]</th>
+ <th align="right">[% 'Delivery Terms' | $T8 %]</th>
+
+ <td>
+ [% L.select_tag('cv.delivery_term_id', SELF.all_delivery_terms, default = SELF.cv.delivery_term_id, value_key = 'id', title_key = 'description', with_empty = 1) %]
+ </td>
- <td>
- [% L.select_tag('cv.klass', SELF.all_pricegroups, default = SELF.cv.klass, value_key = 'id', title_key = 'pricegroup', with_empty = 1) %]
- </td>
- [% END %]
</tr>
<tr>
<td>
[% L.checkbox_tag('cv.direct_debit', checked = SELF.cv.direct_debit, for_submit=1) %]
</td>
+
+ [% IF ( SELF.is_customer() ) %]
+ <th align="right">[% 'Preisklasse' | $T8 %]</th>
+
+ <td>
+ [% L.select_tag('cv.klass', SELF.all_pricegroups, default = SELF.cv.klass, value_key = 'id', title_key = 'pricegroup', with_empty = 1) %]
+ </td>
+ [% END %]
+
</tr>
<tr>
[%- USE L %]
<div id="vcnotes">
- [% IF ( NOTES && NOTES.size ) %]
+ [% IF ( SELF.notes && SELF.notes.size ) %]
<p>
<table>
<tr>
[%- FOREACH row = SELF.notes %]
<tr class="listrow[% loop.count % 2 %]">
<td>
- [% L.hidden_tag('notes[+].id', row.id) %]
- [% IF ( !NOTE_id || (NOTE_id != row.id) ) %]
- [% L.checkbox_tag('notes[].delete', 0) %]
+ [% IF ( !SELF.note || SELF.note.id != row.id ) %]
+ [% L.checkbox_tag('delete_notes[]', value = row.id) %]
[% END %]
</td>
<td>
- <a href="ct.pl?action=edit&db=[% HTML.url(db) %]&id=[% HTML.url(id) %]&edit_note_id=[% HTML.url(row.id) %]">[% HTML.escape(row.subject) %]</a>
+ <a href="controller.pl?action=CustomerVendor/edit&db=[% SELF.is_vendor() ? 'vendor' : 'customer' %]&id=[% HTML.url(SELF.cv.id) %]¬e_id=[% HTML.url(row.id) %]">[% HTML.escape(row.subject) %]</a>
</td>
<td>
- [% HTML.escape(row.created_on) %]
+ [% row.itime.to_kivitendo | html %]
</td>
<td>
- [% IF ( row.created_by_name ) %]
- [% HTML.escape(row.created_by_name) %]
- [% ELSE %]
- [% HTML.escape(row.created_by_login) %]
- [% END %]
+ [% row.employee.safe_name | html %]
</td>
<td>
- [% HTML.escape(row.follow_up_date) %]
+ [% row.follow_up.follow_up_date.to_kivitendo | html %]
</td>
<td>
- [% IF ( row.created_for_name ) %]
- [% HTML.escape(row.created_for_name) %]
- [% ELSE %]
- [% HTML.escape(row.created_for_login) %]
- [% END %]
+ [% row.follow_up.created_for.safe_name | html %]
</td>
<td>
- [% IF ( row.follow_up_date ) %]
- [% IF ( row.follow_up_done ) %]
+ [% IF ( row.follow_up.follow_up_date ) %]
+ [% IF ( row.follow_up.done ) %]
[% 'Yes' | $T8 %]
[% ELSE %]
[% 'No' | $T8 %]
</p>
[% END %]
+
<div class="listtop">
- [% IF ( NOTE_id ) %]
+ [% IF ( SELF.note.id ) %]
[% 'Edit note' | $T8 %]
[% ELSE %]
[% 'Add note' | $T8 %]
--- /dev/null
+[% USE HTML %][% USE T8 %][% USE L %][% USE LxERP %]
+
+ <form method="post" action="controller.pl">
+ <div class="listtop">[% FORM.title %]</div>
+
+[%- INCLUDE 'common/flash.html' %]
+
+ <table>
+ <tr>
+ <td>[%- 'Description' | $T8 %]</td>
+ <td>
+ <input name="delivery_term.description" value="[%- HTML.escape(SELF.delivery_term.description) %]">
+ </td>
+ </tr>
+
+ <tr>
+ <td>[%- 'Long Description' | $T8 %]</td>
+ <td>
+ <input name="delivery_term.description_long" value="[%- HTML.escape(SELF.delivery_term.description_long) %]" size="60">
+ </td>
+ </tr>
+
+ [%- FOREACH language = SELF.languages %]
+ <tr>
+ <td>[%- HTML.escape(language.description) %] ([%- LxERP.t8('Translation') %])</td>
+ <td>
+ <input name="translation_[% language.id %]" value="[%- HTML.escape(SELF.delivery_term.translated_attribute('description_long', language, 1)) %]" size="60">
+ </td>
+ </tr>
+ [%- END %]
+
+ <p>
+ <input type="hidden" name="id" value="[% SELF.delivery_term.id %]">
+ <input type="hidden" name="action" value="DeliveryTerm/dispatch">
+ <input type="submit" class="submit" name="action_[% IF SELF.delivery_term.id %]update[% ELSE %]create[% END %]" value="[% 'Save' | $T8 %]">
+ [%- IF SELF.delivery_term.id %]
+ <input type="submit" class="submit" name="action_destroy" value="[% 'Delete' | $T8 %]"
+ onclick="if (confirm('[% 'Are you sure you want to delete this delivery term?' | $T8 %]')) return true; else return false;">
+ [%- END %]
+ <a href="[% SELF.url_for(action => 'list') %]">[%- 'Abort' | $T8 %]</a>
+ </p>
+
+ </form>
+
--- /dev/null
+[% USE HTML %][% USE T8 %][% USE L %][% USE LxERP %]
+
+ <div class="listtop">[% FORM.title %]</div>
+
+[%- INCLUDE 'common/flash.html' %]
+
+ <form method="post" action="controller.pl">
+ [% IF !DELIVERY_TERMS.size %]
+ <p>
+ [%- 'No delivery term has been created yet.' | $T8 %]
+ </p>
+
+ [%- ELSE %]
+ <table id="delivery_term_list">
+ <thead>
+ <tr class="listheading">
+ <th align="center"><img src="image/updown.png" alt="[ LxERP.t8('reorder item') %]"></th>
+ <th>[%- 'Description' | $T8 %]</th>
+ <th>[%- 'Long Description' | $T8 %]</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ [%- FOREACH delivery_term = DELIVERY_TERMS %]
+ <tr class="listrow[% loop.count % 2 %]" id="delivery_term_id_[% delivery_term.id %]">
+ <td align="center" class="dragdrop"><img src="image/updown.png" alt="[ LxERP.t8('reorder item') %]"></td>
+ <td>
+ <a href="[% SELF.url_for(action => 'edit', id => delivery_term.id) %]">
+ [%- HTML.escape(delivery_term.description) %]
+ </a>
+ </td>
+ <td>[%- HTML.escape(delivery_term.description_long) %]</td>
+ </tr>
+ [%- END %]
+ </tbody>
+ </table>
+ [%- END %]
+
+ <p>
+ <a href="[% SELF.url_for(action => 'new') %]">[%- 'Create new delivery term' | $T8 %]</a>
+ </p>
+ </form>
+
+ [% L.sortable_element('#delivery_term_list tbody', url => 'controller.pl?action=DeliveryTerm/reorder', with => 'delivery_term_id') %]
+
<td><textarea name="notes" rows="[% LxERP.numtextrows(notes, 25, 8, 2) %]" cols="25" wrap="soft"[% RO %]>[% HTML.escape(notes) %]</textarea></td>
<td><textarea name="intnotes" rows="[% LxERP.numtextrows(intnotes, 35, 8, 2) %]" cols="35" wrap="soft"[% RO %]>[% HTML.escape(intnotes) %]</textarea></td>
</tr>
+
+ <tr>
+ <th align="right">[% 'Delivery Terms' | $T8 %]</th>
+ <td>[% L.select_tag('delivery_term_id', ALL_DELIVERY_TERMS, default = delivery_term_id, with_empty = 1, title_key = 'description') %]</td>
+ </tr>
</table>
</td>
</tr>
<tr>
- <th align="right">[% 'From' | $T8 %]</th>
+ <th align="right">[% 'Delivery Order Date' | $T8 %] [% 'From' | $T8 %]</th>
<td>
[% L.date_tag('transdatefrom') %]
</td>
</td>
</tr>
+ <tr>
+ <th align="right">[% 'Reqdate' | $T8 %] [% 'From' | $T8 %]</th>
+ <td>
+ [% L.date_tag('reqdatefrom') %]
+ </td>
+ <th align="right">[% 'Bis' | $T8 %]</th>
+ <td>
+ [% L.date_tag('reqdateto') %]
+ </td>
+ </tr>
+
<tr>
<th align="right">[% 'Include in Report' | $T8 %]</th>
<td colspan="5">
<tr>
<td>
<input name="l_transdate" id="l_transdate" class="checkbox" type="checkbox" value="Y" checked>
- <label for="l_transdate">[% 'Date' | $T8 %]</label>
+ <label for="l_transdate">[% 'Delivery Order Date' | $T8 %]</label>
</td>
<td>
<input name="l_reqdate" id="l_reqdate" class="checkbox" type="checkbox" value="Y" checked>
var taxkeyposition = taxkey.lastIndexOf(found[0]);
var account = taxkey.substr(0, taxkeyposition);
- var xmlhttp;
- if (window.XMLHttpRequest)
- {// code for IE7+, Firefox, Chrome, Opera, Safari
- xmlhttp=new XMLHttpRequest();
- }
- else
- {// code for IE6, IE5
- xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
- }
- xmlhttp.onreadystatechange=function()
- {
- if (xmlhttp.readyState==4 && xmlhttp.status==200)
- {
- var element = document.getElementById("taxchart_" + row);
- element.innerHTML = xmlhttp.responseText;
- }
- }
- xmlhttp.open("GET","gl.pl?action=get_tax_dropdown&accno=" + account + "&select_index=" + index,true);
- xmlhttp.send();
+ $.ajax({
+ url: 'gl.pl?action=get_tax_dropdown',
+ data: { accno: account,
+ selected_index: index},
+ dataType: 'html',
+ success: function (new_html) {
+ $("#taxchart_" + row).html(new_html);
+ },
+ });
};
function copy_debit_to_credit() {
<tr>
<th align="left">[% 'Notes' | $T8 %]</th>
<th align="left">[% 'Internal Notes' | $T8 %]</th>
- <th align="left">[% 'Payment Options' | $T8 %]</th>
+ <th align="right">[% 'Payment / Delivery Options' | $T8 %]</th>
</tr>
<tr valign="top">
<td>
<textarea name="intnotes" rows="[% rows %]" cols="35" wrap="soft">[% intnotes %]</textarea>
</td>
<td>
- [%- L.checkbox_tag('direct_debit', 'checked', direct_debit, 'label', LxERP.t8('direct debit')) %]
+ <table>
+ <tr>
+ <th align="right">[% 'Delivery Terms' | $T8 %] </th>
+ <td>
+ [% L.select_tag('delivery_term_id', ALL_DELIVERY_TERMS, default = delivery_term_id, with_empty = 1, title_key = 'description', style = 'width: 250px') %]
+ </td>
+ </tr>
+ <tr>
+ <th align="right">[% 'direct debit' | $T8 %]</th>
+ <td>
+ [%- L.checkbox_tag('direct_debit', 'checked', direct_debit) %]
+ </td>
+ </tr>
+ </table>
</td>
</tr>
[%- IF id && follow_ups.size %]
<tr>
<th align="left">[% 'Notes (will appear on hard copy)' | $T8 %]</th>
<th align="left">[% 'Internal Notes' | $T8 %]</th>
- <th align="right">[% 'Payment Terms' | $T8 %]</th>
+ <th align="right">[% 'Payment / Delivery Options' | $T8 %]</th>
</tr>
<tr valign="top">
<td>
<textarea name="intnotes" rows="[% rows %]" cols="35">[% intnotes %]</textarea>
</td>
<td>
- [%- INCLUDE 'generic/multibox.html'
- name = 'payment_id',
- style = 'width: 250px',
- DATA = payment_terms,
- id_key = 'id',
- label_key = 'description',
- show_empty = 1
- allow_textbox = 0 -%]
- <script type='text/javascript'>$('#payment_id').change(function(){ if (this.value) set_duedate()})</script>
- <br>
- [%- L.checkbox_tag('direct_debit', 'checked', direct_debit, 'label', LxERP.t8('direct debit')) %]
+ <table>
+ <tr>
+ <th align="right">[% 'Payment Terms' | $T8 %]</th>
+ <td>
+ [%- INCLUDE 'generic/multibox.html'
+ name = 'payment_id',
+ style = 'width: 250px',
+ DATA = payment_terms,
+ id_key = 'id',
+ label_key = 'description',
+ show_empty = 1
+ allow_textbox = 0 -%]
+ <script type='text/javascript'>$('#payment_id').change(function(){ if (this.value) set_duedate()})</script>
+ </td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Delivery Terms' | $T8 %] </th>
+ <td>
+ [% L.select_tag('delivery_term_id', ALL_DELIVERY_TERMS, default = delivery_term_id, with_empty = 1, title_key = 'description', style = 'width: 250px') %]
+ </td>
+ </tr>
+ <tr>
+ <th align="right">[% 'direct debit' | $T8 %]</th>
+ <td>
+ [%- L.checkbox_tag('direct_debit', 'checked', direct_debit) %]
+ </td>
+ </tr>
+ </table>
</td>
</tr>
[%- IF id && follow_ups.size %]
label_key = 'description',
show_empty = 1 -%]
</td>
- </tr>
+ </tr>
+ <tr>
+ <th align="right">[% 'Delivery Terms' | $T8 %]</th>
+ <td>[% L.select_tag('delivery_term_id', ALL_DELIVERY_TERMS, default = delivery_term_id, with_empty = 1, title_key = 'description', style = 'width: 250px') %]</td>
+ </tr>
[%- IF is_sales_ord %]
<tr>
<th align="right">[% 'Salesman' | $T8 %]</th>
<td>[% L.select_tag('salesman_id', ALL_EMPLOYEES, title_key='safe_name', with_empty=1, style='width:250px') %]</td>
</tr>
+ <tr>
+ <th align="right">[% 'Steuersatz' | $T8 %]</th>
+ <td>[% L.select_tag('taxzone_id', ALL_TAXZONES, with_empty=1, title_key='description', style='width: 250px') %]</td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Shipping Point' | $T8 %]</th>
+ <td colspan="3">[% L.input_tag('shippingpoint', '', style='width:250px') %]</td>
+ </tr>
<tr>
<th align="right">[% 'Transaction description' | $T8 %]</th>
<td colspan="3"><input name="transaction_description" style="width: 250px"></td>
<input name="l_customernumber" id="l_customernumber" class="checkbox" type="checkbox" value="Y">
<label for="l_customernumber">[% 'Customer Number' | $T8 %]</label>
</td>
+ <td>[%- L.checkbox_tag('l_taxzone', label => LxERP.t8('Steuersatz')) %]</td>
+ <td>[%- L.checkbox_tag('l_shippingpoint', label => LxERP.t8('Shipping Point')) %]</td>
</tr>
<tr>
<td>