Access: Increment/Decrement a number with 2 dots - ms-access

I have two fields for current version number and previous version number in a form. What I want to do is when I enter the current version number (which is written like this 18.04.15), the previous version number on the next text box to automatically fill itself with 18.04.14.
I tried:
=[txtCurrentVersion]-1 in the control source, but obviously because I'm not decrementing by one, it didn't work.
Would appreciate some guidance, thanks :)

Below code splits the text based on dot and subracts one from last item.
Private Sub txtCurrentVersion_AfterUpdate()
If Nz(Me.txtCurrentVersion, "") <> "" Then
Me.txtPrevVersion.Value = Split(Me.txtCurrentVersion, ".")(0) & "." & Split(Me.txtCurrentVersion, ".")(1) & "." & Split(Me.txtCurrentVersion, ".")(2) - 1
End If
End Sub

I would suggest creating a function where you give the function 3 parameters, the 1st is the current version number string, the 2nd is the version number level (0 for major number, 1 for subversion number and 2 for minor version number) and the value to increase or decrease.
for example:
Function ModifyVersion(VersionNumber, NumberLevel, Number)
If VersionNumber <> "" AND NumberLevel >= 0 AND NumberLevel < 3 Then
dim VersionArray
VersionArray = Split(VersionNumber, ".")
Select Case NumberLevel
Case 0
VersionArray(0) = VersionArray(0) + Number
Case 1
VersionArray(1) = VersionArray(1) + Number
Case 2
VersionArray(2) = VersionArray(2) + Number
End Select
ModifyVersion = VersionArray(0) & "." & VersionArray(1) & "." & VersionArray(2)
End If
End Function
Then to decrease one from the minor version number use:
VersionNumber = [txtCurrentVersion]
Dim UpdateVersion
UpdateVersion = ModifyVersion(VersionNumber, 2, -1)

Related

IIf query decimal removal

Trying to attempt the following in MS Access.
Convert data in one field to an 18 digit number starting with 01 in another field.
There are also some conditions that have to be met:
the first dash should become double zeros
the second dash should be removed
the third and fourth dash should be a single zero
the decimal must also be replaced with a zero
My query works fine until the decimal is the 15th character in the data.
Here is the query:
SELECT MasterVacant.ParcelIdNumber,
"01" + Mid([ParcelIdNumber],1,2) + "00" + Mid([ParcelIdNumber],4,2) + Mid([ParcelIdNumber],7,1)
+ IIf(Mid([ParcelIDNumber],11,1) = "", "0"+Mid([ParcelIDNumber],9,2), Mid([ParcelIDNumber],9,3))
+ IIf(Mid([ParcelIDNumber],14,1) = ".", "0"+Mid([ParcelIDNumber],12,2), Mid([ParcelIDNumber],12,3))
+ Mid([ParcelIDNumber],15,3) AS ParcelNumber
FROM MasterVacant;
Here is a start and finish example...
'12-06-1-00-50.000-RR' should become '011200061000050000'
'12-06-3-07-09.000-RR' should become '011200063007009000'
'13-35-1-01-129.000-RR' should become '011300035100112900'
However, instead of getting `0113000351001129000' I get '013000351001129.00'.
The issue is how do I remove the decimal when the decimal is the 15th character like in the third set of example?
I receive the data as a single column. Some of it is below....
1. 13-35-1-07-29.000-RR
2. 13-35-1-01-112.000-RR (Removing the decimal when the data is like this is the issue)
3. 13-35-4-01-01.000-RR
4. 13-35-4-02-04.000-RR
5. 13-35-1-13-17.000-RR
The output for the above data should be
1. 011300351007029000
2. 011300351001112000
3. 011300354001001000
4. 011300354002004000
5. 011300351013017000
Use a custom function:
Public Function Make18(ByVal Value As String) As String
Const Head As String = "01"
Const Tail As String = "000"
Const Lead As String = "00"
Dim Parts As Variant
Dim Part As Integer
Dim Result As String
Parts = Split(Split(Value, ".")(0), "-")
For Part = LBound(Parts) To UBound(Parts)
Select Case Part
Case 0
Parts(Part) = Head & Parts(Part)
Case 1
Parts(Part) = Lead & Parts(Part)
Case 3, 4
Parts(Part) = Right(Lead & Parts(Part), 3)
End Select
Next
Result = Join(Parts, "") & Tail
Make18 = Result
End Function
and your query becomes:
SELECT
MasterVacant.ParcelIdNumber,
Make18([ParcelIdNumber]) AS ParcelNumber
FROM
MasterVacant;
I am assuming you meant the opposite where:
12-06-1-00-50.000-RR should become 011200061000050000
12-06-3-07-09.000-RR should become 011200063007009000
13-35-1-01-129.000-RR should become 0113000351001129.00
I would recommend the REPLACE() in MSACCESS to strip the dashes out. Once you have the dashes out you can MID()
Unfortunately your attempted code does something different with the 3rd row because 3 zeros are being put in when there should be only two in my opinion.
Try in a text box:
=Replace("13-35-1-01-129.000-RR","-","")
will return 1335101129.000RR
and see if that assists you in making your code.
Maybe go one step further and put it in a function.

How to get a drop-down filter in Spotfire Information Link?

Generally people use the default option that Spotfire gives. Connect to the DB and pull the set of columns that you need and create an Information Link and load the data to Spotfire.
However, I am using SQL Query to fetch data to Spotfire. I am creating a table similar to Views, and writing a simple stored procedure to pull the data:
Create procedure ProcA(In Start_Date date, IN End_Date date, In Site_Name text)
Begin
SELECT * FROM TableA where day between Start_Date and End_Date and
site_name = Site_Name;
This works fine if I am not using site name filtering.
The Information Links helps in filtering the date properly. But when it comes to Site Name, nothing works.
There are 2 requirements:
Is it possible to give a drop-down just like how filter comes for Date
How to pass multiple site names to pull only those sites into the Spotfire file
TL;DR: There are better ways to do this; if it's just for the column names, I don't think it's worth it to do part 2, since it's easy enough to change the sql in the information link, but it's possible.
Okay, I will try (read: fail) not to be too long-winded.
1) Is it possible to do a drop-down for dates? Yes. The easiest way to do this would be to pull a data table with all of your date choices available for the end user. Here's an example finding a list of better way to generate months/year table Remember when creating your dropdownlist that your Document Property has to have the Data type "Date", and then you should be able to set property values through Unique Values in column against your date column from the new data, the same as you would do for a string drop-down list.
If you have a small subset of specific dates to choose from, this probably isn't too bad. If the drop down list gets longer, your end-users can type in the date they're looking for to speed up their search (though in my experience, a lot of them will scroll through until they find the date they're looking for).
While this is perfectly acceptable, if you're at all comfortable adding javascript, I'd personally recommend using a Popup Calendar These are fairly straightforward for end-users, and can allow them to use the calendar or type it themselves. (And if they type something that isn't a date in, it's even kind enough to inform them with red letters and an exclamation mark that they haven't typed an actual date)
2) How to pass multiple site names to pull only those sites into the Spotfire file
Hoo boi, where to start.
Step one: How do you want to select your list of Site Names? I'm going to go ahead and assume you have a data table with a list of distinct Site Names.
Your next choice is how to let your user select which Site Names they want. General options are using a List Box Filter, displaying a table and using marked rows, or providing a text area where the user can type their selections themselves.
When I needed to do this, I did a combo of a data table and a text area, so that's what I'm going to describe here.
I start off by providing the user with a text area, formatted to "specific size" with a larger than usual height to prompt that, yes, they are allowed to type multiple rows. If they know the values they're looking for, they can type them in manually, or copy paste from an excel file, etc.
If they don't know what they're looking for, the list of Site Names would be in a Table displayed for the user, where they can then mark the rows they want on the visualization and push a button which will do a cursor through the list of marked Site Names, concatenate them together, and put them in the text box previously mentioned (Note: if you don't want to let them enter their list manually, you can leave off the text area, combine these next two pieces of code, and throw it straight into the SpecialFilterProperty).
Please note that cursors are slow; if you have more than a few thousand rows to cycle through, this may stall out for a few seconds.
Code for the button:
from Spotfire.Dxp.Application.Visuals import CrossTablePlot
from Spotfire.Dxp.Data import IndexSet
from Spotfire.Dxp.Data import RowSelection
from Spotfire.Dxp.Data import DataValueCursor
from Spotfire.Dxp.Data import DataSelection
TextFltr = ""
crossSource = Document.Data.Tables["Distinct_SiteNames"]
##Get a Row Count
rowCount = Document.Data.Tables["Distinct_SiteNames"].RowCount
##Index Set of all our rows
rowIndexSet=Document.ActiveMarkingSelectionReference.GetSelection(Document.Data.Tables["Distinct_SiteNames"]).AsIndexSet()
allRows = IndexSet(rowCount,True)
if rowIndexSet.IsEmpty != True:
allRows = rowIndexSet
colCurs = DataValueCursor.CreateFormatted(crossSource.Columns["Site_Name"])
##Optional: Loop through to determine average value
colTotal = ''
for row in crossSource.GetRows(allRows, colCurs):
colTotal += ', ' + colCurs.CurrentValue
if TextFltr == "":
TextFltr += colTotal[2:]
else:
TextFltr += colTotal
Document.Properties["SelectedSiteNames"] = TextFltr
from System.Collections.Generic import Dictionary
from Spotfire.Dxp.Application.Scripting import ScriptDefinition
import clr
scriptDef = clr.Reference[ScriptDefinition]()
Document.ScriptManager.TryGetScript("Change Special Filter Value", scriptDef)
params = Dictionary[str, object]()
Document.ScriptManager.ExecuteScript(scriptDef.ScriptCode, params)
At the bottom it references a second script; this is the script attached to the button that parses through the text area when the user wants to submit their selections and refresh the data table.
The General Code I've used is here, script titled "Change Special Filter Value", which allows delimiting by newline, tabs, commas, quotes, and a few others. Feel free to add or subtract here, depending on your user-base's needs.
strVals = Document.Properties["SelectedSiteNames"]
lst = ""
cnt = 0
x = 0
y = 0
z = 0
for letter in strVals:
if y == 1:
if letter == " ":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == ",":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == "\n":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == "\r":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == "'":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == '"':
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == '\t':
lst = lst + "'" + strVals[x:z] + "', "
y = 0
else:
if letter <> " " and letter <> "," and letter <> "\n" and letter <> "\r" and letter <> "'" and letter <> '"' and letter <> "\t":
if y == 0:
cnt += 1
print letter
x = z
y = 1
z += 1
if y == 1:
lst = lst + "'" + strVals[x:z] + "', "
print lst
lst = lst.upper()
if len(lst) > 0:
lst = lst[1:len(lst) - 3]
Document.Properties["SpecialFilterValue"] = lst
Step one is now complete! You have a list of all your selected site names in a property that you can now pass to your stored procedure.
Note: I believe there's a limit to the number of characters Spotfire can pass through a string value. In my previous testing, I think it's been over 500,000 characters (it's been a while, so I don't remember exactly), so you have a lot of leeway, but it does exist, and depending on which data source you're using, it may be lower.
Step Two: Alter the stored Procedure
Your stored procedure will basically be something along the lines of this:
Create procedure ProcA(In Start_Date date, IN End_Date date, In Site_Name text)
Begin
DECLARE #Script nvarchar(max) =
N'
Select * from TableA where day between Start_Date and End_Date and Site_Name in (' + #Site_Name + ') '
EXECUTE (#Script)
Downright easy in comparison!
(No loop after all! The bizarre use case I was remembering doesn't apply here, unless you're also using a data base that doesn't allow you to pass parameters directly...)

Using .Find in a Recursive Function

I am trying to find the row number in a sheet using the .Find function in a recursive function.
I set an object called Found = .Find.... and it works great... for a little bit. I set it when I'm 1 level of recursion deep, then set it again when I'm 2 levels deep. Then, my code finds the end of the path and starts backing up until it gets back to 1 level deep, but not my Found object has been re-declared and kept its values from the 2nd level. My other variables (ThisRow etc...) keep the value of the level that they are in, and that's what I would like to do with the object Found. Is there a way that I can declare Found locally so that it's value doesn't extend to the next function, and can't be overwritten in a deeper level? You can find my code below for reference.
Here is my current code - irrelevant parts cut out:
Public Function FindChildren()
ThisRow = AnswerRow 'Also declared before function call
BeenHereCell = Cells(ThisRow, "O").Address
If Range(BeenHereCell).Value = "Yes" Then
Exit Function 'That means we've already been there
End If
Range(BeenHereCell).Value = "Yes"
With Worksheets("MasterScore").Range("j1:j50000")
Set Found = .Find(NextQuestionID, LookIn:=xlValues)
If Not Found Is Nothing Then
firstAddress = Found.Address
NextCell = Found.Address
Do
AnswerRow = Range(NextCell).Row
FindChildren 'This is where it's recursive.
Set Found = .FindNext(Found)
NextCell = Found.Address
Loop While Not Found Is Nothing And Found.Address <> firstAddress
End If
End With
End Function
Now I have gotten around it by activating cells, but it makes my code a lot slower. Currently I am using this:
Set Found = Worksheets("MasterScore").Range("j1:j50000").Find(NextQuestionID, LookIn:=xlValues)
If Not Found Is Nothing Then
Count = 1
Do
Columns("J:J").Select
FirstFoundRow = Selection.Find(What:=NextQuestionID, After:=ActiveCell, LookIn:= _
xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=False, SearchFormat:=False).Row
For i = 1 To Count
Selection.FindNext(After:=ActiveCell).Activate
Next i
AnswerRow = ActiveCell.Row
If AnswerRow = FirstFoundRow And Count <> 1 Then Exit Do
FindChildren
Count = Count + 1
Loop
End If
This way, I don't have to set the value of the object again, but I have to iterate through it.FindNext quite a few times and each time it runs that line its also activating the row. I really just want something like.
AnswerRow = .Find(nth instance of NextQuestionID).Row
(I have about 50k rows and the count goes to about 20 pretty often so it really takes a while).
I'd appreciate any ideas! Currently my code is working, but it's going to take a good part of the day to complete, and I'll need to run this again at some point!
I ended up finding a way to speed it up a little bit. I think this could help someone so I will share what I've found.
It's not the best solution (I would have preferred to just declare the object locally so my other functions wouldn't change it's value), but at least with this I'm not looping through 20 or so finds every iteration of the Do Loop.
Set Found = Worksheets("MasterScore").Range("j1:j50000").Find(NextQuestionID, LookIn:=xlValues)
If Not Found Is Nothing Then
NextAnswerRange = "j" & 1 & ":" & "j50000" 'The first search will be from the beginning
Do
Set Found = Worksheets("MasterScore").Range(NextAnswerRange).Find(NextQuestionID, LookIn:=xlValues)
NextCell = Found.Address
AnswerRow = Range(NextCell).Row
NextAnswerRange = "j" & AnswerRow & ":" & "j50000"
If LastAnswerRange = NextAnswerRange Then Exit Function 'This would mean we've reached the end.
LastAnswerRange = NextAnswerRange
FindChildren
Loop
End If
End Function
So we know we've already covered our bases with previous ranges since it always finds the immediate next. We just change the range of the search each time and it will find the next value.
A weird thing about this solution is that if you are looking for a value among range 70 -> 50,000 and you have the answer your looking for on row 70, it will actually find the next row (it skips that first one). But, if there aren't any rows past 70 that have the answer, it will actually take the value from row 70. That meant that I couldn't do
NextAnswerRange = "j" & AnswerRow + 1 & ":" & "j50000"
because it would miss some values. Doing it without the + 1 meant at the end of the document I would end up searching the same last value over and over (it would never go back to Found Is Nothing) so I had to put in the check to see if the LastAnswerRange = NextAnswerRange.
I hope this helps someone. I don't think it's the most elegant solution, but it's a lot faster than what I had.

Access VBA - How is this code grouping all info for one policy together?

So, I inherited some code (below) from someone else and I'm trying to understand how it works. I understand msot of the code (though I'm pretty new to Access VBA) but the one part I don't get is how this code groups all the info for one policy together.
The situation is as follows. To get premium data for a specific policy, from our company database, we have to get it one coverage per line. But, I want all premiums, one for each coverage, all on the same line. So, this code puts it all together from many lines into one line. For simplicity, I knocked it down to 3 total coverages, though there are many more. As I read the code, it seems to assume that all the info for one policy is together, like the 1, 2, or 3 rows for a specific policy are in order. But, even when I, for example, order the table by the premium (amount) column, it still gets all the premium for one policy on one line. I don't see anywhere in the code that should make this work. The code is comparing the policy number on one line to the policy number on the next. If they are the same, group the premium together. If they are different, don't. Again, I could order the table so that the records for one policy are not together, but the end result still comes out right. Am I missing something? Is it something in Access doing it? Thanks for any help!
Option Compare Database
Option Explicit
' Premium is imported with one row for each coverage per policy, so possibly several rows per policy.
' This procedure takes several rows per policy and makes them into one row.
Sub ScrubPremium()
Dim i As Long, j As Long, k As Long
Dim NumRecords As Long, found As Long, UniqueCount As Long
Dim tempPolicyNum As String, tempCoverage As String, tempPremium As Single
Dim PolicyNumArray() As String, PremiumArray() As Single, TotalPremiumArray() As Single
Dim db As DAO.Database
Set db = CurrentDb
Dim infile As Variant, outfile As Variant
Set infile = db.OpenRecordset("Imported Premium")
CurrentDb.Execute "DELETE * FROM [Finalized Premium]"
Set outfile = db.OpenRecordset("Finalized Premium")
NumRecords = infile.RecordCount
ReDim PolicyNumArray(NumRecords)
ReDim PremiumArray(NumRecords, 3)
ReDim TotalPremiumArray(NumRecords)
infile.MoveFirst
'initialize PremiumArray
For i = 1 To NumRecords
For j = 1 To NumPremiums
PremiumArray(i, j) = 0
Next j
Next i
'populate arrays
UniqueCount = 0
For i = 1 To NumRecords
tempPolicyNum = infile![Policy_Number]
tempCoverage = infile![Coverage]
tempPremium = infile![Premium]
k = 0
found = 0
Do Until k = UniqueCount Or found = 1 'check for unique policy
If tempPolicyNum = PolicyNumArray(k + 1) Then
found = 1
Else
k = k + 1
End If
Loop
If found = 0 Then
UniqueCount = UniqueCount + 1
PolicyNumArray(k + 1) = tempPolicyNum
End If
Select Case tempCoverage
Case "Comprehensive"
j = 1
Case "Collision"
j = 2
Case Else
j = 3
End Select
PremiumArray(k + 1, j) = PremiumArray(k + 1, j) + tempPremium
TotalPremiumArray(k + 1) = TotalPremiumArray(k + 1) + tempPremium
infile.MoveNext
Next i
'Populate table
For i = 1 To UniqueCount
outfile.AddNew
outfile![Full Policy Number] = PolicyNumArray(i)
outfile![Comp Premium] = PremiumArray(i, 1)
outfile![Coll Premium] = PremiumArray(i, 2)
outfile![Other Premium] = PremiumArray(i, 3)
outfile![Total Premium] = TotalPremiumArray(i)
outfile.Update
Next i
infile.Close
outfile.Close
End Sub
The code is comparing the policy number on one line to the policy
number on the next. If they are the same, group the premium together.
If they are different, don't.
Almost.
There are two loops here. One walks through each row of your input file. On each row of the input file, a second loop walks through (potentially all of) PolicyNumArray, looking for policy numbers that match the number taken from the input file.
If I were you, I'd step through this with the debugger. Make sure it's doing what you expect it to do. I'd want to look closely at this part (some lines snipped).
UniqueCount = 0
For i = 1 To NumRecords
k = 0
Do Until k = UniqueCount

Allow user to separate dates with period in MS Access

I am working with an Access database where I have a form that contains several date entry fields. I have a new user that is used to using a period as a delimiter for dates so instead of "6/22/11" or "6-22-11", they enter "6.22.11". I would like to continue to allow this type of entry, but Access converts "6.22.11" to a time instead of a date. I have tried setting the format on the text box to "Short Date" with no help. I have also tried adding code to the "Lost Focus" event but that is to late since Access has already done the conversion. The "Before Update" event fires before the conversion but will not allow me to change the text in the textbox. Any ideas on how I can allow all three forms of date entry?
your above example
Private Sub Texto0_KeyPress(KeyAscii As Integer)
If Chr(KeyAscii) = "." Then
KeyAscii = Asc("/")
End If
End Sub
works for me.
Another aproximation is play with the BeforeUpdate and AfterUpdate events.
In BeforeUpdate you cannot modify de content of the control, but you can set a flag (a variable defined at module/form level) in the AfterUpdate event and change the content: it will trigger again the BeforeUpdate, but in this case, because is flagged you sould ignore it and unflag.
http://office.microsoft.com/en-us/access-help/control-data-entry-formats-with-input-masks-HA010096452.aspx
Input Mask.
I wrote the following function for a user who was used to entering 6 and 8 digit dates in input masks by just typing a string of numbers with no delimiter. You should be able to modify it for your purposes:
'---------------------------------------------------------------------------
' Purpose : Enables entry of 8-digit dates with no delimiters: 12312008
' Usage : Set OnChange: =DateCtlChange([Form].[ActiveControl])
' 8/ 6/09 : Allow entry of 6-digit dates with no delimiters
' (year 2019 and 2020 must still be entered as 8-digit dates)
'---------------------------------------------------------------------------
Function DateCtlChange(DateCtl As TextBox)
Dim s As String, NewS As String
On Error GoTo Err_DateCtlChange
s = DateCtl.Text
Select Case Len(s)
Case 6
If s Like "######" Then
If Right(s, 2) <> "19" And Right(s, 2) <> "20" Then
NewS = Left(s, 2) & "/" & Mid(s, 3, 2) & "/" & Mid(s, 5, 2)
End If
End If
Case 8
If s Like "########" Then
NewS = Left(s, 2) & "/" & Mid(s, 3, 2) & "/" & Mid(s, 5, 4)
End If
End Select
If IsDate(NewS) Then
DateCtl.Text = NewS
DateCtl.SelStart = Len(DateCtl.Text)
End If
Exit_DateCtlChange:
Exit Function
Err_DateCtlChange:
Select Case Err.Number
'Error 2101 is raised when we try to set the text to a date
' that fails the date control's validation
Case 2101 'The setting you entered isn't valid for this property.
'Log error but don't show user
Case Else
'Add your custom error logging here
End Select
Resume Exit_DateCtlChange
End Function
Access uses the system date and time format to determine how to translate a value. Another option - that would affect every program on this user's computer - is this: http://office.microsoft.com/en-us/access-help/change-the-default-date-time-number-or-measurement-format-HA010351415.aspx?CTT=1#BM2
Alternatively you can use spaces instead of slashes in dates in Access. Thus the user can use the left hand on the space bar and use the right hand on the numeric keyboard. I feel this is much easier to use than either the slash or the hyphen.