Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision cdb079eb

Von Cem Aydin vor etwa 2 Monaten hinzugefügt

  • ID cdb079ebb32c7c8130312cddd39f6c1d39cc6599
  • Vorgänger 3abd8072
  • Nachfolger be93d983

Schweizer Banküberweisung via XML implementiert

Analog zu "Überweisung via SEPA XML"

Das Feature kann in der Mandantenkonfiguration ein-/ausgeschaltet
werden. Wenn eingeschaltet, wird anstatt des SEPA export ein Export
gemäss dem schweizer Standard erstellt. Dementsprechend werden die
Texte im Menü und in den Templates ersetzt, und im Code mittels
Weiche auf den schweizer Export umgeschaltet.

Gemäss Standard:
Swiss Payment Standards
Customer Credit Transfer Initiation (pain.001)

Unterschiede anzeigen:

SL/SEPA.pm
31 31

  
32 32
  my $is_sepa_blocked = $params{vc} eq 'customer' ? 'FALSE' : "${arap}.is_sepa_blocked";
33 33

  
34
  my $swiss_data = $::instance_conf->get_sepa_swiss_xml_export eq 1 && $arap eq 'ap' ?
35
                    " vc.iban AS vc_iban, vc.bic AS vc_bic, ap.qrbill_data AS qrbill_data, " : '';
36

  
34 37
  # open_amount is not the current open amount according to bookkeeping, but
35 38
  # the open amount minus the SEPA transfer amounts that haven't been closed yet
36 39
  my $query =
......
39 42
         (${arap}.transdate + pt.terms_skonto) as skonto_date, (pt.percent_skonto * 100) as percent_skonto,
40 43
         (${arap}.amount - (${arap}.amount * pt.percent_skonto)) as amount_less_skonto,
41 44
         (${arap}.amount * pt.percent_skonto) as skonto_amount,
42
         vc.name AS vcname, vc.language_id, ${arap}.duedate as duedate, ${arap}.direct_debit,
45
         vc.name AS vcname, vc.language_id, vc.iban AS vc_iban, vc.bic AS vc_bic,
46
         ${arap}.duedate as duedate, ${arap}.direct_debit,
43 47
         ${is_sepa_blocked} AS is_sepa_blocked,
44 48
         vc.${vc_vc_id} as vc_vc_id,
49
         $swiss_data
45 50

  
46 51
         COALESCE(vc.iban, '') <> '' AND COALESCE(vc.bic, '') <> '' ${mandate} AS vc_bank_info_ok,
47 52

  
......
237 242
    my $mandator_id = $params{vc} eq 'customer' ? ', mandator_id, mandate_date_of_signature' : '';
238 243

  
239 244
    if ($params{details}) {
240
      $columns = qq|, arap.invnumber, arap.invoice, arap.transdate AS reference_date, vc.name AS vc_name, vc.${vc}number AS vc_number, c.accno AS chart_accno, c.description AS chart_description ${mandator_id}|;
245
      $columns = qq|, arap.invnumber, arap.invoice, arap.transdate AS reference_date,
246
                      arap.qrbill_data AS qrbill_data, vc.name AS vc_name,
247
                      vc.${vc}number AS vc_number, vc.street AS vc_street,
248
                      vc.zipcode AS vc_zipcode, vc.city AS vc_city, vc.country AS vc_country,
249
                      c.accno AS chart_accno, c.description AS chart_description ${mandator_id},
250
                      cur.name AS currency_name|;
241 251
      $joins   = qq|LEFT JOIN ${arap} arap ON (sei.${arap}_id = arap.id)
242 252
                    LEFT JOIN ${vc} vc     ON (arap.${vc}_id  = vc.id)
243
                    LEFT JOIN chart c      ON (sei.chart_id   = c.id)|;
253
                    LEFT JOIN chart c      ON (sei.chart_id   = c.id)
254
                    LEFT JOIN currencies cur ON (arap.currency_id = cur.id)
255
                    |;
244 256
    }
245 257

  
246 258
    $query = qq|SELECT sei.*
SL/SEPA/SwissXML.pm
1
package SL::SEPA::SwissXML;
2

  
3
use strict;
4
use utf8;
5

  
6
use parent qw(SL::SEPA::XML);
7

  
8
use Carp;
9
use Encode;
10
use POSIX qw(strftime);
11
use XML::Writer;
12

  
13
use SL::Iconv;
14
use SL::SEPA::XML::SwissTransaction;
15

  
16
sub add_transaction {
17
  my $self = shift;
18

  
19
  foreach my $transaction (@_) {
20
    croak "Expecting hash reference." if (ref $transaction ne 'HASH');
21
    push @{ $self->{transactions} }, SL::SEPA::XML::SwissTransaction->new(%{ $transaction }, 'sepa' => $self);
22
  }
23

  
24
  return 1;
25
}
26

  
27
sub _group_transactions {
28
  my $self    = shift;
29

  
30
  my $grouped = {
31
    'sum_amount' => 0,
32
    'groups'     => { },
33
  };
34

  
35
  foreach my $transaction (@{ $self->{transactions} }) {
36
    my $key                      = $self->{grouped} ? join("\t", map { $transaction->get($_) } qw(src_bic src_iban execution_date is_sepa_payment)) : 'all';
37
    $grouped->{groups}->{$key} ||= {
38
      'sum_amount'   => 0,
39
      'transactions' => [ ],
40
    };
41

  
42
    push @{ $grouped->{groups}->{$key}->{transactions} }, $transaction;
43

  
44
    $grouped->{groups}->{$key}->{sum_amount} += $transaction->{amount};
45
    $grouped->{sum_amount}                   += $transaction->{amount};
46
  }
47

  
48
  return $grouped;
49
}
50

  
51
sub to_xml {
52
  my $self = shift;
53

  
54
  croak "No transactions added yet." if (!@{ $self->{transactions} });
55

  
56
  my $output = '';
57

  
58
  my $xml    = XML::Writer->new(OUTPUT      => \$output,
59
                                DATA_MODE   => 1,
60
                                DATA_INDENT => 2,
61
                                ENCODING    => 'utf-8');
62

  
63
  my @now       = localtime;
64
  # (removed time zone stuff in accordance with SIX examples)
65
  my $now_str   = strftime('%Y-%m-%dT%H:%M:%S', @now);
66

  
67
  my $payment_inf_id_counter = 1;
68
  my $transaction_id_counter = 1;
69

  
70
  my $grouped_transactions = $self->_group_transactions();
71

  
72
  # Note: the transaction->get function, respectively the encode function
73
  # that is used there produces garbled characters for example:
74
  # encode('UTF-8', '& & äöü <123>') -> &amp; &amp; äöü &lt;123&gt;
75
  # not fully clear why, for now I'll omit the function in some places
76

  
77
  $xml->xmlDecl();
78

  
79
  $xml->startTag('Document',
80
                 'xmlns'              => "urn:iso:std:iso:20022:tech:xsd:pain.001.001.09",
81
                 'xmlns:xsi'          => 'http://www.w3.org/2001/XMLSchema-instance',
82
                 'xsi:schemaLocation' => "urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.ch.03.xsd");
83

  
84
  $xml->startTag('CstmrCdtTrfInitn');
85

  
86
  $xml->startTag('GrpHdr');
87
  $xml->dataElement('MsgId', $self->_restricted_identification_sepa1($self->{message_id}));
88
  $xml->dataElement('CreDtTm', $now_str);
89
  $xml->dataElement('NbOfTxs', scalar @{ $self->{transactions} });
90
  $xml->dataElement('CtrlSum', $self->_format_amount($grouped_transactions->{sum_amount}));
91

  
92
  $xml->startTag('InitgPty');
93
  $xml->dataElement('Nm', encode('UTF-8', substr($self->{company}, 0, 70)));
94
  # (details about the software)
95
  $xml->startTag('CtctDtls');
96
  $xml->startTag('Othr');
97
  $xml->dataElement('ChanlTp', 'NAME');
98
  $xml->dataElement('Id', 'Kivitendo');
99
  $xml->endTag('Othr');
100
  $xml->endTag('CtctDtls');
101
  # TODO (optional): get current kivitendo version
102
  # $xml->startTag('Othr');
103
  # $xml->dataElement('ChanlTp', 'VRSN');
104
  # $xml->dataElement('Id', '...');
105
  # $xml->endTag('Othr');
106
  $xml->endTag('InitgPty');
107

  
108
  $xml->endTag('GrpHdr');
109

  
110
  foreach my $key (keys %{ $grouped_transactions->{groups} }) {
111
    my $transaction_group  = $grouped_transactions->{groups}->{$key};
112
    my $master_transaction = $transaction_group->{transactions}->[0];
113

  
114
    $xml->startTag('PmtInf');
115

  
116
    my $payment_inf_id = 'PMTINF-' . sprintf('%010d', $payment_inf_id_counter);
117
    $xml->dataElement('PmtInfId', $payment_inf_id);
118

  
119
    $xml->dataElement('PmtMtd', 'TRF');
120

  
121
    # if batch booking true there will be "1 booking per group"
122
    # (according to swiss business rules specification)
123
    # I don't fully understand how there can be 1 booking for multiple transactions
124
    # furthermore in that case we should make sure there is only 1 currency in the group
125
    # (function _group_transactions)
126
    # so for now I'm setting this to false
127
    $xml->dataElement('BtchBookg', 'false');
128

  
129
    if ($master_transaction->get('is_sepa_payment')) {
130
      $xml->startTag('PmtTpInf');
131
      $xml->startTag('SvcLvl');
132
      $xml->dataElement('Cd', 'SEPA');
133
      $xml->endTag('SvcLvl');
134
      $xml->endTag('PmtTpInf');
135
    }
136

  
137
    $xml->startTag('ReqdExctnDt');
138
    $xml->dataElement('Dt', $master_transaction->get('execution_date'));
139
    $xml->endTag('ReqdExctnDt');
140
    $xml->startTag('Dbtr');
141
    # not using get here because it's garbling the characters
142
    $xml->dataElement('Nm', $self->{company});
143
    $xml->endTag('Dbtr');
144

  
145
    $xml->startTag('DbtrAcct');
146
    $xml->startTag('Id');
147
    $xml->dataElement('IBAN', $master_transaction->get('src_iban', 34));
148
    $xml->endTag('Id');
149
    $xml->endTag('DbtrAcct');
150

  
151
    $xml->startTag('DbtrAgt');
152
    $xml->startTag('FinInstnId');
153
    $xml->dataElement('BICFI', $master_transaction->get('src_bic', 20));
154
    $xml->endTag('FinInstnId');
155
    $xml->endTag('DbtrAgt');
156

  
157
    foreach my $transaction (@{ $transaction_group->{transactions} }) {
158
      $xml->startTag('CdtTrfTxInf');
159

  
160
      $xml->startTag('PmtId');
161

  
162
      # using a simple counter here, analog to examples from SIX
163
      my $instr_id = 'INSTRID-' .
164
        sprintf('%010d', $payment_inf_id_counter) . '-' .
165
        sprintf('%010d', $transaction_id_counter);
166
      $xml->dataElement('InstrId', $instr_id);
167

  
168
      $xml->dataElement('EndToEndId',
169
        $self->_restricted_identification_sepa1($transaction->get('end_to_end_id')));
170
      $xml->endTag('PmtId');
171

  
172
      $xml->startTag('Amt');
173
      $xml->startTag('InstdAmt', 'Ccy' => $transaction->{currency});
174
      $xml->characters($self->_format_amount($transaction->{amount}));
175
      $xml->endTag('InstdAmt');
176
      $xml->endTag('Amt');
177

  
178
      if ($transaction->{is_sepa_payment}) {
179
        $xml->startTag('CdtrAgt');
180
        $xml->startTag('FinInstnId');
181
        $xml->dataElement('BICFI', $transaction->{dst_bic});
182
        $xml->endTag('FinInstnId');
183
        $xml->endTag('CdtrAgt');
184
      }
185

  
186
      $xml->startTag('Cdtr');
187
      if ($transaction->{is_qrbill}) {
188
        $xml->dataElement('Nm', $transaction->{creditor_name});
189
        $xml->startTag('PstlAdr');
190
        # don't use empty elements here, according to SIX validator
191
        $xml->dataElement('StrtNm', $transaction->{creditor_street_name}) if $transaction->{creditor_street_name};
192
        $xml->dataElement('BldgNb', $transaction->{creditor_building_number}) if $transaction->{creditor_building_number};
193
        $xml->dataElement('PstCd', $transaction->{creditor_postal_code});
194
        $xml->dataElement('TwnNm', $transaction->{creditor_town_name});
195
        $xml->dataElement('Ctry', $transaction->{creditor_country});
196
        $xml->endTag('PstlAdr');
197
      } else {
198
        $xml->dataElement('Nm', $transaction->{company});
199
        if ($transaction->{has_creditor_address}) {
200
          $xml->startTag('PstlAdr');
201
          $xml->dataElement('StrtNm', $transaction->{creditor_street_name}) if $transaction->{creditor_street_name};
202
          $xml->dataElement('BldgNb', $transaction->{creditor_building_number}) if $transaction->{creditor_building_number};
203
          $xml->dataElement('PstCd', $transaction->{creditor_postal_code});
204
          $xml->dataElement('TwnNm', $transaction->{creditor_town_name});
205
          $xml->dataElement('Ctry', $transaction->{creditor_country});
206
          $xml->endTag('PstlAdr');
207
        }
208
      }
209
      $xml->endTag('Cdtr');
210

  
211
      $xml->startTag('CdtrAcct');
212
      $xml->startTag('Id');
213
      $xml->dataElement('IBAN', $transaction->get('dst_iban', 34));
214
      $xml->endTag('Id');
215
      $xml->endTag('CdtrAcct');
216

  
217
      $xml->startTag('RmtInf');
218
      if ($transaction->{end_to_end_id} =~ /^ENDTOENDID-(QRR|SCOR)$/) {
219
        $xml->startTag('Strd');
220
        $xml->startTag('CdtrRefInf');
221
        $xml->startTag('Tp');
222
        $xml->startTag('CdOrPrtry');
223
        if ($transaction->{end_to_end_id} eq 'ENDTOENDID-QRR') {
224
          $xml->dataElement('Prtry', 'QRR');
225
        } else {
226
          $xml->dataElement('Cd', 'SCOR');
227
        }
228
        $xml->endTag('CdOrPrtry');
229
        $xml->endTag('Tp');
230
        $xml->dataElement('Ref', $transaction->get('reference'));
231
        $xml->endTag('CdtrRefInf');
232
        if ($transaction->{unstructured_message}) {
233
          $xml->dataElement('AddtlRmtInf', $transaction->get('unstructured_message'));
234
        }
235
        $xml->endTag('Strd');
236
      } else {
237
        if ($transaction->{reference}) {
238
          $xml->dataElement('Ustrd', $transaction->get('reference', 140));
239
        }
240
      }
241
      $xml->endTag('RmtInf');
242

  
243
      $xml->endTag('CdtTrfTxInf');
244

  
245
      $transaction_id_counter++;
246
    }
247

  
248
    $xml->endTag('PmtInf');
249

  
250
    $payment_inf_id_counter++;
251
  }
252

  
253
  $xml->endTag('CstmrCdtTrfInitn');
254
  $xml->endTag('Document');
255
  $xml->end();
256

  
257
  return $output;
258
}
259

  
260
1;
261

  
262
# Local Variables:
263
# coding: utf-8
264
# End:
SL/SEPA/XML/SwissTransaction.pm
1
package SL::SEPA::XML::SwissTransaction;
2

  
3
use strict;
4

  
5
use parent qw(SL::SEPA::XML::Transaction);
6

  
7
use Carp;
8
use Encode;
9
use List::Util qw(first);
10
use POSIX qw(strftime);
11

  
12
sub _init {
13
  my $self       = shift;
14
  my %params     = @_;
15

  
16
  $self->{sepa}  = $params{sepa};
17
  delete $params{sepa};
18

  
19
  my $missing_parameter = first { !$params{$_} } qw(src_iban src_bic dst_iban company reference amount end_to_end_id);
20
  croak "Missing parameter: $missing_parameter" if ($missing_parameter);
21

  
22
  $params{end_to_end_id}  ||= 'NOTPROVIDED';
23
  $params{execution_date} ||= strftime "%Y-%m-%d", localtime;
24

  
25
  croak "Execution date format wrong for '$params{execution_date}': not YYYY-MM-DD." if ($params{execution_date} !~ /^\d{4}-\d{2}-\d{2}$/);
26

  
27
  map { $self->{$_} = $self->{sepa}->{iconv}->convert($params{$_})       } keys %params;
28
  map { $self->{$_} =~ s/\s+//g                                          } qw(src_iban src_bic dst_iban);
29
  map { $self->{$_} = $self->{sepa}->_replace_special_chars($self->{$_}) } qw(company reference end_to_end_id);
30
}
31

  
32
1;
bin/mozilla/sepa.pl
14 14
use SL::ReportGenerator;
15 15
use SL::SEPA;
16 16
use SL::SEPA::XML;
17
use SL::SEPA::SwissXML;
18
use SL::Helper::QrBillParser;
19
use SL::Helper::ISO3166;
20

  
21
use SL::Helper::QrBillFunctions qw(
22
  get_street_name_from_address_line
23
  get_building_number_from_address_line
24
);
17 25

  
18 26
require "bin/mozilla/common.pl";
19 27
require "bin/mozilla/reportgenerator.pl";
......
26 34
  my $vc            = $form->{vc} eq 'customer' ? 'customer' : 'vendor';
27 35
  my $vc_no         = $form->{vc} eq 'customer' ? $::locale->text('VN') : $::locale->text('CN');
28 36

  
29
  $form->{title}    = $vc eq 'customer' ? $::locale->text('Prepare bank collection via SEPA XML') : $locale->text('Prepare bank transfer via SEPA XML');
37
  my $swiss_export = $::instance_conf->get_sepa_swiss_xml_export;
38

  
39
  $form->{title}    = $vc eq 'customer' ?
40
                        $::locale->text('Prepare bank collection via SEPA XML') :
41
                        $swiss_export ?
42
                          $locale->text('Prepare bank transfer via swiss XML') :
43
                          $locale->text('Prepare bank transfer via SEPA XML');
30 44

  
31 45
  my $bank_accounts = SL::DB::Manager::BankAccount->get_all_sorted( query => [ obsolete => 0 ] );
32 46

  
......
66 80
    $invoice->{reference_prefix_vc}  = ' '  . $prefix_vc_number unless $prefix_vc_number =~ m/^ /;
67 81
  }
68 82

  
83
  # for swiss export override database check because of different cases
84
  if ($swiss_export) {
85
    foreach my $invoice (@{ $invoices }) {
86
      # determine invoice type
87
      if ($invoice->{qrbill_data}) {
88
        $invoice->{type} = 'QRBILL';
89

  
90
        # vendor iban comes from qrbill data
91
        # no further checks needed
92
        $invoice->{vc_bank_info_ok} = 1;
93

  
94
      } elsif ($invoice->{vc_iban} =~ m/^(CH|LI)/) {
95
        $invoice->{type} = 'DOMESTIC';
96

  
97
        # vendor iban is needed
98
        $invoice->{vc_bank_info_ok} = $invoice->{vc_iban} ? 1 : 0;
99

  
100
      } else {
101
        $invoice->{type} = 'SEPA';
102

  
103
        # vendor iban and bic are needed
104
        $invoice->{vc_bank_info_ok} = $invoice->{vc_iban} && $invoice->{vc_bic} ? 1 : 0
105
      }
106
    }
107
  }
108

  
69 109
  setup_sepa_add_transfer_action_bar();
70 110

  
71 111
  $form->header();
......
86 126
  my $myconfig      = \%main::myconfig;
87 127
  my $vc            = $form->{vc} eq 'customer' ? 'customer' : 'vendor';
88 128

  
89
  $form->{title}    = $vc eq 'customer' ? $::locale->text('Create bank collection via SEPA XML') : $locale->text('Create bank transfer via SEPA XML');
129
  my $swiss_export = $::instance_conf->get_sepa_swiss_xml_export;
130

  
131
  $form->{title}    = $vc eq 'customer' ?
132
                        $::locale->text('Create bank collection via SEPA XML') :
133
                        $swiss_export ?
134
                          $locale->text('Create bank transfer via swiss XML') :
135
                          $locale->text('Create bank transfer via SEPA XML');
90 136

  
91 137
  my $bank_accounts = SL::DB::Manager::BankAccount->get_all_sorted( query => [ obsolete => 0 ] );
92 138
  if (!scalar @{ $bank_accounts }) {
......
149 195

  
150 196
  my ($vc_bank_info);
151 197
  my $error_message;
152

  
153 198
  my @bank_columns    = qw(iban bic);
154
  push @bank_columns, qw(mandator_id mandate_date_of_signature) if $vc eq 'customer';
155 199

  
156
  if ($form->{confirmation}) {
157
    $vc_bank_info = { map { $_->{id} => $_ } @{ $form->{vc_bank_info} || [] } };
200
  # separate validation for swiss export
201
  if (!$swiss_export) {
202
    push @bank_columns, qw(mandator_id mandate_date_of_signature) if $vc eq 'customer';
203

  
204
    if ($form->{confirmation}) {
205
      $vc_bank_info = { map { $_->{id} => $_ } @{ $form->{vc_bank_info} || [] } };
158 206

  
159
    foreach my $info (values %{ $vc_bank_info }) {
160
      if (any { !$info->{$_} } @bank_columns) {
161
        $error_message = $locale->text('The bank information must not be empty.');
162
        last;
207
      foreach my $info (values %{ $vc_bank_info }) {
208
        if (any { !$info->{$_} } @bank_columns) {
209
          $error_message = $locale->text('The bank information must not be empty.');
210
          last;
211
        }
163 212
      }
164 213
    }
214
  } else {
215
    ($error_message, $vc_bank_info) = validate_vendors_swiss_export(\@bank_transfers);
165 216
  }
166 217

  
167 218
  if ($error_message || !$form->{confirmation}) {
168
    my @vc_ids                 = uniq map { $_->{vc_id} } @bank_transfers;
169
    $vc_bank_info            ||= CT->get_bank_info('vc' => $vc,
170
                                                   'id' => \@vc_ids);
171
    my @vc_bank_info           = sort { lc $a->{name} cmp lc $b->{name} } values %{ $vc_bank_info };
219
    if (!$swiss_export) {
220
      my @vc_ids = uniq map { $_->{vc_id} } @bank_transfers;
221

  
222
      $vc_bank_info ||= CT->get_bank_info('vc' => $vc, 'id' => \@vc_ids);
223
    }
224

  
225
    my @vc_bank_info = sort { lc $a->{name} cmp lc $b->{name} } values %{ $vc_bank_info };
172 226

  
173 227
    setup_sepa_create_transfer_action_bar(is_vendor => $vc eq 'vendor');
174 228

  
......
198 252
                                     'vc'             => $vc);
199 253

  
200 254
    $form->header();
201
    print $form->parse_html_template('sepa/bank_transfer_created', { 'id' => $id, 'vc' => $vc });
255
    print $form->parse_html_template('sepa/bank_transfer_created',
256
                                      {
257
                                        'id' => $id,
258
                                        'vc' => $vc,
259
                                      });
202 260
  }
203 261

  
204 262
  $main::lxdebug->leave_sub();
205 263
}
206 264

  
265
sub validate_vendors_swiss_export {
266
  my ($bank_transfers) = @_;
267

  
268
  my $form          = $main::form;
269
  my $locale        = $main::locale;
270
  my $myconfig      = \%main::myconfig;
271

  
272
  # determine unique vendor types
273
  my %unique_vendor_types;
274
  for my $bt (@$bank_transfers) {
275
    my $uid = "$bt->{vc_id}_$bt->{type}";
276
    $unique_vendor_types{$uid} = {
277
      vc_id => $bt->{vc_id},
278
      type => $bt->{type}
279
    } unless defined $unique_vendor_types{$uid};
280
  }
281

  
282
  # get bank info for unique vendor types
283
  my $vendors = $form->{vc_bank_info} ?
284
    { map { $_->{id} => $_ } @{ $form->{vc_bank_info} } } :
285
    CT->get_bank_info('vc' => 'vendor', 'id' => [ map { $_->{vc_id} } values %unique_vendor_types ]);
286

  
287
  # combine bank info with unique vendor types
288
  for my $unique_vendor (values %unique_vendor_types) {
289
    $vendors->{$unique_vendor->{vc_id}}->{type} = $unique_vendor->{type};
290
  }
291

  
292
  # validate bank info for unique vendor types
293
  my $error_message;
294
  for my $vendor (values %$vendors) {
295
    next if ($vendor->{type} eq 'QRBILL');
296
    if ($vendor->{type} eq 'DOMESTIC' && !$vendor->{iban}) {
297
      $error_message = $locale->text('The bank information must not be empty.');
298
      last;
299
    } elsif ($vendor->{type} eq 'SEPA' && (!$vendor->{iban} || !$vendor->{bic})) {
300
      $error_message = $locale->text('The bank information must not be empty.');
301
      last;
302
    }
303
  }
304

  
305
  return $error_message, $vendors;
306
}
307

  
207 308
sub bank_transfer_search {
208 309
  $main::lxdebug->enter_sub();
209 310

  
......
267 368
    'closed'      => { 'text' => $locale->text('Closed'), },
268 369
    num_invoices  => { 'text' => $locale->text('Number of invoices'), },
269 370
    sum_amounts   => { 'text' => $locale->text('Sum of all amounts'), },
270
    message_ids   => { 'text' => $locale->text('SEPA message IDs'), },
371
    message_ids   => { 'text' => !$::instance_conf->get_sepa_swiss_xml_export ? $locale->text('SEPA message IDs') : $locale->text('Message IDs'), },
271 372
  );
272 373

  
273 374
  my @columns = qw(selected id export_date employee executed closed num_invoices sum_amounts message_ids);
......
390 491
    has_executed              => $has_executed,
391 492
  );
392 493

  
393
  $form->{title}    = $locale->text('View SEPA export');
494
  $form->{title}    = !$::instance_conf->get_sepa_swiss_xml_export ?
495
                        $locale->text('View SEPA export') :
496
                        $locale->text('View bank transfer');
394 497
  $form->header();
395 498
  print $form->parse_html_template('sepa/bank_transfer_edit',
396 499
                                   { ids                       => \@ids,
......
514 617
  my $vc       = $form->{vc} eq 'customer' ? 'customer' : 'vendor';
515 618
  my $defaults = SL::DB::Default->get;
516 619

  
620
  my $swiss_export = $::instance_conf->get_sepa_swiss_xml_export;
621

  
517 622
  if (!$defaults->company) {
518 623
    $form->show_generic_error($locale->text('You have to enter a company name in the client configuration.'));
519 624
  }
......
547 652

  
548 653
  my $message_id = strftime('MSG%Y%m%d%H%M%S', localtime) . sprintf('%06d', $$);
549 654

  
550
  my $sepa_xml   = SL::SEPA::XML->new('company'     => $defaults->company,
551
                                      'creditor_id' => $defaults->sepa_creditor_id,
552
                                      'src_charset' => 'UTF-8',
553
                                      'message_id'  => $message_id,
554
                                      'grouped'     => 1,
555
                                      'collection'  => $vc eq 'customer',
556
    );
655
  my $sepa_xml;
656
  my %sepa_xml_params = (
657
    'company'     => $defaults->company,
658
    'creditor_id' => $defaults->sepa_creditor_id,
659
    'src_charset' => 'UTF-8',
660
    'message_id'  => $message_id,
661
    'grouped'     => 1,
662
    'collection'  => $vc eq 'customer',
663
  );
664
  if (!$swiss_export) {
665
      $sepa_xml = SL::SEPA::XML->new(%sepa_xml_params);
666
  } else {
667
      $sepa_xml = SL::SEPA::SwissXML->new(%sepa_xml_params);
668
  }
557 669

  
558 670
  foreach my $item (@items) {
559 671
    my $requested_execution_date;
......
573 685
      }
574 686
    }
575 687

  
576
    $sepa_xml->add_transaction({ 'src_iban'       => $item->{our_iban},
577
                                 'src_bic'        => $item->{our_bic},
578
                                 'dst_iban'       => $item->{vc_iban},
579
                                 'dst_bic'        => $item->{vc_bic},
580
                                 'company'        => $item->{vc_name},
581
                                 'company_number' => $item->{vc_number},
582
                                 'amount'         => $item->{amount},
583
                                 'reference'      => $item->{reference},
584
                                 'mandator_id'    => $mandator_id,
585
                                 'reference_date' => $item->{reference_date},
586
                                 'execution_date' => $requested_execution_date,
587
                                 'end_to_end_id'  => $item->{end_to_end_id},
588
                                 'date_of_signature' => $item->{mandate_date_of_signature}, });
688
    my $transaction_data = {
689
      'src_iban'       => $item->{our_iban},
690
      'src_bic'        => $item->{our_bic},
691
      'dst_iban'       => $item->{vc_iban},
692
      'dst_bic'        => $item->{vc_bic},
693
      'company'        => $item->{vc_name},
694
      'company_number' => $item->{vc_number},
695
      'amount'         => $item->{amount},
696
      'reference'      => $item->{reference},
697
      'mandator_id'    => $mandator_id,
698
      'reference_date' => $item->{reference_date},
699
      'execution_date' => $requested_execution_date,
700
      'end_to_end_id'  => $item->{end_to_end_id},
701
      'date_of_signature' => $item->{mandate_date_of_signature},
702
    };
703

  
704
    # set data for swiss xml export
705
    if ($swiss_export) {
706

  
707
      $transaction_data->{currency} = $item->{currency_name};
708
      $transaction_data->{is_qrbill} = 0;
709
      $transaction_data->{is_sepa_payment} = 0;
710

  
711
      if ($item->{qrbill_data}) {
712
        my $qr_obj = SL::Helper::QrBillParser->new($item->{qrbill_data});
713
        # check if valid qr-bill
714
        if (!$qr_obj->is_valid) {
715
          $form->show_generic_error($locale->text('QR bill data invalid.'));
716
        }
717
        $transaction_data->{is_qrbill} = 1;
718

  
719
        # set qr reference type
720
        # setting these according to example pain_001_Example_PT_D_QRR_SCOR.xml
721
        # 'QRR'
722
        if ($qr_obj->{payment_reference}->{reference_type} eq 'QRR') {
723
          $transaction_data->{end_to_end_id} = 'ENDTOENDID-QRR';
724
        }
725
        # 'SCOR'
726
        elsif ($qr_obj->{payment_reference}->{reference_type} eq 'SCOR') {
727
          $transaction_data->{end_to_end_id} = 'ENDTOENDID-SCOR';
728
        }
729
        # 'NON'
730
        # (using the default end_to_end_id given above)
731

  
732
        # data for remittance information
733
        # this contains the reference for 'QRR' or 'SCOR'
734
        $transaction_data->{reference} = $qr_obj->{payment_reference}->{reference};
735
        # this can be used in any case
736
        $transaction_data->{unstructured_message} = $qr_obj->{additional_information}->{unstructured_message};
737

  
738
        # set currency and amount
739
        $transaction_data->{currency} = $qr_obj->{payment_amount_information}->{currency};
740
        $transaction_data->{amount} = $qr_obj->{payment_amount_information}->{amount};
741

  
742
        # set creditor name and address from qr data
743
        $transaction_data->{creditor_name} = $qr_obj->{creditor}->{name};
744
        $transaction_data->{creditor_street_name} = $qr_obj->get_creditor_street_name;
745
        $transaction_data->{creditor_building_number} = $qr_obj->get_creditor_building_number;
746
        $transaction_data->{creditor_postal_code} = $qr_obj->get_creditor_post_code;
747
        $transaction_data->{creditor_town_name} = $qr_obj->get_creditor_town_name;
748
        $transaction_data->{creditor_country} = $qr_obj->{creditor}->{country};
749

  
750
        # set creditor iban
751
        $transaction_data->{dst_iban} = $qr_obj->{creditor_information}->{iban};
752
      } else {
753
        # if no qr-bill data is given, we want to set the creditor address from the vc data
754
        # this is not needed for the creditor name as it is set above
755

  
756
        $transaction_data->{has_creditor_address} = 0;
757

  
758
        if ($item->{vc_zipcode} && $item->{vc_city} && $item->{vc_country}) {
759
          $transaction_data->{has_creditor_address} = 1;
760

  
761
          $transaction_data->{creditor_street_name} = get_street_name_from_address_line($item->{vc_street});
762
          $transaction_data->{creditor_building_number} = get_building_number_from_address_line($item->{vc_building_number});
763

  
764
          $transaction_data->{creditor_postal_code} = $item->{vc_zipcode};
765
          $transaction_data->{creditor_town_name} = $item->{vc_city};
766
          # use ISO 3166-1 alpha-2 country code
767
          $transaction_data->{creditor_country} = SL::Helper::ISO3166::map_name_to_alpha_2_code($item->{vc_country});
768
        }
769

  
770
        # if the Iban does not start with 'CH' or 'LI' we assume it is a SEPA payment
771
        if ($item->{vc_iban} !~ /^(CH|LI)/) {
772
          $transaction_data->{is_sepa_payment} = 1;
773

  
774
          # check if currency is EUR
775
          if ($transaction_data->{currency} ne 'EUR') {
776
            $form->show_generic_error($locale->text('SEPA payments must be in EUR.'));
777
          }
778

  
779
          # check if destination BIC is set
780
          if (!$transaction_data->{dst_bic}) {
781
            $form->show_generic_error($locale->text('SEPA payments require a destination BIC.'));
782
          }
783
          # TODO: set reference (unstructured or SCOR)
784
          # -> where should the data come from?
785
        }
786
      }
787
    }
788

  
789
    $sepa_xml->add_transaction($transaction_data);
589 790
  }
590 791

  
591 792
  # Store the message ID used in each of the entries in order to
......
599 800

  
600 801
  my $xml = $sepa_xml->to_xml();
601 802

  
602
  print $cgi->header('-type'                => 'application/octet-stream',
603
                     '-content-disposition' => 'attachment; filename="SEPA_' . $message_id . ($vc eq 'customer' ? '.cdd' : '.cct') . '"',
604
                     '-content-length'      => length $xml);
803
  my $header;
804
  if ($swiss_export) {
805
    # I had to use encode for the length here, otherwise the xml output was cut off
806
    $header = {
807
      '-type' => 'text/xml; charset=utf-8',
808
      '-content-disposition' => 'attachment; filename="SWISS_' . $message_id . '.xml' . '"',
809
      '-content-length' => length Encode::encode('utf-8', $xml),
810
    };
811
  } else {
812
    $header = {
813
      '-type' => 'application/octet-stream',
814
      '-content-disposition' => 'attachment; filename="SEPA_' . $message_id . ($vc eq 'customer' ? '.cdd' : '.cct') . '"',
815
      '-content-length' => length $xml,
816
    };
817
  }
818

  
819
  print $cgi->header($header);
605 820
  print $xml;
606 821

  
607 822
  $main::lxdebug->leave_sub();
......
711 926
      combobox => [
712 927
        action => [ t8('Actions') ],
713 928
        action => [
714
          t8('SEPA XML download'),
929
          !$::instance_conf->get_sepa_swiss_xml_export ? t8('SEPA XML download') : t8('Swiss XML download'),
715 930
          submit => [ '#form', { action => 'bank_transfer_download_sepa_xml' } ],
716 931
          checks => [ [ 'kivi.check_if_entries_selected', '[name="ids[]"]' ] ],
717 932
        ],
......
724 939
          t8('Mark as closed'),
725 940
          submit => [ '#form', { action => 'bank_transfer_mark_as_closed' } ],
726 941
          checks => [ [ 'kivi.check_if_entries_selected', '[name="ids[]"]' ] ],
727
          confirm => [ $params{is_vendor} ? t8('Do you really want to close the selected SEPA exports? No payment will be recorded for bank transfers that haven\'t been marked as executed yet.')
728
                                          : t8('Do you really want to close the selected SEPA exports? No payment will be recorded for bank collections that haven\'t been marked as executed yet.') ],
942
          confirm => [ !$::instance_conf->get_sepa_swiss_xml_export ?
943
                          $params{is_vendor} ? t8('Do you really want to close the selected SEPA exports? No payment will be recorded for bank transfers that haven\'t been marked as executed yet.')
944
                                             : t8('Do you really want to close the selected SEPA exports? No payment will be recorded for bank collections that haven\'t been marked as executed yet.')
945
                                             : t8('Do you really want to close the selected Swiss XML exports? No payment will be recorded for bank transfers that haven\'t been marked as executed yet.') ],
729 946
        ],
730 947
        action => [
731
          t8('Undo SEPA exports'),
948
          !$::instance_conf->get_sepa_swiss_xml_export ? t8('Undo SEPA exports') : t8('Undo Swiss XML exports'),
732 949
          submit => [ '#form', { action => 'bank_transfer_undo_sepa_xml' } ],
733 950
          checks => [ [ 'kivi.check_if_entries_selected', '[name="ids[]"]' ] ],
734
          confirm => [ t8('Do you really want to undo the selected SEPA exports? You have to reassign the export again.') ],
951
          confirm => [ !$::instance_conf->get_sepa_swiss_xml_export ?
952
            t8('Do you really want to undo the selected SEPA exports? You have to reassign the export again.') :
953
            t8('Do you really want to undo the selected Swiss XML exports? You have to reassign the export again.') ],
735 954
        ],
736 955
      ], # end of combobox "Actions"
737 956
    );
menus/user/00-erp.yaml
770 770
  id: cash_bank_transfer_via_sepa
771 771
  name: Bank transfer via SEPA
772 772
  order: 400
773
  access: cash
773
  access: cash & !client/sepa_swiss_xml_export
774
  module: sepa.pl
775
  params:
776
    action: bank_transfer_add
777
    vc: vendor
778
- parent: cash
779
  id: cash_bank_transfer_via_swiss_xml
780
  name: Bank transfer via swiss XML
781
  order: 450
782
  access: cash & client/sepa_swiss_xml_export
774 783
  module: sepa.pl
775 784
  params:
776 785
    action: bank_transfer_add
......
854 863
  id: cash_reports_bank_transfers_via_sepa
855 864
  name: Bank transfers via SEPA
856 865
  order: 400
857
  access: cash
866
  access: cash & !client/sepa_swiss_xml_export
867
  module: sepa.pl
868
  params:
869
    action: bank_transfer_search
870
    vc: vendor
871
- parent: cash_reports
872
  id: cash_reports_bank_transfers_via_swiss_xml
873
  name: Bank transfers via swiss XML
874
  order: 400
875
  access: cash & client/sepa_swiss_xml_export
858 876
  module: sepa.pl
859 877
  params:
860 878
    action: bank_transfer_search
templates/design40_webpages/client_config/_features.html
238 238
        <td>[% L.input_tag('defaults.sepa_set_skonto_date_buffer_in_days_as_number', SELF.defaults.sepa_set_skonto_date_buffer_in_days_as_number, size=10, class='wi-small' ) %]</td>
239 239
        <td class="long-desc">[% LxERP.t8('In addition to the above date functions, subtract the following amount of days from the calculated date as a buffer.') %]</td>
240 240
      </tr>
241
      <tr>
242
        <th>[% 'Activate Swiss Bank Transfer XML Export' | $T8 %]</th>
243
        <td>[% L.yes_no_tag('defaults.sepa_swiss_xml_export', SELF.defaults.sepa_swiss_xml_export) %]</td>
244
        <td class="long-desc">[% LxERP.t8('Bank transfer via SEPA is replaced by Bank transfer via swiss XML. According to Swiss Payment Standards, Customer Credit Transfer Initiation (pain.001).') %]</td>
245
      </tr>
241 246

  
242 247
      <tr>
243 248
        <th class="caption" colspan="3">[% LxERP.t8("Experimental Features") %]</th>
templates/design40_webpages/sepa/bank_transfer_add.html
59 59
          <input type="hidden" id="amount_less_skonto_[% loop.count %]" name="amount_less_skonto_[% loop.count %]" value="[% LxERP.format_amount(invoice.amount_less_skonto, 2) %]">
60 60
          <input type="hidden" id="invoice_open_amount_[% loop.count %]" name="invoice_open_amount_[% loop.count %]" value="[% LxERP.format_amount(invoice.open_amount - invoice.transfer_amount, 2) %]">
61 61
          <input type="hidden" id="skonto_amount_[% loop.count %]" name="skonto_amount_[% loop.count %]" value="[% LxERP.format_amount(invoice.skonto_amount, 2) %]">
62
          [% L.hidden_tag('bank_transfers[].type', invoice.type) %]
62 63
          [% IF invoice.vc_bank_info_ok && !invoice.is_sepa_blocked %]
63 64
            [% L.checkbox_tag("ids[]", value=invoice.id, checked=invoice.checked) %]
64 65
          [% END %]
......
124 125
  <p><sup>(1)</sup>
125 126
    [% IF is_vendor %]
126 127
      [% 'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' | $T8 %]
127
      [% "The required information consists of the IBAN and the BIC." | $T8 %]
128
      [% IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
129
        [% "The required information consists of the IBAN and the BIC." | $T8 %]
130
      [% ELSE %]
131
        [% "The required information consists of the IBAN for domestic payments. And IBAN and the BIC for SEPA payments." | $T8 %]
132
      [% END %]
128 133
    [% ELSE %]
129 134
      [% 'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' | $T8 %]
130 135
      [% "The required information consists of the IBAN, the BIC, the mandator ID and the mandate's date of signature." | $T8 %]
templates/design40_webpages/sepa/bank_transfer_create.html
50 50
      <th>[% IF is_vendor %][% 'Vendor' | $T8 %][% ELSE %][% LxERP.t8('Customer') %][% END %]</th>
51 51
      <th>[% 'IBAN' | $T8 %]</th>
52 52
      <th>[% 'BIC' | $T8 %]</th>
53
      <th>[% 'Bank' | $T8 %]</th>
53
      [% IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
54
        <th>[% 'Bank' | $T8 %]</th>
55
      [% END %]
54 56
      [% IF vc == 'customer' %]
55 57
        <th>[% 'Mandator ID' | $T8 %]</th>
56 58
        <th>[% 'Mandate Date of Signature' | $T8 %]</th>
......
60 62
  <tbody>
61 63
    [% FOREACH vbi = VC_BANK_INFO %]
62 64
      <tr class="listrow[% loop.count % 1 %]">
63
        <td> <input type="hidden" name="vc_bank_info[+].id" value="[% HTML.escape(vbi.id) %]"> <input type="hidden" name="vc_bank_info[].name" value="[% HTML.escape(vbi.name) %]"> [% HTML.escape(vbi.name) %] </td>
64
        <td><input type="text" name="vc_bank_info[].iban" size="34" value="[% HTML.escape(vbi.iban.substr(0, 34)) %]" maxlength="34"></td>
65
        <td><input type="text" name="vc_bank_info[].bic" size="20" value="[% HTML.escape(vbi.bic.substr(0, 20)) %]" maxlength="20"></td>
66
        <td><input type="text" name="vc_bank_info[].bank" size="30" value="[% HTML.escape(vbi.bank) %]"></td>
67
        [% IF vc == 'customer' %]
68
          <td><input type="text" name="vc_bank_info[].mandator_id" size="30" value="[% HTML.escape(vbi.mandator_id) %]"></td>
69
          <td>[% L.date_tag("vc_bank_info[].mandate_date_of_signature", vbi.mandate_date_of_signature) %]</td>
65
        [% IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
66
          <td> <input type="hidden" name="vc_bank_info[+].id" value="[% HTML.escape(vbi.id) %]"> <input type="hidden" name="vc_bank_info[].name" value="[% HTML.escape(vbi.name) %]"> [% HTML.escape(vbi.name) %] </td>
67
          <td><input type="text" name="vc_bank_info[].iban" size="34" value="[% HTML.escape(vbi.iban.substr(0, 34)) %]" maxlength="34"></td>
68
          <td><input type="text" name="vc_bank_info[].bic" size="20" value="[% HTML.escape(vbi.bic.substr(0, 20)) %]" maxlength="20"></td>
69
          <td><input type="text" name="vc_bank_info[].bank" size="30" value="[% HTML.escape(vbi.bank) %]"></td>
70
          [% IF vc == 'customer' %]
71
            <td><input type="text" name="vc_bank_info[].mandator_id" size="30" value="[% HTML.escape(vbi.mandator_id) %]"></td>
72
            <td>[% L.date_tag("vc_bank_info[].mandate_date_of_signature", vbi.mandate_date_of_signature) %]</td>
73
          [% END %]
74
        [% ELSE %]
75
          [% L.hidden_tag('vc_bank_info[+].id', vbi.id) %]
76
          [% L.hidden_tag('vc_bank_info[].type', vbi.type) %]
77
          [% IF vbi.type == 'QRBILL' %]
78
            [% L.hidden_tag('vc_bank_info[].name', vbi.name) %]
79
          [% ELSIF vbi.type == 'DOMESTIC' %]
80
            <td>
81
              [% HTML.escape(vbi.name) %]
82
              [% L.hidden_tag('vc_bank_info[].name', vbi.name) %]
83
            </td>
84
            <td>[% L.input_tag('vc_bank_info[].iban', vbi.iban) %]</td>
85
          [% ELSE %]
86
            <td>
87
              [% HTML.escape(vbi.name) %]
88
              [% L.hidden_tag('vc_bank_info[].name', vbi.name) %]
89
            </td>
90
            <td>[% L.input_tag('vc_bank_info[].iban', vbi.iban) %]</td>
91
            <td>[% L.input_tag('vc_bank_info[].bic', vbi.bic) %]</td>
92
          [% END %]
70 93
        [% END %]
71 94
      </tr>
72 95
    [% END %]
......
99 122
          <input type="hidden" id="amount_less_skonto_[% loop.count %]" name="amount_less_skonto_[% loop.count %]" value="[% LxERP.format_amount(bank_transfer.amount_less_skonto, 2) %]">
100 123
          <input type="hidden" id="skonto_amount_[% loop.count %]" name="skonto_amount_[% loop.count %]" value="[% LxERP.format_amount(bank_transfer.skonto_amount, 2) %]">
101 124
          <input type="hidden" id="invoice_open_amount_[% loop.count %]" name="invoice_open_amount_[% loop.count %]" value="[% LxERP.format_amount(bank_transfer.open_amount, 2) %]">
125
          [% IF INSTANCE_CONF.get_sepa_swiss_xml_export %]
126
            [% L.hidden_tag('bank_transfers[].type', bank_transfer.type) %]
127
          [% END %]
102 128
          [% IF loop.first || (previous_vcname != bank_transfer.vcname) %]
103 129
            <a href="controller.pl?action=CustomerVendor/edit&db=[% vc %]&id=[% HTML.url(bank_transfer.vc_id) %]&callback=[% HTML.url('sepa.pl?action=bank_transfer_add&vc=' _ vc) %]">
104 130
              [% GET HTML.escape(bank_transfer.vcname); SET previous_vcname = bank_transfer.vcname; %]
templates/design40_webpages/sepa/bank_transfer_created.html
3 3

  
4 4
<h1>[% title %]</h1>
5 5

  
6
<p>[% 'The SEPA export has been created.' | $T8 %]</p>
7
<ul>
8
  <li><a href="sepa.pl?action=bank_transfer_download_sepa_xml&id=[% HTML.url(id) %]&vc=[% HTML.url(vc) %]"> [% 'Download SEPA XML export file' | $T8 %] </a></li>
9
  <li><a href="sepa.pl?action=bank_transfer_list&l_open=1&l_not_executed=1&vc=[% HTML.url(vc) %]"> [% 'List open SEPA exports' | $T8 %] </a></li>
10
</ul>
6
[% IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
7
  <p>[% 'The SEPA export has been created.' | $T8 %]</p>
8
  <ul>
9
    <li><a href="sepa.pl?action=bank_transfer_download_sepa_xml&id=[% HTML.url(id) %]&vc=[% HTML.url(vc) %]"> [% 'Download SEPA XML export file' | $T8 %] </a></li>
10
    <li><a href="sepa.pl?action=bank_transfer_list&l_open=1&l_not_executed=1&vc=[% HTML.url(vc) %]"> [% 'List open SEPA exports' | $T8 %] </a></li>
11
  </ul>
12
[% ELSE %]
13
  <p>[% 'The swiss XML export has been created.' | $T8 %]</p>
14
  <ul>
15
    <li><a href="sepa.pl?action=bank_transfer_download_sepa_xml&id=[% HTML.url(id) %]"> [% 'Download swiss XML export file' | $T8 %] </a></li>
16
    <li><a href="sepa.pl?action=bank_transfer_list&l_open=1&l_not_executed=1&vc=[% HTML.url(vc) %]"> [% 'List open bank exports' | $T8 %] </a></li>
17
  </ul>
18
[% END %]
templates/design40_webpages/sepa/bank_transfer_search.html
22 22
      </td>
23 23
    </tr>
24 24
    <tr>
25
      <th>[% LxERP.t8("SEPA message ID") %]</th>
25
      <th>
26
        [% IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
27
          [% LxERP.t8("SEPA message ID") %]
28
        [% ELSE %]
29
          [% LxERP.t8("Message ID") %]
30
        [% END %]
31
      </th>
26 32
      <td>[% L.input_tag("f_message_id") %]</td>
27 33
    </tr>
28 34
    <tr>
templates/webpages/client_config/_features.html
528 528
   <td align="right">[% LxERP.t8('In addition to the above date functions, subtract the following amount of days from the calculated date as a buffer.') %]</td>
529 529
   <td>[% L.input_tag('defaults.sepa_set_skonto_date_buffer_in_days_as_number', SELF.defaults.sepa_set_skonto_date_buffer_in_days_as_number, style=style) %]</td>
530 530
  </tr>
531
  <tr>
532
    <td align="right">[% LxERP.t8('Bank transfer via SEPA is replaced by Bank transfer via swiss XML. According to Swiss Payment Standards, Customer Credit Transfer Initiation (pain.001).') %]</td>
533
    <td>[% L.yes_no_tag('defaults.sepa_swiss_xml_export', SELF.defaults.sepa_swiss_xml_export) %]</td>
534
  </tr>
531 535
  <tr><td class="listheading" colspan="4">[% LxERP.t8("Experimental Features") %]</td></tr>
532 536
  <tr>
533 537
   <td align="right">[% LxERP.t8('new order controller') %]</td>
templates/webpages/sepa/bank_transfer_add.html
51 51
     <input type="hidden" id="amount_less_skonto_[% loop.count %]" name="amount_less_skonto_[% loop.count %]" value="[% LxERP.format_amount(invoice.amount_less_skonto, 2) %]">
52 52
     <input type="hidden" id="invoice_open_amount_[% loop.count %]" name="invoice_open_amount_[% loop.count %]" value="[% LxERP.format_amount(invoice.open_amount - invoice.transfer_amount, 2) %]">
53 53
     <input type="hidden" id="skonto_amount_[% loop.count %]" name="skonto_amount_[% loop.count %]" value="[% LxERP.format_amount(invoice.skonto_amount, 2) %]">
54

  
54
     [% L.hidden_tag('bank_transfers[].type', invoice.type) %]
55 55

  
56 56
     <tr class="listrow[% IF (!invoice.vc_bank_info_ok && invoice.checked) || invoice.is_sepa_blocked %]_error[% END %]">
57 57
      <td align="center">
......
117 117
    <sup>(1)</sup>
118 118
    [%- IF is_vendor %]
119 119
     [%- 'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' | $T8 %]
120
     [% "The required information consists of the IBAN and the BIC." | $T8 %]
120
     [%- IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
121
      [% "The required information consists of the IBAN and the BIC." | $T8 %]
122
     [%- ELSE %]
123
      [% "The required information consists of the IBAN for domestic payments. And IBAN and the BIC for SEPA payments." | $T8 %]
124
     [%- END %]
121 125
    [%- ELSE %]
122 126
     [%- 'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' | $T8 %]
123 127
     [% "The required information consists of the IBAN, the BIC, the mandator ID and the mandate's date of signature." | $T8 %]
templates/webpages/sepa/bank_transfer_create.html
47 47
     <th class="listheading">[%- IF is_vendor %][% 'Vendor' | $T8 %][%- ELSE %][%- LxERP.t8('Customer') %][%- END %]</th>
48 48
     <th class="listheading">[% 'IBAN' | $T8 %]</th>
49 49
     <th class="listheading">[% 'BIC' | $T8 %]</th>
50
     <th class="listheading">[% 'Bank' | $T8 %]</th>
50
     [%- IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
51
      <th class="listheading">[% 'Bank' | $T8 %]</th>
52
     [%- END %]
51 53
     [% IF vc == 'customer' %]
52 54
      <th class="listheading">[% 'Mandator ID' | $T8 %]</th>
53 55
      <th class="listheading">[% 'Mandate Date of Signature' | $T8 %]</th>
......
56 58

  
57 59
    [%- FOREACH vbi = VC_BANK_INFO %]
58 60
    <tr class="listrow[% loop.count % 1 %]">
59
     <td>
60
      <input type="hidden" name="vc_bank_info[+].id" value="[% HTML.escape(vbi.id) %]">
61
      <input type="hidden" name="vc_bank_info[].name" value="[% HTML.escape(vbi.name) %]">
62
      [% HTML.escape(vbi.name) %]
63
     </td>
64
     <td><input name="vc_bank_info[].iban" size="34" value="[% HTML.escape(vbi.iban.substr(0, 34)) %]" maxlength="34"></td>
65
     <td><input name="vc_bank_info[].bic" size="20" value="[% HTML.escape(vbi.bic.substr(0, 20)) %]" maxlength="20"></td>
66
     <td><input name="vc_bank_info[].bank" size="30" value="[% HTML.escape(vbi.bank) %]"></td>
67
     [% IF vc == 'customer' %]
68
      <td><input name="vc_bank_info[].mandator_id" size="30" value="[% HTML.escape(vbi.mandator_id) %]"></td>
69
      <td>[% L.date_tag("vc_bank_info[].mandate_date_of_signature", vbi.mandate_date_of_signature) %]</td>
70
     [%- END %]
61
      [%- IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
62
        <td>
63
          <input type="hidden" name="vc_bank_info[+].id" value="[% HTML.escape(vbi.id) %]">
64
          <input type="hidden" name="vc_bank_info[].name" value="[% HTML.escape(vbi.name) %]">
65
          [% HTML.escape(vbi.name) %]
66
        </td>
67
        <td><input type="text" name="vc_bank_info[].iban" size="34" value="[% HTML.escape(vbi.iban.substr(0, 34)) %]" maxlength="34"></td>
68
        <td><input type="text" name="vc_bank_info[].bic" size="20" value="[% HTML.escape(vbi.bic.substr(0, 20)) %]" maxlength="20"></td>
69
        <td><input type="text" name="vc_bank_info[].bank" size="30" value="[% HTML.escape(vbi.bank) %]"></td>
70
        [%- IF vc == 'customer' %]
71
          <td><input type="text" name="vc_bank_info[].mandator_id" size="30" value="[% HTML.escape(vbi.mandator_id) %]"></td>
72
          <td>[% L.date_tag("vc_bank_info[].mandate_date_of_signature", vbi.mandate_date_of_signature) %]</td>
73
        [%- END %]
74
      [%- ELSE %]
75
        [% L.hidden_tag('vc_bank_info[+].id', vbi.id) %]
76
        [% L.hidden_tag('vc_bank_info[].type', vbi.type) %]
77
        [%- IF vbi.type == 'QRBILL' %]
78
          [% L.hidden_tag('vc_bank_info[].name', vbi.name) %]
79
        [%- ELSIF vbi.type == 'DOMESTIC' %]
80
          <td>
81
            [% HTML.escape(vbi.name) %]
82
            [% L.hidden_tag('vc_bank_info[].name', vbi.name) %]
83
          </td>
84
          <td>[% L.input_tag('vc_bank_info[].iban', vbi.iban) %]</td>
85
        [%- ELSE %]
86
          <td>
87
            [% HTML.escape(vbi.name) %]
88
            [% L.hidden_tag('vc_bank_info[].name', vbi.name) %]
89
          </td>
90
          <td>[% L.input_tag('vc_bank_info[].iban', vbi.iban) %]</td>
91
          <td>[% L.input_tag('vc_bank_info[].bic', vbi.bic) %]</td>
92
        [%- END %]
93
      [%- END %]
71 94
    </tr>
72 95
    [%- END %]
73 96
   </table>
......
96 119
     <input type="hidden" id="amount_less_skonto_[% loop.count %]" name="amount_less_skonto_[% loop.count %]" value="[% LxERP.format_amount(bank_transfer.amount_less_skonto, 2) %]">
97 120
     <input type="hidden" id="skonto_amount_[% loop.count %]" name="skonto_amount_[% loop.count %]" value="[% LxERP.format_amount(bank_transfer.skonto_amount, 2) %]">
98 121
     <input type="hidden" id="invoice_open_amount_[% loop.count %]" name="invoice_open_amount_[% loop.count %]" value="[% LxERP.format_amount(bank_transfer.open_amount, 2) %]">
99

  
122
     [%- IF INSTANCE_CONF.get_sepa_swiss_xml_export %]
123
      [% L.hidden_tag('bank_transfers[].type', bank_transfer.type) %]
124
     [%- END %]
100 125
     <tr class="listrow[% loop.count % 2 %]">
101 126
      <td>
102 127
       [%- IF loop.first || (previous_vcname != bank_transfer.vcname) %]
templates/webpages/sepa/bank_transfer_created.html
6 6
  [% 'The SEPA export has been created.' | $T8 %]
7 7
 </p>
8 8

  
9
 <p>
9
[% IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
10
  <p>[% 'The SEPA export has been created.' | $T8 %]</p>
10 11
  <ul>
11
   <li>
12
    <a href="sepa.pl?action=bank_transfer_download_sepa_xml&id=[% HTML.url(id) %]&vc=[% HTML.url(vc) %]">
13
     [% 'Download SEPA XML export file' | $T8 %]
14
    </a>
15
   </li>
16

  
17
   <li>
18
    <a href="sepa.pl?action=bank_transfer_list&l_open=1&l_not_executed=1&vc=[% HTML.url(vc) %]">
19
     [% 'List open SEPA exports' | $T8 %]
20
    </a>
21
   </li>
12
    <li><a href="sepa.pl?action=bank_transfer_download_sepa_xml&id=[% HTML.url(id) %]&vc=[% HTML.url(vc) %]"> [% 'Download SEPA XML export file' | $T8 %] </a></li>
13
    <li><a href="sepa.pl?action=bank_transfer_list&l_open=1&l_not_executed=1&vc=[% HTML.url(vc) %]"> [% 'List open SEPA exports' | $T8 %] </a></li>
22 14
  </ul>
23
 </p>
24

  
15
[% ELSE %]
16
  <p>[% 'The swiss XML export has been created.' | $T8 %]</p>
17
  <ul>
18
    <li><a href="sepa.pl?action=bank_transfer_download_sepa_xml&id=[% HTML.url(id) %]"> [% 'Download swiss XML export file' | $T8 %] </a></li>
19
    <li><a href="sepa.pl?action=bank_transfer_list&l_open=1&l_not_executed=1&vc=[% HTML.url(vc) %]"> [% 'List open bank exports' | $T8 %] </a></li>
20
  </ul>
21
[% END %]
templates/webpages/sepa/bank_transfer_search.html
18 18
    </tr>
19 19

  
20 20
    <tr>
21
     <td align="right">[% LxERP.t8("SEPA message ID") %]</td>
21
     <td align="right">
22
      [%- IF !INSTANCE_CONF.get_sepa_swiss_xml_export %]
23
        [% LxERP.t8("SEPA message ID") %]
24
      [%- ELSE %]
25
        [% LxERP.t8("Message ID") %]
26
      [%- END %]
27
     </td>
22 28
     <td>[% L.input_tag("f_message_id") %]</td>
23 29
    </tr>
24 30

  

Auch abrufbar als: Unified diff