Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 6a12a968

Von Niclas Zimmermann vor etwa 9 Jahren hinzugefügt

  • ID 6a12a968761127af91e9da8db7579be2836bcaaa
  • Vorgänger 28fee2e2
  • Nachfolger b09bc3de

Bankerweiterung - Zwischenstand, erster Entwurf

Erstellung von Tabelle bank_transactions

Import von Bankbewegungen (in Tabelle bank_transactions)

Menu-Eintrag war noch nicht commitet

Controller für die Bank-Transaktionen

Dialog hin- und her

Achtung: noch mit Debug-Statements!

Dies und das für Bank_transactions...

BankTransaction in RecordLinks

Kann verknüpfte Belege Speichern

Man kann Rechnungen mehr oder weniger schon als bezahlt markieren

Erweiterung für EK-Rechnungen (funktioniert noch nicht ganz)

EK-Rechnungen und erste Vorschläge für Rechnung bezahlen

Information von Rechnungen + Javascript statt Ajax

Style als Link lassen

Deckt verschiedene Spezialfälle ab

Datums- und Zahlenformatierungen (noch nicht fertig)

Datumsformat und Übersetzungen

Sub date wieder aus LxERP entfernt

Logik für automatisches Zuweisen

Bericht für BTs (noch nicht ganz fertig)

Formatierungen für Zeilen mit ReportGenerator

Löschen-Knopf und paar Sachen

Entwurf laden und mit Parametern aus form überschreiben

Aufruf mit Parametern als get für Kreditorenbuchung:

ap.pl?action=load_draft&id=ap-invoice-1385202327-644205-8150&invnumber=dsfdf&remove_draft=0&paid_1=123,45

Rechnung aus Bankbeleg erstellen

Bankkonten-Filter und id auf bank_account statt local...

Verknüpfte Belege mit id-Verknüpfung zu local_bank_account

CsvImport mit ID auf BankAccounts (noch nicht getestet)

Behebt Bugs bei Csv-Import von Bankbewegungen

Währungs-ID statt Währungsstring benutzen

Passt den Bankbewegungs-Import an die neuen Änderungen der Helfer an

Filter für create_invoice

Vergessene Templates

Filter für create invoice funktionier halb

Rest für den Filter von create Invoice

Auswahllistenbegrenzer bei Lieferantenfilter beachten

Mehr Daten bei 'create invoice' übernehmen

Wenn man eine Kreditorenbuchung aus einer Bankbewegung erstellt,
werden nun mehr Daten aus der Bankbewegung in die Vorlage über-
nommen.

Änderungen in drafts.pl (gehören zu letztem Commit)

Betrag richtig formatieren und Form submitten

Bei assign invoice werden jetzt Rechnungsbeträge richtig formatiert
und es werden Rechnungen gesucht, wenn man auf 'Enter' drückt.

Weiterleitung auf List und Rechnungsbezahlen repariert

Vorzeichen wechseln bei create invoice

Das Vorzeichen von der Bankbewegung muss geändert werden, wenn man
aus einer Bankbewegung eine Kreditorenbuchung erstellt.

Sortierung von Spalten bei Bankbewegungen

Geoffery's Änderungen bzgl. Vorschlagsuche

BankTransaction - create_invoice filtert für Kreditorenbuchungen

Im Bank-Dropdown Bankname zuerst anzeigen

Und die Breite vom Dropdown angepasst.

Bankauszug verbuchen Menüpunkt nicht unter Berichte

Banktransactions - Spalten in Tabellen umsortiert

Banktransactions Tooltip - bt-Info in Tooltip Fenster aufnehmen

Damit man innerhalb des Tooltip Fensters die wichtigsten Infos auf einen
Blick hat.

Sortierreihenfolge

Es wird jetzt zuerst nach Alphabet, dann gegen das Alphabet sortiert.

Banktransactions

Verbuchte Beträge werden jetzt in der BankTransaction gespeichert.

Sortierung auf DB-Ebene, falls möglich

Anzeige von verbuchten Beträgen der BankTransactions

Kleinerer Bug

Ein paar Debug-Statements entfernt

Verknüpfte Rechnungen im BankTransaction-Bericht anzeigen.

Kontenabgleich

Erste Schritte zum Kontenabgleich.

Kontenabgleich funktioniert schon ganz gut.

TODO: nochmal Funktionen checken
TODO: Filter hinzufügen

Kontenabgleich

Filter für BT und PR im Kontenabgleich.

TODO: Datumsfilter für beide gemeinsam umschreiben und Bilanzierung anzeigen.

Kontenabgleich übergreifender Datumsfilter

Das transdate wird jetzt auch übergreifend für Bankbewegungen und
verbuchte Zahlungen gesetzt.

TODO: Nach Änderung dieser Daten sollten die Tabellen neugeladen
und alle Teilfilter zurück auf 0 gesetzt werden.

Kontenabgleich - ein paar Sachen ausprobiert

Ist nur zum weitermachen (alle Änderungen nur Tests).

Kontenabgleich Kleiner Bug in Search

Kontenabgleich - Erste Übersicht

Kontenabgleich Anzeige von abgeglichenen Buchungen mit Bankbuchungen
funktioniert schonmal

Nächstes: Filter.

Kontenabgleich Filtern ist jetzt in der Übersicht möglich.

Next: Spalte reconciliation_link durch group oder so ersetzen.

Kontenabgleich In rec_group in reconciliation_links

In der Tabelle reconciliation_links werden jetzt keine ids mehr auf
reconciliation_links gespeichert. Stattdessen erkennt man an der
rec_group, welche Zeilen zusammengehören.

Kontenabgleich Berechnung von Summen

Jetzt werden Summen von Bankbewegungen und cleared payments angezeigt.

Next: Anhaken und abgleichen implementieren.

Kontenabgleich Checkboxen fügen Elemente zur Tabelle

Es gibt jetzt Checkboxen neben BT's und BB's. Wenn man sie anhakt,
wird die BT bzw. die BB in eine Tabelle zum abgleichen gesteckt.

TODO: Button zum abgleichen programmieren.

Kontenabgleich Man kann jetzt auch im Report abgleichen

Es fehlt noch: Abbruch-Action, falls beim Abgleichen etwas fehlerhaft ist.

Kontenabgleich Manager für ReconciliationLink

Wurde vergessen zu committen

Kontenabgleich Vorzeichenfehler bei AccTransactions behoben.

In der Anzeige muss das Vorzeichen von AccTransactions bei Buchungen
auf Bank geändert werden.

Kontenabgleich Code aufräumen und Sortierung nach Datum

Es wird jetzt immer nach Datum sortiert. Weiterhin wurde im Code
eine sub von actions nach helpers geschoben.

Kontenabgleich Abbruch nach Fehlern beim Abgleichen

Wenn beim Abgleichen Fehler auftreten, wird man jetzt auf die Über-
sichtsseite geleitet.

Kontenabgleich/BankTransactions Upgrade-Script für Tabellen anpassen

Einige Änderungen der Tabellen sind in den Scripten jetzt enthalten.

Kontenabgleich Farbliche Hinterlegung und Entfernung von Debug-Code

Nicht zugewiesene BankTransactions und Buchungen auf Bankkonten
werden im Kontenabgleich jetzt rot hinterlegt.

Weiterhin wurden noch Debug-Statements entfernt.

TODO: Erweiterung auf andere css Klassen (nicht nur kivitendo, sondern
auch mobile/alte lx-office-styles etc...)

Kontenabgleich Behebt Bug

Unter gewissen Umständen wurden nach der Filterung noch alte Daten
beibehalten. Jetzt wird alles erneuert.

Kontenabgleich Filter für cleared

Man kann jetzt in der Übersicht auch nach cleared und uncleared filtern.

Kontenabgleich Erste Schritte für Automatischen Abgleich

Bisher werden Vorschläge nur dargestellt.

TODO: Button 'Abgleichen' für Vorschläge programmieren.

Kontenabgleich Vorschläge können jetzt abgeglichen werden

Vorschläge können jetzt auch automatisch abgeglichen werden, wenn
man sie anhakt und danach auf abgleichen klickt.

Kontenabgleich Anzeige von Reference

Es gibt jetzt einen Link im Kontenabgleich auf ar/ap/gl.

Kontenabgleich Kunden-/Lieferantennamen

Kunden- und Lieferantennamen werden jetzt für AccTransactions an-
gezeigt. Im Falle einer Dialogbuchung erscheint ihre Beschreibung.

Kontenabgleich Untersortierung nach Betrag

Bisher wurde nur nach Datum sortiert. Innerhalb eines Datums wird
jetzt zusätzlich nach Betrag sortiert.

Kontenabgleich/BankTransactions Entfernung Debug-Statements

Kontenabgleich BankTransactions als verbucht markieren

BankTransactions werden jetzt als verbucht markiert, wenn sie abge-
glichen sind. Ansonsten wurde noch ein kleiner Syntax-Fehler behoben.

Kontenabgleich Bilanz über alle Buchungen berechnen

Die Bilanz wird jetzt unabhängig vom Filter sowohl für cleared als
auch uncleared berechnet. Weiterhin wurde noch ein Vorzeichenfehler
in der Bilanz für Bankbewegungen behoben und der Code etwas verschoben.

Kontenabgleich Verlinkungen aufheben

Man kann jetzt auch Verlinkungen nach dem Abgleichen aufheben.

Kontenabgleich Code-Formatierung und kleine Verbesserung in der Anzeige

Formatierung von Code wurde geändert, sowie einige kleine Besserungen
in der Anzeige (zum Beispiel sind Beträge jetzt rechts orientiert
in der Tabellenzeile).

Kontenabgleich Tabellenhöhe mit relativer Größe

Die Tabellenhöhe hängt jetzt relativ von der Größe des Bildschirms
ab anstatt von einer festen Pixel-Anzahl.

Kontenabgleich Ok message für Flash eingebaut

Kontenabgleich gehört noch zum Flash-message-Commit

Kontenabgleich Zusammenführen verschiedener Abgleichmöglichkeiten

Bisher wurden verschiedene Methoden implementiert, den Kontenabgleich
zu machen. In diesem Commit werden zwei verschiedene Möglichkeiten
unter einen Hut gebracht. Es ist auf der erstellten Seite auch ohne
weiteres Möglich weitere Möglichkeiten hinzuzufügen.

Kontenabgleich Anzeige von Overview umbauen

Die Anzeige von Overview wurde verändert, so dass man jetzt noch
schneller abgleichen kann.

TODO: Anzeige von Proposals der Anzeige von Overview angleichen.

Kontenabgleich Behebt Syntaxfehler

Kontenabgleich Umgang mit Stornos ändern

Der Umgang mit Stornos wurde geändert. Statt Stornos nicht anzuzeigen,
gibt es jetzt einen Filter dafür. Der Saldo von BBs und BTs errechnet
sich jetzt nur noch aus den gefilterten Objekten.

Weiterhin gibt es jetzt eine onchange-Action auf den Datumsfeldern
im Filter und der Filter-Button wird nicht mehr angezeigt.

Kontenabgleich Anpassung der proposals an neue Ansicht

Automatische Vorschläge werden jetzt auch in der neuen Darstellung
angezeigt.

Kontenabgleich Mastercheckbox wieder da und Spaltenreihenfolge vertauscht

Die Checkbox, bei der man alle Vorschläge an-/abhaken kann, wird
jetzt wieder angezeigt. Weiterhin wird jetzt auch der Betrag und
das Belegdatum weiter vorne angezeigt.

Kontenabgleich Saldo in der richtigen Spalte

Der Saldo wird jetzt in der richtigen Spalte angezeigt.

Kontenabgleich Buchungen außerhalb des Filters ausgrauen

Bisher kam es vor, dass Buchungen, die in einen Zeitraum außerhalb
des Filters fallen (weil ihre Gegenbuchung in den gefilterten Zeitraum
fällt), trotzdem angezeigt wurden, aber nicht in der
Summe mitgerechnet wurden. Solche Buchungen werden jetzt nur noch
in grauer Schrift angezeigt.

Kontenabgleich Abgeglichene Buchungen besser Filtern

Es kam bisher vor, dass abgeglichene Buchungen, bei denen eine Buchung
nach dem gefilterten Zeitraum un eine Buchung vor dem gefilterten
Zeitraum lag, angezeigt wurden. Dabei war es bisher nicht erforderlich,
dass eine Buchung im gefilterten Zeitraum lag. Jetzt muss mindestens
eine Buchung im gefilterten Zeitraum liegen, damit die abgeglichenen
Buchungen auch angezeigt werden.

Kontenabgleich Absolute Bilanz anzeigen

Jetzt wird auch immer die Summe von BB's und BT's von Beginn der
Buchungen bis zum "Todate" angezeigt (inklusive Stornos).

Banktransactions nach Datum filtern

Man kann jetzt Bankbewegungen nach Datum filtern. Weiterhin funktioniert
jetzt auch der Callback, nachdem man eine Debitorenbuchung erstellt.

Banktransactions: Datumsfilter nach Sortierung beibehalten

Bisher ging der Datumsfilter nach der Sortierung verloren, jetzt
wird er beibehalten.

Kontenabgleich Mehr Vorschläge machen

Bisher wurden nur Vorschläge gemacht, wenn Rechnungen via Bankbewegungen
bezahlt wurden. Jetzt werden auch Vorschläge gemacht, wenn Beträge
deckungsgleich sind und Kontoverbindung von Kunde/Lieferant und
Bankbeleg übereinstimmen.

Kontenabgleich Reconciliate-Button taucht nicht auf

Behebt einen Bug, bei dem der Abgleichen-Knopf nicht auftaucht,
obwohl er das sollte. Das Problem lag daran, dass die beiden Zahlen
6.286,18 und -6.286,18 aufaddiert in Perl nicht Null ergaben.

Kontenabgleich Fügt mehr Proposals hinzu

Auf diese Weise werden noch mehr Übereinstimmungen gefunden.

Banktransactions Teilzahlungen funktionieren jetzt besser

Bisher haben Teilzahlungen zwar funktioniert, jedoch tauchten die
Bankbewegungen, mit denen sie erstellt wurden immer wieder in der
Liste auf. Es lag daran, dass invoice_amount in der Tabelle
bank_transactions falsch gesetzt wurde.

Banktransactions 40 statt 5 Bankbewegungen pro Seite anzeigen

Kontenabgleich Entfernen von Tests

Entfernt alten Code, der die ersten Tests enthielt.

Kontenabgleich Umbenennung von Controller

Der Controller SL/Controller/Reconciliation_3.pm wird umbenannt in
SL/Controller/Reconciliation.pm

Kontenabgleich Umbenennung von Ordner whole_reconciliation

Der Order whole_reconciliation wurde in reconciliation umbenannt.

Banktransactions Minuspunkte für transdate vergeben

Wenn eine Rechnung nicht 30 Tage vor der Bankbewegung liegt, so
wird jetzt ein Minuspunkt für die Vorschläge vergeben.

Kontenabgleich/Banktransaction Rechte hinzufügen

Bank CSV Import - Purpose zusammengefügt

Icons bei Reconciliation

Kontenabgleich Icons in Vorschlägen übernehmen

Icons werden jetzt auch bei Vorschlägen benutzt. Weiterhin wurde
unsinniges Attribut bei Icons im Overview entfernt.

Kontenabgleich bei Import Standard-Währung setzen

Beim Import wird jetzt die Standard-Währung eingetragen, wenn keine
andere Währung vorhanden ist. Weiterhin ist die Währung jetzt auch ein
Pflichtfeld durch ein NOT NULL-Constraint.

Kontenabgleich Spalte in Summenzeile ergänzen

Aufgrund der neu hinzugekommenen Icons musste noch eine Spalte in
der Zeile eingefügt werden, wo die Summen angezeigt werden.

Banktransaction Betrag im Filter parsen

Beim Suchen von Rechnungen im "Rechnung hinzufügen"-Modus wurde der
Betrag im Filter nicht geparst. Man musste bisher also immer einen
Punkt statt einem Komma eingeben.

Kontenabgleich CSS verbessern

Bisher konnte man nicht bis an den unteren Bildschirmrand scrollen.
Dieser Commit versucht dies zu beheben. Allerdings ist es unklar,
ob das Problem durch den Commit behoben ist.

Banktransactions Automatisches bezahlen von Vorschlägen

Banktransactions Speichern von Vorschlägen

Automatische Vorschläge, die kivitendo schon macht, können jetzt
auch benutzt werden, um vorgeschlagene Rechnungen direkt zu speichern.

Banktransactions Bugfix

Bisher wurden der "Rechnung speichern"-Button und der "Save proposals"-
Button nur getogglet. Das hat dazu geführt, dass wenn man zweimal
auf den Reiter "Proposals" drückt, der Rechnung speichern-Butten
angezeigt wurde, obwohl der Save proposals-Button angezeigt werden
musste. Jetzt werden die Buttons versteckt und angezeigt, was den
Fehler behebt.

Banktransactions - kein von und bis wenn leer

Die Wörter "von" und "bis" nur anzeigen, wenn auch ein von- oder
bis-Datum gesetzt ist.

Bankbewegungen - in Tooltip Tagesdelta anzeigen

Anzahl der Tage zwischen (vorgeschlagenem) Rechnungsdatum und
Bankeingangsdatum wird in Klammern hinter dem Bankdatum angezeigt.

Banktransactions - bei Proposals trotz sub-cent Matchen

Übereinstimmung muß nicht genau sein, sondern kleiner 1 Cent.
Wegen Rundungsproblematik.

String-Vergleich bei Banktransactions - mind. 3 Zeichen

Mindestlänge für Wortvergleich.

testing - agreement modifying tests

Banktransactions Belegdatum bei Rechnung zuweisen

Wenn man auf Rechnung zuweisen klickt, wird jetzt sowohl von dem
Bankbeleg als auch von den Rechnungen das Belegdatum angezeigt.
Weiterhin wurde ein Filter für das Datum hinzugefügt.

Kontenabgleich/Banktransactions

Reiter 'Set cleared entfernen' im Kontenabgleich.

Bei Vorgeschlagenen Rechnungen anzeigen, ob EK oder VK.

Deutsche Übersetzungen

VERSION angepasst

MT940 Importer der aqbanking-cli über CSV-Import aufruft

Noch alles hartkodiert
Kein Dublettencheck

MT940 Importer der aqbanking-cli über CSV-Import aufruft

Noch alles hartkodiert
Kein Dublettencheck

Bank - in Bankkontodropdowns Kontoname berücksichtigen

Bankerweiterung - Offener Betrag bei Rechnungsfilter

wenn man aus Bankbuchungen eine Rechnung zuweisen möchte.

RB - corrected all after rebase

RB - GLTransaction.pm nach Rebase gefixed

Unterschiede anzeigen:

SL/Auth.pm
977 977
    ["general_ledger",                 $locale->text("Transactions, AR transactions, AP transactions")],
978 978
    ["datev_export",                   $locale->text("DATEV Export")],
979 979
    ["cash",                           $locale->text("Receipt, payment, reconciliation")],
980
    ["bank_transaction",               $locale->text("Bank transactions")],
980 981
    ["--reports",                      $locale->text('Reports')],
981 982
    ["report",                         $locale->text('All reports')],
982 983
    ["advance_turnover_tax_return",    $locale->text('Advance turnover tax return')],
SL/Controller/BankTransaction.pm
1
package SL::Controller::BankTransaction;
2

  
3
# idee- möglichkeit bankdaten zu übernehmen in stammdaten
4
# erst Kontenabgleich, um alle gl-Einträge wegzuhaben
5
use strict;
6

  
7
use parent qw(SL::Controller::Base);
8

  
9
use SL::Controller::Helper::GetModels;
10
use SL::Controller::Helper::ReportGenerator;
11
use SL::ReportGenerator;
12

  
13
use SL::DB::BankTransaction;
14
use SL::Helper::Flash;
15
use SL::Locale::String;
16
use SL::SEPA;
17
use SL::DB::Invoice;
18
use SL::DB::PurchaseInvoice;
19
use SL::DB::RecordLink;
20
use SL::JSON;
21
use SL::DB::Chart;
22
use SL::DB::AccTransaction;
23
use SL::DB::Tax;
24
use SL::DB::Draft;
25
use SL::DB::BankAccount;
26

  
27
use Rose::Object::MakeMethods::Generic
28
(
29
 'scalar --get_set_init' => [ qw(models) ],
30
);
31

  
32
__PACKAGE__->run_before('check_auth');
33

  
34

  
35
#
36
# actions
37
#
38

  
39
sub action_search {
40
  my ($self) = @_;
41

  
42
  my $bank_accounts = SL::DB::Manager::BankAccount->get_all();
43

  
44
  $self->render('bank_transactions/search',
45
                 label_sub => sub { t8('#1 - Account number #2, bank code #3, #4', $_[0]->name, $_[0]->account_number, $_[0]->bank_code, $_[0]->bank, )},
46
                 BANK_ACCOUNTS => $bank_accounts);
47
}
48

  
49
sub action_list_all {
50
  my ($self) = @_;
51

  
52
  my $transactions = $self->models->get;
53

  
54
  $self->make_filter_summary;
55
  $self->prepare_report;
56

  
57
  $self->report_generator_list_objects(report => $self->{report}, objects => $transactions);
58
}
59

  
60
sub action_list {
61
  my ($self) = @_;
62

  
63
  if (!$::form->{filter}{bank_account}) {
64
    flash('error', t8('No bank account chosen!'));
65
    $self->action_search;
66
    return;
67
  }
68

  
69
  my $sort_by = $::form->{sort_by} || 'transdate';
70
  $sort_by = 'transdate' if $sort_by eq 'proposal';
71
  $sort_by .= $::form->{sort_dir} ? ' DESC' : ' ASC';
72

  
73
  my $fromdate = $::locale->parse_date_to_object(\%::myconfig, $::form->{filter}->{fromdate});
74
  my $todate   = $::locale->parse_date_to_object(\%::myconfig, $::form->{filter}->{todate});
75
  $todate->add( days => 1 ) if $todate;
76

  
77
  my @where = ();
78
  push @where, (transdate => { ge => $fromdate }) if ($fromdate);
79
  push @where, (transdate => { lt => $todate })   if ($todate);
80

  
81
  my $bank_transactions = SL::DB::Manager::BankTransaction->get_all(where => [ amount => {ne => \'invoice_amount'},
82
                                                                               local_bank_account_id => $::form->{filter}{bank_account},
83
                                                                               @where ],
84
                                                                    with_objects => [ 'local_bank_account', 'currency' ],
85
                                                                    sort_by => $sort_by, limit => 10000);
86

  
87
  my $all_open_ar_invoices = SL::DB::Manager::Invoice->get_all(where => [amount => { gt => \'paid' }], with_objects => 'customer');
88
  my $all_open_ap_invoices = SL::DB::Manager::PurchaseInvoice->get_all(where => [amount => { gt => \'paid' }], with_objects => 'vendor');
89

  
90
  my @all_open_invoices;
91
  push @all_open_invoices, @{ $all_open_ar_invoices };
92
  push @all_open_invoices, @{ $all_open_ap_invoices };
93

  
94
  foreach my $bt (@{ $bank_transactions }) {
95
    next unless $bt->{remote_name};  # bank has no name, usually fees, use create invoice to assign
96
    foreach my $open_invoice (@all_open_invoices){
97
      $open_invoice->{agreement} = 0;
98

  
99
      #compare banking arrangements
100
      my ($bank_code, $account_number);
101
      $bank_code      = $open_invoice->customer->bank_code      if $open_invoice->is_sales;
102
      $account_number = $open_invoice->customer->account_number if $open_invoice->is_sales;
103
      $bank_code      = $open_invoice->vendor->bank_code        if ! $open_invoice->is_sales;
104
      $account_number = $open_invoice->vendor->account_number   if ! $open_invoice->is_sales;
105
      ($bank_code eq $bt->remote_bank_code
106
        && $account_number eq $bt->remote_account_number) ? ($open_invoice->{agreement} += 2) : ();
107

  
108
      my $datediff = $bt->transdate->{utc_rd_days} - $open_invoice->transdate->{utc_rd_days};
109
      $open_invoice->{datediff} = $datediff;
110

  
111
      #compare amount
112
#      (abs($open_invoice->amount) == abs($bt->amount)) ? ($open_invoice->{agreement} += 2) : ();
113
# do we need double abs here? 
114
      (abs(abs($open_invoice->amount) - abs($bt->amount)) < 0.01) ? ($open_invoice->{agreement} += 4) : ();
115

  
116
      #search invoice number in purpose
117
      my $invnumber = $open_invoice->invnumber;
118
# possible improvement: match has to have more than 1 character?
119
      $bt->purpose =~ /\b$invnumber\b/i ? ($open_invoice->{agreement} += 2) : ();
120

  
121
      #check sign
122
      if ( $open_invoice->is_sales && $bt->amount < 0 ) {
123
        $open_invoice->{agreement} -= 1;
124
      };
125
      if ( ! $open_invoice->is_sales && $bt->amount > 0 ) {
126
        $open_invoice->{agreement} -= 1;
127
      };
128

  
129
      #search customer/vendor number in purpose
130
      my $cvnumber;
131
      $cvnumber = $open_invoice->customer->customernumber if $open_invoice->is_sales;
132
      $cvnumber = $open_invoice->vendor->vendornumber     if ! $open_invoice->is_sales;
133
      $bt->purpose =~ /\b$cvnumber\b/i ? ($open_invoice->{agreement}++) : ();
134

  
135
      #compare customer/vendor name and account holder
136
      my $cvname;
137
      $cvname = $open_invoice->customer->name if $open_invoice->is_sales;
138
      $cvname = $open_invoice->vendor->name   if ! $open_invoice->is_sales;
139
      $bt->remote_name =~ /\b$cvname\b/i ? ($open_invoice->{agreement}++) : ();
140

  
141
      #Compare transdate of bank_transaction with transdate of invoice
142
      #Check if words in remote_name appear in cvname
143
      $open_invoice->{agreement} += &check_string($bt->remote_name,$cvname);
144

  
145
      $open_invoice->{agreement} -= 1 if $datediff < -5; # dies hebelt eventuell Vorkasse aus
146
      $open_invoice->{agreement} += 1 if $datediff < 30; # dies hebelt eventuell Vorkasse aus
147

  
148
      # only if we already have a good agreement, let date further change value of agreement.
149
      # this is so that if there are several open invoices which are all equal (rent jan, rent feb...) the one with the best date match is chose over the others
150
      # another way around this is to just pre-filter by periods instead of matching everything
151
      if ( $open_invoice->{agreement} > 5 ) {
152
        if ( $datediff == 0 ) { 
153
          $open_invoice->{agreement} += 3;
154
        } elsif  ( $datediff > 0 and $datediff <= 14 ) {
155
          $open_invoice->{agreement} += 2;
156
        } elsif  ( $datediff >14 and $datediff < 35) {
157
          $open_invoice->{agreement} += 1;
158
        } elsif  ( $datediff >34 and $datediff < 120) {
159
          $open_invoice->{agreement} += 1;
160
        } elsif  ( $datediff < 0 ) {
161
          $open_invoice->{agreement} -= 1;
162
        } else {
163
          # e.g. datediff > 120
164
        };
165
      };
166

  
167
      #if ($open_invoice->transdate->{utc_rd_days} == $bt->transdate->{utc_rd_days}) {  
168
        #$open_invoice->{agreement} += 4;
169
        #print FH "found matching date for invoice " . $open_invoice->invnumber . " ( " . $bt->transdate->{utc_rd_days} . " . \n";
170
      #} elsif (($open_invoice->transdate->{utc_rd_days} + 30) < $bt->transdate->{utc_rd_days}) {  
171
        #$open_invoice->{agreement} -= 1;
172
      #} else {
173
        #$open_invoice->{agreement} -= 2;
174
        #print FH "found nomatch date -2 for invoice " . $open_invoice->invnumber . " ( " . $bt->transdate->{utc_rd_days} . " . \n";
175
      #};
176
      #print FH "agreement after date_agreement: " . $open_invoice->{agreement} . "\n";
177

  
178

  
179

  
180
    }
181
# finished going through all open_invoices
182

  
183
    # go through each bt
184
    # for each open_invoice try to match it to each open_invoice and store agreement in $open_invoice->{agreement} (which gets overwritten each time for each bt)
185
    #    calculate 
186
#  
187

  
188
    $bt->{proposals} = [];
189
    my $agreement = 11;
190
    # wird nie ausgeführt, bzw. nur ganz am Ende
191
# oder einmal am Anfang?
192
# es werden maximal 7 vorschläge gemacht?
193
    # 7 mal wird geprüft, ob etwas passt
194
    while (scalar @{ $bt->{proposals} } < 1 && $agreement-- > 0) {
195
      $bt->{proposals} = [ grep { $_->{agreement} > $agreement } @all_open_invoices ];
196
      #Kann wahrscheinlich weg:
197
#      map { $_->{style} = "green" } @{ $bt->{proposals} } if $agreement >= 5;
198
#      map { $_->{style} = "orange" } @{ $bt->{proposals} } if $agreement < 5 and $agreement >= 3;
199
#      map { $_->{style} = "red" } @{ $bt->{proposals} } if $agreement < 3;
200
      $bt->{agreement} = $agreement;  # agreement value at cutoff, will correspond to several results if threshold is 7 and several are already above 7
201
    }
202
  }  # finished one bt
203
  # finished all bt
204

  
205
  # separate filter for proposals (second tab, agreement >= 5 and exactly one match)
206
  # to qualify as a proposal there has to be
207
  # * agreement >= 5
208
  # * there must be only one exact match 
209
  # * depending on whether sales or purchase the amount has to have the correct sign (so Gutschriften don't work?)
210

  
211
  my @proposals = grep { $_->{agreement} >= 5
212
                         and 1 == scalar @{ $_->{proposals} }
213
                         and (@{ $_->{proposals} }[0]->is_sales ? abs(@{ $_->{proposals} }[0]->amount - $_->amount) < 0.01  : abs(@{ $_->{proposals} }[0]->amount + $_->amount) < 0.01) } @{ $bank_transactions };
214

  
215
  #Sort bank transactions by quality of proposal
216
  $bank_transactions = [ sort { $a->{agreement} <=> $b->{agreement} } @{ $bank_transactions } ] if $::form->{sort_by} eq 'proposal' and $::form->{sort_dir} == 1;
217
  $bank_transactions = [ sort { $b->{agreement} <=> $a->{agreement} } @{ $bank_transactions } ] if $::form->{sort_by} eq 'proposal' and $::form->{sort_dir} == 0;
218

  
219

  
220
  $self->render('bank_transactions/list',
221
                title             => t8('List of bank transactions'),
222
                BANK_TRANSACTIONS => $bank_transactions,
223
                PROPOSALS         => \@proposals,
224
                bank_account      => SL::DB::Manager::BankAccount->find_by(id => $::form->{filter}{bank_account}) );
225
}
226

  
227
sub check_string {
228
    my $bankstring = shift;
229
    my $namestring = shift;
230
    return 0 unless $bankstring and $namestring;
231

  
232
    my @bankwords = grep(/^\w+$/, split(/\b/,$bankstring));
233

  
234
    my $match = 0;
235
    foreach my $bankword ( @bankwords ) {
236
        # only try to match strings with more than 2 characters
237
        next unless length($bankword)>2; 
238
        if ( $namestring =~ /\b$bankword\b/i ) {
239
            $match++;
240
        };
241
    };
242
    return $match;
243
};
244

  
245
sub action_assign_invoice {
246
  my ($self) = @_;
247

  
248
  $self->{transaction} = SL::DB::Manager::BankTransaction->find_by(id => $::form->{bt_id});
249

  
250
  $self->render('bank_transactions/assign_invoice', { layout  => 0 },
251
                title      => t8('Assign invoice'),);
252
}
253

  
254
sub action_create_invoice {
255
  my ($self) = @_;
256
  my %myconfig = %main::myconfig;
257

  
258
  $self->{transaction} = SL::DB::Manager::BankTransaction->find_by(id => $::form->{bt_id});
259
  my $vendor_of_transaction = SL::DB::Manager::Vendor->find_by(account_number => $self->{transaction}->{remote_account_number});
260

  
261
  my $drafts = SL::DB::Manager::Draft->get_all(where => [ module => 'ap'] , with_objects => 'employee');
262

  
263
  my @filtered_drafts;
264

  
265
  foreach my $draft ( @{ $drafts } ) {
266
    my $draft_as_object = YAML::Load($draft->form);
267
    my $vendor = SL::DB::Manager::Vendor->find_by(id => $draft_as_object->{vendor_id});
268
    $draft->{vendor} = $vendor->name;
269
    $draft->{vendor_id} = $vendor->id;
270
    push @filtered_drafts, $draft;
271
  }
272

  
273
  #Filter drafts
274
  @filtered_drafts = grep { $_->{vendor_id} == $vendor_of_transaction->id } @filtered_drafts if $vendor_of_transaction;
275

  
276
  my $all_vendors = SL::DB::Manager::Vendor->get_all();
277

  
278
  $self->render('bank_transactions/create_invoice', { layout  => 0 },
279
      title      => t8('Create invoice'),
280
      DRAFTS     => \@filtered_drafts,
281
      vendor_id  => $vendor_of_transaction ? $vendor_of_transaction->id : undef,
282
      vendor_name => $vendor_of_transaction ? $vendor_of_transaction->name : undef,
283
      ALL_VENDORS => $all_vendors,
284
      limit      => $myconfig{vclimit},
285
      callback   => $self->url_for(action                => 'list',
286
                                   'filter.bank_account' => $::form->{filter}->{bank_account},
287
                                   'filter.todate'       => $::form->{filter}->{todate},
288
                                   'filter.fromdate'     => $::form->{filter}->{fromdate}),
289
      );
290
}
291

  
292
sub action_filter_drafts {
293
  my ($self) = @_;
294

  
295
  $self->{transaction} = SL::DB::Manager::BankTransaction->find_by(id => $::form->{bt_id});
296
  my $vendor_of_transaction = SL::DB::Manager::Vendor->find_by(account_number => $self->{transaction}->{remote_account_number});
297

  
298
  my $drafts = SL::DB::Manager::Draft->get_all(with_objects => 'employee');
299

  
300
  my @filtered_drafts;
301

  
302
  foreach my $draft ( @{ $drafts } ) {
303
    my $draft_as_object = YAML::Load($draft->form);
304
    my $vendor = SL::DB::Manager::Vendor->find_by(id => $draft_as_object->{vendor_id});
305
    $draft->{vendor} = $vendor->name;
306
    $draft->{vendor_id} = $vendor->id;
307
    push @filtered_drafts, $draft;
308
  }
309

  
310
  my $vendor_name = $::form->{vendor};
311
  my $vendor_id = $::form->{vendor_id};
312

  
313
  #Filter drafts
314
  @filtered_drafts = grep { $_->{vendor_id} == $vendor_id } @filtered_drafts if $vendor_id;
315
  @filtered_drafts = grep { $_->{vendor} =~ /$vendor_name/i } @filtered_drafts if $vendor_name;
316

  
317
  my $output  = $self->render(
318
      'bank_transactions/filter_drafts',
319
      { output      => 0 },
320
      DRAFTS => \@filtered_drafts,
321
      );
322

  
323
  my %result = ( count => 0, html => $output );
324

  
325
  $self->render(\to_json(\%result), { type => 'json', process => 0 });
326
}
327

  
328
sub action_ajax_add_list {
329
  my ($self) = @_;
330

  
331
  my @where_sale     = (amount => { ne => \'paid' });
332
  my @where_purchase = (amount => { ne => \'paid' });
333

  
334
  if ($::form->{invnumber}) {
335
    push @where_sale,     (invnumber => { ilike => '%' . $::form->{invnumber} . '%'});
336
    push @where_purchase, (invnumber => { ilike => '%' . $::form->{invnumber} . '%'});
337
  }
338

  
339
  if ($::form->{amount}) {
340
    push @where_sale,     (amount => $::form->parse_amount(\%::myconfig, $::form->{amount}));
341
    push @where_purchase, (amount => $::form->parse_amount(\%::myconfig, $::form->{amount}));
342
  }
343

  
344
  if ($::form->{vcnumber}) {
345
    push @where_sale,     ('customer.customernumber' => { ilike => '%' . $::form->{vcnumber} . '%'});
346
    push @where_purchase, ('vendor.vendornumber'     => { ilike => '%' . $::form->{vcnumber} . '%'});
347
  }
348

  
349
  if ($::form->{vcname}) {
350
    push @where_sale,     ('customer.name' => { ilike => '%' . $::form->{vcname} . '%'});
351
    push @where_purchase, ('vendor.name'   => { ilike => '%' . $::form->{vcname} . '%'});
352
  }
353

  
354
  if ($::form->{transdatefrom}) {
355
    my $fromdate = $::locale->parse_date_to_object(\%::myconfig, $::form->{transdatefrom});
356
    push @where_sale,     ('transdate' => { ge => $fromdate});
357
    push @where_purchase, ('transdate' => { ge => $fromdate});
358
  }
359

  
360
  if ($::form->{transdateto}) {
361
    my $todate = $::locale->parse_date_to_object(\%::myconfig, $::form->{transdateto});
362
    $todate->add(days => 1);
363
    push @where_sale,     ('transdate' => { lt => $todate});
364
    push @where_purchase, ('transdate' => { lt => $todate});
365
  }
366

  
367
  my $all_open_ar_invoices = SL::DB::Manager::Invoice->get_all(where => \@where_sale, with_objects => 'customer');
368
  my $all_open_ap_invoices = SL::DB::Manager::PurchaseInvoice->get_all(where => \@where_purchase, with_objects => 'vendor');
369

  
370
  my @all_open_invoices;
371
  push @all_open_invoices, @{ $all_open_ar_invoices };
372
  push @all_open_invoices, @{ $all_open_ap_invoices };
373

  
374
  @all_open_invoices = sort { $a->id <=> $b->id } @all_open_invoices;
375
  #my $all_open_invoices = SL::DB::Manager::Invoice->get_all(where => \@where);
376

  
377
  my $output  = $self->render(
378
      'bank_transactions/add_list',
379
      { output      => 0 },
380
      INVOICES => \@all_open_invoices,
381
      );
382

  
383
  my %result = ( count => 0, html => $output );
384

  
385
  $self->render(\to_json(\%result), { type => 'json', process => 0 });
386
}
387

  
388
sub action_ajax_accept_invoices {
389
  my ($self) = @_;
390

  
391
  my @selected_invoices;
392
  foreach my $invoice_id (@{ $::form->{invoice_id} || [] }) {
393
    my $invoice_object = SL::DB::Manager::Invoice->find_by(id => $invoice_id);
394
    $invoice_object ||= SL::DB::Manager::PurchaseInvoice->find_by(id => $invoice_id);
395

  
396
    push @selected_invoices, $invoice_object;
397
  }
398

  
399
  $self->render('bank_transactions/invoices', { layout => 0 },
400
                INVOICES => \@selected_invoices,
401
                bt_id    => $::form->{bt_id} );
402
}
403

  
404
sub action_save_invoices {
405
  my ($self) = @_;
406

  
407
  my $invoice_hash = delete $::form->{invoice_ids};
408

  
409
  while ( my ($bt_id, $invoice_ids) = each(%$invoice_hash) ) {
410
    my $bank_transaction = SL::DB::Manager::BankTransaction->find_by(id => $bt_id);
411
    my $sign = $bank_transaction->amount < 0 ? -1 : 1;
412
    my $amount_of_transaction = $sign * $bank_transaction->amount;
413

  
414
    my @invoices;
415
    foreach my $invoice_id (@{ $invoice_ids }) {
416
      push @invoices, (SL::DB::Manager::Invoice->find_by(id => $invoice_id) || SL::DB::Manager::PurchaseInvoice->find_by(id => $invoice_id));
417
    }
418
    @invoices = sort { return 1 if ($a->is_sales and $a->amount > 0);
419
                          return 1 if (!$a->is_sales and $a->amount < 0);
420
                          return -1; } @invoices                if $bank_transaction->amount > 0;
421
    @invoices = sort { return -1 if ($a->is_sales and $a->amount > 0);
422
                       return -1 if (!$a->is_sales and $a->amount < 0);
423
                       return 1; } @invoices                    if $bank_transaction->amount < 0;
424

  
425
    foreach my $invoice (@invoices) {
426
      if ($amount_of_transaction == 0) {
427
        flash('warning',  $::locale->text('There are invoices which could not be payed by bank transaction #1 (Account number: #2, bank code: #3)!',
428
                                            $bank_transaction->purpose,
429
                                            $bank_transaction->remote_account_number,
430
                                            $bank_transaction->remote_bank_code));
431
        last;
432
      }
433
      #pay invoice or go to the next bank transaction if the amount is not sufficiently high
434
      if ($invoice->amount <= $amount_of_transaction) {
435
        $invoice->pay_invoice(chart_id => $bank_transaction->local_bank_account->chart_id, trans_id => $invoice->id, amount => $invoice->amount, transdate => $bank_transaction->transdate);
436
        if ($invoice->is_sales) {
437
          $amount_of_transaction -= $sign * $invoice->amount;
438
          $bank_transaction->invoice_amount($bank_transaction->invoice_amount + $invoice->amount);
439
        } else {
440
          $amount_of_transaction += $sign * $invoice->amount if (!$invoice->is_sales);
441
          $bank_transaction->invoice_amount($bank_transaction->invoice_amount - $invoice->amount);
442
        }
443
      } else {
444
        $invoice->pay_invoice(chart_id => $bank_transaction->local_bank_account->chart_id, trans_id => $invoice->id, amount => $amount_of_transaction, transdate => $bank_transaction->transdate);
445
        $bank_transaction->invoice_amount($bank_transaction->amount) if $invoice->is_sales;
446
        $bank_transaction->invoice_amount($bank_transaction->amount) if !$invoice->is_sales;
447
        $amount_of_transaction = 0;
448
      }
449

  
450
      #Record a link from the bank transaction to the invoice
451
      my @props = (
452
          from_table => 'bank_transactions',
453
          from_id    => $bt_id,
454
          to_table   => $invoice->is_sales ? 'ar' : 'ap',
455
          to_id      => $invoice->id,
456
          );
457

  
458
      my $existing = SL::DB::Manager::RecordLink->get_all(where => \@props, limit => 1)->[0];
459

  
460
      SL::DB::RecordLink->new(@props)->save if !$existing;
461
    }
462
    $bank_transaction->save;
463
  }
464

  
465
  $self->action_list();
466
}
467

  
468
sub action_save_proposals {
469
  my ($self) = @_;
470

  
471
  foreach my $bt_id (@{ $::form->{proposal_ids} }) {
472
    #mark bt as booked
473
    my $bt = SL::DB::Manager::BankTransaction->find_by(id => $bt_id);
474
    $bt->invoice_amount($bt->amount);
475
    $bt->save;
476

  
477
    #pay invoice
478
    my $arap = SL::DB::Manager::Invoice->find_by(id => $::form->{"proposed_invoice_$bt_id"});
479
    $arap    = SL::DB::Manager::PurchaseInvoice->find_by(id => $::form->{"proposed_invoice_$bt_id"}) if not defined $arap;
480
    $arap->pay_invoice(chart_id  => $bt->local_bank_account->chart_id,
481
                       trans_id  => $arap->id,
482
                       amount    => $arap->amount,
483
                       transdate => $bt->transdate);
484
    $arap->save;
485

  
486
    #create record link
487
    my @props = (
488
        from_table => 'bank_transactions',
489
        from_id    => $bt_id,
490
        to_table   => $arap->is_sales ? 'ar' : 'ap',
491
        to_id      => $arap->id,
492
        );
493

  
494
    my $existing = SL::DB::Manager::RecordLink->get_all(where => \@props, limit => 1)->[0];
495

  
496
    SL::DB::RecordLink->new(@props)->save if !$existing;
497
  }
498

  
499
  flash('ok', t8('#1 proposal(s) saved.', scalar @{ $::form->{proposal_ids} }));
500

  
501
  $self->action_list();
502
}
503

  
504
#
505
# filters
506
#
507

  
508
sub check_auth {
509
  $::auth->assert('bank_transaction');
510
}
511

  
512
#
513
# helpers
514
#
515

  
516
sub make_filter_summary {
517
  my ($self) = @_;
518

  
519
  my $filter = $::form->{filter} || {};
520
  my @filter_strings;
521

  
522
  my @filters = (
523
    [ $filter->{"transdate:date::ge"},  $::locale->text('Transdate') . " " . $::locale->text('From Date') ],
524
    [ $filter->{"transdate:date::le"},  $::locale->text('Transdate') . " " . $::locale->text('To Date')   ],
525
    [ $filter->{"valutadate:date::ge"}, $::locale->text('Valutadate') . " " . $::locale->text('From Date') ],
526
    [ $filter->{"valutadate:date::le"}, $::locale->text('Valutadate') . " " . $::locale->text('To Date')   ],
527
    [ $filter->{"amount:number"},       $::locale->text('Amount')                                           ],
528
    [ $filter->{"bank_account_id:integer"}, $::locale->text('Local bank account')                                           ],
529
  );
530

  
531
  for (@filters) {
532
    push @filter_strings, "$_->[1]: $_->[0]" if $_->[0];
533
  }
534

  
535
  $self->{filter_summary} = join ', ', @filter_strings;
536
}
537

  
538
sub prepare_report {
539
  my ($self)      = @_;
540

  
541
  my $callback    = $self->models->get_callback;
542

  
543
  my $report      = SL::ReportGenerator->new(\%::myconfig, $::form);
544
  $self->{report} = $report;
545

  
546
  my @columns     = qw(transdate valudate remote_name remote_account_number remote_bank_code amount invoice_amount invoices currency purpose local_account_number local_bank_code id);
547
  my @sortable    = qw(transdate valudate remote_name remote_account_number remote_bank_code amount                                  purpose local_account_number local_bank_code);
548

  
549
  my %column_defs = (
550
    transdate             => { sub => sub { $_[0]->transdate_as_date } },
551
    valutadate            => { sub => sub { $_[0]->valutadate_as_date } },
552
    remote_name           => { },
553
    remote_account_number => { },
554
    remote_bank_code      => { },
555
    amount                => { sub => sub { $_[0]->amount_as_number },
556
                               align => 'right' },
557
    invoice_amount        => { sub => sub { $_[0]->invoice_amount_as_number },
558
                               align => 'right' },
559
    invoices              => { sub => sub { $_[0]->linked_invoices } },
560
    currency              => { sub => sub { $_[0]->currency->name } },
561
    purpose               => { },
562
    local_account_number  => { sub => sub { $_[0]->local_bank_account->account_number } },
563
    local_bank_code       => { sub => sub { $_[0]->local_bank_account->bank_code } },
564
    id                    => {},
565
  );
566

  
567
  map { $column_defs{$_}->{text} ||= $::locale->text( $self->models->get_sort_spec->{$_}->{title} ) } keys %column_defs;
568

  
569
  $report->set_options(
570
    std_column_visibility => 1,
571
    controller_class      => 'BankTransaction',
572
    output_format         => 'HTML',
573
    top_info_text         => $::locale->text('Bank transactions'),
574
    title                 => $::locale->text('Bank transactions'),
575
    allow_pdf_export      => 1,
576
    allow_csv_export      => 1,
577
  );
578
  $report->set_columns(%column_defs);
579
  $report->set_column_order(@columns);
580
  $report->set_export_options(qw(list filter));
581
  $report->set_options_from_form;
582
  $self->models->disable_pagination if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
583
  $self->models->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable);
584

  
585
  my $bank_accounts = SL::DB::Manager::BankAccount->get_all();
586
  my $label_sub = sub { t8('#1 - Account number #2, bank code #3, #4', $_[0]->name, $_[0]->account_number, $_[0]->bank_code, $_[0]->bank )};
587

  
588
  $report->set_options(
589
    raw_top_info_text     => $self->render('bank_transactions/report_top',    { output => 0 }, BANK_ACCOUNTS => $bank_accounts, label_sub => $label_sub),
590
    raw_bottom_info_text  => $self->render('bank_transactions/report_bottom', { output => 0 }),
591
  );
592
}
593

  
594
sub init_models {
595
  my ($self) = @_;
596

  
597
  SL::Controller::Helper::GetModels->new(
598
    controller => $self,
599
    sorted => {
600
      _default => {
601
        by    => 'transdate',
602
        dir   => 1,
603
      },
604
      transdate             => t8('Transdate'),
605
      remote_name           => t8('Remote name'),
606
      amount                => t8('Amount'),
607
      invoice_amount        => t8('Assigned'),
608
      invoices              => t8('Linked invoices'),
609
      valutadate            => t8('Valutadate'),
610
      remote_account_number => t8('Remote account number'),
611
      remote_bank_code      => t8('Remote bank code'),
612
      currency              => t8('Currency'),
613
      purpose               => t8('Purpose'),
614
      local_account_number  => t8('Local account number'),
615
      local_bank_code       => t8('Local bank code'),
616
    },
617
    with_objects => [ 'local_bank_account', 'currency' ],
618
  );
619
}
620

  
621
1;
SL/Controller/CsvImport.pm
18 18
use SL::Controller::CsvImport::Project;
19 19
use SL::Controller::CsvImport::Order;
20 20
use SL::JSON;
21
use SL::Controller::CsvImport::BankTransaction;
21 22
use SL::BackgroundJob::CsvImport;
22 23
use SL::System::TaskServer;
23 24

  
......
268 269
            : $self->type eq 'inventories'       ? $::locale->text('CSV import: inventories')
269 270
            : $self->type eq 'projects'          ? $::locale->text('CSV import: projects')
270 271
            : $self->type eq 'orders'            ? $::locale->text('CSV import: orders')
272
            : $self->type eq 'bank_transactions' ? $::locale->text('CSV import: bank transactions')
273
            : $self->type eq 'mt940'             ? $::locale->text('CSV import: MT940')
271 274
            : die;
272 275

  
273 276
  if ($self->{type} eq 'customers_vendors' or $self->{type} eq 'orders'  ) {
......
289 292

  
290 293
  $self->profile_from_form;
291 294

  
292
  if ($::form->{file}) {
295

  
296
  if ( $::form->{file} && $::form->{FILENAME} =~ /\.940$/ ) {
297
    my $mt940_file = SL::SessionFile->new($::form->{FILENAME}, mode => '>');
298
    $mt940_file->fh->print($::form->{file});
299
    $mt940_file->fh->close;
300

  
301
    my $aqbin = '/usr/bin/aqbanking-cli';
302
    my $cmd = "$aqbin --cfgdir=\"users\" import --importer=\"swift\" --profile=\"SWIFT-MT940\" -f " . $mt940_file->file_name . " | $aqbin --cfgdir=\"users\" listtrans --exporter=\"csv\" --profile=\"AqMoney2\" ";
303
    my $converted_mt940;
304
    open(MT, "$cmd |");
305
    $converted_mt940 .=  '"transaction_id";"local_bank_code";"local_account_number";"remote_bank_code";"remote_account_number";"transdate";"valutadate";"amount";"currency";"remote_name";"remote_name_1";"purpose";"purpose1";"purpose2";"purpose3";"purpose4";"purpose5";"purpose6";"purpose7";"purpose8";"purpose9";"purpose10";"purpose11"' . "\n";
306
    my $headerline = <MT>;  # discard original header line
307
    while (<MT>) {
308
      $converted_mt940 .= $_;
309
    };
293 310
    my $file = SL::SessionFile->new($self->csv_file_name, mode => '>');
294
    $file->fh->print($::form->{file});
311
    $file->fh->print($converted_mt940);
295 312
    $file->fh->close;
313
  } elsif ($::form->{file}) {
314
      my $file = SL::SessionFile->new($self->csv_file_name, mode => '>');
315
      $file->fh->print($::form->{file});
316
      $file->fh->close;
296 317
  }
297 318

  
298 319
  my $file = SL::SessionFile->new($self->csv_file_name, mode => '<', encoding => $self->profile->get('charset'));
......
618 639
       : $self->{type} eq 'inventories'       ? SL::Controller::CsvImport::Inventory->new(@args)
619 640
       : $self->{type} eq 'projects'          ? SL::Controller::CsvImport::Project->new(@args)
620 641
       : $self->{type} eq 'orders'            ? SL::Controller::CsvImport::Order->new(@args)
642
       : $self->{type} eq 'bank_transactions' ? SL::Controller::CsvImport::BankTransaction->new(@args)
643
       : $self->{type} eq 'mt940'             ? SL::Controller::CsvImport::BankTransaction->new(@args)
621 644
       :                                        die "Program logic error";
622 645
}
623 646

  
SL/Controller/CsvImport/BankTransaction.pm
1
package SL::Controller::CsvImport::BankTransaction;
2

  
3
use strict;
4

  
5
use SL::Helper::Csv;
6
use SL::Controller::CsvImport::Helper::Consistency;
7
use SL::DB::BankTransaction;
8

  
9
use Data::Dumper;
10

  
11
use parent qw(SL::Controller::CsvImport::Base);
12

  
13
use Rose::Object::MakeMethods::Generic
14
(
15
 'scalar --get_set_init' => [ qw(table bank_accounts_by) ],
16
);
17

  
18
sub init_class {
19
  my ($self) = @_;
20
  $self->class('SL::DB::BankTransaction');
21
}
22

  
23
sub init_bank_accounts_by {
24
  my ($self) = @_;
25

  
26
  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $self->all_bank_accounts } } ) } qw(id account_number) };
27
}
28

  
29
sub check_objects {
30
  my ($self) = @_;
31

  
32
  $self->controller->track_progress(phase => 'building data', progress => 0);
33

  
34
  my $i;
35
  my $num_data = scalar @{ $self->controller->data };
36
  foreach my $entry (@{ $self->controller->data }) {
37
    $self->controller->track_progress(progress => $i/$num_data * 100) if $i % 100 == 0;
38

  
39
    $self->check_bank_account($entry);
40
    $self->check_currency($entry, take_default => 1);
41

  
42
    $self->join_purposes($entry);
43
    #TODO: adde checks für die Variablen
44
  } continue {
45
    $i++;
46
  }
47

  
48
  $self->add_cvar_raw_data_columns;
49
}
50

  
51
sub setup_displayable_columns {
52
  my ($self) = @_;
53

  
54
  $self->SUPER::setup_displayable_columns;
55

  
56
  $self->add_displayable_columns({ name => 'transaction_id',   description => $::locale->text('Transaction ID') },
57
                                 { name => 'local_bank_code',   description => $::locale->text('Own bank code') },
58
                                 { name => 'local_account_number',   description => $::locale->text('Own bank account number') },
59
                                 { name => 'local_bank_account_id',   description => $::locale->text('ID of own bank account') },
60
                                 { name => 'remote_bank_code',   description => $::locale->text('Bank code of the goal/source') },
61
                                 { name => 'remote_account_number',   description => $::locale->text('Account number of the goal/source') },
62
                                 { name => 'transdate',   description => $::locale->text('Date of transaction') },
63
                                 { name => 'valutadate',   description => $::locale->text('Valuta') },
64
                                 { name => 'amount',   description => $::locale->text('Amount') },
65
                                 { name => 'currency',   description => $::locale->text('Currency') },
66
                                 { name => 'currency_id',       description => $::locale->text('Currency (database ID)')          },
67
                                 { name => 'remote_name',   description => $::locale->text('Name of the goal/source') },
68
                                 { name => 'remote_name_1',   description => $::locale->text('Name of the goal/source') },
69
                                 { name => 'purpose',   description => $::locale->text('Purpose') },
70
                                );
71
}
72

  
73
sub check_bank_account {
74
  my ($self, $entry) = @_;
75

  
76
  my $object = $entry->{object};
77

  
78
  # Check whether or not local_bank_account ID is valid.
79
  if ($object->local_bank_account_id && !$self->bank_accounts_by->{id}->{ $object->local_bank_account_id }) {
80
    push @{ $entry->{errors} }, $::locale->text('Error: Invalid local bank account');
81
    return 0;
82
  }
83

  
84
  # Check whether or not local_bank_account ID, local_account_number and local_bank_code are consistent.
85
  if ($object->local_bank_account_id && $entry->{raw_data}->{local_account_number}) {
86
    my $bank_account = $self->bank_accounts_by->{id}->{ $object->local_bank_account_id };
87
    if ($bank_account->account_number ne $entry->{raw_data}->{local_account_number}) {
88
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid local bank account');
89
      return 0;
90
    }
91
    if ($entry->{raw_data}->{local_bank_code} && $entry->{raw_data}->{local_bank_code} ne $bank_account->bank_code) {
92
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid local bank account');
93
      return 0;
94
    }
95

  
96
  }
97

  
98
  # Map account information to ID if given.
99
  if (!$object->local_bank_account_id && $entry->{raw_data}->{local_account_number}) {
100
    my $bank_account = $self->bank_accounts_by->{account_number}->{ $entry->{raw_data}->{local_account_number} };
101
    if (!$bank_account) {
102
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid local bank account');
103
      return 0;
104
    }
105
    if ($entry->{raw_data}->{local_bank_code} && $entry->{raw_data}->{local_bank_code} ne $bank_account->bank_code) {
106
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid local bank account');
107
      return 0;
108
    }
109

  
110
    $object->local_bank_account_id($bank_account->id);
111
  }
112

  
113
  return $object->local_bank_account_id ? 1 : 0;
114
}
115

  
116
sub join_purposes {
117
  my ($self, $entry) = @_;
118

  
119
  my $object = $entry->{object};
120

  
121
  my $purpose = join('', $entry->{raw_data}->{purpose},
122
                        $entry->{raw_data}->{purpose1},
123
                        $entry->{raw_data}->{purpose2},
124
                        $entry->{raw_data}->{purpose3},
125
                        $entry->{raw_data}->{purpose4},
126
                        $entry->{raw_data}->{purpose5},
127
                        $entry->{raw_data}->{purpose6},
128
                        $entry->{raw_data}->{purpose7},
129
                        $entry->{raw_data}->{purpose8},
130
                        $entry->{raw_data}->{purpose9},
131
                        $entry->{raw_data}->{purpose10},
132
                        $entry->{raw_data}->{purpose11} );
133
  $object->purpose($purpose);
134
}
135

  
136
1;
SL/Controller/CsvImport/Base.pm
6 6
use List::MoreUtils qw(pairwise any);
7 7

  
8 8
use SL::Helper::Csv;
9

  
10
use SL::DB::BankAccount;
9 11
use SL::DB::Customer;
10 12
use SL::DB::Language;
11 13
use SL::DB::PaymentTerm;
......
19 21
use Rose::Object::MakeMethods::Generic
20 22
(
21 23
 scalar                  => [ qw(controller file csv test_run save_with_cascade) ],
22
 'scalar --get_set_init' => [ qw(profile displayable_columns existing_objects class manager_class cvar_columns all_cvar_configs all_languages payment_terms_by delivery_terms_by all_vc vc_by clone_methods) ],
24
 'scalar --get_set_init' => [ qw(profile displayable_columns existing_objects class manager_class cvar_columns all_cvar_configs all_languages payment_terms_by delivery_terms_by all_bank_accounts all_vc vc_by clone_methods) ],
23 25
);
24 26

  
25 27
sub run {
......
141 143
  return SL::DB::Manager::Language->get_all;
142 144
}
143 145

  
146
sub init_all_bank_accounts {
147
  my ($self) = @_;
148

  
149
  return SL::DB::Manager::BankAccount->get_all;
150
}
151

  
144 152
sub init_payment_terms_by {
145 153
  my ($self) = @_;
146 154

  
SL/Controller/Helper/GetModels/Sorted.pm
6 6
use Carp;
7 7
use List::MoreUtils qw(uniq);
8 8

  
9
use Data::Dumper;
10

  
9 11
use Rose::Object::MakeMethods::Generic (
10 12
  scalar => [ qw(by dir specs form_data) ],
11 13
  'scalar --get_set_init' => [ qw(form_params) ],
SL/Controller/Project.pm
22 22
use SL::Helper::Flash;
23 23
use SL::Locale::String;
24 24

  
25
use Data::Dumper;
26

  
25 27
use Rose::Object::MakeMethods::Generic
26 28
(
27 29
 scalar => [ qw(project linked_records) ],
......
41 43
  my %params;
42 44

  
43 45
  $params{CUSTOM_VARIABLES}  = CVar->get_configs(module => 'Projects');
46

  
44 47
  ($params{CUSTOM_VARIABLES_FILTER_CODE}, $params{CUSTOM_VARIABLES_INCLUSION_CODE})
45 48
    = CVar->render_search_options(variables      => $params{CUSTOM_VARIABLES},
46 49
                                  include_prefix => 'l_',
SL/Controller/Reconciliation.pm
1
package SL::Controller::Reconciliation;
2

  
3
use strict;
4

  
5
use parent qw(SL::Controller::Base);
6

  
7
use SL::Locale::String;
8
use SL::JSON;
9
use SL::Controller::Helper::ParseFilter;
10
use SL::Helper::Flash;
11

  
12
use SL::DB::BankTransaction;
13
use SL::DB::BankAccount;
14
use SL::DB::AccTransaction;
15
use SL::DB::ReconciliationLink;
16

  
17
use Rose::Object::MakeMethods::Generic (
18
  'scalar --get_set_init' => [ qw(cleared BANK_ACCOUNTS) ],
19
);
20

  
21
__PACKAGE__->run_before('check_auth');
22
__PACKAGE__->run_before('_bank_account');
23

  
24
#
25
# actions
26
#
27

  
28
sub action_search {
29
  my ($self) = @_;
30

  
31
  $self->render('reconciliation/search',
32
                 label_sub => sub { t8('#1 - Account number #2, bank code #3, #4',
33
                                        $_[0]->name,
34
                                        $_[0]->bank,
35
                                        $_[0]->account_number,
36
                                        $_[0]->bank_code) });
37
}
38

  
39
sub action_reconciliation {
40
  my ($self) = @_;
41

  
42
  $self->_get_linked_transactions;
43

  
44
  $self->_get_balances;
45

  
46
  $self->render('reconciliation/form',
47
                title => t8('Reconciliation'),
48
                label_sub => sub { t8('#1 - Account number #2, bank code #3, #4',
49
                                        $_[0]->name,
50
                                        $_[0]->bank,
51
                                        $_[0]->account_number,
52
                                        $_[0]->bank_code) });
53
}
54

  
55
sub action_load_overview {
56
  my ($self) = @_;
57

  
58
  $self->_get_proposals;
59

  
60
  $self->_get_linked_transactions;
61

  
62
  $self->_get_balances;
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff