Offertenummer als autonummer-veld verstandig?

Status
Niet open voor verdere reacties.

remcop1989

Gebruiker
Lid geworden
29 mrt 2012
Berichten
492
Zoals de TT al zegt: is het verstandig om een offertenummer in een tabel vast te leggen als autonummer-veld?

Ik werk nu namelijk met de DMax+1 functie om nieuwe offertenummers te genereren.

Gevaar aan autonummering lijkt mij dat ieder nummer maar 1 keer te gebruiken is, ook na verwijderen. Maar het is dan ook niet de bedoeling dat een offerte verwijderd wordt (staat zo ingesteld al)

Graag jullie mening :)
 
Je hebt in een ander draadje al gezien dat er verschillend gedacht wordt over offertenummers. Als je niet exact weet wat de wettelijke verplichting is, dan zou ik eerst contact opnemen met een boekhouder of notaris o.i.d., zodat je voor jezelf een nummerstrategie kunt bedenken. Je zou kunnen nummeren op jaarbasis (elk jaar met 1 beginnen en oplopend doornummeren, dus 2012-0012, 2012-0013 etc, of elke maand met een nieuw nummer beginnen (20120912, 20120913,20121001;20121002). Of gewoon bij 1 beginnen, en doorlopend nummeren tot je failliet gaat....
Wettelijk moeten de nummers uniek zijn en oplopend, voor zover ik kan heb gezien is verder niet omschreven hoe je het verder inricht.
In een database met een Autonummer heb je in ieder geval altijd unieke nummers, maar als je per ongeluk een nieuw record aanmaakt, dan is dat nummer weg, tenzij je gelijk comprimeert. Dus dat werkt wat lastiger. Maar de nummers blijven natuurlijk wel oplopen.
Met DMax kun je het nummer ook automatisch laten oplopen, waarbij je dat ook kunt doen met een functie als je met een subnummering (jaar, maand etc) wilt werken.
 
Ja, uniek blijven ze jaarlijks doordat er uiteindelijk ook nog het jaartal en initialen van de gebruiker aan toe worden gevoegd.

Oorzaak van de vraag is echter dat DMax niet werkt als er nog geen offerte in de db staat en ik wil geen "dummie-offerte" met offertenummer "0000" moeten aanmaken omdat ik dan ook de verplichte velden "onzinnig" moet invullen.
 
Met een functie werkt dat prima; met een IIF kun je dat overigens ook wel afvangen, zodat een nieuw nummer altijd met 1 begint.
 
Zoals ik al zei: met een functie. Een voorbeeldje:

Code:
Function NieuwVolgNummer(Veld As String, Tabel As String) As String
Dim strVolgNummer As String
Dim strNieuwVolgNummer As String
Dim strSQL As String
Dim tmpNummer

    '------------------------------------------------------------------------------------------------
    ' Huidige volgnummer uit tabel lezen die als parameter is meegegeven.
    '------------------------------------------------------------------------------------------------
    strSQL = "SELECT TOP 1 [" & Veld & "] FROM [" & Tabel & "] "
    strSQL = strSQL & "ORDER BY [" & Veld & "] DESC"
    With CurrentDb.OpenRecordset(strSQL)
        If .RecordCount > 0 And Nz(.Fields(0).Value, 0) <> 0 Then
            strVolgNummer = .Fields(0).Value
        Else
            strVolgNummer = Format(0, "0000")
        End If
    End With
    
    '------------------------------------------------------------------------------------------------
    ' Kijken of er een koppelteken in het volgnummer zit; vervolgens nieuw nummer maken.
    '------------------------------------------------------------------------------------------------
    If InStr(strVolgNummer, "-") > 0 Then
        tmpNummer = Split(strVolgNummer, "-")
        If CInt(tmpNummer(0)) = Year(Date) Then
            strNieuwVolgNummer = Format(tmpNummer(0) & Format(tmpNummer(1) + 1, "000"), "0000-000")
        Else
            strNieuwVolgNummer = Format(Year(Date) & Format(1, "000"), "0000-000")
        End If
    Else
        strNieuwVolgNummer = Format(Year(Date) & Format(1, "000"), "0000-000")
    End If
    
    '------------------------------------------------------------------------------------------------
    ' Nieuw nummer toekennen aan de functie.
    '------------------------------------------------------------------------------------------------
    NieuwVolgNummer = strNieuwVolgNummer

End Function
En in een formulier gebruik je 'm dan als standaardwaarde voor je volgnummerveld:
Code:
=NieuwVolgNummer("Volgnummer";"Users")
Als voorbeeld. Het resultaat is in dit geval: 2012-044
 
Uuh.....dit lijkt veel op mijn lessen Frans van de middelbare school: veel leuke termen die ik kan uitspreken maar geen idee wat het betekent.

Wat doet de code en welke gegevens in de code moet ik precies aanpassen? "Veld" en "Tabel" lijken me helder, maar welke nog meer? (just to be sure)

http://www.mijnbestand.nl/Bestand-UKCAWA87PRLD.mdb

Heb de functie ingevoegd, maar ik krijg de melding "kan niet naar opgegeven record gaan" als ik het formulier "nieuwe offerte" open.

Het gaat om het tekstvak "offertenummer" op het formulier "nieuwe offerte". Deze is gekoppeld aan de tabel [Offertes] en veld [Offertenummer]
 
Laatst bewerkt:
De code is compleet, dus daar hoef je niks aan te veranderen :)
De grap van een universele functie is dat die altijd werkt; het enige dat je dus hoeft te doen is hem op de juiste manier aan te roepen. Dat gebeurt bij de eigenschappen van het tekstvak, waar je de Standaardwaarde invult met de onderste regel code die ik als voorbeeld geef. Het enige dat de functie nodig heeft is het veld dat het volgnummer bevat, en de naam van de tabel. Dus nogmaals:
Code:
=NieuwVolgNummer("Veldnaam met het volgnummer";"Tabel met het volgnummer")
Je hoeft de functie alleen aan te passen als er een andere nummervorm nodig is. Deze functie maakt volgnummers op basis van een jaartal. Wil je doorlopend nummeren op basis van een medewerkerscode, dan werkt deze functie natuurlijk niet goed. Hij werkt ook niet als het format anders is. Dat is nu: ####-###.
 
Je gebruikt een Numeriek veld voor het offertenummer. Mijn functie werkt op een tekstveld, omdat er een nummer wordt samengesteld op basis van een getal met voorloopnullen.
Maar er is een regeltje weggevallen, en wel deze:
Code:
    With CurrentDb.OpenRecordset(strSQL)
[B]        On Error Resume Next[/B]
Bij een lege tabel valt er natuurlijk niks op te halen, en dan krijg je deze melding. Als er eenmaal records zijn, vervalt het probleem.

OVerigens blijf je db's posten met nutteloze relaties; ik hoop voor je dat je in de produktie dat wel hebt geregeld. En dat de relatie tussen Contactpersonen en Offertes daar is verwijderd, en die tussen Producten en Offertedetails wèl werkt.
 
Dat had ik nog niet gedaan nee dat met die relaties ( nu dus wel ).

Nieuw offertenummer werkt nu :)

Hoe (waar) moet ik de code aanpassen om hem in het volgende format te krijgen:

Code:
Off-YYYYII####

Hierin is:

Off- = gewoon een stukje tekst dat aan iedere offerte vooraf moet
YYYY = het huidige jaar
II = de initialen van de huidige gebruiker zoals vastgelegd in de tabel gebruikers
#### = cijfers in het format "0000"
 
Laatst bewerkt:
Je veldopmaak werkt nu natuurlijk niet, want de opmaak regel je in de functie. De gebruikersinformatie zit er ook niet in, maar die kun je in de functie inbouwen.

Code:
Function NieuwVolgNummer(Veld As String, Tabel As String) As String
Dim strVolgNummer As String
Dim strNieuwVolgNummer As String
Dim strSQL As String, sInit As String
Dim tmpNummer
Dim tmp

    '------------------------------------------------------------------------------------------------
    ' Huidige volgnummer uit tabel lezen die als parameter is meegegeven.
    '------------------------------------------------------------------------------------------------
    On Error Resume Next
    sInit = DLookup("Initialen", "Gebruikers", "GebruikerID=" & UserID)
    If sInit & "" = "" Then sInit = "XX"
    On Error GoTo 0
    strSQL = "SELECT DISTINCT TOP 1 [" & Veld & "] FROM [" & Tabel & "] " _
        & "WHERE InStr(1, [Offertenummer],'" & sInit & "') >0 "
    strSQL = strSQL & "ORDER BY [" & Veld & "] DESC"
    
    With CurrentDb.OpenRecordset(strSQL)
        On Error Resume Next
        If .RecordCount > 0 And Nz(.Fields(0).Value, 0) <> 0 Then
            strVolgNummer = .Fields(0).Value
        Else
            strVolgNummer = Format(0, "0000")
        End If
    End With
    
    '------------------------------------------------------------------------------------------------
    ' Kijken of er een koppelteken in het volgnummer zit; vervolgens nieuw nummer maken.
    '------------------------------------------------------------------------------------------------
    If InStr(strVolgNummer, "-") > 0 Then
        tmpNummer = Split(strVolgNummer, "-")
        If CInt(Left(tmpNummer(UBound(tmpNummer)), 4)) = Year(Date) Then
            strNieuwVolgNummer = _
                CInt(Left(tmpNummer(UBound(tmpNummer)), 4)) & _
                sInit & _
                Right("0000" & CInt(Right(tmpNummer(UBound(tmpNummer)), 4)) + 1, 4)
        Else
            strNieuwVolgNummer = Year(Date) & sInit & CStr("0001")
        End If
    Else
        strNieuwVolgNummer = Year(Date) & sInit & CStr("0001")
    End If
    
    '------------------------------------------------------------------------------------------------
    ' Nieuw nummer toekennen aan de functie.
    '------------------------------------------------------------------------------------------------
    NieuwVolgNummer = "Off-" & strNieuwVolgNummer

End Function
Omdat ik je formulier maar niet aan de praat krijg (geen idee wat je allemaal hebt uitgespookt) heb ik het getest d.m.v. een knop. En dat werkt. Eerst inloggen, en dan een nieuwe offerte maken.
Veldeigenschappen dus op Tekst, veldlengte 20.
 
Knippen en plakken van de code werkt perfect! Super!

Zou je me de code (als je écht tijd hebt) ook een keer stukje voor stukje kunnen uitleggen?:o
 
Tuurlijk! De belangrijkste regels:
Code:
    sInit = DLookup("Initialen", "Gebruikers", "GebruikerID=" & UserID)
    If sInit & "" = "" Then sInit = "XX"
Haalt de initialen op van de ingelogde gebruiker. Gaat dat fout om wat voor reden dan ook, dan wordt daarvoor de tekst XX gebruikt.
Code:
    strSQL = "SELECT DISTINCT TOP 1 [" & Veld & "] FROM [" & Tabel & "] " _
        & "WHERE InStr(1, [Offertenummer],'" & sInit & "') >0 "
    strSQL = strSQL & "ORDER BY [" & Veld & "] DESC"
Deze code maakt een SQL die met Instr() de initialen opzoekt. Het hoogste nummer moet alleen worden opgezocht van de specifieke werknemer, dus daar moet je op filteren.
Code:
    With CurrentDb.OpenRecordset(strSQL)
        On Error Resume Next
        If .RecordCount > 0 And Nz(.Fields(0).Value, 0) <> 0 Then
            strVolgNummer = .Fields(0).Value
        Else
            strVolgNummer = Format(0, "0000")
        End If
    End With
Hier kijk je of er wat gevonden is of niet. Als dat niet het geval is, dan een standaarwaarde in strVolgnummer.
Code:
    If InStr(strVolgNummer, "-") > 0 Then
        tmpNummer = Split(strVolgNummer, "-")
        If CInt(Left(tmpNummer(UBound(tmpNummer)), 4)) = Year(Date) Then
            strNieuwVolgNummer = _
                CInt(Left(tmpNummer(UBound(tmpNummer)), 4)) & _
                sInit & _
                Right("0000" & CInt(Right(tmpNummer(UBound(tmpNummer)), 4)) + 1, 4)
        Else
            strNieuwVolgNummer = Year(Date) & sInit & CStr("0001")
        End If
    Else
        strNieuwVolgNummer = Year(Date) & sInit & CStr("0001")
    End If
Hier kijk je eerst of er een koppelteken in het nummer zit. Alles links ervan heb je niet nodig,dus met SPLIT scheid je de tekst op koppelteken. Je krijgt nu een matrix variabele met een aantal elementen. (2 waarschijnlijk). In de laatste zit het eigenlijke nummer, dus dat ga je verder bewerken.
Code:
If CInt(Left(tmpNummer(UBound(tmpNummer)), 4)) = Year(Date)
Hier kijk je of de eerste 4 tekens van het volgnummer gelijk zijn aan het huidige jaar. Zo ja: doorgaan!
Een matrix variabele kan bij 1 beginnen te tellen, of bij 0. Dat leg je zelf vast met de regel Option Base 1 als je niet bij 0 wilt starten. Als je met 1 begint, dan is de hoogste waarde van de matrix 2, en anders 1. Omdat je dat zeker wilt weten, gebruik je UBound(matrix) om die waarde uit te lezen. UBound(tmpNummer) levert dus een getal op, en wel van de hoogste matrix waarde. Daar heb je de waarde van nodig, dus dan krijg je: tmpNummer(UBound(tmpNummer)). En dat levert je string op. De eerste 4 cijfers daarvan zijn het jaar, en de laatste 4 het volgnummer. Dus die pak je met:
Code:
Right(tmpNummer(UBound(tmpNummer)), 4)
Die zet je, om het verhaal af te maken, om naar een getal, en daar tel je 1 bij op.
Code:
CInt(Right(tmpNummer(UBound(tmpNummer)), 4)) + 1
Het volledige volgnummer bouw je dus op uit 3 delen: jaar (Left stuk), Initiaal (sInit) en volgnummer (Right stuk)
Code:
strNieuwVolgNummer = CInt(Left(tmpNummer(UBound(tmpNummer)), 4)) & sInit & Right("0000" & CInt(Right(tmpNummer(UBound(tmpNummer)), 4)) + 1, 4)
En uiteindelijk zet je daar "Off-" voor.
 
Dank je! :)

Als ik het offertenummer niet per werknemer wil hebben (dus niet 2 werknemers met nummer 0004 bijvoorbeeld), is het dan voldoende om deze regel eruit te halen:

Code:
& "WHERE InStr(1, [Offertenummer],'" & sInit & "') >0 "

Intern is namelijk besloten om geen dubbele nummers (met andere initialen) meer te gebruiken. Dus bijvoorbeeld niet:

Off-2012RP0034
Off-2012YC0034

Maar gewoon:
Off-2012RP0034
Off-2012YC0035
 
Dan moet het opzoeken anders, maar wel wat lastiger vrees ik. Want je moet nu naar het Jaar kijken, en het bijbehorende hoogste nummer. En dat gaat met de huidige structuur niet.
 
Zou je mij de laatste code die je gegeven hebt dan willen aanpassen zodat er gebruik wordt gemaakt van 1 nummering voor iedereen en niet een nummering per persoon. Bovendien mag bij nader inzien toch het "Off-" stukje weggelaten worden.

Het gaat om deze code:
Code:
Function NieuwVolgNummer(Veld As String, Tabel As String) As String
Dim strVolgNummer As String
Dim strNieuwVolgNummer As String
Dim strSQL As String, sInit As String
Dim tmpNummer
Dim tmp

    '------------------------------------------------------------------------------------------------
    ' Huidige volgnummer uit tabel lezen die als parameter is meegegeven.
    '------------------------------------------------------------------------------------------------
    On Error Resume Next
    sInit = DLookup("Initialen", "Gebruikers", "GebruikerID=" & UserID)
    If sInit & "" = "" Then sInit = "XX"
    On Error GoTo 0
    strSQL = "SELECT DISTINCT TOP 1 [" & Veld & "] FROM [" & Tabel & "] " _
        & "WHERE InStr(1, [Offertenummer],'" & sInit & "') >0 "
    strSQL = strSQL & "ORDER BY [" & Veld & "] DESC"
    
    With CurrentDb.OpenRecordset(strSQL)
        On Error Resume Next
        If .RecordCount > 0 And Nz(.Fields(0).Value, 0) <> 0 Then
            strVolgNummer = .Fields(0).Value
        Else
            strVolgNummer = Format(0, "0000")
        End If
    End With
    
    '------------------------------------------------------------------------------------------------
    ' Kijken of er een koppelteken in het volgnummer zit; vervolgens nieuw nummer maken.
    '------------------------------------------------------------------------------------------------
    If InStr(strVolgNummer, "-") > 0 Then
        tmpNummer = Split(strVolgNummer, "-")
        If CInt(Left(tmpNummer(UBound(tmpNummer)), 4)) = Year(Date) Then
            strNieuwVolgNummer = _
                CInt(Left(tmpNummer(UBound(tmpNummer)), 4)) & _
                sInit & _
                Right("0000" & CInt(Right(tmpNummer(UBound(tmpNummer)), 4)) + 1, 4)
        Else
            strNieuwVolgNummer = Year(Date) & sInit & CStr("0001")
        End If
    Else
        strNieuwVolgNummer = Year(Date) & sInit & CStr("0001")
    End If
    
    '------------------------------------------------------------------------------------------------
    ' Nieuw nummer toekennen aan de functie.
    '------------------------------------------------------------------------------------------------
    NieuwVolgNummer = "Off-" & strNieuwVolgNummer

End Function

:o
 
Je moest eens weten hoeveel het scheelt als je van tevoren alle eisen voor de database in een functioneel ontwerp zou zetten; er zijn steden in minder tijd gebouwd :) Maar zonder gekheid: je begint hopelijk te snappen dat het bouwen van de database altijd de laatste stap moet zijn, als alle andere zaken zijn beschreven en vastgelegd. Eerst gaan bouwen, en dan de vraag stellen wat het systeem eigenlijk moet kunnen, is met afstand dè manier om het niet te doen.

Je wilt dus het hoogste nummer per jaar, maar kunt daarbij niet de functie Max gebruiken, of, wat we in de functie doen, een aflopende TOP 1 query op volgnummer per jaar. Je maakt bijvoorbeeld een volgnummer met de opmaak 2012-0001 ... 2012-007-145, 2013-0001, 2013-0002 en bij zo'n nummer is het simpel: TOP 1, en aflopend op nummer. Dan heb je altijd het hoogste nummer. Maar omdat jullie (om nu volslagen onbruikbare redenen) middenin de initialen van een medewerker willen, werkt dit niet meer. Een nummer als 2012AG0288 zal altijd onder 2012RW0018 komen te staan, omdat de R nu eenmaal hoger in het alfabet staat als de A. Je zult dus de query anders moeten maken. Bijvoorbeeld door twee selecties te maken: één op de eerste 4 cijfers voor het jaar, en één voor de laatste 4 cijfers. Op basis van het laatste veld kun je dan aflopend sorteren, en op basis van het eerste veld kun je filteren.
Ik zou zeggen: probeer het zelf eens. Of, echt een heel stuk makkelijker, praat nog eens met je baas of die letters wel enig nut hebben...
 
Er zijn inderdaad zaken die ik van tevoren had kunnen voorzien, daar heb je gelijk in. Echter nu de input ver klaar is kom ik toch nog achter wat zaken die anders of erbij kunnen. Dat laatste is in mijn ogen onoverkomelijk.
 
Daar kom je dan nog wel achter dat dat niet zo is :)
Een query die links kijkt naar het jaartal, en rechts naar de 4 cijfers met het nummer, kan zo:
Code:
    strSQL = "SELECT TOP 1 [" & Veld & "] FROM Users " _
        & "GROUP BY [" & Veld & "], Left([" & Veld & "],4), Val(Right([" & Veld & "],4)) " _
        & "HAVING (CInt(Left([" & Veld & "], 4)) = " & Year(Date) & ") " _
        & "ORDER BY Left([" & Veld & "],4) DESC , Val(Right([" & Veld & "],4)) DESC;"
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan