Merge branch 'master' of vc.linet-services.de:public/lx-office-erp
authorThomas Heck <theck@linet-services.de>
Tue, 28 Aug 2012 15:50:56 +0000 (17:50 +0200)
committerThomas Heck <theck@linet-services.de>
Tue, 28 Aug 2012 15:50:56 +0000 (17:50 +0200)
SL/Controller/BackgroundJob.pm [new file with mode: 0644]
SL/DB/BackgroundJob.pm
SL/DB/Manager/BackgroundJob.pm
SL/Template/Plugin/L.pm
config/kivitendo.conf.default
locale/de/all
menu.ini
templates/webpages/background_job/form.html [new file with mode: 0644]
templates/webpages/background_job/list.html [new file with mode: 0644]

diff --git a/SL/Controller/BackgroundJob.pm b/SL/Controller/BackgroundJob.pm
new file mode 100644 (file)
index 0000000..3176eb6
--- /dev/null
@@ -0,0 +1,140 @@
+package SL::Controller::BackgroundJob;
+
+use strict;
+
+use parent qw(SL::Controller::Base);
+
+use SL::DB::BackgroundJob;
+use SL::Helper::Flash;
+use SL::System::TaskServer;
+
+use Rose::Object::MakeMethods::Generic
+(
+  scalar                  => [ qw(background_job) ],
+  'scalar --get_set_init' => [ qw(task_server) ],
+);
+
+__PACKAGE__->run_before('check_auth');
+__PACKAGE__->run_before('check_task_server');
+__PACKAGE__->run_before('load_background_job', only => [ qw(edit update destroy execute) ]);
+
+#
+# actions
+#
+
+sub action_list {
+  my ($self) = @_;
+
+  $self->render('background_job/list',
+                title           => $::locale->text('Background jobs'),
+                BACKGROUND_JOBS => SL::DB::Manager::BackgroundJob->get_all_sorted);
+}
+
+sub action_new {
+  my ($self) = @_;
+
+  $self->background_job(SL::DB::BackgroundJob->new(cron_spec => '* * * * *'));
+  $self->render('background_job/form', title => $::locale->text('Create a new background job'));
+}
+
+sub action_edit {
+  my ($self) = @_;
+  $self->render('background_job/form', title => $::locale->text('Edit background job'));
+}
+
+sub action_create {
+  my ($self) = @_;
+
+  $self->background_job(SL::DB::BackgroundJob->new);
+  $self->create_or_update;
+}
+
+sub action_update {
+  my ($self) = @_;
+  $self->create_or_update;
+}
+
+sub action_destroy {
+  my ($self) = @_;
+
+  if (eval { $self->background_job->delete; 1; }) {
+    flash_later('info',  $::locale->text('The background job has been deleted.'));
+  } else {
+    flash_later('error', $::locale->text('The background job could not be destroyed.'));
+  }
+
+  $self->redirect_to(action => 'list');
+}
+
+sub action_save_and_execute {
+  my ($self) = @_;
+
+  return unless $self->create_or_update;
+  $self->action_execute;
+}
+
+sub action_execute {
+  my ($self) = @_;
+
+  my $history = $self->background_job->run;
+  if ($history->status eq 'success') {
+    flash_later('info', $::locale->text('The background job was executed successfully.'));
+  } else {
+    flash_later('error', $::locale->text('There was an error executing the background job.'));
+  }
+
+  $self->redirect_to(controller => 'BackgroundJobHistory', action => 'show', id => $history->id);
+}
+
+#
+# filters
+#
+
+sub check_auth {
+  $::auth->assert('admin');
+}
+
+#
+# helpers
+#
+
+sub create_or_update {
+  my $self   = shift;
+  my $return = shift;
+  my $is_new = !$self->background_job->id;
+  my $params = delete($::form->{background_job}) || { };
+
+  $self->background_job->assign_attributes(%{ $params });
+
+  my @errors = $self->background_job->validate;
+
+  if (@errors) {
+    flash('error', @errors);
+    $self->render('background_job/form', title => $is_new ? $::locale->text('Create a new background job') : $::locale->text('Edit background job'));
+    return;
+  }
+
+  $self->background_job->update_next_run_at;
+  $self->background_job->save;
+
+  flash_later('info', $is_new ? $::locale->text('The background job has been created.') : $::locale->text('The background job has been saved.'));
+  return if $return;
+
+  $self->redirect_to(action => 'list');
+}
+
+sub load_background_job {
+  my ($self) = @_;
+  $self->background_job(SL::DB::BackgroundJob->new(id => $::form->{id})->load);
+}
+
+sub init_task_server {
+  return SL::System::TaskServer->new;
+}
+
+sub check_task_server {
+  my ($self) = @_;
+  flash('warning', $::locale->text('The task server does not appear to be running.')) if !$self->task_server->is_running;
+}
+
+1;
index 743a6b5..ce1c399 100644 (file)
@@ -11,6 +11,16 @@ use SL::DB::Manager::BackgroundJob;
 use SL::DB::BackgroundJobHistory;
 
 use SL::BackgroundJob::Test;
+use SL::System::Process;
+
+__PACKAGE__->before_save('_before_save_set_next_run_at');
+
+sub _before_save_set_next_run_at {
+  my ($self) = @_;
+
+  $self->update_next_run_at if !$self->next_run_at;
+  return 1;
+}
 
 sub update_next_run_at {
   my $self = shift;
@@ -67,4 +77,24 @@ sub data_as_hash {
   return {};
 }
 
+sub validate {
+  my ($self) = @_;
+
+  my @errors;
+
+  push @errors, $::locale->text('The execution type is invalid.') if ($self->type         || '') !~ m/^(?: once | interval )$/x;
+
+  if (   (($self->package_name || '') !~ m/^ [A-Z][A-Za-z0-9]+ $/x)
+      || ! -f (SL::System::Process::exe_dir() . "/SL/BackgroundJob/" . $self->package_name . ".pm")) {
+    push @errors, $::locale->text('The package name is invalid.');
+  }
+
+  eval {
+    DateTime::Event::Cron->new_from_cron($self->cron_spec)->next(DateTime->now_local);
+    1;
+  } or push @errors, $::locale->text('The execution schedule is invalid.');
+
+  return @errors;
+}
+
 1;
index 96af863..1bb89ee 100644 (file)
@@ -5,10 +5,17 @@ use strict;
 use SL::DB::Helper::Manager;
 use base qw(SL::DB::Helper::Manager);
 
+use SL::DB::Helper::Sorted;
+
 sub object_class { 'SL::DB::BackgroundJob' }
 
 __PACKAGE__->make_manager_methods;
 
+sub _sort_spec {
+  return ( default => [ 'next_run_at', 1 ],
+           columns => { SIMPLE => 'ALL' } );
+}
+
 sub cleanup {
   my $class = shift;
   $class->delete_all(where => [ and => [ type => 'once', last_run_at => { lt => DateTime->now_local->subtract(days => '1') } ] ]);
index 0b4967d..43ac5d2 100644 (file)
@@ -252,6 +252,14 @@ sub options_for_select {
   return $code;
 }
 
+sub yes_no_tag {
+  my ($self, $name, $value) = splice @_, 0, 3;
+  my %attributes            = _hashify(@_);
+
+  my $options               = $self->options_for_select([ [ 1, $::locale->text('Yes') ], [ 0, $::locale->text('No') ] ], default => $value ? 1 : 0);
+  return $self->select_tag($name, $options, %attributes);
+}
+
 sub javascript {
   my ($self, $data) = @_;
   return $self->html_tag('script', $data, type => 'text/javascript');
@@ -352,7 +360,7 @@ sub vendor_selector {
                                               default      => $actual_vendor_id,
                                               title_sub    => sub { $_[0]->vendornumber . " : " . $_[0]->name },
                                               'with_empty' => 1);
-  
+
   return $self->select_tag($name, $options_str, %params);
 }
 
@@ -367,7 +375,7 @@ sub part_selector {
                                               default      => $actual_part_id,
                                               title_sub    => sub { $_[0]->partnumber . " : " . $_[0]->description },
                                               'with_empty' => 1);
-  
+
   return $self->select_tag($name, $options_str, %params);
 }
 
@@ -629,6 +637,13 @@ L</options_for_select> function. If C<$options_string> is an array
 reference then it will be passed to L</options_for_select>
 automatically.
 
+=item C<yes_no_tag $name, $value, %attributes>
+
+Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
+calling L<select_tag> and L<options_for_select>. C<$value> determines
+which entry is selected. The C<%attributes> are passed through to
+L<select_tag>.
+
 =item C<input_tag $name, $value, %attributes>
 
 Creates a HTML 'input type=text' tag named C<$name> with the value
index ff5fc3f..879dab0 100644 (file)
@@ -118,7 +118,7 @@ port = 25
 # determines whether or not encryption is used and which kind. For
 # 'tls' the module 'Net::SMTP::TLS' is required; for 'ssl'
 # 'Net::SMTP::TLS' is required and 'none' only uses 'Net::SMTP'.
-security = tls
+security = none
 # Authentication is only used if 'login' is set. You should only use
 # that with 'tls' or 'ssl' encryption.
 login =
index 28db532..932db27 100644 (file)
@@ -198,6 +198,7 @@ $self->{texts} = {
   'Are you sure you want to delete Order Number' => 'Soll der Auftrag mit folgender Nummer wirklich gelöscht werden:',
   'Are you sure you want to delete Quotation Number' => 'Sind Sie sicher, dass Angebotnummer gelöscht werden soll?',
   'Are you sure you want to delete Transaction' => 'Buchung wirklich löschen?',
+  'Are you sure you want to delete this background job?' => 'Sind Sie sicher, dass Sie diesen Hintergrund-Job löschen möchten?',
   'Are you sure you want to delete this business?' => 'Sind Sie sicher, dass Sie diesen Kunden-/Lieferantentyp löschen wollen?',
   'Are you sure you want to delete this department?' => 'Sind Sie sicher, dass Sie diese Abteilung löschen wollen?',
   'Are you sure you want to delete this payment term?' => 'Wollen Sie diese Zahlungsbedingungen wirklich löschen?',
@@ -243,6 +244,9 @@ $self->{texts} = {
   'Back'                        => 'Zurück',
   'Back to login'               => 'Zurück zur Anmeldung',
   'Back to the login page'      => 'Zurück zur Loginseite',
+  'Background job history'      => 'Verlauf der Hintergrund-Jobs',
+  'Background jobs'             => 'Hintergrund-Jobs',
+  'Background jobs and task server' => 'Hintergrund-Jobs und Task-Server',
   'Backup Dataset'              => 'Datenbank sichern',
   'Backup file'                 => 'Sicherungsdatei',
   'Backup of dataset'           => 'Sicherung der Datenbank',
@@ -449,6 +453,7 @@ $self->{texts} = {
   'Create Chart of Accounts'    => 'Zu verwendender Kontenplan',
   'Create Dataset'              => 'Neue Datenbank anlegen',
   'Create Date'                 => 'Erstelldatum',
+  'Create a new background job' => 'Einen neuen Hintergrund-Job anlegen',
   'Create a new business'       => 'Einen neuen Kunden-/Lieferantentyp erfassen',
   'Create a new department'     => 'Eine neue Abteilung erfassen',
   'Create a new payment term'   => 'Neue Zahlungsbedingungen anlegen',
@@ -472,6 +477,7 @@ $self->{texts} = {
   'Create customers and vendors. Edit all vendors. Edit only customers where salesman equals employee (login)' => 'Kunden und Lieferanten erfassen. Alle Lieferanten bearbeiten. Nur Kunden bearbeiten bei denen der Verkäufer gleich Bearbeiter (login) ist',
   'Create invoice?'             => 'Rechnung erstellen?',
   'Create new'                  => 'Neu erfassen',
+  'Create new background job'   => 'Neuen Hintergrund-Job anlegen',
   'Create new business'         => 'Kunden-/Lieferantentyp erfassen',
   'Create new department'       => 'Neue Abteilung erfassen',
   'Create new payment term'     => 'Neue Zahlungsbedingung anlegen',
@@ -535,6 +541,7 @@ $self->{texts} = {
   'DR'                          => 'S',
   'DUNNING STARTED'             => 'Mahnprozess gestartet',
   'DUNS-Nr'                     => 'DUNS-Nr.',
+  'Data'                        => 'Daten',
   'Database'                    => 'Datenbank',
   'Database Administration'     => 'Datenbankadministration',
   'Database Connection Test'    => 'Test der Datenbankverbindung',
@@ -736,6 +743,7 @@ $self->{texts} = {
   'Edit Vendor Invoice'         => 'Einkaufsrechnung bearbeiten',
   'Edit Warehouse'              => 'Lager bearbeiten',
   'Edit and delete a group'     => 'Gruppen bearbeiten und l&ouml;schen',
+  'Edit background job'         => 'Hintergrund-Job bearbeiten',
   'Edit bank account'           => 'Bankkonto bearbeiten',
   'Edit business'               => 'Kunden-/Lieferantentyp bearbeiten',
   'Edit custom variable'        => 'Benutzerdefinierte Variable bearbeiten',
@@ -805,10 +813,13 @@ $self->{texts} = {
   'Exchangerate Difference'     => 'Wechselkursunterschied',
   'Exchangerate for payment missing!' => 'Es fehlt der Wechselkurs für die Bezahlung!',
   'Exchangerate missing!'       => 'Es fehlt der Wechselkurs!',
+  'Execute now'                 => 'Jetzt ausführen',
   'Executed'                    => 'Ausgeführt',
   'Execution date'              => 'Ausführungsdatum',
   'Execution date from'         => 'Ausführungsdatum von',
   'Execution date to'           => 'Ausführungsdatum bis',
+  'Execution schedule'          => 'Ausführungszeitplan',
+  'Execution type'              => 'Ausführungsart',
   'Existing Buchungsgruppen'    => 'Existierende Buchungsgruppen',
   'Existing Datasets'           => 'Existierende Datenbanken',
   'Existing file on server'     => 'Auf dem Server existierende Datei',
@@ -1066,6 +1077,7 @@ $self->{texts} = {
   'Last Service Number'         => 'Letzte Dienstleistungsnr.',
   'Last Transaction'            => 'Letzte Buchung',
   'Last Vendor Number'          => 'Letzte Lieferantennummer',
+  'Last run at'                 => 'Zeitpunkt letzter Ausführung',
   'Lastcost (with X being a number)' => 'Einkaufspreis (X ist eine fortlaufende Zahl)',
   'Lead'                        => 'Kundenquelle',
   'Leave host and port field empty unless you want to make a remote connection.' => 'F&uuml;r lokale Verbindungen "Rechner" und "Port" freilassen.',
@@ -1090,6 +1102,7 @@ $self->{texts} = {
   'List Transactions'           => 'Buchungsliste',
   'List Warehouses'             => 'Lager anzeigen',
   'List bank accounts'          => 'Bankkonten anzeigen',
+  'List current background jobs' => 'Aktuelle Hintergrund-Jobs anzeigen',
   'List export'                 => 'Export anzeigen',
   'List of bank accounts'       => 'Liste der Bankkonten',
   'List of bank collections'    => 'Bankeinzugsliste',
@@ -1197,6 +1210,7 @@ $self->{texts} = {
   'New vendor'                  => 'Neuer Lieferant',
   'New window/tab'              => 'Neues Fenster/Tab',
   'Next Dunning Level'          => 'Nächste Mahnstufe',
+  'Next run at'                 => 'Zeitpunkt nächster Ausführung',
   'No'                          => 'Nein',
   'No %s was found matching the search parameters.' => 'Es wurde kein %s gefunden, auf den die Suchparameter zutreffen.',
   'No Company Address given'    => 'Keine Firmenadresse hinterlegt!',
@@ -1206,6 +1220,7 @@ $self->{texts} = {
   'No Dataset selected!'        => 'Keine Datenbank ausgewählt!',
   'No Vendor was found matching the search parameters.' => 'Zu dem Suchbegriff wurde kein Händler gefunden',
   'No action defined.'          => 'Keine Aktion definiert.',
+  'No background job has been created yet.' => 'Es wurden noch keine Hintergrund-Jobs angelegt.',
   'No backup file has been uploaded.' => 'Es wurde keine Sicherungsdatei hochgeladen.',
   'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' => 'Für diesen Kunden wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.',
   'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' => 'Für diesen Lieferanten wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.',
@@ -1317,6 +1332,7 @@ $self->{texts} = {
   'POSTED'                      => 'Gebucht',
   'POSTED AS NEW'               => 'Als neu gebucht',
   'PRINTED'                     => 'Gedruckt',
+  'Package name'                => 'Paketname',
   'Packing Lists'               => 'Lieferschein',
   'Page'                        => 'Seite',
   'Page #1/#2'                  => 'Seite #1/#2',
@@ -1595,6 +1611,7 @@ $self->{texts} = {
   'Save and Quotation'          => 'Speichern und Angebot',
   'Save and RFQ'                => 'Speichern und Lieferantenanfrage',
   'Save and close'              => 'Speichern und schlie&szlig;en',
+  'Save and execute'            => 'Speichern und ausführen',
   'Save as new'                 => 'als neu speichern',
   'Save draft'                  => 'Entwurf speichern',
   'Save profile'                => 'Profil speichern',
@@ -1742,6 +1759,7 @@ $self->{texts} = {
   'Tab'                         => 'Tabulator',
   'Target bank account'         => 'Zielkonto',
   'Target table'                => 'Zieltabelle',
+  'Task server control'         => 'Task-Server-Steuerung',
   'Tax'                         => 'Steuer',
   'Tax Consultant'              => 'Steuerberater/-in',
   'Tax Included'                => 'Steuer im Preis inbegriffen',
@@ -1803,6 +1821,10 @@ $self->{texts} = {
   'The assistant could not find anything wrong with #1. Maybe the problem has been solved in the meantime.' => 'Der Korrekturassistent konnte kein Problem bei #1 feststellen. Eventuell wurde das Problem in der Zwischenzeit bereits behoben.',
   'The authentication database is not reachable at the moment. Either it hasn\'t been set up yet or the database server might be down. Please contact your administrator.' => 'Die Authentifizierungs-Datenbank kann momentan nicht erreicht werden. Entweder wurde sie noch nicht eingerichtet, oder der Datenbankserver ist momentan nicht verfügbar. Bitte wenden Sie sich an Ihren Administrator.',
   'The available options depend on the varibale type:' => 'Die verf&uuml;gbaren Optionen h&auml;ngen vom Variablentypen ab:',
+  'The background job could not be destroyed.' => 'Der Hintergrund-Job konnte nicht gelöscht werden.',
+  'The background job has been created.' => 'Der Hintergrund-Job wurden angelegt.',
+  'The background job has been deleted.' => 'Der Hintergrund-Job wurde gelöscht.',
+  'The background job has been saved.' => 'Der Hintergrund-Job wurde gespeichert.',
   'The backup you upload here has to be a file created with &quot;pg_dump -o -Ft&quot;.' => 'Die von Ihnen hochzuladende Sicherungsdatei muss mit dem Programm und den Parametern &quot;pg_dump -o -Ft&quot; erstellt worden sein.',
   'The bank information must not be empty.' => 'Die Bankinformationen müssen vollständig ausgefüllt werden.',
   'The base unit does not exist or it is about to be deleted in row %d.' => 'Die Basiseinheit in Zeile %d existiert nicht oder soll gel&ouml;scht werden.',
@@ -1849,6 +1871,8 @@ $self->{texts} = {
   'The dunnings have been printed.' => 'Die Mahnung(en) wurden gedruckt.',
   'The email address is missing.' => 'Die Emailadresse fehlt.',
   'The end date is the last day for which invoices will possibly be created.' => 'Das Enddatum ist das letztmögliche Datum, an dem eine Rechnung erzeugt wird.',
+  'The execution schedule is invalid.' => 'Der Ausführungszeitplan ist ungültig.',
+  'The execution type is invalid.' => 'Der Ausführungstyp ist ungültig.',
   'The factor is missing in row %d.' => 'Der Faktor fehlt in Zeile %d.',
   'The factor is missing.'      => 'Der Faktor fehlt.',
   'The first reason is that kivitendo contained a bug which resulted in the wrong taxkeys being recorded for transactions in which two entries are posted for the same chart with different taxkeys.' => 'Der erste Grund war ein Fehler in kivitendo, der dazu führte, dass bei einer Transaktion, bei der zwei Buchungen mit unterschiedlichen Steuerschlüsseln auf dasselbe Konto durchgeführt wurden, die falschen Steuerschlüssel gespeichert wurden.',
@@ -1879,6 +1903,7 @@ $self->{texts} = {
   'The number of days for full payment' => 'Die Anzahl Tage, bis die Rechnung in voller Höhe bezahlt werden muss',
   'The old file containing the user information is still present (&quot;#1&quot;). Do you want to migrate these users into the database? If not then you will not be able to log in with any of the users present in the old file.' => 'Die alte Datei mit den Benutzerdaten existiert in dieser Installation noch immer (&quot;#1&quot;). Wollen Sie diese Benutzer in die neue Authentifizierungsdatenbank migrieren lassen? Falls nicht, so werden Sie sich nicht mehr mit den Benutzerdaten aus der alten Mitgliedsdatei anmelden können.',
   'The option field is empty.'  => 'Das Optionsfeld ist leer.',
+  'The package name is invalid.' => 'Der Paketname ist ungültig.',
   'The parts for this delivery order have already been transferred in.' => 'Die Artikel dieses Lieferscheins wurden bereits eingelagert.',
   'The parts for this delivery order have already been transferred out.' => 'Die Artikel dieses Lieferscheins wurden bereits ausgelagert.',
   'The parts have been removed.' => 'Die Waren wurden aus dem Lager entnommen.',
@@ -1918,6 +1943,7 @@ $self->{texts} = {
   'The subject is missing.'     => 'Der Betreff fehlt.',
   'The tables for user management and authentication do not exist. They will be created in the next step in the following database:' => 'Die Tabellen zum Speichern der Benutzerdaten und zur Benutzerauthentifizierung wurden nicht gefunden. Sie werden in der folgenden Datenbank angelegt:',
   'The tabulator character'     => 'Das Tabulator-Symbol',
+  'The task server does not appear to be running.' => 'Der Task-Server scheint nicht zu laufen.',
   'The third way is to download the module from the above mentioned URL and to install the module manually following the installations instructions contained in the source archive.' => 'Die dritte Variante besteht darin, das Paket von der oben genannten URL herunterzuladen und es manuell zu installieren. Beachten Sie dabei die im Paket enthaltenen Installationsanweisungen.',
   'The transaction is shown below in its current state.' => 'Nachfolgend wird angezeigt, wie die Buchung momentan aussieht.',
   'The unit has been saved.'    => 'Die Einheit wurde gespeichert.',
@@ -2276,6 +2302,7 @@ $self->{texts} = {
   'missing'                     => 'Fehlbestand',
   'month'                       => 'Monatliche Abgabe',
   'monthly'                     => 'monatlich',
+  'never'                       => 'niemals',
   'new Window'                  => 'neues Fenster',
   'next'                        => 'vor',
   'no'                          => 'nein',
@@ -2286,11 +2313,13 @@ $self->{texts} = {
   'not delivered'               => 'nicht geliefert',
   'not executed'                => 'nicht ausgeführt',
   'not logged in'               => 'nicht eingeloggt',
+  'not set'                     => 'nicht gesetzt',
   'not transferred in yet'      => 'noch nicht eingelagert',
   'not transferred out yet'     => 'noch nicht ausgelagert',
   'not yet executed'            => 'Noch nicht ausgeführt',
   'number'                      => 'Nummer',
   'oe.pl::search called with unknown type' => 'oe.pl::search mit unbekanntem Typ aufgerufen',
+  'one-time execution'          => 'einmalige Ausführung',
   'only OB Transactions'        => 'nur EB-Buchungen',
   'open'                        => 'Offen',
   'order'                       => 'Reihenfolge',
@@ -2318,6 +2347,7 @@ $self->{texts} = {
   'quotation_list'              => 'angebotsliste',
   'release_material'            => 'Materialausgabebe',
   'reorder item'                => 'Eintrag umsortieren',
+  'repeated execution'          => 'wiederholte Ausführung',
   'report_generator_dispatch_to is not defined.' => 'report_generator_dispatch_to ist nicht definiert.',
   'report_generator_nextsub is not defined.' => 'report_generator_nextsub ist nicht definiert.',
   'request_quotation'           => 'Angebotsanforderung',
index 0f8a3e4..ca7c972 100644 (file)
--- a/menu.ini
+++ b/menu.ini
@@ -797,6 +797,25 @@ submenu=1
 module=acctranscorrections.pl
 action=analyze_filter
 
+[System--Background jobs and task server]
+ACCESS=admin
+module=menu.pl
+action=acc_menu
+target=acc_menu
+submenu=1
+
+[System--Background jobs and task server--List current background jobs]
+module=controller.pl
+action=BackgroundJob/list
+
+[System--Background jobs and task server--Background job history]
+module=controller.pl
+action=BackgroundJobHistory/list
+
+[System--Background jobs and task server--Task server control]
+module=controller.pl
+action=TaskServer/show
+
 [System--Audit Control]
 module=am.pl
 action=audit_control
@@ -810,7 +829,6 @@ ACCESS=admin
 module=controller.pl
 action=Employee/list
 
-
 [Program]
 
 [Program--Preferences]
diff --git a/templates/webpages/background_job/form.html b/templates/webpages/background_job/form.html
new file mode 100644 (file)
index 0000000..c3d784c
--- /dev/null
@@ -0,0 +1,52 @@
+[% USE HTML %][% USE L %][% USE LxERP %]
+<body>
+
+ <form method="post" action="controller.pl">
+  <div class="listtop">[% FORM.title %]</div>
+
+[%- INCLUDE 'common/flash.html' %]
+
+  <table>
+   <tr>
+    <td>[%- LxERP.t8('Active') %]</td>
+    <td>[% L.yes_no_tag("background_job.active", SELF.background_job.active) %]</td>
+   </tr>
+
+   <tr>
+    <td>[%- LxERP.t8('Execution type') %]</td>
+    <td>[% L.select_tag("background_job.type", L.options_for_select([ [ 'once', LxERP.t8('one-time execution') ], [ 'interval', LxERP.t8('repeated execution') ] ],
+                                                                    'default' => SELF.background_job.type)) %]</td>
+   </tr>
+
+   <tr>
+    <td>[%- LxERP.t8('Package name') %]</td>
+    <td>[% L.input_tag("background_job.package_name", SELF.background_job.package_name, 'size' => 40) %]</td>
+   </tr>
+
+   <tr>
+    <td>[%- LxERP.t8('Execution schedule') %]</td>
+    <td>[% L.input_tag("background_job.cron_spec", SELF.background_job.cron_spec, 'size' => 40) %]</td>
+   </tr>
+
+   <tr>
+    <td>[%- LxERP.t8('Data') %]</td>
+    <td>[% L.textarea_tag("background_job.data", SELF.background_job.data, 'cols' => 80, 'rows' => 10) %]</td>
+   </tr>
+
+  </table>
+
+  <p>
+   [% L.hidden_tag("id", SELF.background_job.id) %]
+   [% L.hidden_tag("action", "BackgroundJob/dispatch") %]
+   [% L.submit_tag("action_" _  (SELF.background_job.id ? "update" : "create"), LxERP.t8('Save')) %]
+   [%- IF SELF.background_job.id %]
+    [% L.submit_tag("action_execute", LxERP.t8("Execute now")) %]
+    [% L.submit_tag("action_destroy", LxERP.t8("Delete"), "confirm", LxERP.t8("Are you sure you want to delete this background job?")) %]
+   [%- ELSE %]
+    [% L.submit_tag("action_save_and_execute", LxERP.t8("Save and execute")) %]
+   [%- END %]
+   <a href="[% SELF.url_for(action => 'list') %]">[%- LxERP.t8('Abort') %]</a>
+  </p>
+ </form>
+</body>
+</html>
diff --git a/templates/webpages/background_job/list.html b/templates/webpages/background_job/list.html
new file mode 100644 (file)
index 0000000..c0ef999
--- /dev/null
@@ -0,0 +1,75 @@
+[% USE HTML %][% USE L %][% USE LxERP %]
+
+<body>
+ <div class="listtop">[% FORM.title %]</div>
+
+[%- INCLUDE 'common/flash.html' %]
+
+ <form method="post" action="controller.pl">
+  [% IF !BACKGROUND_JOBS.size %]
+   <p>
+    [%- LxERP.t8('No background job has been created yet.') %]
+   </p>
+
+  [%- ELSE %]
+   <table id="background_job_list" width="100%">
+    <thead>
+    <tr class="listheading">
+     <th>[%- LxERP.t8('Execution type') %]</th>
+     <th>[%- LxERP.t8('Package name') %]</th>
+     <th>[%- LxERP.t8('Active') %]</th>
+     <th>[%- LxERP.t8('Execution schedule') %]</th>
+     <th>[%- LxERP.t8('Last run at') %]</th>
+     <th>[%- LxERP.t8('Next run at') %]</th>
+    </tr>
+    </thead>
+
+    <tbody>
+    [%- FOREACH background_job = BACKGROUND_JOBS %]
+    <tr class="listrow[% loop.count % 2 %]" id="background_job_id_[% background_job.id %]">
+     <td>
+      [%- IF background_job.type == 'once' %]
+       [%- LxERP.t8('one-time execution') %]
+      [%- ELSIF background_job.type == 'interval' %]
+       [%- LxERP.t8('repeated execution') %]
+      [% ELSE %]
+       [%- HTML.escape(background_job.type) %]
+      [%- END %]
+     <td>
+      <a href="[% SELF.url_for(action => 'edit', id => background_job.id) %]">
+       [%- HTML.escape(background_job.package_name) %]
+      </a>
+     </td>
+     <td align="right">[% IF background_job.active %][%- LxERP.t8('yes') %][% ELSE %][%- LxERP.t8('no') %][%- END %]</td>
+     <td>[%- HTML.escape(background_job.cron_spec) %]</td>
+     <td>
+      [%- IF background_job.last_run_at %]
+       [%- HTML.escape(background_job.last_run_at.to_lxoffice('precision' => 'second')) %]
+      [%- ELSE %]
+       [%- LxERP.t8('never') %]
+      [%- END %]
+     </td>
+
+     <td>
+      [%- IF background_job.next_run_at %]
+       [%- HTML.escape(background_job.next_run_at.to_lxoffice('precision' => 'second')) %]
+      [%- ELSE %]
+       [%- LxERP.t8('not set') %]
+      [%- END %]
+     </td>
+    </tr>
+    [%- END %]
+    </tbody>
+   </table>
+  [%- END %]
+
+  <hr size="3" noshade>
+
+  <p>
+   <a href="[% SELF.url_for(action => 'new') %]">[%- LxERP.t8('Create new background job') %]</a>
+   |
+   <a href="[% SELF.url_for(controller => 'TaskServer', action => 'show') %]">[%- LxERP.t8('Task server control') %]</a>
+  </p>
+ </form>
+</body>
+</html>