my $self = shift;
my $dbh = $self->dbconnect();
- my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value
- FROM auth.user_config cfg
- LEFT JOIN auth."user" u ON (cfg.user_id = u.id)|;
+ my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value, s.mtime AS last_action
+
+ FROM auth."user" AS u
+
+ LEFT JOIN auth.user_config AS cfg
+ ON (cfg.user_id = u.id)
+
+ LEFT JOIN auth.session_content AS sc_login
+ ON (sc_login.sess_key = 'login' AND sc_login.sess_value = ('--- ' \|\| u.login \|\| '\n'))
+
+ LEFT JOIN auth.session AS s
+ ON (s.id = sc_login.session_id)
+ |;
my $sth = prepare_execute_query($main::form, $dbh, $query);
my %users;
while (my $ref = $sth->fetchrow_hashref()) {
- $users{$ref->{login}} ||= { 'login' => $ref->{login}, 'id' => $ref->{id} };
+
+ $users{$ref->{login}} ||= {
+ 'login' => $ref->{login},
+ 'id' => $ref->{id},
+ 'last_action' => $ref->{last_action},
+ };
$users{$ref->{login}}->{$ref->{cfg_key}} = $ref->{cfg_value} if (($ref->{cfg_key} ne 'login') && ($ref->{cfg_key} ne 'id'));
}
number_value => { type => 'numeric', precision => 5, scale => 25 },
itime => { type => 'timestamp', default => 'now()' },
mtime => { type => 'timestamp' },
- sub_module => { type => 'text' },
+ sub_module => { type => 'text', default => '', not_null => 1 },
],
primary_key_columns => [ 'id' ],
} elsif ($params{trans_type} eq 'ar_transaction') {
$link = {
- 'url' => 'ar.pl?action=editid=' . $params{trans_id},
+ 'url' => 'ar.pl?action=edit&id=' . $params{trans_id},
'title' => $locale->text('AR Transaction') . " $params{trans_info}",
};
} elsif ($params{trans_type} eq 'ap_transaction') {
$link = {
- 'url' => 'ap.pl?action=editid=' . $params{trans_id},
+ 'url' => 'ap.pl?action=edit&id=' . $params{trans_id},
'title' => $locale->text('AP Transaction') . " $params{trans_info}",
};
$reference_date = $reference_date ? conv_dateq($reference_date) . '::DATE' : 'current_date';
my $dbh = $self->get_standard_dbh($myconfig);
+ my $payment_id;
+
+ if($self->{payment_id}) {
+ $payment_id = $self->{payment_id};
+ } elsif($self->{vendor_id}) {
+ my $query = 'SELECT payment_id FROM vendor WHERE id = ?';
+ ($payment_id) = selectrow_query($self, $dbh, $query, $self->{vendor_id});
+ }
+
my $query = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
- my ($duedate) = selectrow_query($self, $dbh, $query, $self->{payment_id});
+ my ($duedate) = selectrow_query($self, $dbh, $query, $payment_id);
$main::lxdebug->leave_sub();
# setup sales contacts
$query = qq|SELECT e.id, e.name
FROM employee e
- WHERE (e.sales = '1') AND (NOT e.id = ?)|;
+ WHERE (e.sales = '1') AND (NOT e.id = ?)
+ ORDER BY name|;
$self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
# this is for self
{ id => $self->{employee_id},
name => $self->{employee} });
- # sort the whole thing
- @{ $self->{all_employees} } =
- sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
-
-
# prepare query for departments
$query = qq|SELECT id, description
FROM department
my ($query, $query_add, @values, @ids, $sth);
- my $ic_cvar_configs = CVar->get_configs(module => 'IC',
- dbh => $dbh);
-
# translate the ids (given by id_# and trans_id_#) into one array of ids, so we can join them later
map {
push @ids, $form->{"trans_id_$_"}
foreach my $column (values %{ $self->{columns} }) {
$column->{visible} = $self->{options}->{std_column_visibility} unless defined $column->{visible};
}
+
+ if( $::form->{report_generator_csv_options_for_import} ) {
+ foreach my $key (keys %{ $self->{columns} }) {
+ $self->{columns}{$key}{text} = $key;
+ }
+ }
$self->set_column_order(sort keys %{ $self->{columns} });
}
sub _parse_multipart_formdata {
my ($target, $temp_target, $input) = @_;
my ($name, $filename, $headers_done, $content_type, $boundary_found, $need_cr, $previous, $p_attachment, $encoding, $transfer_encoding);
+ my $data_start = 0;
+
+ # teach substr and length to use good ol' bytes, not 'em fancy characters
+ use bytes;
# We SHOULD honor encodings and transfer-encodings here, but as hard as I
# looked I couldn't find a reasonably recent webbrowser that makes use of
$ENV{'CONTENT_TYPE'} =~ /multipart\/form-data\s*;\s*boundary\s*=\s*(.+)$/;
my $boundary = '--' . $1;
+ my $index = 0;
+ my $line_length;
foreach my $line (split m/\n/, $input) {
- last if (($line eq "${boundary}--") || ($line eq "${boundary}--\r"));
+ $line_length = length $line;
+
+ if ($line =~ /^\Q$boundary\E(--)?\r?$/) {
+ my $last_boundary = $1;
+ my $data = substr $input, $data_start, $index - $data_start;
+ $data =~ s/\r?\n$//;
- if (($line eq $boundary) || ($line eq "$boundary\r")) {
- ${ $previous } =~ s|\r?\n$|| if $previous;
- ${ $previous } = Encode::decode($encoding, $$previous) if $previous && !$filename && !$transfer_encoding eq 'binary';
+ if ($previous && !$filename && $transfer_encoding && $transfer_encoding ne 'binary') {
+ ${ $previous } = Encode::decode($encoding, $data);
+ } else {
+ ${ $previous } = $data;
+ }
undef $previous;
undef $filename;
$need_cr = 0;
$encoding = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
$transfer_encoding = undef;
-
+ last if $last_boundary;
next;
}
if (!$line) {
$headers_done = 1;
+ $data_start = $index + $line_length + 1;
next;
}
next unless $previous;
- ${ $previous } .= "${line}\n";
+ } continue {
+ $index += $line_length + 1;
}
- ${ $previous } =~ s|\r?\n$|| if $previous;
-
$::lxdebug->leave_sub(2);
}
$file_name =~ s:.*/::g;
$file_name = "${path}/${file_name}";
+ $self->file_name($file_name);
+
if ($params{mode}) {
my $mode = $params{mode};
$self->fh(IO::File->new($file_name, $mode));
}
- $self->file_name($file_name);
-
return $self;
}
+sub open {
+ my ($self, $mode) = @_;
+ return $self->fh(IO::File->new($self->file_name, $mode));
+}
+
sub exists {
my ($self) = @_;
return -f $self->file_name;
it has been created for "customer.csv" then the value returned might
be C<users/session_files/e8789b98721347/customer.csv>.
+=item C<open, %params]>
+
+Opens the file_name given at creation with the given parameters.
+
=item C<exists>
Returns trueish if the file exists.
--- /dev/null
+package SL::SessionFile::Random;
+
+use strict;
+use parent qw(SL::SessionFile);
+
+my @CHARS = ('A'..'Z', 'a'..'z', 0..9, '_');
+my $template = 'X' x 10;
+use constant MAX_TRIES => 1000;
+
+sub new {
+ my ($class, %params) = @_;
+
+ my $filename;
+ my $tries = 0;
+ $filename = _get_file() while $tries++ < MAX_TRIES && (!$filename || -e $filename);
+
+ $class->SUPER::new($filename, %params);
+}
+
+sub _get_file {
+ my $filename = $template;
+ $filename =~ s/X(?=X*\z)/$CHARS[ int( rand( @CHARS ) ) ]/ge;
+ $filename;
+}
+
+1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::SessionFile::Random - SessionFile with a random name
+
+=head1 SYNOPSIS
+
+ use SL::SessionFile::Random;
+
+ # Create a session file named "customer.csv" (relative names only)
+ my $sfile = SL::SessionFile::Random->new("w");
+ $sfile->fh->print("col1;col2;col3\n" .
+ "value1;value2;value3\n");
+ $sfile->fh->close;
+
+=head1 DESCRIPTION
+
+This modules gives you a random file in the current session cache that is guaranteed to be unique
+
+=head1 FUNCTIONS
+
+same as SL::SessioNFile
+
+=head1 BUGS
+
+NONE yet.
+
+=head1 AUTHOR
+
+Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut
my @values;
my $query =
- qq|SELECT ct.id as customerid, ct.name as customername,ct.customernumber,ct.country,ar.invnumber,ar.id,ar.transdate,p.partnumber,pg.partsgroup,i.parts_id,i.qty,i.price_factor,i.discount,i.description as description,i.lastcost,i.sellprice,i.fxsellprice,i.marge_total,i.marge_percent,i.unit,b.description as business,e.name as employee,e2.name as salesman, to_char(ar.transdate,'Month') as month, to_char(ar.transdate, 'YYYYMM') as nummonth | .
+ qq|SELECT ct.id as customerid, ct.name as customername,ct.customernumber,ct.country,ar.invnumber,ar.id,ar.transdate,p.partnumber,pg.partsgroup,i.parts_id,i.qty,i.price_factor,i.discount,i.description as description,i.lastcost,i.sellprice,i.fxsellprice,i.marge_total,i.marge_percent,i.unit,b.description as business,e.name as employee,e2.name as salesman, to_char(ar.transdate,'Month') as month, to_char(ar.transdate, 'YYYYMM') as nummonth, p.unit as parts_unit | .
qq|FROM invoice i | .
qq|JOIN ar on (i.trans_id = ar.id) | .
qq|JOIN parts p on (i.parts_id = p.id) | .
# with JavaScript Calendar
$button1 = qq|
- <td><input name=transdate id=transdate size=11 title="$myconfig{dateformat}" value="$form->{transdate}" onBlur=\"check_right_date_format(this)\" $readonly></td>
+ <td><input name=transdate onchange="set_duedate()" id=transdate size=11 title="$myconfig{dateformat}" value="$form->{transdate}" onBlur=\"check_right_date_format(this)\" $readonly></td>
<td><input type=button name=transdate id="trigger1" value=|
. $locale->text('button') . qq|></td>
|;
# without JavaScript Calendar
$button1 =
- qq|<td><input name=transdate id=transdate size=11 title="$myconfig{dateformat}" value="$form->{transdate}" onBlur=\"check_right_date_format(this)\" $readonly></td>|;
+ qq|<td><input name=transdate onchange="set_duedate()" id=transdate size=11 title="$myconfig{dateformat}" value="$form->{transdate}" onBlur=\"check_right_date_format(this)\" $readonly></td>|;
$button2 =
qq|<td><input name=duedate id=duedate size=11 title="$myconfig{dateformat}" value="$form->{duedate}" onBlur=\"check_right_date_format(this)\" $readonly></td>|;
}
}
sub form_footer {
- $main::lxdebug->enter_sub();
+ $::lxdebug->enter_sub;
+ $::auth->assert('general_ledger');
- my $form = $main::form;
- my %myconfig = %main::myconfig;
- my $locale = $main::locale;
- my $cgi = $::request->{cgi};
+ my $num_due;
+ my $num_follow_ups;
+ if ($::form->{id}) {
+ my $follow_ups = FU->follow_ups('trans_id' => $::form->{id});
- $main::auth->assert('general_ledger');
-
- my $follow_ups_block;
- if ($form->{id}) {
- my $follow_ups = FU->follow_ups('trans_id' => $form->{id});
-
- if (@{ $follow_ups} ) {
- my $num_due = sum map { $_->{due} * 1 } @{ $follow_ups };
- $follow_ups_block = qq|<p>| . $locale->text("There are #1 unfinished follow-ups of which #2 are due.", scalar @{ $follow_ups }, $num_due) . qq|</p>|;
+ if (@{ $follow_ups }) {
+ $num_due = sum map { $_->{due} * 1 } @{ $follow_ups };
+ $num_follow_ups = scalar @{ $follow_ups }
}
}
- print qq|
+ my $transdate = $::form->datetonum($::form->{transdate}, \%::myconfig);
+ my $closedto = $::form->datetonum($::form->{closedto}, \%::myconfig);
-$follow_ups_block
-
-<input name=callback type=hidden value="$form->{callback}">
-<input name="gldate" type="hidden" value="| . Q($form->{gldate}) . qq|">
-|
-. $cgi->hidden('-name' => 'draft_id', '-default' => [$form->{draft_id}])
-. $cgi->hidden('-name' => 'draft_description', '-default' => [$form->{draft_description}])
-. qq|
-
-<br>
-|;
-
- if (!$form->{id} && $form->{draft_id}) {
- print(NTI($cgi->checkbox('-name' => 'remove_draft', '-id' => 'remove_draft',
- '-value' => 1, '-checked' => $form->{remove_draft},
- '-label' => '')) .
- qq| <label for="remove_draft">| .
- $locale->text("Remove draft when posting") .
- qq|</label><br>|);
- }
-
- my $transdate = $form->datetonum($form->{transdate}, \%myconfig);
- my $closedto = $form->datetonum($form->{closedto}, \%myconfig);
+ my $storno = $::form->{id}
+ && !IS->has_storno(\%::myconfig, $::form, 'ap')
+ && !IS->is_storno( \%::myconfig, $::form, 'ap', $::form->{id})
+ && ($::form->{totalpaid} == 0 || $::form->{totalpaid} eq '');
- print qq|<input class="submit" type="submit" name="action" id="update_button" value="| . $locale->text('Update') . qq|">|;
+ $::form->header;
+ print $::form->parse_html_template('ap/form_footer', {
+ num_due => $num_due,
+ num_follow_ups => $num_follow_ups,
+ show_post_draft => ($transdate > $closedto) && !$::form->{id},
+ show_storno => $storno,
+ });
- if ($form->{id}) {
- if ($form->{radier}) {
- print qq| <input class=submit type=submit name=action value="| . $locale->text('Post') . qq|">
- <input class=submit type=submit name=action value="| . $locale->text('Delete') . qq|">
-|;
- }
- # ToDO: - insert a global check for stornos, so that a storno is only possible a limited time after saving it
- print qq| <input class=submit type=submit name=action value="| . $locale->text('Storno') . qq|"> |
- if ($form->{id} && !IS->has_storno(\%myconfig, $form, 'ap') && !IS->is_storno(\%myconfig, $form, 'ap', $form->{id}) && (($form->{totalpaid} == 0) || ($form->{totalpaid} eq "")));
-
- print qq| <input class=submit type=submit name=action value="| . $locale->text('Post Payment') . qq|">
- <input class=submit type=submit name=action value="| . $locale->text('Use As Template') . qq|">
- <input type="button" class="submit" onclick="follow_up_window()" value="| . $locale->text('Follow-Up') . qq|">
-|;
- } elsif (($transdate > $closedto) && !$form->{id}) {
- print qq|
- <input class=submit type=submit name=action value="| . $locale->text('Post') . qq|"> | .
- NTI($cgi->submit('-name' => 'action', '-value' => $locale->text('Save draft'), '-class' => 'submit'));
- }
- # button for saving history
- if($form->{id} ne "") {
- print qq| <input type="button" class="submit" onclick="set_history_window($form->{id});" name="history" id="history" value="| . $locale->text('history') . qq|"> |;
- }
- # /button for saving history
- # mark_as_paid button
- if($form->{id} ne "") {
- print qq| <input type="submit" class="submit" name="action" value="| . $locale->text('mark as paid') . qq|"> |;
- }
- # /mark_as_paid button
- print "
-</form>
-
-</body>
-</html>
-";
-
- $main::lxdebug->leave_sub();
+ $::lxdebug->leave_sub;
}
sub mark_as_paid {
# use JavaScript Calendar or not
$form->{jsscript} = 1;
- #write Trigger
- my $jsscript = Form->write_trigger(\%myconfig, "2", "transdate", "BL", "trigger1", "reqdate", "BL", "trigger2");
-
my @old_project_ids = ($form->{"globalproject_id"});
map({ push(@old_project_ids, $form->{"project_id_$_"})
if ($form->{"project_id_$_"}); } (1..$form->{"rowcount"}));
};
$row->{donumber}->{link} = $edit_url . "&id=" . E($dord->{id}) . "&callback=${callback}";
- $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id}) . "&callback=${callback}";
-
+ $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id}) . "&callback=${callback}" if $dord->{oe_id};
$report->add_data($row);
$idx++;
my $row_set = [ $row ];
- if (($form->{l_subtotal} eq 'Y')
+ if ( ($form->{l_subtotal} eq 'Y' && !$form->{report_generator_csv_options_for_import} )
&& (($idx == (scalar @{ $form->{GL} } - 1))
|| ($ref->{ $form->{sort} } ne $form->{GL}->[$idx + 1]->{ $form->{sort} }))) {
push @{ $row_set }, create_subtotal_row(\%subtotals, \@columns, \%column_alignment, [ qw(debit credit) ], 'listsubtotal');
$idx++;
}
- $report->add_separator();
-
# = 0 for balanced ledger
my $balanced_ledger = $totals{debit} + $totals{debit_tax} - $totals{credit} - $totals{credit_tax};
$data .= $sh;
$row->{balance}->{data} = $data;
-
- $report->add_data($row);
+
+ if ( !$form->{report_generator_csv_options_for_import} ) {
+ $report->add_separator();
+ $report->add_data($row);
+ }
my $raw_bottom_info_text;
$idx++;
}
- if ($form->{"l_linetotal"}) {
+ if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
use POSIX qw(strftime);
use List::Util qw(sum first);
+use SL::AM;
use SL::VK;
use SL::IS;
use SL::ReportGenerator;
$form->{title} = $locale->text('Sales Report');
@columns =
- qw(description invnumber transdate customernumber customername partnumber partsgroup country business transdate qty unit sellprice sellprice_total discount lastcost lastcost_total marge_total marge_percent employee salesman);
+ qw(description invnumber transdate customernumber customername partnumber partsgroup country business transdate qty parts_unit sellprice sellprice_total discount lastcost lastcost_total marge_total marge_percent employee salesman);
my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs_ic }, @{ $cvar_configs_ct };
my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs_ic }, @{ $cvar_configs_ct };
'invnumber' => { 'text' => $locale->text('Invoice Number'), },
'transdate' => { 'text' => $locale->text('Invoice Date'), },
'qty' => { 'text' => $locale->text('Quantity'), },
- 'unit' => { 'text' => $locale->text('Unit'), },
+ 'parts_unit' => { 'text' => $locale->text('Base unit'), },
'sellprice' => { 'text' => $locale->text('Sales price'), },
'sellprice_total' => { 'text' => $locale->text('Sales net amount'), },
'lastcost_total' => { 'text' => $locale->text('Purchase net amount'), },
map { $column_defs{$_}->{visible} = $form->{"l_$_"} eq 'Y' } @columns;
- my %column_alignment = map { $_ => 'right' } qw(lastcost sellprice sellprice_total lastcost_total unit discount marge_total marge_percent qty);
+ my %column_alignment = map { $_ => 'right' } qw(lastcost sellprice sellprice_total lastcost_total parts_unit discount marge_total marge_percent qty);
# so now the check-box "Description" is only used as switch for part description in invoice-mode
my $idx = 0;
+ my $basefactor;
+ my $all_units = AM->retrieve_all_units();
+
foreach my $ar (@{ $form->{AR} }) {
+ $basefactor = $all_units->{$ar->{unit}}->{factor} / $all_units->{$ar->{parts_unit}}->{factor};
+ $basefactor = 1 unless $basefactor;
$ar->{price_factor} = 1 unless $ar->{price_factor};
# calculate individual sellprice
# discount was already accounted for in db sellprice
- $ar->{sellprice} = $ar->{sellprice} / $ar->{price_factor};
+ $ar->{sellprice} = $ar->{sellprice} / $ar->{price_factor} / $basefactor;
$ar->{lastcost} = $ar->{lastcost} / $ar->{price_factor};
- $ar->{sellprice_total} = $ar->{qty} * ( $ar->{fxsellprice} * ( 1 - $ar->{discount} ) ) ;
- $ar->{lastcost_total} = $ar->{qty} * $ar->{lastcost};
+ $ar->{sellprice_total} = $ar->{qty} * ( $ar->{fxsellprice} * ( 1 - $ar->{discount} ) ) / $ar->{price_factor};
+ $ar->{lastcost_total} = $ar->{qty} * $ar->{lastcost} * $basefactor;
# marge_percent wird neu berechnet, da Wert in invoice leer ist (Bug)
$ar->{marge_percent} = $ar->{sellprice_total} ? (($ar->{sellprice_total}-$ar->{lastcost_total}) / $ar->{sellprice_total} * 100) : 0;
# marge_total neu berechnen
# wird laufend bei jeder Position neu berechnet
$totals{marge_percent} = $totals{sellprice_total} ? ( ($totals{sellprice_total} - $totals{lastcost_total}) / $totals{sellprice_total} ) * 100 : 0;
+ #passt die qty an die gewählte Einheit an
+ #qty wurde bisher noch für andere Berechnungen benötigt und daher erst am Schluss überschrieben
+ $ar->{qty} *= $basefactor;
+
map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, 2) } qw(marge_total marge_percent);
map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, $form->{"decimalplaces"} )} qw(lastcost sellprice sellprice_total lastcost_total);
my $row_set = [ { map { $_ => { 'data' => $entry->{$_}, 'align' => $column_alignment{$_} } } @columns } ];
- if (($form->{subtotal} eq 'Y')
+ if ( ($form->{subtotal} eq 'Y' && !$form->{report_generator_csv_options_for_import} )
&& (($idx == (scalar @contents - 1))
|| ($entry->{$sort_col} ne $contents[$idx + 1]->{$sort_col}))) {
$idx++;
}
- if ($column_defs{stock_value}->{visible}) {
+ if ( $column_defs{stock_value}->{visible} && !$form->{report_generator_csv_options_for_import} ) {
$report->add_separator();
my $row = { map { $_ => { 'data' => '', 'class' => 'listsubtotal', } } @columns };
background-color:#D1D1D1;\r
}\r
\r
-\r
+.DHTMLSuite_menuItem_textContent\r
+{\r
+ border-bottom-style: none !important;\r
+ background-color: inherit !important;\r
+ color: inherit !important;\r
+}\r
background-color:#6A8CCB; /* background color for the separator - blue */\r
}\r
\r
-\r
+.DHTMLSuite_menuItem_textContent\r
+{\r
+ border-bottom-style: none !important;\r
+ background-color: inherit !important;\r
+ color: inherit !important;\r
+}\r
}else{ \r
/* Add events */\r
var tmpVar = this.objectIndex/1;\r
- this.divElement.onclick = function(e) { DHTMLSuite.variableStorage.arrayOfDhtmlSuiteObjects[tmpVar].__navigate(e); }\r
+ //this.divElement.onclick = function(e) { DHTMLSuite.variableStorage.arrayOfDhtmlSuiteObjects[tmpVar].__navigate(e); }\r
this.divElement.onmousedown = this.__clickMenuItem; // on mouse down effect\r
this.divElement.onmouseup = this.__rolloverMenuItem; // on mouse up effect\r
this.divElement.onmouseover = this.__rolloverMenuItem; // mouse over effect\r
parentEl.style.backgroundPosition = 'left center'; \r
}\r
if(this.modelItemRef.itemText){\r
- var div = document.createElement('DIV');\r
+ var div;\r
+ if( this.modelItemRef.url )\r
+ {\r
+ div = document.createElement('a');\r
+ div.href = this.modelItemRef.url;\r
+ div.target = this.modelItemRef.frameTarget;\r
+ div.style.display = 'block';\r
+ }\r
+ else\r
+ div = document.createElement('div');\r
+ \r
div.className = 'DHTMLSuite_textContent';\r
div.innerHTML = this.modelItemRef.itemText; \r
div.className = this.cssPrefix + 'menuItem_textContent';\r
'Filter for customer variables' => 'Filter für benutzerdefinierte Kundenvariablen',
'Filter for item variables' => 'Filter für benutzerdefinierte Artikelvariablen',
'Finish' => 'Abschließen',
+ 'First 20 Lines' => 'Nur erste 20 Datensätze',
'Fix transaction' => 'Buchung korrigieren',
'Fix transactions' => 'Buchungen korrigieren',
'Folgekonto' => 'Folgekonto',
'From' => 'Von',
'From Date' => 'Von',
'Full Access' => 'Vollzugriff',
+ 'Full Preview' => 'Alles',
'Full access to all functions' => 'Vollzugriff auf alle Funktionen',
'Fwd' => 'Vorwärts',
'GL Transaction' => 'Dialogbuchung',
'Hardcopy' => 'Seite drucken',
'Has serial number' => 'Hat eine Serienummer',
'Heading' => 'Überschrift',
- 'Headings' => 'Überschriften',
'Help' => 'Hilfe',
'Help Template Variables' => 'Hilfe zu Dokumenten-Variablen',
'Help on column names' => 'Hilfe zu Spaltennamen',
'Language missing!' => 'Sprache fehlt!',
'Language saved!' => 'Sprache gespeichert!',
'Languages' => 'Sprachen',
+ 'Last Action' => 'Letzte Aktivität',
'Last Article Number' => 'Letzte Artikelnummer',
'Last Cost' => 'Einkaufspreis',
'Last Credit Note Number' => 'Letzte Gutschriftnummer',
'Main sorting' => 'Hauptsortierung',
'Make' => 'Lieferant',
'Make (with X being a number)' => 'Lieferant (X ist eine fortlaufende Zahl)',
+ 'Make compatible for import' => 'Für den Import kompatibel machen',
'Make default profile' => 'Zu Standardprofil machen',
'Manage Custom Variables' => 'Benutzerdefinierte Variablen',
'Mandantennummer' => 'Mandantennummer',
'On Hand' => 'Auf Lager',
'On Order' => 'Ist bestellt',
'One or more Perl modules missing' => 'Ein oder mehr Perl-Module fehlen',
+ 'Only Warnings and Errors' => 'Nur Warnungen und Fehler',
'Only due follow-ups' => 'Nur fällige Wiedervorlagen',
'Only shown in item mode' => 'werden nur im Artikelmodus angezeigt',
'Oops. No valid action found to dispatch. Please report this case to the Lx-Office team.' => 'Ups. Es wurde keine gültige Funktion zum Aufrufen gefunden. Bitte berichten Sie diesen Fall den Lx-Office-Entwicklern.',
'Prepare bank transfer via SEPA XML' => 'Überweisung via SEPA XML vorbereiten',
'Prepayment' => 'Vorauszahlung',
'Preview' => 'Druckvorschau',
+ 'Preview Mode' => 'Vorschaumodus',
'Previous transdate text' => 'wurde gespeichert am',
'Previous transnumber text' => 'Letzte Buchung mit der Buchungsnummer',
'Price' => 'Preis',
'not configured' => 'nicht konfiguriert',
'not delivered' => 'nicht geliefert',
'not executed' => 'nicht ausgeführt',
+ 'not logged in' => 'nicht eingeloggt',
'not transferred in yet' => 'noch nicht eingelagert',
'not transferred out yet' => 'noch nicht ausgelagert',
'not yet executed' => 'Noch nicht ausgeführt',
use SL::BackgroundJob::ALL;
use SL::Form;
use SL::Helper::DateTime;
+use SL::InstanceConfiguration;
use SL::LXDebug;
use SL::LxOfficeConf;
use SL::Locale;
package main;
- $::lxdebug = LXDebug->new;
- $::locale = Locale->new($::lx_office_conf{system}->{language});
- $::form = Form->new;
- $::auth = SL::Auth->new;
- $::request = { cgi => CGI->new({}) };
+ $::lxdebug = LXDebug->new;
+ $::locale = Locale->new($::lx_office_conf{system}->{language});
+ $::form = Form->new;
+ $::auth = SL::Auth->new;
+ $::instance_conf = SL::InstanceConfiguration->new;
+ $::request = { cgi => CGI->new({}) };
die 'cannot reach auth db' unless $::auth->session_tables_present;
--- /dev/null
+-- @tag: custom_variables_sub_module_not_null
+-- @description: sub_module in custom_variables auf NOT NULL ändern.
+-- @encoding: utf-8
+-- @depends: release_2_7_0
+UPDATE custom_variables SET sub_module = '' WHERE sub_module IS NULL;
+ALTER TABLE custom_variables ALTER COLUMN sub_module SET DEFAULT '';
+ALTER TABLE custom_variables ALTER COLUMN sub_module SET NOT NULL;
+
use Data::Dumper;
use SL::LxOfficeConf;
use SL::InstanceConfiguration;
-SL::LxOfficeConf->read;
sub _login {
my $login = shift;
}
sub login {
+ SL::LxOfficeConf->read;
+
my $login = shift || $::lx_office_conf{testing}{login} || 'demo';
_login($login);
}
--- /dev/null
+use strict;
+use utf8;
+
+use lib 't';
+use lib 'modules/fallback';
+BEGIN {
+ unshift @INC, 'modules/override';
+}
+
+use Support::TestSetup;
+use Test::More tests => 2;
+use Data::Dumper;
+require Test::Deep;
+use Encode;
+
+use SL::Request;
+
+Support::TestSetup::login();
+
+open my $fh, '<', 't/request/post_multipart_1' or die "can't load test";
+my $data = do { $/ = undef; <$fh> };
+
+my $t = {};
+my $tt = {};
+
+local $ENV{CONTENT_TYPE} = 'multipart/form-data; boundary=---------------------------23281168279961';
+SL::Request::_parse_multipart_formdata($t, $tt, $data);
+
+
+my $blob = Encode::encode('utf-8', qq|\x{feff}Stunde;Montag;Dienstag;Mittwoch;Donnerstag;Freitag
+1;Mathe;Deutsch;Englisch;Mathe;Kunst
+2;Sport;Französisch;Geschichte;Sport;Geschichte
+3;Sport;"Religion ev;kath";Kunst;;Kunst|);
+
+my $t_cmp = {
+ 'profile' => {
+ 'name' => undef,
+ 'type' => undef
+ },
+ 'quote_char' => undef,
+ 'file' => $blob,
+ 'custom_sep_char' => undef,
+ 'sep_char' => undef,
+ 'settings' => {
+ 'article_number_policy' => undef,
+ 'sellprice_places' => undef,
+ 'charset' => undef,
+ 'apply_buchungsgruppe' => undef,
+ 'full_preview' => undef,
+ 'parts_type' => undef,
+ 'default_unit' => undef,
+ 'default_buchungsgruppe' => undef,
+ 'duplicates' => undef,
+ 'numberformat' => undef,
+ 'sellprice_adjustment_type' => undef,
+ 'shoparticle_if_missing' => undef,
+ 'sellprice_adjustment' => undef
+ },
+ 'custom_escape_char' => undef,
+ 'action_test' => undef,
+ 'custom_quote_char' => undef,
+ 'escape_char' => undef,
+ 'action' => undef
+ };
+$t_cmp->{ATTACHMENTS}{file}{data} = \$t_cmp->{'file'};
+
+
+is_deeply $t, $t_cmp;
+
+is_deeply $tt,
+ {
+ 'profile' => {
+ 'name' => '',
+ 'type' =>'parts',
+ },
+ 'file' => undef,
+ 'quote_char' => 'quote',
+ 'custom_sep_char' => '',
+ 'sep_char' => 'semicolon',
+ 'settings' => {
+ 'article_number_policy' => 'update_prices',
+ 'sellprice_places' => 2,
+ 'charset' => 'UTF-8',
+ 'apply_buchungsgruppe' => 'all',
+ 'full_preview' => '0',
+ 'parts_type' => 'part',
+ 'default_unit' => 'g',
+ 'default_buchungsgruppe' => '815',
+ 'duplicates' => 'no_check',
+ 'numberformat' => '1.000,00',
+ 'sellprice_adjustment_type' => 'percent',
+ 'shoparticle_if_missing' => '0',
+ 'sellprice_adjustment' =>'0'
+ },
+ 'custom_escape_char' => '',
+ 'action_test' => 'Test und Vorschau',
+ 'ATTACHMENTS' => {
+ 'file' => {
+ 'filename' => 'from_wikipedia.csv'
+ }
+ },
+ 'custom_quote_char' => '',
+ 'escape_char' => 'quote',
+ 'action' => 'CsvImport/dispatch',
+ 'FILENAME' => 'from_wikipedia.csv'
+ };
+
--- /dev/null
+-----------------------------23281168279961
+Content-Disposition: form-data; name="action"
+
+CsvImport/dispatch
+-----------------------------23281168279961
+Content-Disposition: form-data; name="profile.type"
+
+parts
+-----------------------------23281168279961
+Content-Disposition: form-data; name="profile.name"
+
+
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.numberformat"
+
+1.000,00
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.charset"
+
+UTF-8
+-----------------------------23281168279961
+Content-Disposition: form-data; name="sep_char"
+
+semicolon
+-----------------------------23281168279961
+Content-Disposition: form-data; name="custom_sep_char"
+
+
+-----------------------------23281168279961
+Content-Disposition: form-data; name="quote_char"
+
+quote
+-----------------------------23281168279961
+Content-Disposition: form-data; name="custom_quote_char"
+
+
+-----------------------------23281168279961
+Content-Disposition: form-data; name="escape_char"
+
+quote
+-----------------------------23281168279961
+Content-Disposition: form-data; name="custom_escape_char"
+
+
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.duplicates"
+
+no_check
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.article_number_policy"
+
+update_prices
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.sellprice_places"
+
+2
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.sellprice_adjustment"
+
+0
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.sellprice_adjustment_type"
+
+percent
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.shoparticle_if_missing"
+
+0
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.parts_type"
+
+part
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.default_buchungsgruppe"
+
+815
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.apply_buchungsgruppe"
+
+all
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.default_unit"
+
+g
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.full_preview"
+
+0
+-----------------------------23281168279961
+Content-Disposition: form-data; name="file"; filename="from_wikipedia.csv"
+Content-Type: text/comma-separated-values
+
+Stunde;Montag;Dienstag;Mittwoch;Donnerstag;Freitag
+1;Mathe;Deutsch;Englisch;Mathe;Kunst
+2;Sport;Französisch;Geschichte;Sport;Geschichte
+3;Sport;"Religion ev;kath";Kunst;;Kunst
+-----------------------------23281168279961
+Content-Disposition: form-data; name="action_test"
+
+Test und Vorschau
+-----------------------------23281168279961--
<th class="listtop">[% 'Language' | $T8 %]</th>
<th class="listtop">[% 'Dataset' | $T8 %]</th>
<th class="listtop">[% 'Host' | $T8 %]</th>
+ <th class="listtop">[% 'Last Action' | $T8 %]</th>
<!-- <th class="listtop">[% 'Driver' | $T8 %]</th> -->
</tr>
<td> [% HTML.escape(row.countrycode) %]</td>
<td> [% HTML.escape(row.dbname) %]</td>
<td> [% IF row.dbhost %][% HTML.escape(row.dbhost) %][% ELSE %]localhost[% END %]</td>
+ <td>
+ [% IF( row.last_action ) %]
+ [% HTML.escape(row.last_action) %]
+ [% ELSE %]
+ [% 'not logged in' | $T8 %]
+ [% END %]</td>
<!-- <td> [% HTML.escape(row.dbdriver) %]</td> -->
</tr>
[% END %]
--- /dev/null
+[%- USE T8 %]
+[%- USE HTML %]
+[%- USE L %]
+[%- USE LxERP %]
+
+[%- IF (num_follow_ups && num_due) %]
+ <p>[% 'There are #1 unfinished follow-ups of which #2 are due.' | $T8(num_follow_ups, num_due) %]</p>
+[%- END %]
+
+<input name=callback type=hidden value="[% callback | html %]">
+<input name=gldate type=hidden value="[% gldate | html %]">
+<input type=hidden name=draft_id value="[% draft_id %]">
+<input type=hidden name=draft_description value="[% draft_description | html %]">
+
+[%- IF ( !id && draft_id ) %]
+ [% L.checkbox_tag('remove_draft', checked=remove_draft, label=LxERP.t8('Remove draft when posting')) %]
+ <br>
+[%- END %]
+
+<br>
+
+<input class="submit" type="submit" name="action" id="update_button" value="[% 'Update' | $T8 %]">
+
+[%- IF id %]
+ [%- IF radier %]
+ <input class=submit type=submit name=action value="[% 'Post' | $T8 %]">
+ <input class=submit type=submit name=action value="[% 'Delete' | $T8 %]">
+ [%- END %]
+
+ [%- IF show_storno %]
+ <input class=submit type=submit name=action value="[% 'Storno' | $T8 %]">
+ [%- END %]
+
+ <input class=submit type=submit name=action value="[% 'Post Payment' | $T8 %]">
+ <input class=submit type=submit name=action value="[% 'Use As Template' | $T8 %]">
+ <input type="button" class="submit" onclick="follow_up_window()" value="[% 'Follow-Up' | $T8 %]">
+
+[%- ELSIF show_post_draft %]
+ <input class=submit type=submit name=action value="[% 'Post' | $T8 %]">
+ <input type="submit" name="action" value="[% 'Save draft' | $T8 %]" class="submit">
+[%- END %]
+
+[%- IF id %]
+ <input type="submit" onclick="set_history_window([% id %]);" name="history" id="history" value="[% 'history' | $T8 %]">
+ <input type="submit" name="action" value="[% 'mark as paid' | $T8 %]">
+[%- END %]
+
+</form>
+
+<script type="text/javascript">
+<!--
+function set_duedate() {
+ $.ajax({
+ url: 'is.pl?action=set_duedate',
+ data: {
+ invdate: $('#transdate').val(),
+ vendor_id: $('[name=vendor_id]').val(),
+ },
+ dataType: 'text',
+ success: function(data) {
+ $('#duedate').val(data);
+ }
+ });
+ }
+//-->
+</script>
+
+
+</body>
+</html>
</tr>
[%- FOREACH row = SELF.data %]
+ [%- IF (SELF.profile.get('full_preview') == 2) || ((SELF.profile.get('full_preview') == 1) && (row.errors.size || row.information.size)) || ((SELF.profile.get('full_preview') == 0) && (loop.count < 21)) %]
<tr class="[% IF row.errors.size %]redrow[% ELSE %]listrow[% END %][% loop.count % 2 %]">
[%- FOREACH method = SELF.info_headers.methods %]
<td>[%- HTML.escape(row.info_data.$method) %]</td>
</td>
</tr>
[%- END %]
+ [%- END %]
</table>
[%- END %]
[%- INCLUDE 'csv_import/_form_customers_vendors.html' %]
[%- END %]
+ <tr>
+ <th align="right">[%- LxERP.t8('Preview Mode') %]:</th>
+ <td colspan="10">
+ [% L.radio_button_tag('settings.full_preview', value=2, checked=SELF.profile.get('full_preview')==2, label=LxERP.t8('Full Preview')) %]
+ [% L.radio_button_tag('settings.full_preview', value=1, checked=SELF.profile.get('full_preview')==1, label=LxERP.t8('Only Warnings and Errors')) %]
+ [% L.radio_button_tag('settings.full_preview', value=0, checked=!SELF.profile.get('full_preview'), label=LxERP.t8('First 20 Lines')) %]
+ </td>
+ </tr>
+
<tr>
<th align="right">[%- LxERP.t8('Import file') %]:</th>
<td colspan="10">[% L.input_tag('file', '', type => 'file', accept => '*') %]</td>
+[%- IF SAVED_MESSAGE %]
+ <p>[% SAVED_MESSAGE %]</p>
+[%- END %]
+
[%- IF OPTIONS.size %]
<p>
[%- FOREACH option = OPTIONS %]
<td valign="top">
<input type="checkbox" name="report_generator_csv_options_headers" id="report_generator_csv_options_headers" value="1" checked>
<label for="report_generator_csv_options_headers">[% 'Include column headings' | $T8 %]</label>
+ <input type="checkbox" name="report_generator_csv_options_for_import" id="report_generator_csv_options_for_import" value="1">
+ <label for="report_generator_csv_options_for_import">[% 'Make compatible for import' | $T8 %]</label>
</td>
</tr>
<option value="month">[% 'Month' | $T8 %]</option>
</select>
</td>
- <td align=left><input name="l_headers_mainsort" class=checkbox type=checkbox value="Y" checked> [% 'Heading' | $T8 %]</td>
- <td align=left><input name="l_subtotal_mainsort" class=checkbox type=checkbox value="Y" checked> [% 'Subtotal' | $T8 %]</td>
+ <td align=left><input name="l_headers_mainsort" class=checkbox type=checkbox value=Y checked> [% 'Heading' | $T8 %]</td>
+ <td align=left><input name="l_subtotal_mainsort" class=checkbox type=checkbox value=Y checked> [% 'Subtotal' | $T8 %]</td>
</tr>
<tr>
<td align="right">[% 'Secondary sorting' | $T8 %]</td>
</tr>
<tr>
<th align="right">[% 'Item mode' | $T8 %]</th>
- <td colspan="3" align=left><input name="l_parts" class=checkbox type=checkbox value="Y"> ([%'Show items from invoices individually' | $T8 %]) </td>
+ <td colspan="3" align=left><input name="l_parts" class=checkbox type=checkbox value=Y> ([%'Show items from invoices individually' | $T8 %]) </td>
</tr>
<tr>
<th align="right">
[% 'Total sum' | $T8 %]
</th>
- <td colspan="1" align=left><input name="l_total" class=checkbox type=checkbox value="Y" checked></td>
+ <td colspan="1" align=left><input name="l_total" class=checkbox type=checkbox value=Y checked></td>
<td align="right" nowrap>[% 'Decimalplaces' | $T8 %]: </td>
<td colspan="2"><input name="decimalplaces" size="2" value="2"></td>
</tr>
<td colspan="4">([% 'averaged values, in invoice mode only useful when filtered by a part' | $T8 %])</td>
</tr>
<tr>
- <td align=left><input name="l_qty" class=checkbox type=checkbox value="Y" checked>[% 'Quantity' | $T8 %]</td>
- <td align=left><input name="l_discount" class=checkbox type=checkbox value="Y">[% 'Discount' | $T8 %]</td>
+ <td align=left><input name="l_qty" class=checkbox type=checkbox value=Y checked>[% 'Quantity' | $T8 %]</td>
+ <td align=left><input name="l_discount" class=checkbox type=checkbox value=Y>[% 'Discount' | $T8 %]</td>
<td></td>
<td colspan="4">([% 'averaged values, in invoice mode only useful when filtered by a part' | $T8 %])</td>
</tr>
<td align=left><input name="l_description" class=checkbox type=checkbox value=Y checked>[% 'Description' | $T8 %]</td>
<td align=left><input name="l_partnumber" class=checkbox type=checkbox value=Y>[% 'Part Number' | $T8 %]</td>
<td align=left><input name="l_invnumber" class=checkbox type=checkbox value=Y>[% 'Invnumber' | $T8 %]</td>
- <td align=left><input name="l_transdate" class=checkbox type=checkbox value="Y">[% 'Invdate' | $T8 %]</td>
+ <td align=left><input name="l_transdate" class=checkbox type=checkbox value=Y>[% 'Invdate' | $T8 %]</td>
</tr>
<tr>
- <td align=left><input name="l_unit" class=checkbox type=checkbox value="Y">[% 'Unit' | $T8 %]</td>
+ <td align=left><input name="l_parts_unit" class=checkbox type=checkbox value=Y>[% 'Base unit' | $T8 %]</td>
<td align=left><input name="l_partsgroup" class=checkbox type=checkbox value=Y>[% 'Group' | $T8 %]</td>
<td align=left><input name="l_salesman" class=checkbox type=checkbox value=Y>[% 'Salesperson' | $T8 %]</td>
<td align=left><input name="l_employee" class=checkbox type=checkbox value=Y>[% 'Employee' | $T8 %]</td>