]> wagnertech.de Git - mfinanz.git/commitdiff
Merge branch 'master' of vc.linet-services.de:public/lx-office-erp
authorG. Richardson <information@lx-office-hosting.de>
Thu, 8 Nov 2012 10:34:18 +0000 (11:34 +0100)
committerG. Richardson <information@lx-office-hosting.de>
Thu, 8 Nov 2012 10:34:18 +0000 (11:34 +0100)
23 files changed:
.gitignore
SL/CT.pm
SL/Controller/CsvImport/Base.pm
SL/DBUpgrade2.pm
SL/Helper/Csv.pm
SL/Helper/Csv/Dispatcher.pm
SL/IS.pm
SL/Menu.pm
SL/Template/OpenDocument.pm
bin/mozilla/am.pl
bin/mozilla/arap.pl
bin/mozilla/ic.pl
bin/mozilla/io.pl
bin/mozilla/wh.pl
locale/de/all
locale/de_DE/all
menu.ini
scripts/dbupgrade2_tool.pl
t/helper/csv.t
templates/webpages/admin/create_dataset.html
templates/webpages/am/edit_accounts.html
templates/webpages/ic/form_header.html
templates/webpages/menu/menunew.html

index 6c00133f2a0c25e7f6ccfed95fc21e3f311005c0..bc497aea137eea38d452e2ca1c473603fbba0766 100644 (file)
@@ -9,3 +9,4 @@ crm
 /doc/online/*/*.html
 pod2html*
 /doc/build/dobudish*
+/spool/*
index de10623f2c10f0ca6d4a1ad0d7c58ed987a02a37..93c3fcea2a2c1073198529e5114b6fc9c694b3c9 100644 (file)
--- a/SL/CT.pm
+++ b/SL/CT.pm
@@ -598,6 +598,7 @@ sub search {
   my $dbh = $form->dbconnect($myconfig);
 
   my $cv = $form->{db} eq "customer" ? "customer" : "vendor";
+  my $join_records = $form->{l_invnumber} || $form->{l_ordnumber} || $form->{l_quonumber};
 
   my $where = "1 = 1";
   my @values;
@@ -611,7 +612,7 @@ sub search {
   $form->{sort} = $sortorder;
   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
 
-  if ($sortorder !~ /(business|id)/ && 1 >= scalar grep { $form->{$_} } qw(l_ordnumber l_quonumber l_invnumber )) {
+  if ($sortorder !~ /(business|id)/ && !$join_records) {
     $sortorder  = "lower($sortorder) ${sortdir}";
   } else {
     $sortorder .= " ${sortdir}";
@@ -705,21 +706,22 @@ sub search {
 
   my $query =
     qq|SELECT ct.*, b.description AS business | .
+    (qq|, NULL AS invnumber, NULL AS ordnumber, NULL AS quonumber, NULL AS invid, NULL AS module, NULL AS formtype, NULL AS closed | x!! $join_records) .
     qq|FROM $cv ct | .
     qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
     qq|WHERE $where|;
 
   my @saved_values = @values;
   # redo for invoices, orders and quotations
-  if ($form->{l_invnumber} || $form->{l_ordnumber} || $form->{l_quonumber}) {
-    my ($ar, $union, $module);
-    $query = "";
+  if ($join_records) {
+    my $union = "UNION";
 
     if ($form->{l_invnumber}) {
       my $ar = $cv eq 'customer' ? 'ar' : 'ap';
       my $module = $ar eq 'ar' ? 'is' : 'ir';
-
-      $query =
+      push(@values, @saved_values);
+      $query .=
+        qq| UNION | .
         qq|SELECT ct.*, b.description AS business, | .
         qq|  a.invnumber, a.ordnumber, a.quonumber, a.id AS invid, | .
         qq|  '$module' AS module, 'invoice' AS formtype, | .
@@ -728,16 +730,12 @@ sub search {
         qq|JOIN $ar a ON (a.${cv}_id = ct.id) | .
         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
         qq|WHERE $where AND (a.invoice = '1')|;
-
-      $union = qq|UNION|;
     }
 
     if ( $form->{l_ordnumber} ) {
-      if ($union eq "UNION") {
-        push(@values, @saved_values);
-      }
+      push(@values, @saved_values);
       $query .=
-        qq| $union | .
+        qq| UNION | .
         qq|SELECT ct.*, b.description AS business,| .
         qq|  ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
         qq|  'oe' AS module, 'order' AS formtype, o.closed | .
@@ -745,16 +743,12 @@ sub search {
         qq|JOIN oe o ON (o.${cv}_id = ct.id) | .
         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
         qq|WHERE $where AND (o.quotation = '0')|;
-
-      $union = qq|UNION|;
     }
 
     if ( $form->{l_quonumber} ) {
-      if ($union eq "UNION") {
-        push(@values, @saved_values);
-      }
+      push(@values, @saved_values);
       $query .=
-        qq| $union | .
+        qq| UNION | .
         qq|SELECT ct.*, b.description AS business, | .
         qq|  ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
         qq|  'oe' AS module, 'quotation' AS formtype, o.closed | .
index f84d54b0e01421f125d3ad9533cfb1b0280d6afc..656495e3761eee6490e3f96e816b794b4f631977 100644 (file)
@@ -29,6 +29,7 @@ sub run {
                                   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),
                                  ));
 
@@ -212,6 +213,12 @@ sub init_profile {
     $profile{$col} = $name;
   }
 
+  if ($self->can('all_cvar_configs')) {
+    for (@{ $self->all_cvar_configs }) {
+      $profile{ 'cvar_' . $_->name } = '';
+    }
+  }
+
   $self->profile(\%profile);
 }
 
index a39ef931b5f275dffd5ed6645facec9ce9d449f5..a84f6b13877546b9e4377c1de35ec1fd2f87ed73 100644 (file)
@@ -25,12 +25,17 @@ sub init {
 
   $params{path_suffix} ||= '';
   $params{schema}      ||= '';
+  $params{path}          = "sql/" . $params{dbdriver} . "-upgrade2" . $params{path_suffix};
 
   map { $self->{$_} = $params{$_} } keys %params;
 
   return $self;
 }
 
+sub path {
+  $_[0]{path};
+}
+
 sub parse_dbupdate_controls {
   $::lxdebug->enter_sub();
 
@@ -42,7 +47,7 @@ sub parse_dbupdate_controls {
   local *IN;
   my %all_controls;
 
-  my $path = "sql/" . $self->{dbdriver} . "-upgrade2" . $self->{path_suffix};
+  my $path = $self->path;
 
   foreach my $file_name (<$path/*.sql>, <$path/*.pl>) {
     next unless (open(IN, $file_name));
index 5b629ee63b6f9dd181baba6cc475c1aa5f837895..e48161492a9703e94ef544820a7cd1efcb248880 100644 (file)
@@ -10,7 +10,7 @@ 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
+  _objects _parsed _data _errors all_cvar_configs case_insensitive_header
 ) ];
 
 use SL::Helper::Csv::Dispatcher;
@@ -33,6 +33,7 @@ sub new {
     dateformat             => 0,
     ignore_unknown_columns => 0,
     strict_profile         => 0,
+    case_insensitive_header => 0,
   });
   my $self = bless {}, $class;
 
@@ -120,7 +121,25 @@ sub _check_header {
   }
 
   return unless $header;
-  return $self->header([ map { lc } @$header ]);
+
+  # Special case: human stupidity
+  # people insist that case sensitivity doesn't exist and try to enter all
+  # sorts of stuff. at this point we've got a profile (with keys that represent
+  # valid methods), and a header full of strings. if two of them match, the user
+  # 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;
+      }
+    }
+  }
+
+  return $self->header($header);
 }
 
 sub _parse_data {
@@ -345,6 +364,12 @@ these information are unique, and should be connected to preexisting data, you
 will have to do that for yourself. Since you provided the profile, it is
 assumed you know what to do in this case.
 
+If no profile is given, any header field found will be taken as is.
+
+If the path in a profile entry is empty, the field will be subjected to
+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,
@@ -355,11 +380,24 @@ and the return value used instead of the line itself.
 If set, the import will ignore unkown header columns. Useful for lazy imports,
 but deactivated by default.
 
+=item C<case_insensitive_header>
+
+If set, header columns will be matched against profile entries case
+insensitive, and on match the profile name will be taken.
+
+Only works if a profile is given, will die otherwise.
+
+If both C<case_insensitive_header> and C<strict_profile> is set, matched header
+columns will be accepted.
+
 =item C<strict_profile>
 
 If set, all columns to be parsed must be specified in C<profile>. Every header
 field not listed there will be treated like an unknown column.
 
+If both C<case_insensitive_header> and C<strict_profile> is set, matched header
+columns will be accepted.
+
 =back
 
 =head1 ERROR HANDLING
index be3faddd63d5561c78f5703c4511e6b8961f2879..add444b9d751d670410cacf302979f5177acf89d 100644 (file)
@@ -78,7 +78,11 @@ sub parse_profile {
         $self->unknown_column($col, undef);
       }
     } else {
-      push @specs, $self->make_spec($col, $profile->{$col} || $col);
+      if (exists $profile->{$col}) {
+        push @specs, $self->make_spec($col, $profile->{$col});
+      } else {
+        push @specs, $self->make_spec($col, $col);
+      }
     }
   }
 
@@ -91,6 +95,9 @@ sub make_spec {
   my ($self, $col, $path) = @_;
 
   my $spec = { key => $col, steps => [] };
+
+  return unless $path;
+
   my $cur_class = $self->_csv->class;
 
   return unless $cur_class;
index db18d5a43816a36572fc8988bd55c013df941d16..616e2bf6ffec4193dd96762ddb0c7ba1bdad614b 100644 (file)
--- a/SL/IS.pm
+++ b/SL/IS.pm
@@ -2028,7 +2028,7 @@ sub get_pricegroups_for_parts {
 
     my $pricegroup_old = $form->{"pricegroup_old_$i"};
 
-    # sellprice has format 13,0000 or 0,00000, can't check for 0 numerically
+    # sellprice has format 13,0000 or 0,00000,  can't check for 0 numerically
     my $sellprice = $form->{"sellprice_$i"};
     my $pricegroup_id = $form->{"pricegroup_id_$i"};
     $form->{"new_pricegroup_$i"} = $selectedpricegroup_id;
@@ -2130,14 +2130,12 @@ sub get_pricegroups_for_parts {
 
           $pkr->{selected}  = ' selected'; # unless $form->{selected};
           # no customer pricesgroup set
-          if ($pkr->{price_unfmt} == $pkr->{default_sellprice}) {
+          if ($pkr->{price_unfmt} == $pkr->{default_sellprice} || $form->{'sellprice_'.$i} * 1 > 1) {
 
             $pkr->{price} = $form->{"sellprice_$i"};
 
           } else {
 
-# this sub should not set anything and only return. --sschoeling, 20090506
-# is this correct? put in again... -- grichardson 20110119
             $form->{"sellprice_$i"} = $pkr->{price};
           }
 
index 48ac6229daa2b672aa74ad6fb7cdbb24883c1fed..57d8b4e8aa059b1ac044bac668e5df8dfb17f78f 100644 (file)
@@ -59,7 +59,7 @@ sub new {
 }
 
 sub menuitem_new {
-  $main::lxdebug->enter_sub();
+  $main::lxdebug->enter_sub(LXDebug::DEBUG2());
 
   my ($self, $name, $item) = @_;
 
@@ -84,7 +84,7 @@ sub menuitem_new {
     $item->{href}      .= "&" . $form->escape($key) . "=" . $form->escape($value);
   }
 
-  $main::lxdebug->leave_sub();
+  $main::lxdebug->leave_sub(LXDebug::DEBUG2());
 }
 
 sub access_control {
index 12a7bc8c58d78fcf3e53ae76204099c0c5a55d87..b4ea1f2a5d24d2f4a64da6c224766354a8c9f562 100644 (file)
@@ -270,14 +270,14 @@ sub parse {
 
   $zip->contents("content.xml", Encode::encode('utf-8-strict', $new_contents));
 
-  my $styles = $zip->contents("styles.xml");
+  my $styles = Encode::decode('utf-8-strict', $zip->contents("styles.xml"));
   if ($contents) {
     my $new_styles = $self->parse_block($styles);
     if (!defined($new_contents)) {
       $main::lxdebug->leave_sub();
       return 0;
     }
-    $zip->contents("styles.xml", $new_styles);
+    $zip->contents("styles.xml", Encode::encode('utf-8-strict', $new_styles));
   }
 
   $zip->writeToFileNamed($form->{"tmpfile"}, 1);
index c114c7eae02d8d1f7571d83efc0543665770db6a..500547597816ee5a1f0bd441d284ffec3736e242 100644 (file)
@@ -426,10 +426,6 @@ sub save_as_new_account {
   }
 
   $form->{id} = 0;
-  if ($form->{"original_accno"} &&
-      ($form->{"accno"} eq $form->{"original_accno"})) {
-    $form->error($locale->text('Account Number already used!'));
-  }
   $form->redirect($locale->text('Account saved!'))
     if (AM->save_account(\%myconfig, \%$form));
   $form->error($locale->text('Cannot save account!'));
index 27b0e3bfa2003bd48b4d9ce10fc3f47671a943b7..fe5fa2d27634a6a4293f916c9bcab2912b627e07 100644 (file)
@@ -78,6 +78,7 @@ sub check_name {
 
       $form->{"${name}_id"} = $new_id;
 
+      _reset_salesman_id();
       IS->get_customer(\%myconfig, \%$form) if ($name eq 'customer');
       IR->get_vendor(\%myconfig, \%$form) if ($name eq 'vendor');
 
@@ -119,6 +120,7 @@ sub check_name {
         $form->{$name}        = $form->{name_list}[0]->{name};
         $form->{"old$name"}   = qq|$form->{$name}--$form->{"${name}_id"}|;
 
+        _reset_salesman_id();
         IS->get_customer(\%myconfig, \%$form) if ($name eq 'customer');
         IR->get_vendor(\%myconfig, \%$form) if ($name eq 'vendor');
 
@@ -266,6 +268,8 @@ sub name_selected {
   # index for new item
   my $i = $form->{ndx};
 
+  _reset_salesman_id();
+
   $form->{ $form->{vc} }    = $form->{"new_name_$i"};
   $form->{"$form->{vc}_id"} = $form->{"new_id_$i"};
   $form->{"old$form->{vc}"} =
@@ -286,6 +290,14 @@ sub name_selected {
   $main::lxdebug->leave_sub();
 }
 
+# Reset the $::form field 'salesman_id' to the ID of the currently
+# logged in user. Useful when changing to a customer/vendor that has
+# no salesman listed in their master data.
+sub _reset_salesman_id {
+  my $current_employee   = SL::DB::Manager::Employee->current;
+  $::form->{salesman_id} = $current_employee->id if $current_employee && exists $::form->{salesman_id};
+}
+
 sub check_project {
   $main::lxdebug->enter_sub();
 
index 20c670ca39103f05f76c8c4ca40d357517f7394c..b86623b5103673b15198e6e6175311474ea90998 100644 (file)
@@ -38,6 +38,7 @@ use List::MoreUtils qw(any);
 use SL::AM;
 use SL::CVar;
 use SL::IC;
+use SL::Helper::Flash;
 use SL::ReportGenerator;
 
 #use SL::PE;
@@ -1585,6 +1586,10 @@ sub form_header {
   IC->retrieve_buchungsgruppen(\%myconfig, $form);
   @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
 
+  if (!SL::TransNumber->new(number => $form->{partnumber}, type => $form->{item}, id => $form->{id})->is_unique) {
+    flash('info', $::locale->text('This partnumber is not unique. You should change it.'));
+  }
+
   # use JavaScript Calendar or not (yes!)
   $form->{jsscript} = 1;
 
index b518db00edb8b35fad6f71d7f2054a7ca417fab3..37df5546164524bd280da2504e09b1dabd3d701f 100644 (file)
@@ -49,6 +49,7 @@ use SL::IO;
 
 use SL::DB::Language;
 use SL::DB::Printer;
+use SL::Helper::Flash;
 
 require "bin/mozilla/common.pl";
 
@@ -802,6 +803,7 @@ sub validate_items {
 
   # check if items are valid
   if ($form->{rowcount} == 1) {
+    flash('warning', $::locale->text('The action you\'ve chosen has not been executed because the document does not contain any item yet.'));
     &update;
     ::end_of_request();
   }
@@ -1536,23 +1538,27 @@ sub print_form {
   my $emailed = $form->{emailed};
 
   if ($form->{media} eq 'queue') {
-    my %queued = map { s|.*/|| } split / /, $form->{queued};
+    my %queued = map { s|.*[/\\]||; $_ } split / /, $form->{queued};
 
     my $filename;
     my $suffix = ($form->{postscript}) ? '.ps' : '.pdf';
     if ($filename = $queued{ $form->{formname} }) {
-      $form->{queued} =~ s/\Q$form->{formname} $filename\E//;
       unlink $::lx_office_conf{paths}->{spool} . "/$filename";
-      $filename =~ s/\..*$//g;
-      $filename .= $suffix;
-      $form->{OUT} = $::lx_office_conf{paths}->{spool} . "/$filename";
-      $form->{OUT_MODE} = '>';
+      delete $queued{ $form->{formname} };
+
+      $form->{queued}    =  join ' ', %queued;
+      $filename          =~ s/\..*$//g;
+      $filename         .=  $suffix;
+      $form->{OUT}       =  $::lx_office_conf{paths}->{spool} . "/$filename";
+      $form->{OUT_MODE}  =  '>';
+
     } else {
       my $temp_fh;
       ($temp_fh, $filename) = File::Temp::tempfile(
         'kivitendo-spoolXXXXXX',
         SUFFIX => "$suffix",
-        DIR => $::lx_office_conf{paths}->{spool},
+        DIR    => $::lx_office_conf{paths}->{spool},
+        UNLINK => 0,
       );
       close $temp_fh;
       $form->{OUT} = "$filename";
index 0315bb9dcf15b1b458069ce6454d6a6ca6e8ea80..92cfcb69be70140f737ae5703dfeca55a1e1e6be 100644 (file)
@@ -44,6 +44,8 @@ use SL::WH;
 use SL::OE;
 use SL::ReportGenerator;
 
+use SL::DB::Part;
+
 use Data::Dumper;
 
 require "bin/mozilla/common.pl";
@@ -85,8 +87,16 @@ sub transfer_warehouse_selection {
   show_no_warehouses_error() if (!scalar @{ $form->{WAREHOUSES} });
 
   my $units      = AM->retrieve_units(\%myconfig, $form);
+
+  my $part = 0;
+  if ( $form->{parts_id} ) {
+    $part = SL::DB::Part->new();
+    $part->id($form->{parts_id});
+    $part->load();
+  }
+
   # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor
-  $form->{UNITS} = AM->unit_select_data($units, $form->{unit}, 0, $form->{unit});
+  $form->{UNITS} = AM->unit_select_data($units, $form->{unit}, 0, $part ? $part->unit : 0);
 
   if (scalar @{ $form->{WAREHOUSES} }) {
     $form->{warehouse_id} ||= $form->{WAREHOUSES}->[0]->{id};
index f118a533fba315951545526b660fae12e99bd3c9..b378b34ee2b662c48546aaa99db64f7b3f949660 100644 (file)
@@ -87,7 +87,6 @@ $self->{texts} = {
   'Account Link IC_taxpart'     => 'Warenliste Steuer',
   'Account Link IC_taxservice'  => 'Dienstleistungen Steuer',
   'Account Number'              => 'Kontonummer',
-  'Account Number already used!' => 'Kontonummer ist bereits in Benutzung!',
   'Account Number missing!'     => 'Kontonummer fehlt!',
   'Account Nummer'              => 'Kontonummer',
   'Account Type'                => 'Kontoart',
@@ -1041,6 +1040,7 @@ $self->{texts} = {
   'Invoice total less discount' => 'Rechnungssumme abzüglich Skonto',
   'Invoice with Storno (abbreviation)' => 'R(S)',
   'Invoices'                    => 'Rechnungen',
+  'Invoices, Credit Notes & AR Transactions' => 'Rechnungen, Gutschriften & Debitorenbuchungen',
   'Is Searchable'               => 'Durchsuchbar',
   'Is this a summary account to record' => 'Sammelkonto für',
   '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.' => 'Auch nach einer Korrektur kann es mit dieser Buchung noch weitere Probleme geben (z.B. nicht zum Steuerschlüssel passende Steuern), weshalb ein erneutes Ausführen der Hauptbuchanalyse empfohlen wird.',
@@ -1874,6 +1874,7 @@ $self->{texts} = {
   'The access rights have been saved.' => 'Die Zugriffsrechte wurden gespeichert.',
   'The account 3804 already exists, the update will be skipped.' => 'Das Konto 3804 existiert schon, das Update wird übersprungen.',
   'The account 3804 will not be added automatically.' => 'Das Konto 3804 wird nicht automatisch hinzugefügt.',
+  'The action you\'ve chosen has not been executed because the document does not contain any item yet.' => 'Die von Ihnen ausgewählte Aktion wurde nicht ausgeführt, weil der Beleg noch keine Positionen enthält.',
   'The application "#1" was not found on the system.' => 'Die Anwendung "#1" wurde auf dem System nicht gefunden.',
   'The assembly has been created.' => 'Das Erzeugnis wurde hergestellt.',
   'The assistant could not find anything wrong with #1. Maybe the problem has been solved in the meantime.' => 'Der Korrekturassistent konnte kein Problem bei #1 feststellen. Eventuell wurde das Problem in der Zwischenzeit bereits behoben.',
@@ -2057,6 +2058,7 @@ $self->{texts} = {
   'This option controls the inventory system.' => 'Dieser Parameter legt die Warenbuchungsmethode fest.',
   'This option controls the method used for profit determination.' => 'Dieser Parameter legt die Berechnungsmethode für die Gewinnermittlung fest.',
   'This option controls the posting and calculation behavior for the accounting method.' => 'Dieser Parameter steuert die Buchungs- und Berechnungsmethoden für die Versteuerungsart.',
+  'This partnumber is not unique. You should change it.' => 'Diese Artikelnummer ist nicht eindeutig. Bitte wählen Sie eine andere.',
   'This transaction has to be split into several transactions manually.' => 'Diese Buchung muss manuell in mehrere Buchungen aufgeteilt werden.',
   'This update will change the nature the onhand of goods is tracked.' => 'Dieses update &auml;ndert die Art und Weise wie Lagermengen gez&auml;lt werden.',
   'This upgrade script tries to map all existing parts in the database to the newly created Buchungsgruppen.' => 'Dieses Upgradescript versucht, bei allen bestehenden Artikeln neu erstellte Buchungsgruppen zuzuordnen.',
@@ -2174,7 +2176,7 @@ $self->{texts} = {
   'Vendor'                      => 'Lieferant',
   'Vendor (name)'               => 'Lieferant (Name)',
   'Vendor Invoice'              => 'Einkaufsrechnung',
-  'Vendor Invoices'             => 'Einkaufsrechnungen',
+  'Vendor Invoices & AP Transactions' => 'Einkaufsrechnungen & Kreditorenbuchungen',
   'Vendor Name'                 => 'Lieferantenname',
   'Vendor Number'               => 'Lieferantennummer',
   'Vendor Order Number'         => 'Bestellnummer beim Lieferanten',
index 7eb8d8d837282c85c7803133516911d33d9c5d42..52d3ec595da39973497fc6b6170c58ac9881924b 100644 (file)
@@ -2139,7 +2139,7 @@ $self->{texts} = {
   'Weight unit'                 => 'Gewichtseinheit',
   'What <b>term</b> you are looking for?' => 'Nach welchem <b>Begriff</b> wollen Sie suchen?',
   'What type of item is this?'  => 'Was ist dieser Artikel?',
-  'Which is located at doc/Lx-Office-Dokumentation.pdf. Click here: ' => 'Zu finden in doc/Lx-Office-Dokumentation.pdf. Oder hier klicken: ',
+  'Which is located at doc/kivitendo-Dokumentation.pdf. Click here: ' => 'Zu finden in doc/kivitendo-Dokumentation.pdf. Oder hier klicken: ',
   'With Extension Of Time'      => 'mit Dauerfristverlängerung',
   'Workflow Delivery Order'     => 'Workflow Lieferschein',
   'Workflow purchase_order'     => 'Workflow Lieferantenauftrag',
index cb0700309de24377d4541c152a042d2748153a1c..fa99ded7ac91fde32060c7e0691e1904e7f3488a 100644 (file)
--- a/menu.ini
+++ b/menu.ini
@@ -149,7 +149,7 @@ module=do.pl
 action=search
 type=sales_delivery_order
 
-[AR--Reports--Invoices]
+[AR--Reports--Invoices, Credit Notes & AR Transactions]
 ACCESS=invoice_edit
 module=ar.pl
 action=search
@@ -222,7 +222,7 @@ module=do.pl
 action=search
 type=purchase_delivery_order
 
-[AP--Reports--Vendor Invoices]
+[AP--Reports--Vendor Invoices & AP Transactions]
 ACCESS=vendor_invoice_edit
 module=ap.pl
 action=search
index 253706c93df1c89fddff72b134a100bbeab189b5..f4a7bb7e76ba4aef6a6644e39358f99bbf4e8764 100755 (executable)
@@ -43,7 +43,8 @@ use SL::Dispatcher;
 
 my ($opt_list, $opt_tree, $opt_rtree, $opt_nodeps, $opt_graphviz, $opt_help);
 my ($opt_user, $opt_apply, $opt_applied, $opt_unapplied, $opt_format, $opt_test_utf8);
-my ($opt_dbhost, $opt_dbport, $opt_dbname, $opt_dbuser, $opt_dbpassword);
+my ($opt_dbhost, $opt_dbport, $opt_dbname, $opt_dbuser, $opt_dbpassword, $opt_create, $opt_type);
+my ($opt_description, $opt_encoding, @opt_depends);
 
 our (%myconfig, $form, $user, $auth, $locale, $controls, $dbupgrader);
 
@@ -231,6 +232,48 @@ sub dump_nodeps {
     "\n\n";
 }
 
+sub create_upgrade {
+  my (%params) = @_;
+
+  my $filename    = $params{filename};
+  my $dbupgrader  = $params{dbupgrader};
+  my $type        = $params{type}        || '';
+  my $description = $params{description} || '';
+  my $encoding    = $params{encoding}    || 'utf-8';
+  my @depends     = @{ $params{depends} };
+
+  if (!@depends) {
+    my @releases = grep { /^release_/ } keys %$controls;
+    @depends = ((sort @releases)[-1]);
+  }
+
+  my $comment;
+  if ($type eq 'sql') {
+    $comment = '--';
+  } elsif ($type eq 'pl') {
+    $comment = '#';
+  } elsif (!$type) {
+    die 'Error: No --type was given but is required for --create.';
+  } else {
+    die 'Error: Unknown --type. Try "sql" or "pl".';
+  }
+
+  my $full_filename = $dbupgrader->path . '/' . $filename . '.' . $type;
+
+  die "file '$full_filename' already exists, aborting" if -f $full_filename;
+
+
+  open my $fh, ">:utf8", $full_filename or die "can't open $full_filename";
+  print $fh "$comment \@tag: $filename\n";
+  print $fh "$comment \@description: $description\n";
+  print $fh "$comment \@depends: @depends\n";
+  print $fh "$comment \@encoding: $encoding\n";
+  close $fh;
+
+  system("\$EDITOR $full_filename");
+  exit 0;
+}
+
 sub apply_upgrade {
   my $name = shift;
 
@@ -399,6 +442,11 @@ GetOptions("list"         => \$opt_list,
            "user=s"       => \$opt_user,
            "apply=s"      => \$opt_apply,
            "applied"      => \$opt_applied,
+           "create=s"     => \$opt_create,
+           "type=s"       => \$opt_type,
+           "encoding=s"   => \$opt_encoding,
+           "description=s" => \$opt_description,
+           "depends=s"    => \@opt_depends,
            "unapplied"    => \$opt_unapplied,
            "test-utf8"    => \$opt_test_utf8,
            "dbhost:s"     => \$opt_dbhost,
@@ -420,6 +468,12 @@ dump_tree_reverse()                         if ($opt_rtree);
 dump_graphviz('file_name' => $opt_graphviz,
               'format'    => $opt_format)   if (defined $opt_graphviz);
 dump_nodeps()                               if ($opt_nodeps);
+create_upgrade(filename   => $opt_create,
+               dbupgrader  => $dbupgrader,
+               type        => $opt_type,
+               description => $opt_description,
+               encoding    => $opt_encoding,
+               depends     => \@opt_depends) if ($opt_create);
 
 if ($opt_user) {
   $auth = SL::Auth->new();
@@ -456,6 +510,7 @@ if ($opt_unapplied) {
   dump_unapplied();
 }
 
+
 if ($opt_test_utf8) {
   $form->error("--test-utf8 used but no database name given with --dbname.") if (!$opt_dbname);
 
index 63fc858c95d5cb3b8640d6b7fb34ffbf53f7542c..3023833712d988a5bec596f36c06f090fb11fe6d 100644 (file)
@@ -1,11 +1,11 @@
-use Test::More tests => 41;
+use Test::More tests => 47;
 
 use lib 't';
+use utf8;
 
 use Data::Dumper;
-use utf8;
+use Support::TestSetup;
 
-use_ok 'Support::TestSetup';
 use_ok 'SL::Helper::Csv';
 
 Support::TestSetup::login();
@@ -278,6 +278,8 @@ is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'eol bug at the end o
 $csv = SL::Helper::Csv->new(
   file   => \"Description\nKaffee",
   class  => 'SL::DB::Part',
+  case_insensitive_header => 1,
+  profile => { description => 'description' },
 );
 $csv->parse;
 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive header from csv works';
@@ -285,9 +287,11 @@ is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive hea
 #####
 
 $csv = SL::Helper::Csv->new(
-file   => \"Kaffee",
-header => [ 'Description' ],
-class  => 'SL::DB::Part',
+  file   => \"Kaffee",
+  header => [ 'Description' ],
+  class  => 'SL::DB::Part',
+  case_insensitive_header => 1,
+  profile => { description => 'description' },
 );
 $csv->parse;
 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive header as param works';
@@ -302,4 +306,54 @@ $csv = SL::Helper::Csv->new(
 $csv->parse;
 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'utf8 BOM works (bug 1872)';
 
+#####
+
+$csv = SL::Helper::Csv->new(
+  file   => \"Kaffee",
+  header => [ 'Description' ],
+  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",
+  header => [ 'foo' ],
+  class  => 'SL::DB::Part',
+  profile => { foo => '' },
+);
+$csv->parse;
+
+is_deeply $csv->get_data, [ { foo => 'Kaffee' } ], 'empty path still gets parsed into data';
+ok $csv->get_objects->[0], 'empty path gets ignored in object creation';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+  file   => \"Kaffee",
+  header => [ 'foo' ],
+  class  => 'SL::DB::Part',
+  strict_profile => 1,
+  profile => { foo => '' },
+);
+$csv->parse;
+
+is_deeply $csv->get_data, [ { foo => 'Kaffee' } ], 'empty path still gets parsed into data (strict profile)';
+ok $csv->get_objects->[0], 'empty path gets ignored in object creation (strict profile)';
+
+$csv = SL::Helper::Csv->new(
+  file   => \"Phil",
+  header => [ 'CVAR_grOUnDHog' ],
+  class  => 'SL::DB::Part',
+  strict_profile => 1,
+  case_insensitive_header => 1,
+  profile => { cvar_Groundhog => '' },
+);
+$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';
+
 # vim: ft=perl
index 191bbec947863a18866bb053e8e3f8c926bd8ddf..94ddf8292deac43670cec252dc59c310b1880bbc 100644 (file)
@@ -57,7 +57,7 @@
       <select name="inventory_system">
        [% FOREACH row = INVENTORY_SYSTEMS %]<option value=[% HTML.escape(row.name) %] [% IF row.selected %]selected[% END %]>[% HTML.escape(row.name) | $T8 %]</option>[% END %]
       </select>
-     [% '* there are restrictions for the perpetual method, look at chapter "Bemerkungen zu Bestandsmethode"  in' | $T8 %] <a href="doc/Lx-Office-Dokumentation.pdf">Lx-Office-Dokumentation.pdf</a>.
+     [% '* there are restrictions for the perpetual method, look at chapter "Bemerkungen zu Bestandsmethode"  in' | $T8 %] <a href="doc/kivitendo-Dokumentation.pdf">kivitendo-Dokumentation.pdf</a>.
      </td>
 
     </tr>
index dcf0be088524ca2e90fe1776bc1d3dd333b594a7..1c5e7a969820c68eedc2f7fbbf18610aeeb7b7d2 100644 (file)
@@ -19,7 +19,6 @@ $(function() {
 <input type="hidden" name="type"               value="account">
 <input type="hidden" name="orphaned"           value="[% HTML.escape(orphaned) %]">
 <input type="hidden" name="new_chart_valid"    value="[% HTML.escape(new_chart_valid) %]">
-<input type="hidden" name="original_accno"    value="[% HTML.escape(accno) %]">
 <input type="hidden" name="inventory_accno_id" value="[% HTML.escape(inventory_accno_id) %]">
 <input type="hidden" name="income_accno_id"    value="[% HTML.escape(income_accno_id) %]">
 <input type="hidden" name="expense_accno_id"   value="[% HTML.escape(expense_accno_id) %]">
index 4beaf5eadc919f29425389a1475b0b1eb4e2bc32..01185cc2bd094cc541682afd96ccdc16f2031b8c 100644 (file)
@@ -6,6 +6,8 @@
 
  <p><div class="listtop">[% title %]  [% HTML.escape(partnumber) %]  [% HTML.escape(description) %]</div></p>
 
+[% PROCESS 'common/flash.html' %]
+
  <form method="post" name="ic" action="[% script %]">
 
   <input name="id" type="hidden" value="[% HTML.escape(id) %]">
index 7285d867f5b9658172b7aace35d557f2dc3368e1..4c51b6637a0af3f9149218683095bf5e3aaac95f 100644 (file)
@@ -42,7 +42,7 @@ $(clockon);
     [%- HTML.escape(mainitem.title) %]
    </a>
    [%- IF mainitem.subitems %]
-    <ul[%- IF force_ul_width %] width="[% mainitem.max_width * 12 %]"[% END %]>
+    <ul[%- IF force_ul_width %] width="[% mainitem.max_width * 10 %]"[% END %]>
      [%- SET sub1_id = main_id * 100 %]
      [%- FOREACH sub1item = mainitem.subitems %]
       [%- SET sub1_id = sub1_id + 1 %]
@@ -51,7 +51,7 @@ $(clockon);
         [%- HTML.escape(sub1item.title) %]
        </a>
        [%- IF sub1item.subitems %]
-        <ul[%- IF force_ul_width %] width="[% sub1item.max_width * 12 %]"[% END %]>
+        <ul[%- IF force_ul_width %] width="[% sub1item.max_width * 10 %]"[% END %]>
          [%- SET sub2_id = sub1_id * 100 %]
          [%- FOREACH sub2item = sub1item.subitems %]
           [%- SET sub2_id = sub2_id + 1 %]