Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision b8dfb10a

Von Tamino Steinert vor 9 Monaten hinzugefügt

  • ID b8dfb10abe66741a74818a510a74eb84c974d094
  • Vorgänger a2a95b63
  • Nachfolger 48ef5a7c

BJ:ImportRecordEmails: Status vom auto. ZUGFeRD-Import in erw. Status

Unterschiede anzeigen:

SL/BackgroundJob/ImportRecordEmails.pm
10 10
use SL::DB::Manager::EmailImport;
11 11
use SL::Helper::EmailProcessing;
12 12
use SL::Presenter::Tag qw(link_tag);
13
use SL::Locale::String qw(t8);
13 14

  
14 15
use Params::Validate qw(:all);
15 16
use List::MoreUtils qw(any);
......
58 59
          #   , $email_journal->id
59 60
          # );
60 61
          my $email_journal_id = $email_journal->id;
61
          $result .= "Error while processing email journal $email_journal_id attachments with $function_name: $@";
62
          $result .= t8("Error while processing email journal ('#1') attachments with '#2': ", $email_journal_id, $function_name) . $@ . ".";
62 63
        };
63 64
      }
64 65
      if ($created_records  && $config->{processed_imap_flag}) {
......
98 99

  
99 100
  my $result = "";
100 101

  
101
  $result .= "Deleted email import(s): "
102
  $result .= t8("Deleted email import(s): ")
102 103
    . join(', ', @deleted_email_import_ids) . "."
103 104
    if scalar @deleted_email_import_ids;
104 105

  
105
  $result .= "Could not find email import(s): "
106
  $result .= t8("Could not find email import(s): ")
106 107
    . join(', ', @not_found_email_import_ids) . " for deletion."
107 108
    if scalar @not_found_email_import_ids;
108 109

  
SL/Controller/EmailJournal.pm
406 406
  my $record_type = $::form->{"record_type"};
407 407
  die "ZUGFeRD-Import only implemented for ap transaction templates" unless $record_type == 'ap_transaction';
408 408

  
409
  my $attachment_id    = $::form->{attachment_id};
409
  my $attachment_id = $::form->{attachment_id};
410 410

  
411 411
  my $form_defaults;
412 412
  if ($attachment_id) {
SL/Controller/ZUGFeRD.pm
227 227

  
228 228
  unless ( defined $ap_chart_id ) {
229 229
    # If no default account is configured, just use the first AP account found.
230
    my $ap_chart = SL::DB::Manager::Chart->get_all(
230
    my ($ap_chart) = @{SL::DB::Manager::Chart->get_all(
231 231
      where   => [ link => 'AP' ],
232 232
      sort_by => [ 'accno' ],
233
    );
234
    $ap_chart_id = ${$ap_chart}[0]->id;
233
    )};
234
    $ap_chart_id = $ap_chart->id;
235 235
  }
236 236

  
237 237
  my $currency = SL::DB::Manager::Currency->find_by(
SL/DB/Helper/ZUGFeRD.pm
764 764

  
765 765
  # Try to fill in AP account to book against
766 766
  my $ap_chart_id = $::instance_conf->get_ap_chart_id;
767
  my $ap_chart = SL::DB::Manager::Chart->find_by(id => $ap_chart_id);
768

  
769
  unless ( defined $ap_chart ) {
767
  my $ap_chart;
768
  unless ( defined $ap_chart_id ) {
770 769
    # If no default account is configured, just use the first AP account found.
771
    my $ap_charts = SL::DB::Manager::Chart->get_all(
770
    ($ap_chart) = @{SL::DB::Manager::Chart->get_all(
772 771
      where   => [ link => 'AP' ],
773 772
      sort_by => [ 'accno' ],
774
    );
775
    $ap_chart = ${$ap_charts}[0];
773
    )};
774
  } else {
775
    $ap_chart = SL::DB::Manager::Chart->find_by(id => $ap_chart_id);
776 776
  }
777 777

  
778 778
  my $currency = SL::DB::Manager::Currency->find_by(
......
799 799
  my %template_params;
800 800
  my $template_ap = SL::DB::Manager::RecordTemplate->get_first(where => [vendor_id => $vendor->id]);
801 801
  if ($template_ap) {
802
    $template_params{globalproject_id}        = $template_ap->globalproject_id;
802
    $template_params{globalproject_id}        = $template_ap->project_id;
803 803
    $template_params{payment_id}              = $template_ap->payment_id;
804 804
    $template_params{department_id}           = $template_ap->department_id;
805 805
    $template_params{ordnumber}               = $template_ap->ordnumber;
......
813 813
    taxzone_id   => $vendor->taxzone_id,
814 814
    currency_id  => $currency->id,
815 815
    direct_debit => $metadata{'direct_debit'},
816
    invnumber   => $invnumber,
817
    transdate   => $metadata{transdate} || $today->to_kivitendo,
818
    duedate     => $metadata{duedate}   || $today->to_kivitendo,
819
    taxincluded => 0,
820
    intnotes    => $intnotes,
816
    invnumber    => $invnumber,
817
    transdate    => $metadata{transdate} || $today->to_kivitendo,
818
    duedate      => $metadata{duedate}   || $today->to_kivitendo,
819
    taxincluded  => 0,
820
    intnotes     => $intnotes,
821 821
    transactions => [],
822 822
    %template_params,
823 823
  );
......
825 825
  $self->assign_attributes(%params);
826 826

  
827 827
  # parse items
828
  my $template_item;
829
  if ($template_ap && scalar @{$template_ap->items}) {
830
    my $template_item = $template_ap->items->[0];
831
  }
828 832
  foreach my $i (@items) {
829 833
    my %item = %{$i};
830 834

  
831 835
    my $net_total = $item{'subtotal'};
832 836

  
833 837
    # set default values for items
834
    my ($tax, $chart, $project_id);
835
    if ($template_ap) {
836
      my $template_item = $template_ap->items->[0];
837
      $tax = SL::DB::Tax->new(id => $template_item->tax_id)->load();
838
      $chart = SL::DB::Chart->new(id => $template_item->chart_id)->load();
839
      $project_id = $template_item->project_id;
838
    my %line_params;
839
    $line_params{amount} = $net_total;
840
    if ($template_item) {
841
      $line_params{tax_id}     = $template_item->tax->id;
842
      $line_params{chart}      = $template_item->chart;
843
      $line_params{project_id} = $template_item->project_id;
840 844
    } else {
841

  
842
      my $tax_rate = $item{'tax_rate'};
843
      $tax_rate /= 100 if $tax_rate > 1; # XML data is usually in percent
844

  
845
      $tax   = first { $tax_rate              == $_->rate } @{ $taxes };
846
      $tax    //= first { $active_taxkey->tax_id == $_->id }   @{ $taxes };
847
      $tax    //= $taxes->[0];
848

  
849
      $chart = $default_ap_amount_chart;
845
      my $tax_rate  = $item{'tax_rate'};
846
         $tax_rate /= 100 if $tax_rate > 1; # XML data is usually in percent
847
      my $tax   = first { $tax_rate              == $_->rate } @{ $taxes };
848
         $tax //= first { $active_taxkey->tax_id == $_->id }   @{ $taxes };
849
         $tax //= $taxes->[0];
850
      $line_params{tax_id}  = $tax->id;
851
      $line_params{chart}   = $default_ap_amount_chart;
850 852
    }
851 853

  
852
    my %line_params = (
853
      amount => $net_total,
854
      tax_id => $tax->id,
855
      chart  => $chart,
856
    );
857

  
858 854
    $self->add_ap_amount_row(%line_params);
859 855
  }
860 856
  $self->recalculate_amounts();
SL/Helper/EmailProcessing.pm
8 8
use XML::LibXML;
9 9

  
10 10
use SL::ZUGFeRD;
11
use SL::Locale::String qw(t8);
11 12

  
12 13
use SL::DB::PurchaseInvoice;
13 14

  
14 15
sub process_attachments {
15 16
  my ($self, $function_name, $email_journal, %params) = @_;
16 17

  
17
  unless ($self->can("process_attachments_$function_name")) {
18
  my $full_function_name = "process_attachments_$function_name";
19
  unless ($self->can($full_function_name)) {
18 20
    croak "Function not implemented for: $function_name";
19 21
  }
20
  $function_name = "process_attachments_$function_name";
21 22

  
22
  my $processed_count = 0;
23
  my @processed_files;
24
  my @errors;
23 25
  foreach my $attachment (@{$email_journal->attachments_sorted}) {
24
    my $processed = $self->$function_name($email_journal, $attachment, %params);
25
    $processed_count += $processed;
26
    my $attachment_name = $attachment->name;
27
    my $error = $self->$full_function_name($email_journal, $attachment, %params);
28
    if ($error) {
29
      push @errors, "$attachment_name: $error.";
30
    } else {
31
      push @processed_files, $attachment_name;
32
    }
26 33
  }
27
  return $processed_count;
34
  my $extended_status = t8("Processed attachments with function '#1':", $function_name);
35
  if (scalar @processed_files) {
36
    $extended_status .= "\n" . t8("Processed successfully: ")
37
      . join(', ', @processed_files);
38
  }
39
  if (scalar @errors) {
40
    $extended_status .= "\n" . t8("Errors while processing: ")
41
      . "\n" . join("\n", @errors);
42
  }
43
  unless (scalar @processed_files || scalar @errors) {
44
    $extended_status .= "\n" . t8("No attachments.");
45
  }
46
  $email_journal->extended_status(
47
    join "\n", $email_journal->extended_status, $extended_status
48
  );
49
  $email_journal->save;
50
  return scalar @processed_files;
28 51
}
29 52

  
30 53
sub can_function {
......
36 59
  my ($self, $email_journal, $attachment, %params) = @_;
37 60

  
38 61
  my $content = $attachment->content; # scalar ref
39
  my $name = $attachment->name;
40 62

  
41
  return 0 unless $content =~ m/^%PDF|<\?xml/;
63
  return t8("Not a PDF or XML file") unless $content =~ m/^%PDF|<\?xml/;
42 64

  
43 65
  my %res;
44 66
  if ( $content =~ m/^%PDF/ ) {
......
48 70
  }
49 71

  
50 72
  unless ($res{'result'} == SL::ZUGFeRD::RES_OK()) {
51
    my $error = $res{'message'};
52
    $email_journal->extended_status(
53
      join "\n", $email_journal->extended_status,
54
      "Error processing ZUGFeRD attachment $name: $error"
55
    )->save;
56
    return 0;
73
    # my $error = $res{'message'}; # technical error
74
    my $error = t8('No vaild Factur-X/ZUGFeRD file');
75
    return $error;
57 76
  }
58 77

  
59 78
  my $purchase_invoice;
......
62 81
    1;
63 82
  } or do {
64 83
    my $error = $@;
65
    $email_journal->update_attributes(
66
      extended_status =>
67
        join "\n", $email_journal->extended_status,
68
        "Error processing ZUGFeRD attachment $name: $error"
69
    );
70
    return 0;
84
    return $error;
71 85
  };
72 86

  
73 87
  $self->_add_attachment_to_record($email_journal, $attachment, $purchase_invoice);
74 88

  
75
  return 1;
89
  return 0;
76 90
}
77 91

  
78 92
sub _add_attachment_to_record {
locale/de/all
852 852
  'Could not create new project #1' => 'Neues Projekt #1 kann nicht angelegt werden',
853 853
  'Could not extract Factur-X/ZUGFeRD data, data and error message:' => 'Konnte keine Factur-X-/ZUGFeRD-Daten extrahieren, folgende Fehlermeldung und das PDF:',
854 854
  'Could not find an entry for this part in the pricegroup.' => 'Konnte keinen Eintrag für diesen Artikel in der Preisgruppe finden.',
855
  'Could not find email import(s): ' => 'Konnte folgende E-Mailimport(e) nicht finden: ',
855 856
  'Could not load class #1 (#2): "#3"' => 'Konnte Klasse #1 (#2) nicht laden: "#3"',
856 857
  'Could not load class #1, #2' => 'Konnte Klasse #1 nicht laden: "#2"',
857 858
  'Could not load employee'     => 'Konnte Benutzer nicht laden',
......
1189 1190
  'Delete text block'           => 'Textblock löschen',
1190 1191
  'Delete transaction'          => 'Buchung löschen',
1191 1192
  'Deleted'                     => 'Gelöscht',
1193
  'Deleted email import(s): '   => 'Gelöschte E-Mailimport(e): ',
1192 1194
  'Deleting this type of record has been disabled in the configuration.' => 'Das Löschen von dieser Belegart ist in der Konfiguration deaktiviert.',
1193 1195
  'Delivered'                   => 'Geliefert',
1194 1196
  'Delivered amount'            => 'Gelieferter Betrag',
......
1573 1575
  'Error when saving: #1'       => 'Fehler beim Speichern: #1',
1574 1576
  'Error while applying year-end bookings!' => 'Fehler beim Durchführen der Abschlußbuchungen!',
1575 1577
  'Error while creating project with project number of new order number, project number #1 already exists!' => 'Fehler beim Erstellen eines Projekts mit der Projektnummer der neuen Auftragsnummer, Projektnummer #1 existiert bereits!',
1578
  'Error while processing email journal (\'#1\') attachments with \'#2\': ' => 'Fehler beim Verarbeiten der E-Mail-Journals (\'#1\') Anhänge mit \'#2\': ',
1576 1579
  'Error while saving shop order #1. DB Error #2. Generic exception #3.' => 'Fehler beim Speichern der Shop-Bestellung #1. DB Fehler #2. Genereller Fehler #3.',
1577 1580
  'Error with default taxzone'  => 'Ungültige Standardsteuerzone',
1578 1581
  'Error!'                      => 'Fehler!',
......
1668 1671
  'Errors during printing:'     => 'Druckfehler:',
1669 1672
  'Errors in GL transaction:'   => 'Fehler in Dialogbuchung:',
1670 1673
  'Errors while deleting record:' => 'Fehler beim Speichern des Belegs:',
1674
  'Errors while processing: '   => 'Fehler bei Verarbeitung: ',
1671 1675
  'Errors: #1'                  => 'Fehler: #1',
1672 1676
  'Ertrag'                      => 'Ertrag',
1673 1677
  'Ertrag prozentual'           => 'Ertrag prozentual',
......
2487 2491
  'No articles have been added yet.' => 'Es wurden noch keine Artikel hinzugefügt.',
2488 2492
  'No assembly has been selected yet.' => 'Es wurde noch kein Erzeugnis ausgewahlt.',
2489 2493
  'No attachment'               => 'Kein Anhang',
2494
  'No attachments.'             => 'Keine Anhänge',
2490 2495
  'No background job has been created yet.' => 'Es wurden noch keine Hintergrund-Jobs angelegt.',
2491 2496
  'No bank account chosen!'     => 'Kein Bankkonto ausgewählt!',
2492 2497
  'No bank account configured for bank code/BIC #1, account number/IBAN #2.' => 'Kein Bankkonto für BLZ/BIC #1, Kontonummer/IBAN #2 konfiguriert.',
......
2572 2577
  'No transactions yet.'        => 'Bisher keine Buchungen.',
2573 2578
  'No transfers were executed in this export.' => 'In diesem SEPA-Export wurden keine Überweisungen ausgeführt.',
2574 2579
  'No users have been created yet.' => 'Es wurden noch keine Benutzer angelegt.',
2580
  'No vaild Factur-X/ZUGFeRD file' => 'Keine valide Factur-X/ZUGFeRD Datei',
2575 2581
  'No valid invoice(s) found'   => 'Keine gültige(n) Rechnung(en) gefunden.',
2576 2582
  'No valid number entered for pricegroup "#1".' => 'Für Preisgruppe "#1" wurde keine gültige Nummer eingegeben.',
2577 2583
  'No vendor available'         => 'Kein Lieferant verfügbar',
......
2592 2598
  'Normalize Customer / Vendor names' => 'Normalisierung Kunden- / Lieferantennamen',
2593 2599
  'Normalize part description and part notes' => 'Normalisierung Artikelbeschreibung und Artikellangtext (Bemerkung)',
2594 2600
  'Not Discountable'            => 'Nicht rabattierfähig',
2601
  'Not a PDF or XML file'       => 'Keine PDF oder XML Datei',
2595 2602
  'Not delivered'               => 'Nicht geliefert',
2596 2603
  'Not delivered amount'        => 'nicht gelieferte Menge',
2597 2604
  'Not done yet'                => 'Noch nicht fertig',
......
3036 3043
  'Private E-mail'              => 'Private E-Mail',
3037 3044
  'Private Phone'               => 'Privates Tel.',
3038 3045
  'Problem'                     => 'Problem',
3046
  'Processed attachments with function \'#1\':' => 'Anhänge verarbeitet mit Funktion \'#1\':',
3047
  'Processed successfully: '    => 'Erfolgreich verarbeitet: ',
3039 3048
  'Produce'                     => 'Fertigen',
3040 3049
  'Produce Assembly'            => 'Erzeugnis fertigen',
3041 3050
  'Produce Assembly Configuration' => 'Konfiguration für Erzeugnis fertigen',
locale/en/all
852 852
  'Could not create new project #1' => '',
853 853
  'Could not extract Factur-X/ZUGFeRD data, data and error message:' => '',
854 854
  'Could not find an entry for this part in the pricegroup.' => '',
855
  'Could not find email import(s): ' => '',
855 856
  'Could not load class #1 (#2): "#3"' => '',
856 857
  'Could not load class #1, #2' => '',
857 858
  'Could not load employee'     => '',
......
1189 1190
  'Delete text block'           => '',
1190 1191
  'Delete transaction'          => '',
1191 1192
  'Deleted'                     => '',
1193
  'Deleted email import(s): '   => '',
1192 1194
  'Deleting this type of record has been disabled in the configuration.' => '',
1193 1195
  'Delivered'                   => '',
1194 1196
  'Delivered amount'            => '',
......
1573 1575
  'Error when saving: #1'       => '',
1574 1576
  'Error while applying year-end bookings!' => '',
1575 1577
  'Error while creating project with project number of new order number, project number #1 already exists!' => '',
1578
  'Error while processing email journal (\'#1\') attachments with \'#2\': ' => '',
1576 1579
  'Error while saving shop order #1. DB Error #2. Generic exception #3.' => '',
1577 1580
  'Error with default taxzone'  => '',
1578 1581
  'Error!'                      => '',
......
1668 1671
  'Errors during printing:'     => '',
1669 1672
  'Errors in GL transaction:'   => '',
1670 1673
  'Errors while deleting record:' => '',
1674
  'Errors while processing: '   => '',
1671 1675
  'Errors: #1'                  => '',
1672 1676
  'Ertrag'                      => '',
1673 1677
  'Ertrag prozentual'           => '',
......
2486 2490
  'No articles have been added yet.' => '',
2487 2491
  'No assembly has been selected yet.' => '',
2488 2492
  'No attachment'               => '',
2493
  'No attachments.'             => '',
2489 2494
  'No background job has been created yet.' => '',
2490 2495
  'No bank account chosen!'     => '',
2491 2496
  'No bank account configured for bank code/BIC #1, account number/IBAN #2.' => '',
......
2571 2576
  'No transactions yet.'        => '',
2572 2577
  'No transfers were executed in this export.' => '',
2573 2578
  'No users have been created yet.' => '',
2579
  'No vaild Factur-X/ZUGFeRD file' => '',
2574 2580
  'No valid invoice(s) found'   => '',
2575 2581
  'No valid number entered for pricegroup "#1".' => '',
2576 2582
  'No vendor available'         => '',
......
2591 2597
  'Normalize Customer / Vendor names' => '',
2592 2598
  'Normalize part description and part notes' => '',
2593 2599
  'Not Discountable'            => '',
2600
  'Not a PDF or XML file'       => '',
2594 2601
  'Not delivered'               => '',
2595 2602
  'Not delivered amount'        => '',
2596 2603
  'Not done yet'                => '',
......
3035 3042
  'Private E-mail'              => '',
3036 3043
  'Private Phone'               => '',
3037 3044
  'Problem'                     => '',
3045
  'Processed attachments with function \'#1\':' => '',
3046
  'Processed successfully: '    => '',
3038 3047
  'Produce'                     => '',
3039 3048
  'Produce Assembly'            => '',
3040 3049
  'Produce Assembly Configuration' => '',

Auch abrufbar als: Unified diff