Merge branch 'master' of git@lx-office.linet-services.de:lx-office-erp
authorC. Braun <information@lx-office-hosting.de>
Fri, 13 May 2011 12:49:10 +0000 (14:49 +0200)
committerC. Braun <information@lx-office-hosting.de>
Fri, 13 May 2011 12:49:10 +0000 (14:49 +0200)
22 files changed:
SL/Auth.pm
SL/Controller/DebugMenu.pm [new file with mode: 0644]
SL/DBConnect.pm
SL/Dispatcher.pm
SL/IC.pm
SL/LXDebug.pm
SL/SEPA.pm
SL/Template/Plugin/HTMLFixes.pm
bin/mozilla/do.pl
bin/mozilla/ic.pl
bin/mozilla/kopf.pl
bin/mozilla/oe.pl
config/lx_office.conf.default
doc/UPGRADE
doc/changelog
doc/konfigurationsdatei.txt [new file with mode: 0644]
doc/programmierstilrichtlinien.txt
doc/wiederkehrende_rechnungen.txt [new file with mode: 0755]
scripts/console
scripts/installation_check.pl
templates/webpages/menu/header.html [new file with mode: 0644]
templates/webpages/oe/form_header.html

index 3b14def..fca3b8b 100644 (file)
@@ -598,52 +598,33 @@ sub _create_session_id {
 }
 
 sub create_or_refresh_session {
-  $main::lxdebug->enter_sub();
-
-  my $self = shift;
-
-  $session_id ||= $self->_create_session_id();
-
-  my ($form, $dbh, $query, $sth, $id);
-
-  $form  = $main::form;
-  $dbh   = $self->dbconnect();
-
-  $dbh->begin_work;
-  do_query($::form, $dbh, qq|LOCK auth.session_content|);
-
-  $query = qq|SELECT id FROM auth.session WHERE id = ?|;
-
-  ($id)  = selectrow_query($form, $dbh, $query, $session_id);
-
-  if ($id) {
-    do_query($form, $dbh, qq|UPDATE auth.session SET mtime = now() WHERE id = ?|, $session_id);
-
-  } else {
-    do_query($form, $dbh, qq|INSERT INTO auth.session (id, ip_address, mtime) VALUES (?, ?, now())|, $session_id, $ENV{REMOTE_ADDR});
-
-  }
-
-  $self->save_session($dbh);
-
-  $dbh->commit();
-
-  $main::lxdebug->leave_sub();
+  $session_id ||= shift->_create_session_id;
 }
 
 sub save_session {
+  $::lxdebug->enter_sub;
   my $self         = shift;
   my $provided_dbh = shift;
 
   my $dbh          = $provided_dbh || $self->dbconnect(1);
 
-  return unless $dbh;
+   $::lxdebug->leave_sub && return unless $dbh;
 
   $dbh->begin_work unless $provided_dbh;
 
   do_query($::form, $dbh, qq|LOCK auth.session_content|);
   do_query($::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
 
+  my $query = qq|SELECT id FROM auth.session WHERE id = ?|;
+
+  my ($id)  = selectrow_query($::form, $dbh, $query, $session_id);
+
+  if ($id) {
+    do_query($::form, $dbh, qq|UPDATE auth.session SET mtime = now() WHERE id = ?|, $session_id);
+  } else {
+    do_query($::form, $dbh, qq|INSERT INTO auth.session (id, ip_address, mtime) VALUES (?, ?, now())|, $session_id, $ENV{REMOTE_ADDR});
+  }
+
   if (%{ $self->{SESSION} }) {
     my $query = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value) VALUES (?, ?, ?)|;
     my $sth   = prepare_query($::form, $dbh, $query);
@@ -656,6 +637,7 @@ sub save_session {
   }
 
   $dbh->commit() unless $provided_dbh;
+  $::lxdebug->leave_sub;
 }
 
 sub set_session_value {
@@ -1128,17 +1110,13 @@ sub assert {
 }
 
 sub load_rights_for_user {
-  $main::lxdebug->enter_sub();
-
-  my $self  = shift;
-  my $login = shift;
-
-  my $form  = $main::form;
-  my $dbh   = $self->dbconnect();
+  $::lxdebug->enter_sub;
 
+  my ($self, $login) = @_;
+  my $dbh   = $self->dbconnect;
   my ($query, $sth, $row, $rights);
 
-  $rights = {};
+  $rights = { map { $rights->{$_} = 0 } all_rights() };
 
   $query =
     qq|SELECT gr."right", gr.granted
@@ -1149,16 +1127,14 @@ sub load_rights_for_user {
           LEFT JOIN auth."user" u ON (ug.user_id = u.id)
           WHERE u.login = ?)|;
 
-  $sth = prepare_execute_query($form, $dbh, $query, $login);
+  $sth = prepare_execute_query($::form, $dbh, $query, $login);
 
   while ($row = $sth->fetchrow_hashref()) {
     $rights->{$row->{right}} |= $row->{granted};
   }
   $sth->finish();
 
-  map({ $rights->{$_} = 0 unless (defined $rights->{$_}); } SL::Auth::all_rights());
-
-  $main::lxdebug->leave_sub();
+  $::lxdebug->leave_sub;
 
   return $rights;
 }
diff --git a/SL/Controller/DebugMenu.pm b/SL/Controller/DebugMenu.pm
new file mode 100644 (file)
index 0000000..d612d37
--- /dev/null
@@ -0,0 +1,24 @@
+package SL::Controller::DebugMenu;
+
+use strict;
+use parent qw(SL::Controller::Base);
+
+# safety
+__PACKAGE__->run_before(sub { die 'not allowed in config' unless $::lx_office_conf{debug}{show_debug_menu}; });
+
+sub action_reload {
+  my ($self, %params) = @_;
+
+  print $::cgi->redirect('kopf.pl');
+  exit;
+}
+
+sub action_toggle {
+  my ($self, %params) = @_;
+
+  $::lxdebug->level_by_name($::form->{level}, !$::lxdebug->level_by_name($::form->{level}));
+  print $::cgi->redirect('kopf.pl');
+  return;
+}
+
+1;
index fbcf1d5..4e08450 100644 (file)
@@ -12,7 +12,7 @@ sub connect {
   require Log::Log4perl;
   require DBIx::Log4perl;
 
-  my $filename =  $LXDebug::file_name;
+  my $filename =  $::lxdebug->file;
   my $config   =  $::lx_office_conf{debug}->{dbix_log4perl_config};
   $config      =~ s/LXDEBUGFILE/${filename}/g;
 
index c1f31a4..e728976 100644 (file)
@@ -52,7 +52,6 @@ sub pre_request_checks {
       show_error('login/auth_db_unreachable');
     }
   }
-  $::auth->expire_sessions;
 }
 
 sub show_error {
@@ -237,6 +236,7 @@ sub handle_request {
   $::myconfig = ();
   Form::disconnect_standard_dbh;
   $::auth->expire_session_keys->save_session;
+  $::auth->expire_sessions;
   $::auth->reset;
 
   $::lxdebug->end_request;
index b6007d9..cf7c99c 100644 (file)
--- a/SL/IC.pm
+++ b/SL/IC.pm
@@ -502,7 +502,7 @@ sub save {
       if (($form->{"make_$i"}) || ($form->{"model_$i"})) {
         #hli
         $value = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
-        if ($value == $form->{"old_lastcost_$i"})
+        if ($value == $form->parse_amount($myconfig, $form->{"old_lastcost_$i"}))
         {
             if ($form->{"lastupdate_$i"} eq "") {
                 $lastupdate = 'now()';
@@ -1663,7 +1663,7 @@ sub prepare_parts_for_printing {
   }
 
   my $placeholders = join ', ', ('?') x scalar(@part_ids);
-  my $query        = qq|SELECT mm.parts_id, mm.model, v.name AS make
+  my $query        = qq|SELECT mm.parts_id, mm.model, mm.lastcost, v.name AS make
                         FROM makemodel mm
                         LEFT JOIN vendor v ON (mm.make = v.id)
                         WHERE mm.parts_id IN ($placeholders)|;
index b243724..4e77907 100644 (file)
@@ -292,4 +292,24 @@ sub want_request_timer {
   $global_level & REQUEST_TIMER;
 }
 
+sub file {
+  @_ == 2 ? $_[0]->{file} = $_[1] : $_[0]->{file};
+}
+
+sub _by_name {
+  my ($self, $level) = @_;
+  my $meth = $self->can(uc $level);
+  die 'unknown level' unless $meth;
+  $meth->();
+}
+
+sub level_by_name {
+  my ($self, $level, $val) = @_;
+  if (@_ == 3) {
+    $global_level |=  $self->_by_name($level) if  $val;
+    $global_level &= ~$self->_by_name($level) if !$val;
+  }
+  return $global_level & $self->_by_name($level);
+}
+
 1;
index 9a29783..32e763c 100644 (file)
@@ -21,7 +21,7 @@ sub retrieve_open_invoices {
 
   my $query =
     qq|
-       SELECT ${arap}.id, ${arap}.invnumber, ${arap}.${vc}_id, ${arap}.amount AS invoice_amount, ${arap}.invoice,
+       SELECT ${arap}.id, ${arap}.invnumber, ${arap}.${vc}_id as vc_id, ${arap}.amount AS invoice_amount, ${arap}.invoice,
          vc.name AS vcname, vc.language_id, ${arap}.duedate as duedate,
 
          COALESCE(vc.iban, '') <> '' AND COALESCE(vc.bic, '') <> '' AS vc_bank_info_ok,
index 3f328a8..32d3c56 100644 (file)
@@ -17,7 +17,7 @@ use Encode;
 # creating invalid UTF-8 characters upon URL-unescaping.
 
 # The only addition is the "Encode::encode()" line.
-
+no warnings 'redefine';
 sub url {
     my ($self, $text) = @_;
     return undef unless defined $text;
index 10fdf22..1b7a109 100644 (file)
@@ -337,7 +337,19 @@ sub update_delivery_order {
   $payment_id = $form->{payment_id} if $form->{payment_id};
 
   check_name($form->{vc});
-
+  $form->{discount} =  $form->{"$form->{vc}_discount"} if defined $form->{"$form->{vc}_discount"};
+  # Problem: Wenn man ohne Erneuern einen Kunden/Lieferanten
+  # wechselt, wird der entsprechende Kunden/ Lieferantenrabatt
+  # nicht übernommen. Grundproblem: In Commit 82574e78
+  # hab ich aus discount customer_discount und vendor_discount
+  # gemacht und entsprechend an den Oberflächen richtig hin-
+  # geschoben. Die damals bessere Lösung wäre gewesen:
+  # In den Templates nur die hidden für form-discount wieder ein-
+  # setzen dann wäre die Verrenkung jetzt nicht notwendig.
+  # TODO: Ggf. Bugfix 1284, 1575 und 817 wieder zusammenführen
+  # Testfälle: Kunden mit Rabatt 0 -> Rabatt 20 i.O.
+  #            Kunde mit Rabatt 20 -> Rabatt 0  i.O.
+  #            Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
   $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
 
   # for pricegroups
index e561782..13e0a5a 100644 (file)
@@ -1703,6 +1703,9 @@ sub update {
   # parse pricegroups. and no, don't rely on check_form for this...
   map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
 
+  # same for lastcosts
+  map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
+
   if ($form->{item} eq "assembly") {
     my $i = $form->{assembly_rows};
 
index ed11e52..0228566 100644 (file)
 #!/usr/bin/perl
-#
-
-#$| = 1;
-
-#use CGI::Carp qw(fatalsToBrowser);
 
 use strict;
+use DateTime;
 
 sub run {
   my $session_result = shift;
-  %::myconfig = $::auth->read_user($::form->{login})  if  $::form->{login};
-  $::locale   = Locale->new($::myconfig{countrycode}) if $::myconfig{countrycode};
-
-my $form     = $main::form;
-my $locale   = $main::locale;
-
-$form->header;
-my $paramstring = $ENV{"QUERY_STRING"};
-my @felder = split "&", $paramstring;
-my ($name, $wert);
-foreach (@felder) {
-  ($name, $wert) = split "=", $_;
-  $wert =~ tr/+/ /;
-  $name = $wert;
-}
-my $login =
-    "". $locale->text('User') . ": " . $form->{login}
-  . "  [<a href=\"login.pl?action=logout\" target=\"_top\" title=\"". $locale->text('Logout now') . "\">"
-  . $locale->text('Logout')
-  . "</a>] ";
-my ($Sekunden, $Minuten,   $Stunden,   $Monatstag, $Monat,
-    $Jahr,     $Wochentag, $Jahrestag, $Sommerzeit)
-  = localtime(time);
-my $CTIME_String = localtime(time);
-$Monat     += 1;
-$Jahrestag += 1;
-$Monat     = $Monat < 10     ? $Monat     = "0" . $Monat     : $Monat;
-$Monatstag = $Monatstag < 10 ? $Monatstag = "0" . $Monatstag : $Monatstag;
-$Jahr += 1900;
-my @Wochentage = ("Sonntag",    "Montag",  "Dienstag", "Mittwoch",
-                  "Donnerstag", "Freitag", "Samstag");
-my @Monatsnamen = ("",       "Januar",    "Februar", "M&auml;rz",
-                   "April",  "Mai",       "Juni",    "Juli",
-                   "August", "September", "Oktober", "November",
-                   "Dezember");
-my $datum =
-    $Wochentage[$Wochentag] . ", der "
-  . $Monatstag . "."
-  . $Monat . "."
-  . $Jahr . " - ";
-
-#$zeit="<div id='Uhr'>".$Stunden.":".$Minuten.":".$Sekunden."</div>";
-my $zeit = "<div id='Uhr'>" . $Stunden . ":" . $Minuten . "</div>";
 
-print qq|
-<script type="text/javascript">
-<!--
-var clockid=new Array()
-var clockidoutside=new Array()
-var i_clock=-1
-var thistime= new Date()
-var hours= | . $Stunden . qq|;
-var minutes= | . $Minuten . qq|;
-var seconds= | . $Sekunden . qq|;
-if (eval(hours) <10) {hours="0"+hours}
-if (eval(minutes) < 10) {minutes="0"+minutes}
-if (seconds < 10) {seconds="0"+seconds}
-//var thistime = hours+":"+minutes+":"+seconds
-var thistime = hours+":"+minutes
-
-function writeclock() {
-  i_clock++
-  if (document.all \|\| document.getElementById \|\| document.layers) {
-    clockid[i_clock]="clock"+i_clock
-    document.write("<font family=arial size=2><span id='"+clockid[i_clock]+"' style='position:relative'>"+thistime+"</span></font>")
-  }
-}
-
-function clockon() {
-  thistime= new Date()
-  hours=thistime.getHours()
-  minutes=thistime.getMinutes()
-  seconds=thistime.getSeconds()
-  if (eval(hours) <10) {hours="0"+hours}
-  if (eval(minutes) < 10) {minutes="0"+minutes}
-  if (seconds < 10) {seconds="0"+seconds}
-  //thistime = hours+":"+minutes+":"+seconds
-  thistime = hours+":"+minutes
-
-  if (document.all) {
-    for (i=0;i<=clockid.length-1;i++) {
-      var thisclock=eval(clockid[i])
-      thisclock.innerHTML=thistime
-    }
-  }
-
-  if (document.getElementById) {
-    for (i=0;i<=clockid.length-1;i++) {
-      document.getElementById(clockid[i]).innerHTML=thistime
-    }
-  }
-  var timer=setTimeout("clockon()",60000)
-}
-window.onload=clockon
-//-->
-</script>
-|;
+  %::myconfig = $::auth->read_user($::form->{login})  if $::form->{login};
+  $::locale   = Locale->new($::myconfig{countrycode}) if $::myconfig{countrycode};
 
-print qq|
-<body bgcolor="#ffffff" text="#ffffff" link="#ffffff" vlink="#ffffff" alink="#ffffff" topmargin="0" leftmargin="0"  marginwidth="0" marginheight="0" style="background-image: url('image/fade.png'); background-repeat:repeat-x;">
-<script language='JavaScript' src='js/switchmenuframe.js'></script>
-<table border="0" width="100%" background="image/bg_titel.gif" cellpadding="0" cellspacing="0">
-  <tr>|;
-   if ( !($ENV{HTTP_USER_AGENT} =~ /links/i) ) {    # do not show the the links in case of "links" in HTTP_USER_AGENT
-      print qq|
-       <td  style="color:white; font-family:verdana,arial,sans-serif; font-size: 12px;" nowrap>
-       [<a href="JavaScript:Switch_Menu();" title="| . $locale->text('Switch Menu on / off') . qq|">| . $locale->text('Menu') . qq|</a>]
-       &nbsp;[<a HREF="login.pl" target="_blank" title="| . $locale->text('Open a further Lx-Office Window or Tab') . qq|">| . $locale->text('New Win/Tab') . qq|</a>]
-       &nbsp;[<a href="JavaScript:top.main_window.print();" title="| . $locale->text('Hardcopy') . qq|">| . $locale->text('Print') . qq|</a>]
-       &nbsp;[<a href="Javascript:top.main_window.history.back();" title="| . $locale->text('Go one step back') . qq|">| . $locale->text('Back') . qq|</a>]
-       &nbsp;[<a href="Javascript:top.main_window.history.forward();" title="| . $locale->text('Go one step forward') . qq|">| . $locale->text('Fwd') . qq|</a>]
-      </td>|;
-   }
-   print qq|
-    <td align="right" style="vertical-align:middle; color:white; font-family:verdana,arial,sans-serif; font-size: 12px;" nowrap>|
-  . $login . $datum . qq| <script>writeclock()</script>&nbsp;
-    </td>
-  </tr>
-</table>
-</body>
-</html>
-|;
+  $::form->header;
+  print $::form->parse_html_template('menu/header', {
+    now        => DateTime->now,
+    show_debug => $::lx_office_conf{debug}{show_debug_menu},
+    lxdebug    => $::lxdebug,
+    is_links   => ($ENV{HTTP_USER_AGENT} =~ /links/i),
+  });
 }
 
 1;
index 97fc1d2..2acf9fe 100644 (file)
@@ -238,14 +238,14 @@ sub order_links {
 
   $form->{"$form->{vc}_id"} ||= $form->{"all_$form->{vc}"}->[0]->{id} if $form->{"all_$form->{vc}"};
 
-  $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes));
+  $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes shipto_id));
   $form->{shipto} = 1 if $form->{id};
 
   # get customer / vendor
   IR->get_vendor(\%myconfig, \%$form)   if $form->{type} =~ /(purchase_order|request_quotation)/;
   IS->get_customer(\%myconfig, \%$form) if $form->{type} =~ /sales_(order|quotation)/;
 
-  $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id));
+  $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id shipto_id));
   $form->restore_vars(qw(taxincluded)) if $form->{id};
   $form->restore_vars(qw(salesman_id)) if $editing;
   $form->{forex}       = $form->{exchangerate};
index e78f2c3..5f7f272 100644 (file)
@@ -171,6 +171,10 @@ login =
 # location of history file for permanent history
 history_file = users/console_history
 
+# location of a separate log file for the console. everything normally written
+# to the lx-office log will be put here if triggered from the console
+log_file = /tmp/lxoffice_console_debug.log
+
 [debug]
 # Use DBIx::Log4perl for logging DBI calls. The string LXDEBUGFILE
 # will be replaced by the file name configured for $::lxdebug.
@@ -218,6 +222,9 @@ global_level = NONE
 # default.
 watch_form = 0
 
+# Include menu options for debugging in the HTML menu
+show_debug_menu = 0
+
 # If you want to debug the creation of LaTeX files then set this to 1.
 # That way the temporary LaTeX files created during PDF creation are
 # not removed and remain in the "users" directory.
index 03bb3b6..05683f0 100644 (file)
@@ -4,6 +4,25 @@ Wichtige Hinweise zum Upgrade von älteren Versionen
 
 ** BITTE FERTIGEN SIE VOR DEM UPGRADE EIN BACKUP IHRER DATENBANK(EN) AN! **
 
+Upgrade auf v2.6.3
+==================
+
+Mit Version 2.6.3. wurden die beiden Konfigurationsdateien authentication.pl
+und lx-erp.conf, sowie deren Varianten, abgeschafft. Stattdessen gibt es nun
+die Datei lx_office.conf, die aber erst neu angelegt werden muß. Als Vorlage
+dient hierfür die Datei lx_office.conf.default. Die entsprechenden Werte muß
+man selber neu konfigurieren, dies ist automatisiert zu fehleranfällig.
+
+Nach dem Upgrade kann man sich so lange nicht anmelden, bis lx_office.conf
+angelegt und authentication.pl und lx-erp.conf gelöscht oder verschoben wurden.
+
+Es gibt keine local-Variante der lx_office.conf, arbeitet man mit git sollte
+man lx_office.conf nicht einchecken.
+
+Eine etwas ausführlichere Beschreibung findet sich in der Datei:
+doc/konfigurationsdatei.txt
+
+
 Upgrade auf v2.6.2
 ==================
 
index 526158b..eefcecb 100644 (file)
@@ -1,4 +1,4 @@
-###################################
+####################################
 # Veränderungen von Lx-Office ERP #
 ###################################
 
@@ -6,6 +6,19 @@
 
 
 
+  Größere neue Features:
+
+  - Es ist jetzt möglich wiederkehrende Rechnungen zu definieren und zu
+    konfigurieren, dies geschieht im Formular für den Verkaufsauftrag.
+    Mögliche Periodizitäten sind monatlich/quartalsweise/jährlich. Die
+    Hauptkonfiguration hierzu findet sich in der lx_office.conf im Abschnitt
+    [periodic_invoices]
+
+  - Die Überprüfung, wann wiederkehrende Rechnungen erstellt werden sollen,
+    geschieht durch ein weiteres neues Feature, den Taskserver.  Konfiguriert
+    wird der Taskserver in der lx_office.conf im Abschnitt [task_server]. Der
+    Taskserver läuft als eigener daemon im System, ähnlich cron.
+
   Kleinere neue Features und Detailverbesserungen:
 
   - Beim Laden von Rechnungsentwürfen, das Fälligkeits- und Rechnungsdatum duch
@@ -28,6 +41,9 @@
   - Bugfix 1597: Report Lagerbestand wirft Fehler, wenn Artikelnummer ausgeblendet werden soll
   - Bugfix 1569: Zahlungseingang löschen bei Rechnung 
   - Bugfix 1632: Nach Installation 2.6.2-0 aus .deb funktioniert CSV-Import nicht
+  - Bugfix 1633: Stammdatenaufruf aus SEPA-Modul
+  - Bugfix 1575: Kundenrabatt geht verloren
+  - Bugfix 1647: Lieferanten-Einkaufspreise verlieren Nachkommastellen
 
 2011-02-02 - Release 2.6.2
 
diff --git a/doc/konfigurationsdatei.txt b/doc/konfigurationsdatei.txt
new file mode 100644 (file)
index 0000000..18a9e3c
--- /dev/null
@@ -0,0 +1,70 @@
+== Lx-Office Konfigurationsdatei ab Version 2.6.3 ==
+
+Seit Lx-Office 2.6.3. findet sich die Hauptconfigurationsdatei von Lx-Office in
+der Datei config/lx_office.conf.
+
+Diese muß bei der Erstinstallation von Lx-Office (oder Migration von älteren
+Versionen) angelegt werden, als Vorlage dient die Datei
+config/lx_office.conf.default. Die Datei lx_office.conf ist eine
+installationsspezifische Datei, enthält z.B. die wichtigsten Passwörter der
+lokalen Installation, und findet sich auch nicht im Git Repository.  Diese
+Konfigurationsdatei ist unabhängig von den verschiedenen Mandanten, die auf der
+Installation laufen.
+
+Die Konfigurationsdatei besteht aus mehreren Teilen, die entsprechend
+kommentiert sind:
+
+* authentication
+* authentication/database
+* authentication/ldap
+* system
+* features
+* paths
+* applications
+* environment
+* print_templates
+* task_server
+* periodic_invoices
+* console
+* debug
+
+Die üblicherweise wichtigsten Parameter, die am Anfang einzustellen oder zu
+kontrollieren sind, sind:
+
+[authentication]
+admin_password = geheim
+
+[authentication/database]
+host     = localhost
+port     = 5432
+db       = lxerp_auth
+user     = postgres
+password =
+
+[system]:
+* eur
+* dbcharset
+
+Nutzt man wiederkehrende Rechnungen kann man unter [periodic_invoices] den
+Login eines Benutzers angeben, der nach Erstellung der Rechnungen eine
+entsprechende E-Mail mit Informationen über die erstellten Rechnungen bekommt.
+
+Nutzt man den Taskserver für wiederkehrende Rechnungen, muß unter [task_server]
+ein Login eines Benutzers angegeben werden, mit dem sich der Taskserver an
+Lx-Office bei der Datenbank anmeldet, die dem Benutzer zugewiesen ist.
+
+Für Entwickler finden sich unter [debug] wichtige Funktionen, um die
+Fehlersuche zu erleichtern.
+
+
+== Versionen vor 2.6.3 ==
+
+In älteren Lx-Office Versionen gab es im Verzeichnis config die Dateien
+authentication.pl und lx-erp.conf, die jeweils Perl-Dateien waren. Es gab auch
+die Möglichkeit, eine lokale Version der Konfigurationsdatei zu erstellen
+(lx-erp-local.conf), dies ist ab 2.6.3 auch nicht mehr möglich/nötig.
+
+Beim Update von einer älteren Lx-Office Version auf 2.6.3 müssen die
+Einstellungen aus den alten Konfigurationsdateien manuell übertragen werden und
+die alten Konfigurationsdateien gelöscht oder verschoben werden, sonst kommt es
+zu einer Fehlermeldung.
index 67e63ec..4b8b201 100644 (file)
@@ -188,6 +188,6 @@ Einige der Regeln lassen sich automatisch überprüfen, andere nicht.
 
 14. Alle neuen Module müssen use strict verwenden.
 
-    $form, $auth, $locale, $lxdebug, %myconfig sowie der Inhalt der lx-erp.conf
-    werden derzeit aus dem main package importiert. Alle anderen Konstrukte
-    sollten lexikalisch lokal gehalten werden.
+    $form, $auth, $locale, $lxdebug und %myconfig werden derzeit aus dem main
+    package importiert. Alle anderen Konstrukte sollten lexikalisch lokal
+    gehalten werden.
diff --git a/doc/wiederkehrende_rechnungen.txt b/doc/wiederkehrende_rechnungen.txt
new file mode 100755 (executable)
index 0000000..fe9a07c
--- /dev/null
@@ -0,0 +1,78 @@
+Wiederkehrende Rechnungen werden als normale Aufträge definiert und\r
+konfiguriert, mit allen dazugehörigen Kunden- und Artikelangaben. Die\r
+konfigurierten Aufträge werden später automatisch in Rechnungen\r
+umgewandelt, so als ob man den Workflow benutzen würde, und auch die\r
+Auftragsnummer wird übernommen, sodass alle wiederkehrenden\r
+Rechnungen, die aus einem Auftrag erstellt wurden, später leicht\r
+wiederzufinden sind.\r
+\r
+Um einen Auftrag für wiederkehrende Rechnung zu konfigurieren, findet sich beim\r
+Bearbeiten des Auftrags ein neuer Knopf "Konfigurieren", der ein neues Fenster\r
+öffnet, in dem man die nötigen Parameter einstellen kann.  Hinter dem Knopf\r
+wird außerdem noch angezeigt, ob der Auftrag als wiederkehrende Rechnung\r
+konfiguriert ist oder nicht.\r
+\r
+Folgende Parameter kann man konfigurieren:\r
+\r
+* Status:\r
+ Bei aktiven Rechnungen wird automatisch eine Rechnung erstellt, wenn die\r
+ Periodizität erreicht ist (z.B. Anfang eines neuen Monats).\r
+\r
+ Ist ein Auftrag nicht aktiv, so werden für ihn auch keine wiederkehrenden\r
+ Rechnungen erzeugt. Stellt man nach längerer nicht-aktiver Zeit einen Auftrag\r
+ wieder auf aktiv, wird beim nächsten Periodenwechsel für alle Perioden, seit\r
+ der letzten aktiven Periode, jeweils eine Rechnung erstellt. Möchte man dies\r
+ verhindern, muss man vorher das Startdatum neu setzen.\r
+\r
+ Für gekündigte Aufträge werden nie mehr Rechnungen erstellt. Man kann sich\r
+ diese Aufträge aber gesondert in den Berichten anzeigen lassen.\r
+\r
+* Periodizität:\r
+  Ob monatlich, quartalsweise oder jährlich auf neue Rechnungen überprüft\r
+  werden soll. Für jede Periode seit dem Startdatum wird überprüft, ob für die\r
+  Periode (beginnend immer mit dem ersten Tag der Periode) schon eine Rechnung\r
+  erstellt wurde. Unter Umständen können bei einem Startdatum in der\r
+  Vergangenheit gleich mehrere Rechnungen erstellt werden.\r
+\r
+* Buchen auf:\r
+  Das Forderungskonto, in der Regel "Forderungen aus Lieferungen\r
+  und Leistungen". Das Gegenkonto ergibt sich aus den Buchungsgruppen der\r
+  betreffenden Waren.\r
+\r
+* Startdatum: ab welchem Datum auf Rechnungserstellung geprüft werden soll\r
+\r
+* Enddatum: ab wann keine Rechnungen mehr erstellt werden sollen.\r
+\r
+* Automatische Verlängerung um x Monate:\r
+  Sollen die wiederkehrenden Rechnungen bei Erreichen des\r
+  eingetragenen Enddatums weiterhin erstellt werden, so kann man hier\r
+  die Anzahl der Monate eingeben, um die das Enddatum automatisch nach\r
+  hinten geschoben wird.\r
+\r
+* Drucken:\r
+  Sind Drucker konfiguriert, so kann man sich die erstellten Rechnungen auch\r
+  gleich ausdrucken lassen.\r
+\r
+Unter Verkauf->Berichte->Aufträge finden sich zwei neue Checkboxen,\r
+"Wiederkehrende Rechnungen aktiv" und "Wiederkehrende Rechnungen inaktiv", mit\r
+denen man sich einen Überglick über die wiederkehrenden Rechnungen verschaffen\r
+kann.\r
+\r
+Die zeitliche und periodische Überprüfung, ob eine wiederkehrende\r
+Rechnung automatisch erstellt werden soll, geschieht durch den\r
+Taskserver, einen externen Dienst, der automatisch beim Start des\r
+Servers gestartet werden sollte.\r
+\r
+Nach Erstellung der Rechnungen kann eine E-Mail mit Informationen zu\r
+den erstellten Rechnungen verschickt werden. Konfiguriert wird dies in\r
+der Konfigurationsdatei config/lx_office.conf im Abschnitt\r
+[periodic_invoices].\r
+\r
+Will man im laufenden Monat eine monatlich wiederkehrende Rechnung inkl. des\r
+laufenden Monats starten, stellt man das Startdatum auf den Monatsanfang und\r
+wartet ein paar Minuten, bis der Taskserver den neu konfigurieren Auftrag\r
+erkennt und daraus eine Rechnung generiert hat. Alternativ setzt man das\r
+Startdatum auf den Monatsersten des Folgemonats und erstellt die erste Rechnung\r
+direkt manuell über den Workflow.\r
+\r
+\r
index 78b7305..8443321 100755 (executable)
@@ -18,6 +18,7 @@ SL::LxOfficeConf->read;
 
 my $login        = shift || $::lx_office_conf{console}{login}        || 'demo';
 my $history_file =          $::lx_office_conf{console}{history_file} || '/tmp/lxoffice_console_history.log'; # fallback if users is not writable
+my $debug_file   =          $::lx_office_conf{console}{log_file}     || '/tmp/lxoffice_console_debug.log';
 my $autorun      =          $::lx_office_conf{console}{autorun};
 
 # will be configed eventually
@@ -58,7 +59,7 @@ sub lxinit {
 
   package main;
 
-  $::lxdebug = LXDebug->new;
+  $::lxdebug = LXDebug->new(file => $debug_file);
   $::locale = Locale->new($::lx_office_conf{system}->{language});
   $::cgi    = CGI->new qw();
   $::form   = Form->new;
@@ -131,7 +132,7 @@ __END__
 
 =head1 NAME
 
-scripts/console - Lx Office Console
+scripts/console - Lx-Office console
 
 =head1 SYNOPSIS
 
index ce95015..9a28c03 100755 (executable)
@@ -29,7 +29,7 @@ sub check {
 
   my @source_texts = source_texts($module);
   local $" = $/;
-  print <<EOL;
+  print STDERR <<EOL;
 +-----------------------------------------------------------------------------+
   $module->{fullname} could not be loaded.
 
diff --git a/templates/webpages/menu/header.html b/templates/webpages/menu/header.html
new file mode 100644 (file)
index 0000000..359c940
--- /dev/null
@@ -0,0 +1,41 @@
+[%- USE T8 %]
+<style type='text/css'>
+  body { margin: 0; color: white; background: url('image/fade.png') repeat-x; }
+  a:link, a:visited, a:hover, a:active { color:white }
+  body > * { font-size: 12px; font-family:verdana,arial,sans-serif; vertical-align:middle; }
+  table { border:0; width: 100%; background: url('image/bg_titel.gif'); border-spacing:0; }
+  td { padding: 0 }
+</style>
+<script language='javascript' src='js/switchmenuframe.js'></script>
+<body>
+ <table>
+  <tr>
+[% UNLESS is_links %]
+   <td nowrap>
+    [<a href="JavaScript:Switch_Menu();" title="[% 'Switch Menu on / off' | $T8 %]">[% 'Menu' | $T8 %]</a>]
+    [<a HREF="login.pl" target="_blank" title="[% 'Open a further Lx-Office Window or Tab' | $T8 %]">[% 'New Win/Tab' | $T8 %]</a>]
+    [<a href="JavaScript:top.main_window.print();" title="[% 'Hardcopy' | $T8 %]">[% 'Print' | $T8 %]</a>]
+    [<a href="Javascript:top.main_window.history.back();" title="[% 'Go one step back' | $T8 %]">[% 'Back' | $T8 %]</a>]
+    [<a href="Javascript:top.main_window.history.forward();" title="[% 'Go one step forward' | $T8 %]">[% 'Fwd' | $T8 %]</a>]
+   </td>
+[%- END %]
+[% IF show_debug %]
+   <td align='center' nowrap>
+    Debug:
+    [<a href='controller.pl?action=DebugMenu/reload']>FCGI Reload</a>]
+    [<a href='controller.pl?action=DebugMenu/toggle&level=request_timer'>[% IF lxdebug.level_by_name('request_timer') %]<b>Timing</b>[% ELSE %]Timing[% END %]</a>]
+    [<a href='controller.pl?action=DebugMenu/toggle&level=trace'>[% IF lxdebug.level_by_name('trace') %]<b>Trace</b>[% ELSE %]Trace[% END %]</a>]
+    [<a href='controller.pl?action=DebugMenu/toggle&level=query'>[% IF lxdebug.level_by_name('query') %]<b>Query</b>[% ELSE %]Query[% END %]</a>]
+   </td>
+[%- END %]
+   <td align="right" nowrap>
+    [% 'User' | $T8 %]:
+    [% login %]
+    [<a href="login.pl?action=logout" target="_top" title="[% 'Logout now' | $T8 %]">[% 'Logout' | $T8 %]</a>]
+    [% now.to_lxoffice %] -
+    [% now.hms %]
+   </td>
+  </tr>
+ </table>
+</body>
+</html>
index ce916e6..a046761 100644 (file)
                   <tr>
                     <th align="right">[% 'Contact Person' | $T8 %]</th>
                     <td>
-                      [%- INCLUDE 'generic/multibox.html'
-                           name       = 'cp_id',
-                           style      = 'width: 250px',
-                           DATA       = ALL_CONTACTS,
-                           id_key     = 'cp_id',
-                           label_sub  = 'contact_labels',
-                           show_empty = 1 -%]
+                      [% L.select_tag('cp_id', L.options_for_select(ALL_CONTACTS, default=cp_id, value='cp_id', title_sub=\contact_labels, with_empty=1), style='width: 250px') %]
                     </td>
                   </tr>
 [%- END %]
                   <tr>
                     <th align="right">[% 'Shipping Address' | $T8 %]</th>
                     <td>
-                      [%- INCLUDE 'generic/multibox.html'
-                           name       = 'shipto_id',
-                           style      = 'width: 250px',
-                           DATA       = ALL_SHIPTO,
-                           id_key     = 'shipto_id',
-                           label_sub  = 'shipto_labels',
-                           show_empty = 1,
-                           onChange   = "document.getElementById('update_button').click();" -%]
+                      [% L.select_tag('shipto_id', L.options_for_select(ALL_SHIPTO, default=shipto_id, value='shipto_id', title_sub=\shipto_labels, with_empty=1), style='width: 250px', onChange="document.getElementById('update_button').click();") %]
                     </td>
                   </tr>
 [%- END %]
                   <tr>
                     <th align="right">[% 'Steuersatz' | $T8 %]</th>
                     <td>
-                      [%- INCLUDE 'generic/multibox.html'
-                           name       = 'taxzone_id',
-                           style      = 'width: 250px',
-                           DATA       = ALL_TAXZONES,
-                           id_key     = 'id',
-                           label_key  = 'description' -%]
+                      [% L.select_tag('taxzone_id', L.options_for_select(ALL_TAXZONES, default=taxzone_id, title='description'), style='width: 250px') %]
                     </td>
                   </tr>
 [%- IF ALL_DEPARTMENTS %]
                   <tr>
                     <th align="right" nowrap>[% 'Department' | $T8 %]</th>
                     <td colspan="3">
-                      [%- INCLUDE 'generic/multibox.html'
-                           name       = 'department_id',
-                           style      = 'width: 250px',
-                           DATA       = ALL_DEPARTMENTS,
-                           id_key     = 'id',
-                           label_sub  = 'department_labels',
-                           show_empty = 1 -%]
+                      [% L.select_tag('department_id', L.options_for_select(ALL_DEPARTMENTS, default=department_id, title_sub=\department_labels, with_empty=1), style='width:250px') %]
                     </td>
                   </tr>
 [%- END %]
                   <tr>
                     <th align="right">[% 'Employee' | $T8 %]</th>
                     <td>
-                      [%- INCLUDE 'generic/multibox.html'
-                           name       = 'employee_id',
-                           DATA       = ALL_EMPLOYEES,
-                           id_key     = 'id',
-                           label_sub  = 'sales_employee_labels' -%]
+                      [% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, default=employee_id, title_sub=\sales_employee_labels)) %]
                     </td>
                   </tr>
 [%- IF is_sales and ALL_SALESMEN.size %]
                   <tr>
                     <th align="right">[% 'Salesman' | $T8 %]</th>
                     <td>
-                      [%- INCLUDE 'generic/multibox.html'
-                           name       = 'salesman_id',
-                           default    = salesman_id ? salesman_id : employee_id,
-                           DATA       = ALL_SALESMEN,
-                           id_key     = 'id',
-                           label_sub  = 'sales_employee_labels' -%]
+                      [% L.select_tag('salesman_id', L.options_for_select(ALL_SALESMEN, default=(salesman_id ? salesman_id : employee_id), title_sub=\sales_employee_labels)) %]
                     </td>
                   </tr>
 [%- END %]
                   <tr>
                     <th width="70%" align="right" nowrap>[% 'Project Number' | $T8 %]</th>
                     <td>
-                      [%- INCLUDE 'generic/multibox.html'
-                           name       = 'globalproject_id',
-                           DATA       = ALL_PROJECTS,
-                           id_key     = 'id',
-                           label_key  = 'projectnumber',
-                           show_empty = 1,
-                           onChange   = "document.getElementById('update_button').click();" -%]
+                      [%- L.select_tag('globalproject_id', L.options_for_select(ALL_PROJECTS, title='projectnumber', default=globalproject_id, with_empty='1'), onChange="document.getElementById('update_button').click();") %]
                     </td>
                   </tr>
                 </table>