Basically I want to flip the line number values in the two records when someone changes them in the subform
so if i have line:
12345 and I rename 5 to 3 I want 5 renumbered to 3 and 3 renumbered to 5 so I would have 12543 but they reshuffle to 12345 but the records switch places correctly
However I get an error (see below) and if I change record 1 it can't find any records
the code I have so far is:
Private Sub OrderLineNumber_AfterUpdate()
Dim rst As DAO.Recordset
Set rst = Me.Recordset
Dim recNum As Integer
Dim recVal As Double
Dim move As Integer
Dim i As Integer
recNum = Me.CurrentRecord
Me.Requery
DoCmd.GoToRecord , , acGoTo, recNum
recVal = rst!OrderLineNumber.Value
rst.MoveFirst
Do Until rst.EOF
i = rst!OrderLineNumber.Value
If i = recVal Then
move = Me.CurrentRecord
End If
rst!OrderLineNumber.Value = recVal #Here
DoCmd.GoToRecord , , acGoTo, recNum
rst!OrderLineNumber.Value = i
rst.MoveNext
Loop
End Sub
and is failing at #Here with error update or cancelupdate without add new or edit
Before changing a value on a DAO record you need to run the Edit method. And then you need to run the Update method after changing it.
Private Sub OrderLineNumber_AfterUpdate()
Dim rst As DAO.Recordset
Set rst = Me.Recordset
Dim recNum As Integer
Dim recVal As Double
Dim move As Integer
Dim i As Integer
recNum = Me.CurrentRecord
Me.Requery
DoCmd.GoToRecord , , acGoTo, recNum
recVal = rst!OrderLineNumber.Value
rst.MoveFirst
Do Until rst.EOF
i = rst!OrderLineNumber.Value
If i = recVal Then
move = Me.CurrentRecord
End If
rst.Edit
rst!OrderLineNumber.Value = recVal #Here
rst.Update
DoCmd.GoToRecord , , acGoTo, recNum
rst.Edit
rst!OrderLineNumber.Value = i
rst.Update
rst.MoveNext
Loop
End Sub
Here's a function I use that does something quite similar. I have a main form with buttons with up and down arrows on them, and a datasheet subform (continuous form would work too). Every row in the subform has a RowOrder field with a value in it starting from 1. These values get assigned as the user adds new records.
When the user wants to change the order of the records in the subform they just use the arrow buttons located on the main form. One thing not in my code here is checks for new record, or checks to see if they have no records entered. Another thing my function does not do is reorder or fix all the rows. It only affects the row in focus and the one above or below it. Here's the code:
Private Sub cmdUp_Click()
'Put an "Up" button on one of your forms and pass the function a correct form object
Call MoveCurrentRecordUpOrDown("Up", Me.subform1.Form)
End Sub
Private Sub cmdDown_Click()
'Put a "Down" button on one of your forms and pass the function a correct form object
Call MoveCurrentRecordUpOrDown("Down", Me.subform1.Form)
End Sub
Public Function MoveCurrentRecordUpOrDown(sDirection As String, frm As Form)
Dim lPos As Integer
Dim iCurRowOrder As Integer
iCurRowOrder = Nz(frm!RowOrder, 0)
'Check to see if the record is already up against one of the ends
Dim iChange As Integer
Select Case sDirection
Case "Up"
If iCurRowOrder = 1 Then Exit Function 'Cannot move record up
iChange = -1
Case "Down"
If iCurRowOrder = frm.Recordset.RecordCount Then Exit Function 'Cannot move record down
iChange = 1
End Select
lPos = frm.Recordset.AbsolutePosition + iChange
Dim rs As DAO.Recordset
Set rs = frm.RecordsetClone
If Not (rs.EOF And rs.BOF) Then
rs.MoveFirst
Do Until rs.EOF = True
If rs!RowOrder = iCurRowOrder Then
rs.Edit
rs!RowOrder = iCurRowOrder + iChange
rs.Update
ElseIf rs!RowOrder = (iCurRowOrder + iChange) Then
rs.Edit
rs!RowOrder = iCurRowOrder
rs.Update
End If
rs.MoveNext
Loop
frm.Requery
If lPos > (frm.Recordset.RecordCount - 1) Then
lPos = (frm.Recordset.RecordCount - 1)
End If
frm.Recordset.AbsolutePosition = lPos
End If
rs.Close
Set rs = Nothing
End Function
Related
I want to duplicate a form with 3 subforms - to explain it simple: imagine a recipe (main form: some general data; sub form 1: list of ingredients, sub form 2: instructions; sub form 3: prices; sometimes the recipes change only the type of flour, so I don't want to type everything again but just have the same form with a new uniqe ID and this one change in the ingredients list)
duplicating the main form is easy, but the sub forms are empty. there are some ideas i found online, but it seems incredibly difficult (i am coding beginner), see for example Microsofts suggestion: https://support.microsoft.com/en-us/help/208824/acc2000-how-to-duplicate-a-main-form-and-its-subform-detail-records
I basically want to have the same content with a "+1" to the unique ID.
Any ideas?
Thanks!
You can have a button on the main form to run this code to copy parent record and all child records without external queries, nor a requery of the subforms.
Here you copy two subforms. Just extend it with similar code to Copy child records 3 as you have three subforms:
Private Sub CopyButton_Click()
Dim rst As DAO.Recordset
Dim rstAdd As DAO.Recordset
Dim fld As DAO.Field
Dim Count As Integer
Dim Item As Integer
Dim Bookmark As Variant
Dim OldId As Long
Dim NewId As Long
' Copy parent record.
Set rstAdd = Me.RecordsetClone
Set rst = rstAdd.Clone
' Move to current record.
rst.Bookmark = Me.Bookmark
OldId = rst!Id.Value
With rstAdd
.AddNew
For Each fld In .Fields
With fld
If .Attributes And dbAutoIncrField Then
' Skip Autonumber or GUID field.
Else
.Value = rst.Fields(.Name).Value
End If
End With
Next
.Update
' Pick Id of the new record.
.MoveLast
NewId = !Id.Value
End With
' Store location of new record.
Bookmark = rstAdd.Bookmark
' Copy child records 1.
Set rstAdd = Me!subChild1.Form.RecordsetClone
Set rst = rstAdd.Clone
If rstAdd.RecordCount > 0 Then
rstAdd.MoveLast
rstAdd.MoveFirst
End If
Count = rstAdd.RecordCount
For Item = 1 To Count
With rstAdd
.AddNew
For Each fld In .Fields
With fld
If .Attributes And dbAutoIncrField Then
' Skip Autonumber or GUID field.
ElseIf .Name = "FK" Then
' Skip master/child field.
.Value = NewId
Else
.Value = rst.Fields(.Name).Value
End If
End With
Next
.Update
End With
rst.MoveNext
Next
' Copy child records 2.
Set rstAdd = Me!subChild2.Form.RecordsetClone
Set rst = rstAdd.Clone
If rstAdd.RecordCount > 0 Then
rstAdd.MoveLast
rstAdd.MoveFirst
End If
Count = rstAdd.RecordCount
For Item = 1 To Count
With rstAdd
.AddNew
For Each fld In .Fields
With fld
If .Attributes And dbAutoIncrField Then
' Skip Autonumber or GUID field.
ElseIf .Name = "FK" Then
' Skip master/child field.
.Value = NewId
Else
.Value = rst.Fields(.Name).Value
End If
End With
Next
.Update
End With
rst.MoveNext
Next
rst.Close
rstAdd.Close
' Move to the new recordcopy.
Me.Bookmark = Bookmark
Set fld = Nothing
Set rstAdd = Nothing
Set rst = Nothing
End Sub
Note please, that subChildx represent the names of the subform controls, which may differ from the names of the subforms themselves.
I've created a public function in Access. My goal is if the next business day is a holiday I'm calculating one extra day of interest for payoff purposes. Below is the working code I have. The issue I'm haveing is I'm dealing with over 35000 records and the time it takes to run the query is too long. If there is a better way of do this I will definitely give it a try. Thanks!
Public Function HolidayInterest(Perdiem As Currency) As Currency
Dim db As Database
Dim rst As Recordset
Select Case DatePart("w", Date)
Case 6
NextBusDay = Date + 3
Case 7
NextBusDay = Date + 2
Case Else
NextBusDay = Date + 1
End Select
Set db = CurrentDb
Set rst = db.OpenRecordset("tbl_Holidays", dbOpenDynaset)
If Not (rst.EOF And rst.BOF) Then
Do While Not rst.EOF
If rst("HolidayDate") = NextBusDay Then
HolidayInterest = Perdiem
Else
HolidayInterest = 0
End If
rst.MoveNext
Loop
Else
'MsgBox "There are no records in the recordset."
End If
'MsgBox "Finished looping through records."
rst.Close 'Close the recordset
Set rst = Nothing 'Clean up
db.Close
Set db = Nothing
End Function
Here is one solution to avoid the opening the Holiday table 35,000 times. It will load all dates into an Array (only once), then use that array for comparing. But I am curious if your existing process ever worked correctly 100% of the time -- if that table contained more than one holiday? Specifically, when you read the holiday table (regardless of the sort order), then in your loop "If rst("HolidayDate") = NextBusDay Then", since you don't exit the loop if you get a match, your subroutine should always return the results of what happens when checking the last date in the table? Also I didn't find a Dim for NextBusDay, so I added it.
Option Compare Database
Option Explicit
Public blnSetArray As Boolean
Public dHolidays() As Date
Public iHolidays As Integer
Public Function HolidayInterest(Perdiem As Currency) As Currency
Dim db As Database
Dim rst As Recordset
Dim i As Integer
Dim iLoop As Integer
Dim NextBusDay As Date
' Save an array of dates the first time
If blnSetArray = False Then
Set db = CurrentDb
Set rst = db.OpenRecordset("tbl_Holidays", dbOpenDynaset)
i = 0
If Not (rst.EOF And rst.BOF) Then
rst.MoveLast
rst.MoveFirst
iHolidays = rst.RecordCount
ReDim dHolidays(rst.RecordCount)
Do While Not rst.EOF
i = i + 1
dHolidays(i) = rst("HolidayDate")
rst.MoveNext
Loop
End If
blnSetArray = True
rst.Close 'Close the recordset
Set rst = Nothing 'Clean up
db.Close
Set db = Nothing
End If
Select Case DatePart("w", Date)
Case 6
NextBusDay = Date + 3
Case 7
NextBusDay = Date + 2
Case Else
NextBusDay = Date + 1
End Select
HolidayInterest = 0 ' Set as default
If iHolidays > 0 Then
For iLoop = 1 To iHolidays
If dHolidays(iLoop) = NextBusDay Then
HolidayInterest = Perdiem
Exit For ' No need to stay in loop
End If
Next iLoop
Else
'MsgBox "There are no records in the recordset."
End If
'MsgBox "Finished looping through records."
End Function
Function MyTest()
blnSetArray = False
Debug.Print HolidayInterest(100#)
End Function
Apart from the Perdiem value you pass as an argument to your function, the only thing that will affect the return value of your function is the current system date as returned by Date. In other words, on any given day your function will always return either the Perdiem value or zero.
Therefore, we can use a Static variable named TheDateToday to hold the current date and you will only have to hit the [tbl_Holidays] table once on any given day:
Option Compare Database
Option Explicit
Public Function HolidayInterest(Perdiem As Currency) As Currency
Dim db As DAO.Database, qdf As DAO.QueryDef, rst As DAO.Recordset
Dim NextBusDay As Date
Static TheDateToday As Date, NextBusDayIsHoliday As Boolean
If CLng(TheDateToday) <> CLng(Date) Then
TheDateToday = Date
Select Case DatePart("w", TheDateToday)
Case 6
NextBusDay = DateAdd("d", 3, TheDateToday)
Case 7
NextBusDay = DateAdd("d", 2, TheDateToday)
Case Else
NextBusDay = DateAdd("d", 1, TheDateToday)
End Select
Set db = CurrentDb
Set qdf = db.CreateQueryDef("", _
"PARAMETERS prmDate DateTime;" & _
"SELECT * FROM tbl_Holidays WHERE HolidayDate=[prmDate]")
qdf!prmDate = NextBusDay
Set rst = qdf.OpenRecordset(dbOpenSnapshot)
NextBusDayIsHoliday = Not (rst.EOF And rst.BOF)
rst.Close
Set rst = Nothing
Set qdf = Nothing
Set db = Nothing
End If
If NextBusDayIsHoliday Then
HolidayInterest = Perdiem
Else
HolidayInterest = 0
End If
End Function
Sincerely in the dark. My Code:
Public Property Get rowCount() As Integer
rowCount = Counter
End Property
Public Property Let rowCount(ByRef inte As Integer)
Counter = inte
End Property
Private Sub Form_Timer() 'Timer
Dim dbs As DAO.Database
Dim rs As DAO.Recordset
Dim Caption As Field, Form As Field, Count As Integer, holder As Integer, item As String
Dim strForms() As String
Set dbs = CurrentDb
Set rs = dbs.OpenRecordset("MainMenu", dbOpenDynaset)
ReDim strForms(1 To rs.RecordCount())
If rs.RecordCount <> 0 Then
For c = 1 To rs.RecordCount() Step 1 '!!!THIS IS THE PROBLEM!!!
MsgBox CStr(c)
MsgBox rs("Caption")
strForms(c) = rs("Caption")
rs.MoveNext
MsgBox rs("Caption")
Next c
End If
rowCount = 1
holder = rowCount()
If holder <= rs.RecordCount() Then
Me.Command10.Caption = strForms(holder)
rowCount = holder + 1
Else
rowCount = 1
Me.Command10.Caption = strForms(holder)
End If
End Sub
I added all those message boxes in my effort to debug. All I need is that counter to go up. No idea why it is not. Why will this thing not increment?!
The best way is to use rs.MoveFirst, rs.MoveNext and rs.EOF to check for end of records. The following VBA will do what you want.
'Open up a recordset on our table
Set dbs = CurrentDb
Set rs = dbs.OpenRecordset("MyTable", dbOpenDynaset)
'Did we find any records?
If rs.RecordCount > 0 Then
'Move to first record
rs.MoveFirst
'Iterate through each record
Do
'Do stuff with the currentrecord
MsgBox ("Next record ID is: " + CStr(rs("ID")))
'Move to next record
rs.MoveNext
'Exit when we hit the end of the recordset
Loop While rs.EOF <> True
End If
'Close the recordset
rs.Close
Using the RecordCount property might be the problem.
It essesntially just counts the number of times rs.MoveNext had been called.
Try switching the code to a loop like this:
Dim L As Long
Do Until rs.EOF
L = L + 1
MsgBox rs.RecordCount
MsgBox L
rs.MoveNext
Loop
Access Recordsets aren't as easy as .NET DataTables but they've been around a lot longer.
http://msdn.microsoft.com/en-us/library/office/bb208624(v=office.12).aspx
When there is only one record, this expression gives correct calculation, but when there are more than one record the values of last record calculated is reflected in all Total_Time (unbound text box).I have given on load and on open code of the report. Please help me.
Private Sub Report_Load()
strSQL = "SELECT * FROM [q_1ltduty]"
Set db = CurrentDb
Set rs = db.OpenRecordset(strSQL)
rs.MoveFirst
Do While Not rs.EOF
'Assigning values of fields to varia
strtime1 = Op_Time
strtime2 = Cl_Time
'This is a simple expression my code has some more detailed calculations
strhrs = strtime2 - strtime1
strtotalhrs = strhrs
'Printing the variable in Total_Time textbox(unbound)
Me.Total_Time.Value = strtotalhrs
rs.MoveNext
Loop
rs.Close
db.Close
Set db = Nothing
Set rs = Nothing
End Sub
Private Sub Report_Open(Cancel As Integer)
strSQL = "SELECT * FROM [q_1ltduty]"
Me.RecordSource = strSQL
Debug.Print strSQL
Exit Sub
ErrHandler:
MsgBox Err.Description
End Sub
Your variable strtotalhrs contains only the value of strhrs for one record, so each time you go through the loop, the value for the current record erases the value for the previous record. What you should do instead of erasing the value is adding to it.
Before the loop (if it has not already been done):
strtotalhrs = 0
then in the loop, instead of strtotalhrs = strhrs:
strtotalhrs = strtotalhrs + strhrs
and that should give you the sum total instead of the last value.
I have a continuous form with a command button in the footer that updates the current record with a value that will make the record no longer show in the form once requeried. I want the user to be able to click the button and once the record is updated, move to the next record, not the first as is the default behaviour. I have code that I would think should work but doesn't, it keeps going back to the first record on the form.
Private Sub cmdCloseReq_Click()
Dim ReqID As String
ReqID = Me.txtReqID
Dim rst As Recordset
Dim strBookmark As Integer
Set rst = Me.RecordsetClone
rst.MoveNext
If Not rst.EOF Then ' if not end-of-file
strBookmark = rst.Bookmark ' ...save the next record's bookmark
Dim cmd As New ADODB.Command
With cmd
.ActiveConnection = CurrentProject.Connection
.CommandType = adCmdStoredProc
.CommandText = "spUpdateLOG_ReqCompleteDate"
.Parameters("#ReqID") = ReqID
.Execute
End With
Me.Requery
Me.Bookmark = strBookmark
End If
Set rst = Nothing
End Sub
OK, I found a solution based on rene's post. I grab the next records primary key, do an update then after the requesry I find the next record and set the bookmark to that. Here is the code:
Private Sub cmdCloseReq_Click()
Dim ReqID As String
ReqID = Me.txtReqID
Dim rst As New ADODB.Recordset
Dim strBookmark As String
Set rst = Me.RecordsetClone
With rst
.Find "[ReqID] = '" & ReqID & "'"
.MoveNext
strBookmark = rst.Fields(0)
End With
If Not rst.EOF Then ' if not end-of-file
' ...save the next record's bookmark
Dim cmd As New ADODB.Command
With cmd
.ActiveConnection = CurrentProject.Connection
.CommandType = adCmdStoredProc
.CommandText = "spUpdateLOG_ReqCompleteDate"
.Parameters("#ReqID") = ReqID
.Execute
End With ' ...delete the record
Me.Requery
Set rst = Me.RecordsetClone
With rst
.Find "[reqID]= " & strBookmark
Me.Bookmark = .Bookmark
End With
Else
With cmd
.ActiveConnection = CurrentProject.Connection
.CommandType = adCmdStoredProc
.CommandText = "spUpdateLOG_ReqCompleteDate"
.Parameters("#ReqID") = ReqID
.Execute
End With ' ...delete the record
Me.Requery
End If
Set rst = Nothing
I recall that Bookmarks are invalidated after a Requery. If you have a primary key you can better grab that one and after requery move the current record to the previously obtained primary key