use SL::MoreCommon;
use SL::IC;
use SL::IO;
+use SL::TransNumber;
use Data::Dumper;
use strict;
my ($self, $myconfig, $form, $provided_dbh, $payments_only) = @_;
# connect to database, turn off autocommit
- my $dbh = $provided_dbh ? $provided_dbh : $form->dbconnect_noauto($myconfig);
+ my $dbh = $provided_dbh ? $provided_dbh : $form->get_standard_dbh;
my ($query, $sth, $null, $project_id, @values);
my $exchangerate = 0;
&reverse_invoice($dbh, $form);
} else {
+ my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{invnumber}, save => 1);
+ $form->{invnumber} = $trans_number->create_unique unless $trans_number->is_unique;
+
$query = qq|SELECT nextval('glid')|;
($form->{"id"}) = selectrow_query($form, $dbh, $query);
{ name => "FCGI", url => "http://search.cpan.org/~mstrout/" },
{ name => "List::MoreUtils", version => '0.21', url => "http://search.cpan.org/~vparseval/" },
{ name => "PDF::API2", version => '2.000', url => "http://search.cpan.org/~areibens/" },
+ { name => "Readonly", url => "http://search.cpan.org/~roode/" },
+ { name => "Rose::Object", url => "http://search.cpan.org/~jsiracusa/" },
+ { name => "Rose::DB", url => "http://search.cpan.org/~jsiracusa/" },
+ { name => "Rose::DB::Object", url => "http://search.cpan.org/~jsiracusa/" },
{ name => "Template", version => '2.18', url => "http://search.cpan.org/~abw/" },
{ name => "Text::CSV_XS", version => '0.23', url => "http://search.cpan.org/~hmbrand/" },
{ name => "Text::Iconv", version => '1.2', url => "http://search.cpan.org/~mpiotr/" },
--- /dev/null
+package SL::TransNumber;
+
+use strict;
+
+use parent qw(Rose::Object);
+
+use Carp;
+use List::MoreUtils qw(any none);
+use Readonly;
+use SL::DBUtils;
+
+use Rose::Object::MakeMethods::Generic
+(
+ scalar => [ qw(type id number save dbh dbh_provided) ],
+);
+
+# has 'type' => ( is => 'rw', required => 1 );
+# has 'id' => ( is => 'rw' );
+# has 'number' => ( is => 'rw' );
+# has 'save' => ( is => 'rw', default => 1 );
+# has 'dbh' => ( is => 'rw' );
+# has 'dbh_provided' => ( is => 'rw' );
+
+Readonly my @SUPPORTED_TYPES => qw(invoice credit_note customer vendor sales_delivery_order purchase_delivery_order sales_order purchase_order sales_quotation request_quotation);
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ croak "Invalid type " . $self->type if none { $_ eq $self->type } @SUPPORTED_TYPES;
+
+ $self->dbh_provided($self->dbh);
+ $self->dbh($::form->get_standard_dbh) if !$self->dbh;
+ $self->save(1) unless defined $self->save;
+
+ return $self;
+}
+
+sub _get_filters {
+ my $self = shift;
+
+ my $type = $self->type;
+ my %filters = ( where => '' );
+
+ if (any { $_ eq $type } qw(invoice credit_note)) {
+ $filters{trans_number} = "invnumber";
+ $filters{numberfield} = $type eq 'credit_note' ? "cnnumber" : "invnumber";
+ $filters{table} = "ar";
+
+ } elsif (any { $_ eq $type } qw(customer vendor)) {
+ $filters{trans_number} = "${type}number";
+ $filters{numberfield} = "${type}number";
+ $filters{table} = $type;
+
+ } elsif ($type =~ /_delivery_order$/) {
+ $filters{trans_number} = "donumber";
+ $filters{numberfield} = $type eq 'sales_delivery_order' ? "sdonumber" : "pdonumber";
+ $filters{table} = "delivery_orders";
+ $filters{where} = $type =~ /^sales/ ? '(customer_id IS NOT NULL)' : '(vendor_id IS NOT NULL)';
+
+ } elsif ($type =~ /_order$/) {
+ $filters{trans_number} = "ordnumber";
+ $filters{numberfield} = $type eq 'sales_order' ? "sonumber" : "ponumber";
+ $filters{table} = "oe";
+ $filters{where} = 'NOT COALESCE(quotation, FALSE)';
+ $filters{where} .= $type =~ /^sales/ ? ' AND (customer_id IS NOT NULL)' : ' AND (vendor_id IS NOT NULL)';
+
+ } else {
+ $filters{trans_number} = "quonumber";
+ $filters{numberfield} = $type eq 'sales_quotation' ? "sqnumber" : "rfqnumber";
+ $filters{table} = "oe";
+ $filters{where} = 'COALESCE(quotation, FALSE)';
+ $filters{where} .= $type =~ /^sales/ ? ' AND (customer_id IS NOT NULL)' : ' AND (vendor_id IS NOT NULL)';
+ }
+
+ return %filters;
+}
+
+sub is_unique {
+ my $self = shift;
+
+ return undef if !$self->number;
+
+ my %filters = $self->_get_filters();
+
+ my @where;
+ my @values = ($self->number);
+
+ push @where, $filters{where} if $filters{where};
+
+ if ($self->id) {
+ push @where, qq|id <> ?|;
+ push @values, conv_i($self->id);
+ }
+
+ my $where_str = @where ? join(' AND ', map { "($_)" } @where) : '';
+ my $query = <<SQL;
+ SELECT $filters{trans_number}
+ FROM $filters{table}
+ WHERE ($filters{trans_number} = ?)
+ $where_str
+ LIMIT 1
+SQL
+ my ($existing_number) = selectfirst_array_query($main::form, $self->dbh, $query, @values);
+
+ return $existing_number ? 0 : 1;
+}
+
+sub create_unique {
+ my $self = shift;
+
+ my $form = $main::form;
+ my %filters = $self->_get_filters();
+
+ do_query($form, $self->dbh, qq|LOCK TABLE defaults|);
+
+ my $where = $filters{where} ? ' WHERE ' . $filters{where} : '';
+ my $query = <<SQL;
+ SELECT DISTINCT $filters{trans_number}, 1 AS in_use
+ FROM $filters{table}
+ $where
+SQL
+
+ my %numbers_in_use = selectall_as_map($form, $self->dbh, $query, $filters{trans_number}, 'in_use');
+
+ my ($number) = selectfirst_array_query($form, $self->dbh, qq|SELECT $filters{numberfield} FROM defaults|);
+ $number ||= '';
+
+ do {
+ if ($number =~ m/\d+$/) {
+ my $new_number = substr($number, $-[0]) * 1 + 1;
+ my $len_diff = length($number) - $-[0] - length($new_number);
+ $number = substr($number, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_number;
+
+ } else {
+ $number = $number . '1';
+ }
+ } while ($numbers_in_use{$number});
+
+ if ($self->save) {
+ do_query($form, $self->dbh, qq|UPDATE defaults SET $filters{numberfield} = ?|, $number);
+ $self->dbh->commit if !$self->dbh_provided;
+ }
+
+ return $number;
+}
+
+1;
# get customer / vendor
if ($form->{vc} eq 'vendor') {
IR->get_vendor(\%myconfig, \%$form);
+ $form->{discount} = $form->{vendor_discount};
} else {
IS->get_customer(\%myconfig, \%$form);
- # OFFEN tritt bug 1284 auch bei vendor auf?
$form->{discount} = $form->{customer_discount};
}
for my $i (1 .. $form->{rowcount}) {
# für bug 1284
- if ($form->{discount}){ # Falls wir einen Kundenrabatt haben
+ if ($form->{discount}){ # Falls wir einen Lieferanten-/Kundenrabatt haben
# und keinen anderen discount wert an $i ...
- $form->{"discount_$i"} ||= $form->{discount}*100; # ... nehmen wir den kundenrabatt
+ $form->{"discount_$i"} ||= $form->{discount}*100; # ... nehmen wir diesen Rabatt
}
map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice listprice lastcost basefactor);
}
map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
+ # get vendor or customer discount
+ my $vc_discount;
+ my $saved_form = save_form();
+ if ($form->{vc} eq 'vendor') {
+ IR->get_vendor(\%myconfig, \%$form);
+ $vc_discount = $form->{vendor_discount};
+ } else {
+ IS->get_customer(\%myconfig, \%$form);
+ $vc_discount = $form->parse_amount(\%myconfig, $form->{customer_discount});
+ }
+ restore_form($saved_form);
+
$form->{rowcount} = 0;
foreach my $ref (@{ $form->{form_details} }) {
$form->{rowcount}++;
$ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row
map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref };
- map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice discount lastcost);
+ map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost);
+
+ if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben
+ # und keinen anderen discount wert an $i ...
+ $form->{"discount_$form->{rowcount}"} ||= $vc_discount; # ... nehmen wir diesen Rabatt
+ }
+
$form->{"discount_$form->{rowcount}"} = $form->{"discount_$form->{rowcount}"} * 100; #s.a. Bug 1151
# Anm.: Eine Änderung des discounts in der SL/DO.pm->retrieve (select (doi.discount * 100) as discount) ergibt in psql einen
# Wert von 10.0000001490116. Ferner ist der Rabatt in der Rechnung dann bei 1.0 (?). Deswegen lasse ich das hier. jb 10.10.09
+
+ $form->{"discount_$form->{rowcount}"} = $form->format_amount(\%myconfig, $form->{"discount_$form->{rowcount}"});
}
delete $form->{form_details};
my $rows = scalar @{ $form->{item_list} };
- $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{customer_discount} * 100);
+ # Falls kein Kundenrabatt vorhanden ist, den aktuellen Rabatt nicht mit 0% überschreiben,
+ # da hier der Anwender schon manual einen Wert eingetragen haben könnte (analog zu qty) Bugfix: 1412
+ if ($form->{customer_discount}){
+ $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{customer_discount} * 100);
+ }
if ($rows) {
$form->{"qty_$i"} = ($form->{"qty_$i"} * 1) ? $form->{"qty_$i"} : 1;
last SWITCH;
};
}
- }
-
+ hotfix_reformat_date();
+ } # Ende Bericht für vorgewählten Zeitraum (warum auch immer die Prüfung (custom eq true) ist ...
+
RP->income_statement(\%myconfig, \%$form);
($form->{department}) = split /--/, $form->{department};
last SWITCH;
};
}
+ hotfix_reformat_date();
}
last SWITCH;
};
}
+ hotfix_reformat_date();
} else {
# die konvertierungen nur dann durchführen, wenn auch daten gesetzt sind.
# ansonsten ist die prüfung in RP.pm
# immer wahr
if ($form->{fromdate}){
my ($yy, $mm, $dd) = $locale->parse_date(\%myconfig, $form->{fromdate});
- $form->{fromdate} = "${dd}.${mm}.${yy}";
- $form->{comparefromdate} = "01.01.$yy";
+ my $datetime = $locale->parse_date_to_object(\%myconfig, $form->{fromdate});
+ $datetime->set( month => 1,
+ day => 1);
+ $form->{comparefromdate} = $locale->format_date(\%::myconfig, $datetime);
}
if ($form->{todate}){
- my ($yy, $mm, $dd) = $locale->parse_date(\%myconfig, $form->{todate});
- $form->{todate} = "${dd}.${mm}.${yy}";
$form->{comparetodate} = $form->{todate};
}
}
$main::lxdebug->leave_sub();
}
+###
+# Hotfix, um das Datumsformat, die unten hart auf deutsches Datumsformat eingestellt
+# sind, entsprechend mit anderem Formaten (z.B. iso-kodiert) zum Laufen zu bringen (S.a.: Bug 1388)
+sub hotfix_reformat_date {
+
+ $main::lxdebug->enter_sub();
+
+ my $form = $main::form;
+ my %myconfig = %main::myconfig;
+ my $locale = $main::locale;
+
+ if ($myconfig{dateformat} ne 'dd.mm.yyyy'){
+ my $current_dateformat = $myconfig{dateformat};
+ $myconfig{dateformat} = 'dd.mm.yyyy';
+ $form->{fromdate} = $main::locale->reformat_date(\%myconfig, $form->{fromdate}, $current_dateformat);
+ $form->{todate} = $main::locale->reformat_date(\%myconfig, $form->{todate}, $current_dateformat);
+ $form->{comparefromdate} = $main::locale->reformat_date(\%myconfig, $form->{comparefromdate}, $current_dateformat)
+ unless (!defined ($form->{comparefromdate}));
+ $form->{comparetodate} = $main::locale->reformat_date(\%myconfig, $form->{comparetodate}, $current_dateformat)
+ unless (!defined ($form->{comparetodate}));
+
+ # Und wieder zurücksetzen
+ $myconfig{dateformat} = $current_dateformat; #'dd.mm.yyyy';
+ } # Ende Hotifx Bug 1388
+
+ $main::lxdebug->leave_sub();
+}
1;
use List::Util qw(sum first);
use SL::VK;
+use SL::IS;
use SL::ReportGenerator;
use Data::Dumper;
$form->{customer} = $form->unescape($form->{customer});
- ($form->{customername}, $form->{customer_id}) = split(/--/, $form->{customer});
+ if ( $form->{customer} =~ /--/ ) {
+ # Felddaten kommen aus Dropdownbox
+ ($form->{customername}, $form->{customer_id}) = split(/--/, $form->{customer});
+ } else {
+ # Felddaten kommen aus Freitextfeld
+
+ # check_name wird mit no_select => 1 ausgeführt, ist die Abfrage nicht eindeutig kommt ein Fehler
+ # und die Abfrage muß erneut ausgeführt werden
+
+ # Ohne no_select kommt bei Auswahl des Kunden ein Aufruf von update der ins
+ # Nichts führt, daher diese Zwischenlösung
+
+ &check_name('customer', no_select => 1);
+
+ # $form->{customer_id} wurde schon von check_name gesetzt
+ $form->{customername} = $form->{customer};
+ };
# decimalplaces überprüfen oder auf Default 2 setzen
$form->{decimalplaces} = 2 unless $form->{decimalplaces} > 0 && $form->{decimalplaces} < 6;
@item
PDF::API2
@item
+Readonly (benötigt) und Readonly::XS (optional)
+@item
+Rose::Object
+@item
+Rose::DB
+@item
+Rose::DB::Object
+@item
Template
@item
Text::CSV_XS
* PDF::API2
+ * Readonly (benötigt) und Readonly::XS (optional)
+
+ * Rose::Object
+
+ * Rose::DB
+
+ * Rose::DB::Object
+
* Template
* Text::CSV_XS
libdatetime-perl libdbi-perl libdbd-pg-perl libpg-perl
libemail-address-perl libio-stringy-perl liblist-moreutils-perl
libpdf-api2-perl libtemplate-perl libtext-csv-xs-perl
-libtext-iconv-perl liburi-perl libxml-writer-perl libyaml-perl'
+libtext-iconv-perl liburi-perl libxml-writer-perl libyaml-perl
+librose-object-perl librose-db-perl librose-db-object-perl
+libreadonly-xs-perl'
Für Fedora Core benötigen Sie diese Pakete:
<li>Email::Address
<li>List::MoreUtils
<li>PDF::API2
+<li>Readonly (benötigt) und Readonly::XS (optional)
+<li>Rose::Object
+<li>Rose::DB
+<li>Rose::DB::Object
<li>Template
<li>Text::CSV_XS
<li>Text::Iconv
** BITTE FERTIGEN SIE VOR DEM UPGRADE EIN BACKUP IHRER DATENBANK(EN) AN! **
+Upgrade auf v2.6.2
+==================
+
+ Vor dem Einloggen
+ -----------------
+
+Mit Version 2.6.2 sind einige Abhängigkeiten von Perl-Modulen hinzugekommen.
+Bitte führen sie vor dem ersten Aufrufen der einmal den folgenden Befehl im
+Lx-Office Verzeichnis aus:
+
+$ scripts/installation_check.pl
+
+Sollten Module als fehlend markiert sein, folgen Sie bitte den Anweisungen in
+der Installationsanweisung.
+
+Zumindest folgende Module sind neu benötigt:
+
+* Readonly (optional mit Begleitermodul Readonly::XS)
+* Rose::Object, Rose::DB und Rose::DB::Object (die Installation von
+ Rose::DB::Object via CPAN oder den Paketmechanismus Ihrer
+ Distribution sollte für die automatische Installation der anderen
+ zwei Pakete sorgen)
+
+
Upgrade auf v2.6.1
==================
'Add bank account' => 'Bankkonto erfassen',
'Add custom variable' => 'Erweitertes Datenfeld anlegen.',
'Add note' => 'Notiz erfassen',
- 'Add to group' => 'Zu Gruppe hinzufügen',
'Add unit' => 'Einheit hinzufügen',
'Address' => 'Adresse',
'Administration' => 'Administration',
'All of the exports you have selected were already closed.' => 'Alle von Ihnen ausgewählten Exporte sind bereits abgeschlossen.',
'All reports' => 'Alle Berichte (Kontenübersicht, Summen- u. Saldenliste, GuV, BWA, Bilanz, Projektbuchungen)',
'All the selected exports have already been closed, or all of their items have already been executed.' => 'Alle ausgewählten Exporte sind als abgeschlossen markiert, oder für alle Einträge wurden bereits Zahlungen verbucht.',
+ 'All users' => 'Nichtmitglieder',
'Allow access' => 'Zugriff erlauben',
'Allow the following users access to my follow-ups:' => 'Erlaube den folgenden Benutzern Zugriff auf meine Wiedervorlagen:',
'Alternatively you can create a new part which will then be selected.' => 'Sie können auch einen neuen Artikel anlegen, der dann automatisch ausgewählt wird.',
'Edit group ' => 'Gruppe bearbeiten',
'Edit group membership' => 'Gruppenmitgliedschaften bearbeiten',
'Edit groups' => 'Gruppen bearbeiten',
+ 'Edit membership' => 'Mitglieder',
'Edit note' => 'Notiz bearbeiten',
'Edit rights' => 'Rechte bearbeiten',
'Edit templates' => 'Vorlagen bearbeiten',
'May ' => 'Mai',
'May set the BCC field when sending emails' => 'Beim Verschicken von Emails das Feld \'BCC\' setzen',
'Medium Number' => 'Datenträgernummer',
- 'Members not of' => 'Nicht Mitglied in',
- 'Members of' => 'Mitglied in',
'Memo' => 'Memo',
'Menu' => 'Menü',
'Message' => 'Nachricht',
'No transaction selected!' => 'Bitte mindestens einen Haken in der Spalte "auswählen" setzen.',
'No transfers were executed in this export.' => 'In diesem SEPA-Export wurden keine Überweisungen ausgeführt.',
'No unknown units where found.' => 'Es wurden keine unbekannten Einheiten gefunden.',
- 'No user has been selected.' => 'Es wurde kein Benutzer ausgewählt.',
'No valid number entered for pricegroup "#1".' => 'Für Preisgruppe "#1" wurde keine gültige Nummer eingegeben.',
'No vendor has been selected yet.' => 'Es wurde noch kein Lieferant ausgewählt.',
'No warehouse has been created yet or the quantity of the bins is not configured yet.' => 'Es wurde noch kein Lager angelegt, bzw. die dazugehörigen Lagerplätze sind noch nicht konfiguriert.',
'Remove' => 'Entfernen',
'Remove Draft' => 'Entwurf löschen',
'Remove draft when posting' => 'Entwurf beim Buchen löschen',
- 'Remove from group' => 'Aus Gruppe entfernen',
'Removed spoolfiles!' => 'Druckdateien entfernt!',
'Removing marked entries from queue ...' => 'Markierte Einträge werden von der Warteschlange entfernt ...',
'Rename the group' => 'Gruppe umbenennen',
'The unit in row %d has been deleted in the meantime.' => 'Die Einheit in Zeile %d ist in der Zwischentzeit gelöscht worden.',
'The unit in row %d has been used in the meantime and cannot be changed anymore.' => 'Die Einheit in Zeile %d wurde in der Zwischenzeit benutzt und kann nicht mehr geändert werden.',
'The units have been saved.' => 'Die Einheiten wurden gespeichert.',
- 'The user has been added to this group.' => 'Der Benutzer wurde der Gruppe hinzugefügt.',
- 'The user has been removed from this group.' => 'Der Benutzer wurde aus der Gruppe entfernt.',
'The user is a member in the following group(s):' => 'Der Benutzer ist Mitglied in den folgenden Gruppen:',
'The user migration process is complete.' => 'Der Prozess der Benutzerdatenmigration ist abgeschlossen.',
'The variable name must only consist of letters, numbers and underscores. It must begin with a letter. Example: send_christmas_present' => 'Der Datenfeldname darf nur aus Zeichen (keine Umlaute), Ziffern und Unterstrichen bestehen. Er muss mit einem Buchstaben beginnen. <br> Beispiel: <i>cebit_teilnahme_2011</i>',
'User name' => 'Benutzername',
'User saved!' => 'Benutzer gespeichert!',
'Username' => 'Benutzername',
+ 'Users in this group' => 'Mitglieder dieser Gruppe',
'Ust-IDNr' => 'USt-IdNr.',
'Valid from' => 'Gültig ab',
'Valid until' => 'gültig bis',