8 use List::Util qw(first sum);
 
   9 use List::MoreUtils qw(any);
 
  10 use POSIX qw(strftime);
 
  14 use SL::SEPA::XML::Transaction;
 
  31   $self->{transactions} = [];
 
  32   $self->{src_charset}  = 'UTF-8';
 
  35   map { $self->{$_} = $params{$_} if (exists $params{$_}) } qw(src_charset company message_id grouped);
 
  37   $self->{iconv} = Text::Iconv->new($self->{src_charset}, "UTF-8") || croak "Unsupported source charset $self->{src_charset}.";
 
  39   my $missing_parameter = first { !$self->{$_} } qw(company message_id);
 
  40   croak "Missing parameter: $missing_parameter" if ($missing_parameter);
 
  42   map { $self->{$_} = $self->_replace_special_chars(decode('UTF-8', $self->{iconv}->convert($self->{$_}))) } qw(company message_id);
 
  48   foreach my $transaction (@_) {
 
  49     croak "Expecting hash reference." if (ref $transaction ne 'HASH');
 
  50     push @{ $self->{transactions} }, SL::SEPA::XML::Transaction->new(%{ $transaction }, 'sepa' => $self);
 
  56 sub _replace_special_chars {
 
  71   map { $text =~ s/$_/$special_chars{$_}/g; } keys %special_chars;
 
  80   return sprintf '%d.%02d', int($amount), int($amount * 100) % 100;
 
  83 sub _group_transactions {
 
  91   foreach my $transaction (@{ $self->{transactions} }) {
 
  92     my $key                      = $self->{grouped} ? join("\t", map { $transaction->get($_) } qw(src_bic src_iban execution_date)) : 'all';
 
  93     $grouped->{groups}->{$key} ||= {
 
  95       'transactions' => [ ],
 
  98     push @{ $grouped->{groups}->{$key}->{transactions} }, $transaction;
 
 100     $grouped->{groups}->{$key}->{sum_amount} += $transaction->{amount};
 
 101     $grouped->{sum_amount}                   += $transaction->{amount};
 
 110   croak "No transactions added yet." if (!@{ $self->{transactions} });
 
 114   my $xml    = XML::Writer->new(OUTPUT      => \$output,
 
 117                                 ENCODING    => 'utf-8');
 
 120   my $time_zone  = strftime "%z", @now;
 
 121   my $now_str    = strftime('%Y-%m-%dT%H:%M:%S', @now) . substr($time_zone, 0, 3) . ':' . substr($time_zone, 3, 2);
 
 123   my $grouped_transactions = $self->_group_transactions();
 
 126   $xml->startTag('Document',
 
 127                  'xmlns'              => 'urn:sepade:xsd:pain.001.001.02.grp',
 
 128                  'xmlns:xsi'          => 'http://www.w3.org/2001/XMLSchema-instance',
 
 129                  'xsi:schemaLocation' => 'urn:sepade:xsd:pain.001.001.02.grp pain.001.001.02.grp.xsd');
 
 131   $xml->startTag('pain.001.001.02');
 
 133   $xml->startTag('GrpHdr');
 
 134   $xml->dataElement('MsgId', encode('UTF-8', substr($self->{message_id}, 0, 35)));
 
 135   $xml->dataElement('CreDtTm', $now_str);
 
 136   $xml->dataElement('NbOfTxs', scalar @{ $self->{transactions} });
 
 137   $xml->dataElement('CtrlSum', $self->_format_amount($grouped_transactions->{sum_amount}));
 
 138   $xml->dataElement('Grpg', 'MIXD');
 
 140   $xml->startTag('InitgPty');
 
 141   $xml->dataElement('Nm', encode('UTF-8', substr($self->{company}, 0, 70)));
 
 142   $xml->endTag('InitgPty');
 
 144   $xml->endTag('GrpHdr');
 
 146   foreach my $key (keys %{ $grouped_transactions->{groups} }) {
 
 147     my $transaction_group  = $grouped_transactions->{groups}->{$key};
 
 148     my $master_transaction = $transaction_group->{transactions}->[0];
 
 150     $xml->startTag('PmtInf');
 
 151     $xml->dataElement('PmtMtd', 'TRF');
 
 153     $xml->startTag('PmtTpInf');
 
 154     $xml->startTag('SvcLvl');
 
 155     $xml->dataElement('Cd', 'SEPA');
 
 156     $xml->endTag('SvcLvl');
 
 157     $xml->endTag('PmtTpInf');
 
 159     $xml->dataElement('ReqdExctnDt', $master_transaction->get('execution_date'));
 
 160     $xml->startTag('Dbtr');
 
 161     $xml->dataElement('Nm', encode('UTF-8', substr($self->{company}, 0, 70)));
 
 162     $xml->endTag('Dbtr');
 
 164     $xml->startTag('DbtrAcct');
 
 165     $xml->startTag('Id');
 
 166     $xml->dataElement('IBAN', $master_transaction->get('src_iban', 34));
 
 168     $xml->endTag('DbtrAcct');
 
 170     $xml->startTag('DbtrAgt');
 
 171     $xml->startTag('FinInstnId');
 
 172     $xml->dataElement('BIC', $master_transaction->get('src_bic', 20));
 
 173     $xml->endTag('FinInstnId');
 
 174     $xml->endTag('DbtrAgt');
 
 176     $xml->dataElement('ChrgBr', 'SLEV');
 
 178     foreach my $transaction (@{ $transaction_group->{transactions} }) {
 
 179       $xml->startTag('CdtTrfTxInf');
 
 181       $xml->startTag('PmtId');
 
 182       $xml->dataElement('EndToEndId', $transaction->get('end_to_end_id', 35));
 
 183       $xml->endTag('PmtId');
 
 185       $xml->startTag('Amt');
 
 186       $xml->startTag('InstdAmt', 'Ccy' => 'EUR');
 
 187       $xml->characters($self->_format_amount($transaction->{amount}));
 
 188       $xml->endTag('InstdAmt');
 
 191       $xml->startTag('CdtrAgt');
 
 192       $xml->startTag('FinInstnId');
 
 193       $xml->dataElement('BIC', $transaction->get('dst_bic', 20));
 
 194       $xml->endTag('FinInstnId');
 
 195       $xml->endTag('CdtrAgt');
 
 197       $xml->startTag('Cdtr');
 
 198       $xml->dataElement('Nm', $transaction->get('recipient', 70));
 
 199       $xml->endTag('Cdtr');
 
 201       $xml->startTag('CdtrAcct');
 
 202       $xml->startTag('Id');
 
 203       $xml->dataElement('IBAN', $transaction->get('dst_iban', 34));
 
 205       $xml->endTag('CdtrAcct');
 
 207       $xml->startTag('RmtInf');
 
 208       $xml->dataElement('Ustrd', $transaction->get('reference', 140));
 
 209       $xml->endTag('RmtInf');
 
 211       $xml->endTag('CdtTrfTxInf');
 
 214     $xml->endTag('PmtInf');
 
 217   $xml->endTag('pain.001.001.02');
 
 218   $xml->endTag('Document');