Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 1d6b9c08

Von Cem Aydin vor mehr als 1 Jahr hinzugefügt

  • ID 1d6b9c085bb00c112371159b98b12f26736f1f0d
  • Vorgänger 1e572940
  • Nachfolger 0d06d25a

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';
158 203

  
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;
204
    if ($form->{confirmation}) {
205
      $vc_bank_info = { map { $_->{id} => $_ } @{ $form->{vc_bank_info} || [] } };
206

  
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
    #my $bank_info = $unique_vendor->{bank_info};
296
    next if ($vendor->{type} eq 'QRBILL');
297
    if ($vendor->{type} eq 'DOMESTIC' && !$vendor->{iban}) {
298
        $error_message = $locale->text('The bank information must not be empty.');
299
        last;
300
    } elsif ($vendor->{type} eq 'SEPA' && (!$vendor->{iban} || !$vendor->{bic})) {
301
        $error_message = $locale->text('The bank information must not be empty.');
302
        last;
303
    }
304
  }
305

  
306
  return $error_message, $vendors;
307
}
308

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

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

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

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

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

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

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

  
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
    );
656
  my $sepa_xml;
657
  my %sepa_xml_params = (
658
    'company'     => $defaults->company,
659
    'creditor_id' => $defaults->sepa_creditor_id,
660
    'src_charset' => 'UTF-8',
661
    'message_id'  => $message_id,
662
    'grouped'     => 1,
663
    'collection'  => $vc eq 'customer',
664
  );
665
  if (!$swiss_export) {
666
      $sepa_xml = SL::SEPA::XML->new(%sepa_xml_params);
667
  } else {
668
      $sepa_xml = SL::SEPA::SwissXML->new(%sepa_xml_params);
669
  }
557 670

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

  
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}, });
689
    my $transaction_data = {
690
      'src_iban'       => $item->{our_iban},
691
      'src_bic'        => $item->{our_bic},
692
      'dst_iban'       => $item->{vc_iban},
693
      'dst_bic'        => $item->{vc_bic},
694
      'company'        => $item->{vc_name},
695
      'company_number' => $item->{vc_number},
696
      'amount'         => $item->{amount},
697
      'reference'      => $item->{reference},
698
      'mandator_id'    => $mandator_id,
699
      'reference_date' => $item->{reference_date},
700
      'execution_date' => $requested_execution_date,
701
      'end_to_end_id'  => $item->{end_to_end_id},
702
      'date_of_signature' => $item->{mandate_date_of_signature},
703
    };
704

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  
591 793
  # Store the message ID used in each of the entries in order to
......
599 801

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

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

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

  
607 823
  $main::lxdebug->leave_sub();
......
711 927
      combobox => [
712 928
        action => [ t8('Actions') ],
713 929
        action => [
714
          t8('SEPA XML download'),
930
          !$::instance_conf->get_sepa_swiss_xml_export ? t8('SEPA XML download') : t8('Swiss XML download'),
715 931
          submit => [ '#form', { action => 'bank_transfer_download_sepa_xml' } ],
716 932
          checks => [ [ 'kivi.check_if_entries_selected', '[name="ids[]"]' ] ],
717 933
        ],
......
724 940
          t8('Mark as closed'),
725 941
          submit => [ '#form', { action => 'bank_transfer_mark_as_closed' } ],
726 942
          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.') ],
943
          confirm => [ !$::instance_conf->get_sepa_swiss_xml_export ?
944
                          $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.')
945
                                             : 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.')
946
                                             : 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 947
        ],
730 948
        action => [
731
          t8('Undo SEPA exports'),
949
          !$::instance_conf->get_sepa_swiss_xml_export ? t8('Undo SEPA exports') : t8('Undo Swiss XML exports'),
732 950
          submit => [ '#form', { action => 'bank_transfer_undo_sepa_xml' } ],
733 951
          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.') ],
952
          confirm => [ !$::instance_conf->get_sepa_swiss_xml_export ?
953
            t8('Do you really want to undo the selected SEPA exports? You have to reassign the export again.') :
954
            t8('Do you really want to undo the selected Swiss XML exports? You have to reassign the export again.') ],
735 955
        ],
736 956
      ], # end of combobox "Actions"
737 957
    );
menus/user/00-erp.yaml
772 772
  id: cash_bank_transfer_via_sepa
773 773
  name: Bank transfer via SEPA
774 774
  order: 400
775
  access: cash
775
  access: cash & !client/sepa_swiss_xml_export
776
  module: sepa.pl
777
  params:
778
    action: bank_transfer_add
779
    vc: vendor
780
- parent: cash
781
  id: cash_bank_transfer_via_swiss_xml
782
  name: Bank transfer via swiss XML
783
  order: 450
784
  access: cash & client/sepa_swiss_xml_export
776 785
  module: sepa.pl
777 786
  params:
778 787
    action: bank_transfer_add
......
856 865
  id: cash_reports_bank_transfers_via_sepa
857 866
  name: Bank transfers via SEPA
858 867
  order: 400
859
  access: cash
868
  access: cash & !client/sepa_swiss_xml_export
869
  module: sepa.pl
870
  params:
871
    action: bank_transfer_search
872
    vc: vendor
873
- parent: cash_reports
874
  id: cash_reports_bank_transfers_via_swiss_xml
875
  name: Bank transfers via swiss XML
876
  order: 400
877
  access: cash & client/sepa_swiss_xml_export
860 878
  module: sepa.pl
861 879
  params:
862 880
    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