Merge branch 'bankerweiterung_und_skonto'
authorG. Richardson <information@kivitendo-premium.de>
Thu, 7 May 2015 08:36:14 +0000 (10:36 +0200)
committerG. Richardson <information@kivitendo-premium.de>
Thu, 7 May 2015 08:36:14 +0000 (10:36 +0200)
Conflicts:
locale/de/all

20 files changed:
SL/Controller/BankImport.pm [new file with mode: 0644]
SL/Controller/BankTransaction.pm
SL/Controller/CsvImport.pm
SL/Controller/CsvImport/BankTransaction.pm
SL/Controller/CsvImport/Contact.pm
SL/Controller/CsvImport/CustomerVendor.pm
SL/Controller/CsvImport/Inventory.pm
SL/Controller/CsvImport/Order.pm
SL/Controller/CsvImport/Part.pm
SL/Controller/CsvImport/Project.pm
SL/Controller/CsvImport/Shipto.pm
SL/DB/CsvImportProfile.pm
SL/DB/Helper/Payment.pm
SL/Helper/MT940.pm [new file with mode: 0644]
VERSION
locale/de/all
menus/erp.ini
templates/webpages/bankimport/form.html [new file with mode: 0644]
templates/webpages/csv_import/_form_mt940.html [deleted file]
templates/webpages/csv_import/form.html

diff --git a/SL/Controller/BankImport.pm b/SL/Controller/BankImport.pm
new file mode 100644 (file)
index 0000000..58faf85
--- /dev/null
@@ -0,0 +1,39 @@
+package SL::Controller::BankImport;
+use strict;
+use Data::Dumper;
+use parent qw(SL::Controller::Base);
+
+use SL::Locale::String qw(t8);
+use SL::DB::CsvImportProfile;
+use SL::Helper::MT940;
+
+
+sub action_upload_mt940 {
+  my ($self, %params) = @_;
+
+  my $profile = SL::DB::Manager::CsvImportProfile->find_by(name => 'MT940', login => $::myconfig{login});
+  $self->render('bankimport/form', title => $::locale->text('MT940 import'), profile => $profile ? 1 : 0);
+
+}
+
+sub action_import_mt940 {
+  my ($self, %params) = @_;
+
+  die "missing file for action import" unless $::form->{file};
+
+  my $converted_data = SL::Helper::MT940::convert_mt940_data($::form->{file});
+
+  # store the converted data in a session file with a name expected by the profile type "bank_transactions"
+  my $file = SL::SessionFile->new("csv-import-bank_transactions.csv", mode => '>');
+  $file->fh->print($converted_data);
+  $file->fh->close;
+
+  my $profile = SL::DB::Manager::CsvImportProfile->find_by(name => 'MT940', login => $::myconfig{login});
+  die t8("The MT940 import needs an import profile called MT940") unless $profile;
+
+  $self->redirect_to(controller => 'controller.pl', action => 'CsvImport/test', 'profile.type' => 'bank_transactions', 'profile.id' => $profile->id, force_profile => 1);
+
+};
+
+1;
+
index 918d6aa..47089bd 100644 (file)
@@ -216,7 +216,7 @@ sub action_ajax_payment_suggestion {
 
   my $bt = SL::DB::Manager::BankTransaction->find_by( id => $::form->{bt_id} );
   my $invoice = SL::DB::Manager::Invoice->find_by( id => $::form->{prop_id} );
-  $invoice = SL::DB::Manager::PurchaseInvoice->find_By( id => $::form->{prop_id} ) unless $invoice;
+  $invoice = SL::DB::Manager::PurchaseInvoice->find_by( id => $::form->{prop_id} ) unless $invoice;
 
   die unless $bt and $invoice;
 
index 2bc2955..b74b04b 100644 (file)
@@ -224,7 +224,7 @@ sub check_auth {
 sub check_type {
   my ($self) = @_;
 
-  die "Invalid CSV import type" if none { $_ eq $::form->{profile}->{type} } qw(parts inventories customers_vendors addresses contacts projects orders bank_transactions mt940);
+  die "Invalid CSV import type" if none { $_ eq $::form->{profile}->{type} } qw(parts inventories customers_vendors addresses contacts projects orders bank_transactions);
   $self->type($::form->{profile}->{type});
 }
 
@@ -270,7 +270,6 @@ sub render_inputs {
             : $self->type eq 'projects'          ? $::locale->text('CSV import: projects')
             : $self->type eq 'orders'            ? $::locale->text('CSV import: orders')
             : $self->type eq 'bank_transactions' ? $::locale->text('CSV import: bank transactions')
-            : $self->type eq 'mt940'             ? $::locale->text('CSV import: MT940')
             : die;
 
   if ($self->{type} eq 'customers_vendors' or $self->{type} eq 'orders'  ) {
@@ -290,30 +289,16 @@ sub render_inputs {
 sub test_and_import_deferred {
   my ($self, %params) = @_;
 
-  $self->profile_from_form;
+  if ( $::form->{force_profile} && $::form->{profile}->{id} ) {
+    $self->load_default_profile;
+  }  else {
+    $self->profile_from_form;
+  };
 
-  if ( $::form->{file} && $::form->{FILENAME} =~ /\.940$/ ) {
-    my $mt940_file = SL::SessionFile->new($::form->{FILENAME}, mode => '>');
-    $mt940_file->fh->print($::form->{file});
-    $mt940_file->fh->close;
-
-    my $aqbin = $::lx_office_conf{applications}->{aqbanking};
-    die "Can't find aqbanking-cli, please check your configuration file.\n" unless -f $aqbin;
-    my $cmd = "$aqbin --cfgdir=\"users\" import --importer=\"swift\" --profile=\"SWIFT-MT940\" -f " . $mt940_file->file_name . " | $aqbin --cfgdir=\"users\" listtrans --exporter=\"csv\" --profile=\"AqMoney2\" ";
-    my $converted_mt940;
-    open(MT, "$cmd |");
-    $converted_mt940 .=  '"transaction_id";"local_bank_code";"local_account_number";"remote_bank_code";"remote_account_number";"transdate";"valutadate";"amount";"currency";"remote_name";"remote_name_1";"purpose";"purpose1";"purpose2";"purpose3";"purpose4";"purpose5";"purpose6";"purpose7";"purpose8";"purpose9";"purpose10";"purpose11"' . "\n";
-    my $headerline = <MT>;  # discard original header line
-    while (<MT>) {
-      $converted_mt940 .= $_;
-    };
+  if ($::form->{file}) {
     my $file = SL::SessionFile->new($self->csv_file_name, mode => '>');
-    $file->fh->print($converted_mt940);
+    $file->fh->print($::form->{file});
     $file->fh->close;
-  } elsif ($::form->{file}) {
-      my $file = SL::SessionFile->new($self->csv_file_name, mode => '>');
-      $file->fh->print($::form->{file});
-      $file->fh->close;
   }
 
   my $file = SL::SessionFile->new($self->csv_file_name, mode => '<', encoding => $self->profile->get('charset'));
@@ -385,6 +370,7 @@ sub load_default_profile {
   $profile ||= SL::DB::CsvImportProfile->new(type => $self->{type}, login => $::myconfig{login});
 
   $self->profile($profile);
+  $self->worker->set_profile_defaults;
   $self->profile->set_defaults;
 }
 
@@ -640,7 +626,6 @@ sub init_worker {
        : $self->{type} eq 'projects'          ? SL::Controller::CsvImport::Project->new(@args)
        : $self->{type} eq 'orders'            ? SL::Controller::CsvImport::Order->new(@args)
        : $self->{type} eq 'bank_transactions' ? SL::Controller::CsvImport::BankTransaction->new(@args)
-       : $self->{type} eq 'mt940'             ? SL::Controller::CsvImport::BankTransaction->new(@args)
        :                                        die "Program logic error";
 }
 
index 76e9020..4bd9b96 100644 (file)
@@ -15,6 +15,15 @@ use Rose::Object::MakeMethods::Generic
  'scalar --get_set_init' => [ qw(bank_accounts_by) ],
 );
 
+sub set_profile_defaults {
+  my ($self) = @_;
+
+  $self->controller->profile->_set_defaults(
+                       charset       => 'UTF8',  # override charset from defaults
+                       update_policy => 'skip',
+                      );
+};
+
 sub init_class {
   my ($self) = @_;
   $self->class('SL::DB::BankTransaction');
index e653d7c..adaa85c 100644 (file)
@@ -13,6 +13,9 @@ use Rose::Object::MakeMethods::Generic
  scalar => [ qw(table) ],
 );
 
+sub set_profile_defaults {
+};
+
 sub init_class {
   my ($self) = @_;
   $self->class('SL::DB::Contact');
index 0a5c121..b70c0a7 100644 (file)
@@ -17,6 +17,11 @@ use Rose::Object::MakeMethods::Generic
  'scalar --get_set_init' => [ qw(table languages_by businesses_by) ],
 );
 
+sub set_profile_defaults {
+  my ($self) = @_;
+  $self->controller->profile->_set_defaults(table => 'customer');
+};
+
 sub init_table {
   my ($self) = @_;
   $self->table($self->controller->profile->get('table') eq 'customer' ? 'customer' : 'vendor');
index c0dcfb5..c1406d0 100644 (file)
@@ -28,6 +28,9 @@ sub init_class {
   $self->class('SL::DB::Inventory');
 }
 
+sub set_profile_defaults {
+};
+
 sub init_profile {
   my ($self) = @_;
 
index 246438c..f8dbbb1 100644 (file)
@@ -34,6 +34,16 @@ sub init_class {
   $self->class(['SL::DB::Order', 'SL::DB::OrderItem']);
 }
 
+sub set_profile_defaults {
+  my ($self) = @_;
+
+  $self->controller->profile->_set_defaults(
+                       order_column    => $::locale->text('Order'),
+                       item_column     => $::locale->text('OrderItem'),
+                       max_amount_diff => 0.02,
+                      );
+};
+
 
 sub init_settings {
   my ($self) = @_;
index 33301d1..be989ee 100644 (file)
@@ -27,6 +27,24 @@ use Rose::Object::MakeMethods::Generic
                                  translation_columns all_pricegroups) ],
 );
 
+sub set_profile_defaults {
+  my ($self) = @_;
+
+  my $bugru = SL::DB::Manager::Buchungsgruppe->find_by(description => { like => 'Standard%19%' });
+
+  $self->controller->profile->_set_defaults(
+                       sellprice_places          => 2,
+                       sellprice_adjustment      => 0,
+                       sellprice_adjustment_type => 'percent',
+                       article_number_policy     => 'update_prices',
+                       shoparticle_if_missing    => '0',
+                       parts_type                => 'part',
+                       default_buchungsgruppe    => ($bugru ? $bugru->id : undef),
+                       apply_buchungsgruppe      => 'all',
+                      );
+};
+
+
 sub init_class {
   my ($self) = @_;
   $self->class('SL::DB::Part');
index d03de46..4b846dc 100644 (file)
@@ -18,6 +18,9 @@ sub init_class {
   $self->class('SL::DB::Project');
 }
 
+sub set_profile_defaults {
+};
+
 sub init_all_cvar_configs {
   my ($self) = @_;
 
index 1d2fdd7..e32da46 100644 (file)
@@ -11,6 +11,9 @@ use Rose::Object::MakeMethods::Generic
  scalar => [ qw(table) ],
 );
 
+sub set_profile_defaults {
+};
+
 sub init_class {
   my ($self) = @_;
   $self->class('SL::DB::Shipto');
index 9980ad5..8797a7a 100644 (file)
@@ -34,43 +34,6 @@ sub new_with_default {
 sub set_defaults {
   my ($self) = @_;
 
-
-  if ($self->type eq 'parts') {
-    my $bugru = SL::DB::Manager::Buchungsgruppe->find_by(description => { like => 'Standard%19%' });
-
-    $self->_set_defaults(sellprice_places          => 2,
-                         sellprice_adjustment      => 0,
-                         sellprice_adjustment_type => 'percent',
-                         article_number_policy     => 'update_prices',
-                         shoparticle_if_missing    => '0',
-                         parts_type                => 'part',
-                         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,
-                        );
-  } elsif ($self->type eq 'mt940') {
-    $self->_set_defaults(charset       => 'UTF8',
-                         sep_char      => ';',
-                         numberformat  => '1000.00',
-                         update_policy => 'skip',
-                        );
-  } elsif ($self->type eq 'bank_transactions') {
-    $self->_set_defaults(charset       => 'UTF8',
-                         update_policy => 'skip',
-                        );
-  } else {
-    $self->_set_defaults(table => 'customer');
-  }
-
-  # TODO: move the defaults into their own controller
-  # defaults can only be set once, so use these values as default if they
-  # haven't already been set above for one of the special import types
-  # If the values have been set above they won't be overwritten here:
-
   $self->_set_defaults(sep_char     => ',',
                        quote_char   => '"',
                        escape_char  => '"',
index baa2366..9ac3a67 100644 (file)
@@ -551,6 +551,7 @@ sub get_payment_select_options_for_bank_transaction {
 
   my @options;
   if ( $open_amount &&                   # invoice amount not 0
+       $self->skonto_date &&             # check whether skonto applies
        abs(abs($self->amount_less_skonto) - abs($bt->amount)) < 0.01 &&
        $self->check_skonto_configuration) {
          if ( $self->within_skonto_period($bt->transdate) ) {
diff --git a/SL/Helper/MT940.pm b/SL/Helper/MT940.pm
new file mode 100644 (file)
index 0000000..5c8865d
--- /dev/null
@@ -0,0 +1,31 @@
+package SL::Helper::MT940;
+
+use strict;
+
+sub convert_mt940_data {
+  my ($mt940_data) = @_;
+
+  # takes the data from an uploaded mt940 file, converts it to csv via aqbanking and returns the converted data
+  # The uploaded file data is stored as a session file, just like the aqbanking settings file.
+
+  my $import_filename = 'bank_transfer.940';
+  my $sfile = SL::SessionFile->new($import_filename, mode => '>');
+  $sfile->fh->print($mt940_data);
+  $sfile->fh->close;
+
+  my $aqbin = $::lx_office_conf{applications}->{aqbanking};
+  die "Can't find aqbanking-cli, please check your configuration file.\n" unless -f $aqbin;
+  my $cmd = "$aqbin --cfgdir=\"" . $sfile->get_path . "\" import --importer=\"swift\" --profile=\"SWIFT-MT940\" -f " . $sfile->get_path . "/$import_filename | $aqbin --cfgdir=\"" . $sfile->get_path . "\" listtrans --exporter=\"csv\" --profile=\"AqMoney2\" ";
+
+  my $converted_data = '"empty";"local_bank_code";"local_account_number";"remote_bank_code";"remote_account_number";"transdate";"valutadate";"amount";"currency";"remote_name";"remote_name_1";"purpose";"purpose1";"purpose2";"purpose3";"purpose4";"purpose5";"purpose6";"purpose7";"purpose8";"purpose9";"purpose10";"purpose11"' . "\n";
+
+  open my $mt, "-|", "$cmd" || die "Problem with executing aqbanking\n";
+  my $headerline = <$mt>;  # discard original aqbanking header line
+  while (<$mt>) {
+    $converted_data .= $_;
+  };
+  close $mt;
+  return $converted_data;
+};
+
+1;
diff --git a/VERSION b/VERSION
index 928d224..d4afac5 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.2.1-rb
+3.2.1-unstable
index dfc0f97..6ccb262 100755 (executable)
@@ -452,7 +452,6 @@ $self->{texts} = {
   'CSS style for pictures'      => 'CSS Style für Bilder',
   'CSV'                         => 'CSV',
   'CSV export -- options'       => 'CSV-Export -- Optionen',
-  'CSV import: MT940'           => 'CSV Import: MT940',
   'CSV import: bank transactions' => 'CSV Import: Bankbewegungen',
   'CSV import: contacts'        => 'CSV-Import: Ansprechpersonen',
   'CSV import: customers and vendors' => 'CSV-Import: Kunden und Lieferanten',
@@ -1362,6 +1361,7 @@ $self->{texts} = {
   'Import'                      => 'Import',
   'Import CSV'                  => 'CSV-Import',
   'Import Status'               => 'Import Status',
+  'Import a MT940 file:'        => 'Laden Sie eine MT940 Datei hoch:',
   'Import file'                 => 'Import-Datei',
   'Import not started yet, please wait...' => 'Der Taskserver ist gerade ausgelastet. Ihr Import wird gleich gestartet, bitte warten...',
   'Import preview'              => 'Import-Vorschau',
@@ -1942,6 +1942,7 @@ $self->{texts} = {
   'Please contact your administrator.' => 'Bitte wenden Sie sich an Ihren Administrator.',
   'Please correct the settings and try again or deactivate that client.' => 'Bitte korrigieren Sie die Einstellungen und versuchen Sie es erneut, oder deaktivieren Sie diesen Mandanten.',
   'Please create/copy a template named letter.tex in your client template dir' => 'Bitte erstellen / kopieren Sie eine Druckvorlage namens letter.tex in Ihren Mandantenvorlagen-Ordner',
+  'Please create a CSV import profile called "MT940" for the import type bank transactions:' => 'Bitte erstellen Sie ein CSV Import Profil mit dem Namen "MT940" für den Importtyp Bankbewegungen',
   'Please define a taxkey for the following taxes and run the update again:' => 'Bitte definieren Sie einen Steuerschlüssel für die folgenden Steuern und starten Sie dann das Update erneut:',
   'Please do so in the administration area.' => 'Bitte erledigen Sie dies im Administrationsbereich.',
   'Please enter a profile name.' => 'Bitte geben Sie einen Profilnamen an.',
@@ -2587,6 +2588,7 @@ $self->{texts} = {
   'The GL transaction #1 has been deleted.' => 'Die Dialogbuchung #1 wurde gelöscht.',
   'The IBAN is missing.'        => 'Die IBAN fehlt.',
   'The LDAP server "#1:#2" is unreachable. Please check config/kivitendo.conf.' => 'Der LDAP-Server "#1:#2" ist nicht erreichbar. Bitte &uuml;berpr&uuml;fen Sie die Angaben in config/kivitendo.conf.',
+  'The MT940 import needs an import profile called MT940' => 'Der MT940 Import benötigt ein Importprofil mit dem Namen "MT940"',
   'The SEPA export has been created.' => 'Der SEPA-Export wurde erstellt',
   'The SEPA strings have been saved.' => 'Die bei SEPA-Überweisungen verwendeten Begriffe wurden gespeichert.',
   'The WebDAV feature has been used.' => 'Das WebDAV-Feature wurde benutzt.',
index 05f2a29..8e50ab0 100644 (file)
@@ -427,8 +427,7 @@ profile.type=bank_transactions
 [Cash--Bank Import--MT940]
 ACCESS=bank_transaction
 module=controller.pl
-action=CsvImport/new
-profile.type=mt940
+action=BankImport/upload_mt940
 
 [Cash--Bank transactions MT940]
 ACCESS=bank_transaction
diff --git a/templates/webpages/bankimport/form.html b/templates/webpages/bankimport/form.html
new file mode 100644 (file)
index 0000000..1c3d672
--- /dev/null
@@ -0,0 +1,24 @@
+[%- USE HTML %]
+[%- USE LxERP %]
+[%- USE L %]
+[%- USE T8 %]
+
+ <div class="listtop">[% FORM.title %]</div>
+
+ [% IF profile %]
+ <p>
+ [% "Import a MT940 file:" | $T8 %]
+ </p>
+
+ <form method="post" action="controller.pl" enctype="multipart/form-data">
+  [% L.hidden_tag('action', 'BankImport/import_mt940') %]
+
+    [% L.input_tag('file', '', type => 'file', accept => '*') %]
+    [% L.submit_tag('action_import_mt940', LxERP.t8('Import')) %]
+
+ </form>
+ [% ELSE %]
+ <p>
+ [% "Please create a CSV import profile called \"MT940\" for the import type bank transactions:" | $T8 %] <a href="[% SELF.url_for(controller => 'CsvImport', action => 'new', 'profile.type' => 'bank_transactions' ) %]">CsvImport</a>
+ </p>
+ [% END %]
diff --git a/templates/webpages/csv_import/_form_mt940.html b/templates/webpages/csv_import/_form_mt940.html
deleted file mode 100644 (file)
index cb63e7d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-[% USE LxERP %]
-[% USE L %]
-
-<tr>
- <th align="right">[%- LxERP.t8("Existing bank transactions") %]:</th>
- <td colspan="10">
-  [% opts = [ [ 'skip', LxERP.t8('Skip entry') ] , [ 'insert_new', LxERP.t8('Insert new') ] ] %]
-  [% L.select_tag('settings.update_policy', opts, default = SELF.profile.get('update_policy'), style = 'width: 300px') %]
- </td>
-</tr>
-
-
index 745ef16..c40c563 100644 (file)
  [%- INCLUDE 'csv_import/_form_inventories.html' %]
 [%- ELSIF SELF.type == 'orders' %]
  [%- INCLUDE 'csv_import/_form_orders.html' %]
-[%- ELSIF SELF.type == 'mt940' %]
- [%- INCLUDE 'csv_import/_form_mt940.html' %]
 [%- ELSIF SELF.type == 'bank_transactions' %]
  [%- INCLUDE 'csv_import/_form_banktransactions.html' %]
 [%- END %]