G. Richardson [Wed, 9 Oct 2019 08:12:08 +0000 (10:12 +0200)]
Jahresabschluß - GLTransaction->post und Tests
Der YearEnd Controller nutzt nun GLTransaction->post, damit muß man die
acc_trans-Einträge nicht mehr von Hand zusammenbauen, und die Buchungen
passieren automatisch als Transaktion, die Buchungen werden validiert
und es wird ein Historieneintrag erstellt.
G. Richardson [Tue, 8 Oct 2019 13:50:19 +0000 (15:50 +0200)]
GLTransaction - Dialogbuchungen per Rose erstellen
neue Methoden in GLTransaction zum Erstellen von Dialogbuchungen
* post
* validate
* add_chart_booking
An einigen Stellen im Code werden Dialogbuchungen per Hand erstellt,
inkl. Steuern, das soll hiermit vereinheitlicht und vereinfacht
werden.
Acc_trans-Einträge können nun mit wenigen Parametern zu Dialogbuchungen
hinzugefügt werden, die Parameter orientieren sich dabei an den Werten,
wie sie auch an der Oberfläche eingegeben werden (Konto, Soll/Haben,
Steuer). Dabei werden einige der Werte aus der GLTransaction
automatisch übernommen.
Beim Buchen wird eine neue Transaktion gestartet, die Buchung wird
validiert und es wird ein Historieneintrag erstellt.
G. Richardson [Wed, 25 Sep 2019 16:51:58 +0000 (18:51 +0200)]
Jahresabschluß - YearEndTransactions neu implementiert
Der alte Jahresabschluß hatte eine Reihe von Schwächen, z.B. wurde nicht
zwischen Bestands- und Erfolgskonten unterschieden, und es wurde auch
kein Gewinn- oder Verlustvortrag gemacht. Der Anwender mußte selber
entscheiden, welche Konten abgeschlossen werden sollten.
* Saldenvortragskonto, sowie Gewinn- und Verlustvortragskonto müssen in
der Mandantenkonfiguration unter "Standardkonten" konfiguriert werden
* Es wird nicht mehr für jedes Konto eine Dialogbuchung erstellt,
sondern es werden alle Soll- und Habensalden zusammengefasst, und
diese in Summe gegen das Saldenvortragskonto gebucht
* Der Jahresabschluß lässt sich für einen bestimmten Zeitraum mehrmals
ausführen, falls später Buchungen für den Zeitraum hinzukommen.
G. Richardson [Mon, 30 Sep 2019 08:56:14 +0000 (10:56 +0200)]
AccountingPeriod - Methode an get_balance_starting_date übergeben
Für den Fall, daß man die Methode zur Ermittlung des Startdatums nicht
aus der Mandantenkonfiguration nehmen möchte, sondern diese selber
vorgeben, gibt es nun einen neuen Parameter, um die Methode selber zu
bestimmen. Der Default ist weiterhin der Wert aus der
Mandantenkonfiguration.
Moritz Bunkus [Tue, 24 Sep 2019 09:39:00 +0000 (11:39 +0200)]
CSV-Import-Bericht: beim Löschen nur verwaiste Profile löschen
Unter gewissen Umständen kann es passieren, dass es mehrere
`csv_import_reports` mit derselben `profile_id` gibt. In so einem Fall
darf nur dann versucht werden, die Einträge aus `csv_import_profiles`
zu löschen, wenn der letzte `csv_import_reports`-Eintrag gelöscht
wird, der auf auf dieses Profil verlinkt.
Moritz Bunkus [Tue, 3 Sep 2019 12:25:51 +0000 (14:25 +0200)]
BankTransaction: keine Leerzeichen am Ende vom Verwendungszweck einfügen
Wenn man mehrere Felder einfach mit `join(' ', @felder)` zusammenfügt,
so entstehen nun mal ein Haufen Leerzeichen, auch wenn alle Felder
selber leer sind. Das führt konkret beim Import von Banktransaktionen
dazu, dass das Verwendungszweck-Feld, das aus den Feldern
`purpose` (ohne Suffix) und `purpose1` bis `purpose13` gebildet wird,
sehr viele Leerzeichen am Ende oder in der Mitte hat, wenn die
Felder (größtenteils) nicht benutzt werden.
Löscht auch bei bestehenden Positionen in der Datenbank Leerzeichen am
Ende. Leerzeichen am Anfang oder in der Mitte werden hingegen nicht
angefasst.
Primärschlüssel sind zwei andere Spalten, und die Präsenz dieser
Nicht-Primärschlüssel-Spalte zusammen mit Typ »serial« macht Probleme,
weil Rose kein Default für solche Spalten kennt.
Moritz Bunkus [Fri, 30 Aug 2019 12:53:43 +0000 (14:53 +0200)]
DB-Upgrades für Hintergrundjobs von Perl auf SQL umgestellt
Rose-Models dürfen in DB-Upgrade-Scripten nicht verwendet werden, weil
die Perl-Strukturdaten (MetaSetup) in dem Moment schon auf dem neuen
Stand, die Datenbankstrukturen aber auf dem alten Stand sind. Daher
schlagen bei Unterschieden (z.B. eine Spalte soll später noch angelegt
werden, sie existiert aber im neuen MetaSetup schon) halt sämliche
Operationen fehl.
Moritz Bunkus [Mon, 26 Aug 2019 09:36:36 +0000 (11:36 +0200)]
SelfTest: Geschwindigkeitssteigerung durch »NOT EXISTS« anstelle von »NOT IN«
Nicht ganz frische PostgreSQL-Versionen (mindestens bis 9.6 inklusive)
optimieren »NOT IN«-mit-Subquery nicht automatisch und müssen daher
für jede Zeile des äußeren Selects einen linearen Scan auf die
Subquery-Tabelle machen.
Deutlich effektiver ist das in diesem Fall äquivalente »NOT
EXISTS«-mit-Subquery.
Beispiel: Namen aller Kunden, für die noch keine Rechnung geschrieben
wurde. Falsch mit »NOT IN«:
SELECT name
FROM customer
WHERE id NOT IN (
SELECT customer_id
FROM ar
);
Besser und semantisch äquivalent:
SELECT name
FROM customer
WHERE NOT EXISTS (
SELECT customer_id
FROM ar
WHERE customer_id = customer.id
);
Geschwindigkeitssteigerung ist auch ein Euphemismus. Vor der Änderung
war der Selftest bei der LINET-Produktivdatenbank nicht in der Lage,
seine Tests innerhalb von drei Tagen auszuführen. Nach der Änderung
dauert der Selftest weniger als eine Minute.
Neure PostgreSQL-Versionen (z.B. v11) erkennen dieses Pattern
automatisch.
Moritz Bunkus [Mon, 29 Apr 2019 13:54:30 +0000 (15:54 +0200)]
Auth: Unterstützung für multiple Authentifizierungsbackends
Über den Parameter "module" kann man nun multiple Backends angeben,
die nacheinander versucht werden, bis ein Erfolg gemeldet wird oder
die Liste durchlaufen wurde.
Zusätzlich kann man LDAP-Module mehrfach angeben. Damit
unterschiedliche Konfigurationen für jede Modulinstanz benutzt werden
können, wurde die Syntax erweitert: für "LDAP:Config-Abschnitts-Name"
wird "[authentication/Config-Abschnitts-Name]" benutzt. Zwecks
Rückwärtskompatibilität sucht "LDAP" ohne Angabe eines Namens nach dem
bisher auch verwendeten Abschnitt "[authentication/ldap]".
Nützlich ist das Ganze z.B., um einen LDAP-Fallback-Server angeben zu
können, der benutzt wird, wenn der Hauptserver nicht erreichbar sein
sollte.
Moritz Bunkus [Mon, 29 Apr 2019 13:32:22 +0000 (15:32 +0200)]
Auth: mini_error gefixt
$::auth->mini_error wird potenziell zu einem Zeitpunkt aufgerufen, an
dem es die Instanzen von $::form und $::request noch nicht gibt. Da
hier wirklich nur die Bare-Bones-Ausgabe der Fehlermeldung benötigt
wird, machen wir für den Fall manuell ein CGI-Objekt auf.
Moritz Bunkus [Fri, 23 Aug 2019 09:17:39 +0000 (11:17 +0200)]
Task-Server auf unterschiedlichen Maschinen laufen lassen können
Jede Task-Server-Instanz und jeder Hintergrundjob haben nun ein neues
Attribute »node_id«. Darüber kann gesteuert werden, dass bestimmte
Jobs nur von einer bestimmten Instanz ausgeführt werden.
Die »node_id« eines neu angelegten Jobs ist standardmäßig leer. Das
bedeutet, dass ein Job von einer beliebigen Task-Server-Instanz
ausgeführt werden kann.
Die »node_id« eines Task-Servers ist standardmäßig der Hostname (siehe
ausgabe von »perl -MSys::Hostname -le 'print hostname()'«), kann aber
in der Konfigurationsdatei überschrieben werden (»[task_server]« →
»node_id«).
Zusätzlich gibt es den Konfigurationsparameter »[task_server]« →
»only_run_tasks_for_this_node«. Ist dieser Parameter gesetzt, so führt
der Task-Server nur diejenigen Jobs aus, deren »node_id«-Feld mit der
»node_id« der Task-Server-Instanz übereinstimmt. Andernfalls werden
auch diejenigen Jobs ausgeführt, deren »node_id«-Feld leer ist.
Achtung: es findet momentan keinerlei Locking statt. Das bedeutet,
dass es für jede Datenbank nur eine Task-Server-Instanz geben darf,
bei der »only_run_tasks_for_this_node« nicht gesetzt ist. Für
Load-Balancing eignet sich das also bisher noch nicht.
Für jedes Aufwandskonto der Positionen im Lieferantenauftrag wird eine
Zeile in der Kreditorenbuchung erstellt. Gebucht wird standardmäßig
auf des entsprechende Aufwandskonto. In der Mandantenkonfiguration
kann unter Standardkonten ein Konto ausgewählt werden, auf das dann
alle Zeilen gebucht werden.
Die Steuern werden übernommen, sofern diese für das ausgewählte
Aufwandskonto gültig sind. Ansonsten wird die Default-Steuer für das
Aufwandskonto gesetzt.
Der Quellauftrag wird geschlossen, wenn der Betrag aller
Kreditorenbuchungen, die aus Workflows aus dem Quellauftrag entstanden
sind, gleich dem Betrag des Quellauftrags ist.
G. Richardson [Sat, 10 Aug 2019 15:00:10 +0000 (17:00 +0200)]
Part Controller - neuer Tab mit Lagerinformationen
* Übersicht über alle Lagerbestände, wo der Artikel überall gelagert ist
(Derzeit gibt es im Template Variabeln um Zwischensummen und
Nachkommastellen zu kontrollieren)
* Mini-Journal mit den letzten 10 Lagertransaktionen des Artikels
Diese Daten werden nur bei Bedarf geladen, also wenn der Benutzer auf
den neuen Tab "Lagerbewegungen/-bestände" klickt.
Außerdem gibt es Links zu diversen Lageraktionen (Einlagern, Umlagern,
Entnahme), wo der Artikel dann schon vorausgewählt ist.
G. Richardson [Wed, 20 Feb 2019 11:30:16 +0000 (12:30 +0100)]
Aggregatfunktion comma entfernt und Templates angepasst
"comma" war eine alte benutzerdefinierte Aggregatfunktion, die benutzt
wurde, um mehrere aggregierte Werte aus einem GROUP BY in einen
kommaseparierten String umzuwandeln.
Mittlerweile würde man das einfach mit array_agg und array_to_string machen:
array_to_string(array_agg(startdate), ', ') as startdate
Im Template wurden die ',' dann durch '<br>' ersetzt. Stattdessen werden
die Werte im Query nun als array_agg ausgegeben, und im Template wird
eine Schleife über das Arrayref gebildet.
selectall_array_query durch selectcol_array_query ersetzt.
Intern wird nun die DBI-Funktion selectcol_arrayref verwendet, anstatt
dies manuell per Schleife zu machen. Der Name selectall_array_query war
irreführend und der neue Name entspricht nun dem, was man erwartet.
G. Richardson [Thu, 14 Feb 2019 11:01:10 +0000 (12:01 +0100)]
Inventory Controller - Datenbankoptimierungen für mini_journal
Aus Datenbanksicht war das Inventory mini-journal eine Katastrophe.
Die trans_id Abfrage führte zu einem ersten Seq Scan auf der Tabelle inventory.
my $query = 'SELECT trans_id FROM inventory GROUP BY trans_id ORDER BY max(itime) DESC LIMIT 10';
Die Rose Manager Abfrage führte dann zu einem zweiten Seq Scan auf der Tabelle inventory:
$objs = SL::DB::Manager::Inventory->get_all(query => [ trans_id => \@ids ]) if @ids;
Das sind zwei Seq Scans auf eine Tabelle die in kivitendo recht groß werden
kann. Außerdem könnte in der Zwischenzeit ein neuer inventory-Eintrag
dazugekommen sein, der damit ignoriert würde.
Im Template wurde dann auf die Rose-Objekte von part, transfer_type, bin und
warehouse zugegriffen, und da diese noch nicht geladen wurden sorgt das im
Extremfall für 40 weitere Datenbankzugriffe.
Das ist alles schön kompakter perl Code, aber wie könnte man das aus
Datenbanksicht optimieren, also die Anzahl der Zugriffe verringern und nach
Möglichkeit Indexe benutzen?
Die Templatezugriffe können einfach durch ein with_objects verhindert werden:
with_objects => [ 'parts', 'trans_type', 'bin', 'bin.warehouse' ],
Statt einer separaten Abfrage für die trans_ids könnte man diese als eine
Unterabfrage in get_all einführen:
query => [ trans_id => [ \"$query" ] ]
Die get-all-Abfrage kann man aber noch weiter verbessern, indem man nach id
statt trans_id filtert, da es für id im Gegensatz zu trans_id schon einen Index
gibt. Das Ziel für die Unterabfrage sollte also sein, eine Liste von ids zu
bekommen, damit der Index benutzt wird und man sich den Seq Scan spart.
Das mini-Journal zeigt die letzten 10 Lagerbewegungen, die entweder
Einlagerungen, Auslagerungen oder Umlagerungen sein können. Im Fall von
Umlagerungen wären das 2 inventory-Einträge, ansonsten 1. Da wir nicht wissen,
wieviele Umlagerungen dabei sind, holen wir die letzten 20 Einträge, filtern
diese nach den letzten 10 trans_ids, und extrahieren daraus die inventory ids
(zwischen 10 und 20 ids). Die ursprüngliche Abfrage mit dem GROUP BY konnte
keinen index nutzen, da das ORDER BY auf max(itime) statt itime war. Durch das
"limit 20" werden zwar potentiell ein paar Zeilen zu viel geholt, dafür kann
man aber nun einen Index auf inventory(itime) setzen, der von der Abfrage auch
verwendet werden kann, und damit spart man sich auch den letzten Seq Scan auf
inventory.
create index if not exists inventory_itime_idx on inventory (itime);
G. Richardson [Tue, 22 Jan 2019 13:56:24 +0000 (14:56 +0100)]
Spalte taxnumber aus Tabelle tax entfernt
tax.taxnumber war ein redundanter Eintrag, und entsprach dem Wert von
chart.accno aus tax.chart_id.
Z.B. in SKR04 hatte Steuerschlüssel 3 (Umsatzsteuer 19%) die taxnumber
1776 und die chart_id 775 (chart mit id 775 ist das Konto 1776).
Ein Problem dabei ist, daß wenn man in den Konteneinstellungen die
Kontonummer von 1776 ändert, dies nicht automatisch in tax.taxnumber mit
aktualisiert wurde.
Im Code wurde taxnumber v.A. verwendet, um bei Belegen die Steuern zu
gruppieren, mit der taxnumber als Schlüssel.
taxnumber wurde nun also entfernt, und obwohl zum Gruppieren der Steuern
immer noch diese Kontonummer verwendet wird, wird diese Kontonummer
nicht mehr zum Suchen des entsprechenden Taxeintrags verwendet, sondern
die Suche passiert indirekt über die chart_id.
Das ganze System basiert derzeit darauf, daß es für jeden tax-Eintrag ein
eindeutiges Automatikkonto gibt, in der Praxis muß dies aber nicht der
Fall sein!