Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision ba929776

Von Johannes Grassler vor etwa 1 Jahr hinzugefügt

  • ID ba929776d1fc83b3a749b6f95ebff7cdc492b66b
  • Vorgänger c1775b18
  • Nachfolger d2291970

ZUGFeRD: Importiere via SessionFile

Benutze das SessionFile aus

834abeb456898df96e69deaad4a7c27915ad6a57

um die strukturierten Daten der ZUGFeRD/Faktur-X-Rechnung in
das Formular fuer die Kreditorenbuchung zu uebertragen. Diese
Loesung ist deutlich eleganter als die bisherige, die fuer
jeden Import ein RecordTemplate erzeugte und es nach dem
Import in der Datenbank beliess.

Unterschiede anzeigen:

SL/Controller/ZUGFeRD.pm
129 129
  my %res;          # result data structure returned by SL::ZUGFeRD->extract_from_{pdf,xml}()
130 130
  my $parser;       # SL::XMLInvoice object created by SL::ZUGFeRD->extract_from_{pdf,xml}()
131 131
  my $dom;          # DOM object for parsed XML data
132
  my $template_ap;  # SL::DB::RecordTemplate object
133 132
  my $vendor;       # SL::DB::Vendor object
134 133

  
135 134
  my $ibanmessage;  # Message to display if vendor's database and invoice IBANs don't match up
......
145 144

  
146 145
  if ($res{'result'} != SL::ZUGFeRD::RES_OK()) {
147 146
    # An error occurred; log message from parser:
148
    $::lxdebug->message(LXDebug::DEBUG1(), "Could not extract ZUGFeRD data, error message: " . $res{'message'});
149 147
    die(t8("Could not extract Factur-X/ZUGFeRD data, data and error message:") . " $res{'message'}");
150 148
  }
151 149

  
......
175 173
  ) unless $vendor;
176 174

  
177 175

  
178
  # Create a record template for this imported invoice
179
  $template_ap = SL::DB::RecordTemplate->new(
180
      vendor_id=>$vendor->id,
181
  );
182

  
183 176
  # Check IBAN specified on bill matches the one we've got in
184 177
  # the database for this vendor.
185 178
  $ibanmessage = $iban ne $vendor->iban ? "Record IBAN $iban doesn't match vendor IBAN " . $vendor->iban : $iban if $iban;
......
220 213
    name => $metadata{'currency'},
221 214
    );
222 215

  
223
  $template_ap->assign_attributes(
224
    template_name       => t8("Faktur-X/ZUGFeRD/XRechnung import #1, #2", $vendor->name, $invnumber),
225
    template_type       => 'ap_transaction',
226
    direct_debit        => $metadata{'direct_debit'},
227
    notes               => "Faktur-X/ZUGFeRD/XRechnung Import. Type: $metadata{'type'}\nIBAN: " . $ibanmessage,
228
    taxincluded         => 0,
229
    currency_id         => $currency->id,
230
    ar_ap_chart_id      => $ap_chart_id,
231
    );
232

  
233
  $template_ap->save;
234

  
235
  my $default_ap_amount_chart = SL::DB::Manager::Chart->find_by(charttype => 'A');
236

  
237
  foreach my $i ( @items )
238
    {
239
    my %item = %{$i};
240

  
241
    my $net_total = $item{'subtotal'};
242
    my $desc = $item{'description'};
243
    my $tax_rate = $item{'tax_rate'} / 100; # XML data is usually in percent
244

  
245
    my $taxes = SL::DB::Manager::Tax->get_all(
246
      where   => [
247
        chart_categories => { like => '%' . $default_ap_amount_chart->category . '%' },
248
        rate => $tax_rate,
249
      ],
250
    );
251

  
252
    # If we really can't find any tax definition (a simple rounding error may
253
    # be sufficient for that to happen), grab the first tax fitting the default
254
    # category, just like the AP form would do it for manual entry.
255
    if ( scalar @{$taxes} == 0 ) {
256
      $taxes = SL::DB::Manager::Tax->get_all(
257
        where   => [ chart_categories => { like => '%' . $default_ap_amount_chart->category . '%' } ],
258
      );
259
    }
260

  
261
    my $tax = ${$taxes}[0];
262

  
263
    my $item_obj = SL::DB::RecordTemplateItem->new(
264
      amount1 => $net_total,
265
      record_template_id => $template_ap->id,
266
      chart_id      => $default_ap_amount_chart->id,
267
      tax_id      => $tax->id,
268
    );
269
    $item_obj->save;
270
    }
271

  
272 216
  $self->redirect_to(
273 217
    controller                           => 'ap.pl',
274
    action                               => 'load_record_template',
275
    id                                   => $template_ap->id,
218
    action                               => 'load_zugferd',
276 219
    'form_defaults.no_payment_bookings'  => 0,
277 220
    'form_defaults.paid_1_suggestion'    => $::form->format_amount(\%::myconfig, $metadata{'total'}, 2),
278 221
    'form_defaults.invnumber'            => $invnumber,
222
    'form_defaults.AP_chart_id'          => $ap_chart_id,
223
    'form_defaults.currency'             => $currency->name,
279 224
    'form_defaults.duedate'              => $metadata{'duedate'},
280 225
    'form_defaults.transdate'            => $metadata{'transdate'},
281 226
    'form_defaults.notes'                => "ZUGFeRD Import. Type: $metadata{'type'}\nIBAN: " . $ibanmessage,
282 227
    'form_defaults.taxincluded'          => 0,
283 228
    'form_defaults.direct_debit'         => $metadata{'direct_debit'},
229
    'form_defaults.vendor'               => $vendor->name,
230
    'form_defaults.vendor_id'            => $vendor->id,
284 231
    'form_defaults.zugferd_session_file' => $file_name,
285 232
  );
286 233

  
bin/mozilla/ap.pl
56 56
use SL::DB::ValidityToken;
57 57
use SL::Presenter::ItemsList;
58 58
use SL::Webdav;
59
use SL::ZUGFeRD;
59 60
use SL::Locale::String qw(t8);
60 61

  
61 62
require "bin/mozilla/common.pl";
......
109 110
  $::form->show_generic_error($::locale->text("You do not have the permissions to access this function.")) if !       $cache->{_may_view_or_edit_this_invoice};
110 111
}
111 112

  
113
sub load_zugferd {
114
  $::auth->assert('ap_transactions');
115

  
116
  my $data;    # buffer for holding file contents
117

  
118
  my $form_defaults = $::form->{form_defaults};
119
  my $file = SL::SessionFile->new($form_defaults->{zugferd_session_file}, mode => '<');
120
  my $file_name = $file->file_name;
121

  
122
  $::form->{$_} = $form_defaults->{$_} for keys %{ $form_defaults // {} };
123

  
124
  # Defaults
125
  $::form->{title}            = "Add";
126
  $::form->{paidaccounts}     = 1;
127

  
128
  $file->open('<');
129
  if ( ! defined($file->fh->read($data, -s $file->fh)) ) {
130
      SL::Helper::Flash::flash_later('error',
131
        t8('Could not open ZUGFeRD file for reading: #1', $!));
132
  } else {
133

  
134
      my %res;          # result data structure returned by SL::ZUGFeRD->extract_from_{pdf,xml}()
135
      my $parser;       # SL::XMLInvoice object created by SL::ZUGFeRD->extract_from_{pdf,xml}()
136
      my $template_ap;  # SL::DB::RecordTemplate object
137
      my $vendor;       # SL::DB::Vendor object
138
      my %metadata;     # structured data extracted from XML payload
139
      my @items;        # list of invoice items
140
      my $default_ap_amount_chart;
141

  
142
      if ( $data =~ m/^%PDF/ ) {
143
        %res = %{SL::ZUGFeRD->extract_from_pdf($data)};
144
      } else {
145
        %res = %{SL::ZUGFeRD->extract_from_xml($data)};
146
      }
147

  
148

  
149
      $parser = $res{'invoice_xml'};
150
      %metadata = %{$parser->metadata};
151
      @items = @{$parser->items};
152

  
153
      $default_ap_amount_chart = SL::DB::Manager::Chart->find_by(charttype => 'A');
154

  
155
      my $row = 0;
156
      foreach my $i (@items) {
157
        $row++;
158

  
159
        my %item = %{$i};
160

  
161
        my $net_total = $::form->format_amount(\%::myconfig, $item{'subtotal'}, 2);
162
        my $desc = $item{'description'};
163
        my $tax_rate = $item{'tax_rate'} / 100; # XML data is usually in percent
164

  
165
        my $taxes = SL::DB::Manager::Tax->get_all(
166
          where   => [
167
            chart_categories => { like => '%' . $default_ap_amount_chart->category . '%' },
168
            rate => $tax_rate,
169
          ],
170
        );
171

  
172
        # If we really can't find any tax definition (a simple rounding error may
173
        # be sufficient for that to happen), grab the first tax fitting the default
174
        # AP amount chart, just like the AP form would do it for manual entry.
175
        if ( scalar @{$taxes} == 0 ) {
176
          $taxes = SL::DB::Manager::Tax->get_all(
177
            where   => [ chart_categories => { like => '%' . $default_ap_amount_chart->category . '%' } ],
178
          );
179
        }
180

  
181
        my $tax = ${$taxes}[0];
182

  
183
        if (!$tax) {
184
          $row--;
185
          next;
186
        }
187

  
188
        $::form->{"AP_amount_chart_id_${row}"}          = $default_ap_amount_chart->id; # FIXME: add heuristic for picking a better one
189
        $::form->{"previous_AP_amount_chart_id_${row}"} = $default_ap_amount_chart->id; # FIXME: add heuristic for picking a better one
190
        $::form->{"amount_${row}"}                      = $net_total;
191
        $::form->{"taxchart_${row}"}                    = $tax->id . '--' . $tax->rate;
192
      }
193

  
194
      flash('info', $::locale->text("The ZUGFeRD/Factur-X invoice '#1' has been loaded.", $file_name));
195

  
196
      $::form->{form_validity_token} = SL::DB::ValidityToken->create(scope => SL::DB::ValidityToken::SCOPE_PURCHASE_INVOICE_POST())->token;
197
      $::form->{rowcount}         = $row;
198

  
199
      update(
200
        keep_rows_without_amount => 1,
201
        dont_add_new_row         => 1,
202
      );
203
    }
204
}
205

  
112 206
sub load_record_template {
113 207
  $::auth->assert('ap_transactions');
114 208

  

Auch abrufbar als: Unified diff