Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb (https://www.delphipraxis.net/164731-vorgehensweise-fuer-rechnungsnummernvergabe-im-multi-user-betrieb.html)

RWarnecke 27. Nov 2011 17:56

Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Hallo zusammen,

ich muss in einem Programm Rechnungsnummern mit fortlaufender Nummer vergeben ohne eine Lücke zu produzieren. Die Vergabe muss auch noch dann funktionieren, wenn mehrere Leute eine Rechnung erstellen. Ich habe dazu zwei Tabellen für die Rechnung. Die erste Tabelle enthält alle Daten zum Kunden und natürlich die Rechnungsnummer und die zweite Tabelle enthält alle Rechnungspositionen, mit Referenz auf die Rechnungsnummer. Ich hatte mir dazu zwei Möglichkeiten überlegt.
  1. Die Rechnungsnummer wird beim Aufruf des Rechnungsformular vergeben.
  2. Die Rechnungsnummer wird erst vergeben, wenn die Rechnung gespeichert wird.
Bei der ersten Möglichkeit sehe ich das Problem, wenn mehrere Leute das Rechnungsformular geöffnet haben und einer die Eingabe abbricht oder sonst irgendwas passiert, eine Lücke entstehen kann. Bei der zweiten Möglichkeit weiß ich nicht, wie ich das mit dem Speichern übernehmen sollte.

Wie würdet Ihr die Aufgabe lösen ? Eine meiner Möglichkeiten verwenden, wenn ja welche und warum ? Oder gibt es noch einen ganz anderen Ansatz ?

P.S. : Wenn es umbedingt nötig ist, kann das Design der beiden Tabellen geändert werden.

MGC 27. Nov 2011 18:09

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Hallo,

Du hast doch mit Sicherheit eine Tabele, in der ggf. nur die vergebenen Rechnungsnummern stehen. Wenn Du beim Speichern die Tabelle kurzzeitig sperrst, den gröten bereits gespeicherten Wert ausliest, deine demnach nächst höhere Nummer spiecherst und dann die Tabelle für den nächsten freigibst sollte nichts mehr passieren können.

Also im Prinzip Dein 2. Ansatz. Schwierig wird es jedoch, wenn Du mit verschiedenen Severn zu tun hast und diese erst synchronisiert werden. Dort könnte es dann zu Problenmen, bzw. bemerkbaren teitlichen Verzögerungen kommen.

Auf welche Weise ist das DB-System aufgebaut, bzw. wie werden die Zugriffe geregelt udn welche Technologie setzt Du beim Zugriff auf die DB ein?

Viele Grüße,

Neumann 27. Nov 2011 18:27

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Hallo,

ich mache es so:

Es gibt eine Tabelle mit Rechnungsnummern, nur eine Spalte. Wenn eine Nummer gebraucht wird, holt sich der Client die kleinste Nummer aus der Tabelle und löscht sie.

Wenn die Tabelle nahezu leer ist, wird sie wieder aufgefüllt.

Bricht jemand eine Rechnung ab oder es geht sonst etwas schief, wird die Nummer wieder in die Tabelle zurückgeschrieben.

Die Zeit von Nummer holen bis "verbrauchen" halte ich sehr kurz, die Nummer wird erst geholt wenn die Rechnung ausgegeben wird, nicht schon wenn die Positionen bearbeitet werden.

Ist die Rechnung gedruckt und gegebenenfalls bestätigt ist die Nummer entgültig verbraucht. Dann kann man nur noch eine Stornorechnung machen.

Furtbichler 27. Nov 2011 18:30

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Wie werden die Rechnungen erstellt?

Ich mache das so: Es wird zunächst eine offene Rechnung erstellt. Erst wenn diese vollständig (und in der DB vollständig gespeichert) ist, wird per Stored Procedure eine eindeutige und lückenlos steigende Rechnungsnummer erstellt.

haentschman 27. Nov 2011 18:32

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Hallo...

welches DBMS ist im Einsatz ? Persönlich würde ich folgenden Ablauf bevorzugen:
1. RE speichern
2. DBMS vergibt eine Nummer über einen Generator (Firebird)... ist damit eindeutig und fortlaufend
3. Die Query enthält als Rückgabewert die Nummer
4. Mit dieser Nummer können dann die Positionen gespeichert werden (Zuordnung)

Bummi 27. Nov 2011 18:37

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
@haentschman
so kenn ich es auch, unter Berücksichtigung der Vorgaben finde ich die Implementierung von Neumann am interessantesten.

RWarnecke 27. Nov 2011 18:37

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Zitat:

Zitat von MGC (Beitrag 1138105)
Auf welche Weise ist das DB-System aufgebaut, bzw. wie werden die Zugriffe geregelt udn welche Technologie setzt Du beim Zugriff auf die DB ein?

Es soll lediglich nur einen Firebird-Server geben, wo alle drauf zugreifen. Als Datenbankkomponente setze ich UniDAC ein. Die Daten werden aus der Datenbank geholt und in einer TObjectList gespeichert und dort vom Programm auch bearbeitet. Erst wenn es Änderungen in der TObjectList gibt, werden die Daten zurückgeschrieben in die Datenbank.

Zitat:

Zitat von Furtbichler (Beitrag 1138109)
Wie werden die Rechnungen erstellt?

Ich habe ein Formular erstellt, wo ich die Kundendaten eintragen kann und die Rechnungspositionen. Danach ist alles offen, wie die Daten verarbeitet werden. Bevor ich die Rechnung ausdrucke oder abschliesse, möchte ich natürlich noch eine Vorschau haben.

Furtbichler 27. Nov 2011 19:01

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Zitat:

Zitat von haentschman (Beitrag 1138110)
...
2. DBMS vergibt eine Nummer über einen Generator (Firebird)... ist damit eindeutig und fortlaufend
...

Man muss aufpassen, das es beim Speichern kein Rollback geben kann, das ist Alles.

haentschman 27. Nov 2011 19:48

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Klar, in diesem Ausnahmefall (bei fehlerhaftem Eintragen in die DB) wäre dann die Nummer verbraucht. Mit diesem Sonderfall hätten wahrscheinlich alle Lösungen ein Problem und sollte seperat behandelt werden.

TBx 27. Nov 2011 20:02

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Zitat:

Zitat von haentschman (Beitrag 1138128)
Klar, in diesem Ausnahmefall (bei fehlerhaftem Eintragen in die DB) wäre dann die Nummer verbraucht. Mit diesem Sonderfall hätten wahrscheinlich alle Lösungen ein Problem und sollte seperat behandelt werden.

Und genau dafür kann man die vorgeschlagene Möglichkeit mit der Rechnungsnummerntabelle verwenden!

hoika 28. Nov 2011 07:10

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Hallo,

die Sache mit dem Generator hat aber mit der Rechnungsnummer nichts zu tun (?).
Es gibt 2 Felder, RechnungsID (PrimKey,über Generator) und RechnungsNr(über eigene Logik, z.B. Sperrtabelle).


Heiko

Furtbichler 28. Nov 2011 07:40

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Na, die Idee stand im Raum, das man für die Rechnungs-Nr auch einen Generator verwendet.
Ich halte aber die Tabelle mit allen Rechnungsnummern (der nächsten 100 Jahre ;-)) für den einzig richtigen Weg, um Lücken zu vermeiden.

Es ist ja schwer, dem Finanzamt zu vermitteln, weshalb Rechnung Nr.: 12345 nicht eingereicht wurde. Die interessiert dann nicht, wenn man dann die DB-Logdatei vorzeigt, der belegt, das eine Transaktion aufgrund was-weiss-ich fehlschlug.

Allerdings muss man bei der Lösung sicherstellen, das zu Ultimo (z.B. Monatsende) keine Lücken vorhanden sind.

MGC 28. Nov 2011 08:14

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Das Finanzamt sollte mittlerweile aber Erfahrung im Einsatz von DBMS haben udn wissen, dass es immer wieder mal zu Fehlern kommen kann. :evil:

Aufgrund der beschriebenen Architektur würde ich persönlich auch noch immer dabei bleiben die Re-Nr. erst zu vergeben, wenn die Rechnung tatsächlich gespeichert wird.
Bei der Rechnungsspeicherung darf es dann, wie bereits erwähnt, kein Rollback geben.

Man kann natürlich auch darauf vertrauen, dass es, aufgrund weniger Rechnungsstellungen pro Tag, hoffentlich niemals zu einem zeitgleichen Speichervorgang in der DB kommt, aber das wäre mir zu wage.

jobo 28. Nov 2011 09:10

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
OT:
Ist das wirklich so, dass das Finanzamt auf sowas besteht?
Dann vielleicht zum Monatsende oder Quartalsabschluss eine extra Liste mit fortlaufenden Nummern und Verweise auf die "interne" Rechnungsnummer produzieren und abheften.
;)

Sir Rufo 28. Nov 2011 09:13

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Mit MySQL würde das so aussehen:
Code:
CREATE TABLE `beltab` (
`BelID`  bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT ,
`BelTyp`  varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`BelNr`  int(11) NOT NULL DEFAULT 0 ,
`BelInfo`  varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
PRIMARY KEY (`BelID`),
UNIQUE INDEX `UNQ_Typ_Nr` (`BelTyp`, `BelNr`) USING BTREE
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=1044
ROW_FORMAT=COMPACT;

CREATE DEFINER=`root`@`%` TRIGGER `beltab_before_insert` BEFORE INSERT ON `NewTable`
FOR EACH ROW SET NEW.BelNr = GetNewScopeLfd( NEW.BelTyp );

CREATE TABLE `tbl_lfd` (
`Scope`  varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`LastLfd`  int(11) NOT NULL ,
`Step`  int(11) NOT NULL DEFAULT 1 ,
PRIMARY KEY (`Scope`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT;

CREATE DEFINER = `root`@`%` FUNCTION `GetNewScopeLfd`(Scope varchar(50))
 RETURNS int(11)
BEGIN
   DECLARE NewLfd int;

   UPDATE tbl_lfd
      SET LastLfd = LastLfd + Step
      WHERE tbl_lfd.Scope = Scope;

   SELECT LastLfd
      INTO NewLfd
      FROM tbl_lfd
      WHERE tbl_lfd.Scope = Scope;

   RETURN NewLfd;
END;
Zum Testen habe ich folgendes (mit 10+ Programm-Instanzen) ausführen lassen:
Delphi-Quellcode:
unit View.Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics,
  Controls, Forms, Dialogs, DB, DBAccess, Uni, UniProvider,
  MySQLUniProvider, StdCtrls, ExtCtrls;

type
  TForm1 = class( TForm )
    MySQLUniProvider1 : TMySQLUniProvider;
    UniConnection1 : TUniConnection;
    Timer1 : TTimer;
    CheckBox1 : TCheckBox;
    procedure CheckBox1Click( Sender : TObject );
    procedure Timer1Timer( Sender : TObject );
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1 : TForm1;

implementation

{$R *.dfm}

procedure TForm1.CheckBox1Click( Sender : TObject );
begin
  Timer1.Enabled := CheckBox1.Checked;
end;

procedure TForm1.Timer1Timer( Sender : TObject );
begin
  Timer1.Enabled := False;

  if not UniConnection1.Connected
  then
    UniConnection1.Open;

  UniConnection1.StartTransaction;
  try

    UniConnection1.ExecSQL( 'INSERT INTO beltab (BelTyp, BelInfo) VALUES(:BelTyp,:BelInfo);',
      ['RECHNUNG', 'Nur so'] );

    Sleep( 250 );

    if Random( 2 ) = 1
    then
      UniConnection1.Commit
    else
      UniConnection1.Rollback;

  except
    UniConnection1.Rollback;
  end;

  Timer1.Enabled := CheckBox1.Checked;
end;

end.
Es treten weder Lücken auf, noch kommt es zu Ausnahmen, weil eine Beleg-Nummer doppelt vergeben wurde.

ChrisE 28. Nov 2011 09:16

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Zitat:

Zitat von jobo (Beitrag 1138187)
OT:
Ist das wirklich so, dass das Finanzamt auf sowas besteht?

OT:
Soweit ich weiß, ist nur eine eindeutige aber keine fortlaufende Nummer wichtig. Da gab es ein paar Entscheidungen, da man gegenüber einem Kunden den Rückschluss auf die Anzahl der Rechnungen einer Firma verhindern wollte.

Sir Rufo 28. Nov 2011 09:19

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
In der Dokumentation von MySQL steht dazu folgendes:
Zitat:

Zitat von MySQL Dokumentation 5.1
Betrachten wir ein anderes Beispiel: Wir haben in der Tabelle child_codes ein Zählerfeld eines Integer-Typs, das wir dazu benutzen, jedem Kind, das der child-Tabelle hinzugefügt wird, eine eindeutige Kennnummer zu geben. Da wäre es natürlich keine gute Idee, den Wert des Zählers mit einer konsistenten Leseoperation oder im Shared-Modus zu lesen, da zwei Datenbanknutzer dann vielleicht denselben Zählerwert sehen und einen Fehler wegen Schlüsselduplikaten auslösen, sofern sie versuchen, Kindeinträge mit derselben Nummer in die Tabelle einzufügen.

Hier ist LOCK IN SHARE MODE keine gute Lösung. Denn wenn zwei Benutzer gleichzeitig den Zähler lesen, könnte mindestens einer von ihnen in einen Deadlock geraten, wenn er versucht, den Zähler zu aktualisieren.

Hier haben Sie zwei gute Möglichkeiten, das Lesen und Inkrementieren des Zählers zu implementieren: (1) Sie inkrementierten zuerst den Zähler um 1 und führen erst dann die Leseoperation durch, oder (2) Sie lesen den Zähler zuerst im Sperrmodus FOR UPDATE und inkrementieren ihn danach. Der zweite Ansatz kann folgendermaßen implementiert werden:
Code:
SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
Ein SELECT … FOR UPDATE liest die neuesten verfügbaren Daten und errichtet eine exklusive Sperre auf jeder Zeile, die es liest. Somit setzt es dieselben Sperren, die auch ein Searched SQL UPDATE auf den Zeilen erwerben würde.

Die obige Beschreibung ist nur ein Beispiel dafür, wie SELECT … FOR UPDATE funktioniert. In MySQL können Sie einen eindeutigen Identifier grundsätzlich mit nur einem einzigen Tabellenzugriff generieren:
Code:
UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1);
SELECT LAST_INSERT_ID();
Die SELECT-Anweisung ruft nur die Identifier-Information ab (die für die aktuelle Verbindung spezifisch ist). Sie greift auf keine Tabellen zu.

Sperren von IN SHARE MODE- und FOR UPDATE-Leseoperationen werden freigegeben, wenn die Transaktion committet oder zurückgerollt wird.


webcss 28. Nov 2011 09:53

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Ich denke auch, dass es lediglich um die Eindeutigkeit einer Rechnungsnummer zu einem Vorgang geht, d.h. keine doppelten Rechnungsnummern o.ä. Fortlaufend müssen diese aber nicht zwingend sein. Man schaue sich nur mal z.B. die Rechnungen von Amazon an, dort wird eine Folge von (alpha-)numerischen Zeichen in Groß- und Kleinschreibung sowie Ziffern verwendet. Diese sind wohl eindeutig, aber fortlaufend wohl kaum...

Uwe Raabe 28. Nov 2011 10:07

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Ein Zitat der Oberfinanzdirektion Koblenz zu diesem Thema:

Zitat:

Nach den Erörterungen der obersten Finanzbehörden des Bundes und der Länder gilt Folgendes:

...

Angabe einer fortlaufenden Nummer in der Rechnung
Die in § 14 Abs. 4 S. 1 Nr. 4 UStG enthaltene Pflichtangabe einer fortlaufenden Nummer in der Rechnung macht keine zahlenmäßige Abfolge der ausgestellten Rechnungsnummern zwingend, da es lediglich um die Einmaligkeit der erteilten Rechnungsnummer geht. Die Anforderungen an die Rechnung sind vor dem Hintergrund zu interpretieren, dass es um die Verhinderung eines ungerechtfertigten Vorsteuerabzugs geht. Diesbezüglich reicht die Einmaligkeit der Nummerierung aus (Abschn. 185 Abs. 10 UStR).

Sir Rufo 28. Nov 2011 10:18

AW: Vorgehensweise für Rechnungsnummernvergabe im Multi-User Betrieb
 
Zitat:

Zitat von webcss (Beitrag 1138198)
Ich denke auch, dass es lediglich um die Eindeutigkeit einer Rechnungsnummer zu einem Vorgang geht, d.h. keine doppelten Rechnungsnummern o.ä. Fortlaufend müssen diese aber nicht zwingend sein. Man schaue sich nur mal z.B. die Rechnungen von Amazon an, dort wird eine Folge von (alpha-)numerischen Zeichen in Groß- und Kleinschreibung sowie Ziffern verwendet. Diese sind wohl eindeutig, aber fortlaufend wohl kaum...

Woher willst du wissen, dass diese NICHT fortlaufend sind? Kennst du die Nummerierungs-Regeln von amazon?
Gerade weil hier nicht nur mit Ziffern gearbeitet wird, ist es schwer dieses nachzuvollziehen, wenn man die Regel kennt, aber sehr wohl.

Der Hintergrund bei amazon ist aber wohl der, dass die Rechnungsnummern bei denen sehr schnell die Seitenbreite sprengen würden (bei der hohen Anzahl an Rechnungen) und daher haben die eben wohl ein 62er-ZahlenSystem (0..9,A..Z,a..z) genommen um relativ kurze Rechnungsnummern zu erhalten.

Zitat:

Ich hätte da mal eine Nachfrage zu einer Rechnung mit der Nummer 71...[halbe Stunde später]...68


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:12 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz