Dealing with edited fields left in blank on subform - vba

I am having a problem when an user decides to "clear/delete" a line entered on the form during the first update.
I have a form with a subform to give the user the possibility of multiple entries, however, after the user has first edited any field on the subform the respective record gets dirty and recorded, so if the user decides to not use anymore that record and deletes all the info for that line, that line which now is all blank still being recorded, and this is giving me blank lines in my data (The TransactionID and ID are not shown to the user on the form, I added it for the sake of this post).
This can also accidentally happen if on the "new" line, which is not dirty, the user hits any key by mistake, and even deleting it straight away, the record gets dirty for good, so it ends being recorded completely in blank.
I couldn't find a way to avoid recording this blank lines.
I would like to know if it is possible to have buttons to "add a new line" and "delete a line", just like, on the last line it would have a "+" to add new line (so the "new not dirty" line wouldn't appear automatically), and on the precedent lines would have a "-" to remove that specific record (this is just an idea, it can be in any kind of way).
Is that achievable? If it is not, is there a way to simply avoid blank lines to be saved? Like, saving jumping those blank lines, just like in a loop?
More information added for the sake of better explanation.
I didn't include the code before as there is no code behind this, this is just access natural recording set. I am not using DAO Record Set nor SQL to "save" the records (just to delete from table in a discard event), I am using normal bounded controls.
The modifications I did were on Parent form to always stay on the Current Record (Cycle) and On Load event it always goes to new record. Thus, the buttons Save and Cancel, are used to confirm or discard the new record created on the Parent form, because, as I have previously said, the subform is used to enter multiple records under the same record on the Parent Table, in other words, Relationship One-To-Many, therefore the button "Close" holds a simple code for deleting the last entry on the Parent Table (as it still on the current record) and as the tables have "Delete Cascade Relationship" all the information entered into the subform/Child Table get automatically deleted, so I just had to handle the data inserted into the Parent Table, whereas, the save button is used to get the confirmation of saving from the user, and make sure that the user will fill all the fields prior to save.
That is pretty much the code on this form, which is below.
But before the code, I would like to reinforce that, I have made use of the "BeforeUpdate" event, which works fine, it makes the user to add all the fields before opening a new record on the subform, but let's suppose that the user enters 10 records on the subform, then the user decides to "Delete" one of the records (which in this case is not the last one, so he cannot just press "ESC" and I cannot just run "Me.Undo"), so if the user clears the fields (just like in the images I have provided, as I don't know how to delete a record in this scenario yet), the before update event gets triggered and doesn't allow the user to do anything while he/she fills in the blank fields, not even to click in "Cancel". Therefore, in my "Save" button I check all the fields and if one of them is "dirty" them the user must fill all the subsequent fields, however, if all the fields are "empty", the user is still able to save.
In the beginning I thought that those "blank lines" wouldn't happen so often and I would have to clean them sporadically, but it is unbelievable, the amount of blank lines that have been created by the users. I have to clean them almost every week.
I really would like a way to create "+ add line" and "- delete line" buttons, because if the user wants to delete a line he just click on "-" button, the same for add new line.
Again, I appreciate the help. I am really struggling on this. Thanks all.
Private Sub btnSave_Click()
If IsNull(Me.TransactionDate) _
And IsNull(Me.SupplierPayee) _
And IsNull(Me.TotalReceipt) Then
MsgBox "Form in blank!", vbInformation + vbOKOnly, "Saving..."
Exit Sub
End If
If IsNull(Me.TransactionDate) _
Or IsNull(Me.SupplierPayee) _
Or IsNull(Me.TotalReceipt) Then
MsgBox "Please fill in all the fields", vbCritical + vbOKOnly, "Transaction not Saved"
Exit Sub
End If
Me![subfrmPCHeaderDetail].SetFocus
DoCmd.GoToRecord , , acFirst
If IsNull(Me.TransactionDate) _
Or IsNull(Me.SupplierPayee) _
Or IsNull(Me.TotalReceipt) _
Or IsNull(Form_frmPCHeaderDetail.Item) _
Or IsNull(Form_frmPCHeaderDetail.ItemAmount) _
Or IsNull(Form_frmPCHeaderDetail.VATRate) _
Or IsNull(Form_frmPCHeaderDetail.DescriptionPurpose) Then
MsgBox "Please fill in all the fields", vbCritical + vbOKOnly, "Transaction not Saved"
Exit Sub
ElseIf Form_frmPCHeaderDetail.ItemAmount = "0" Then
MsgBox "Amount cannot be €0.00", vbCritical + vbOKOnly, "Transaction not Saved"
Form_frmPCHeaderDetail.ItemAmount.SetFocus
Exit Sub
End If
If Not Me.TotalReceipt.Value = Form_frmPCHeaderDetail.GrossTotal _
Or IsNull(Me.TotalReceipt) Then
MsgBox "Please check the Receipt Amount Details as ""Gross Total"" and ""Receipt Total"" are not matching"
Exit Sub
End If
If Me.ReceiptScanned.AttachmentCount = 0 Then
MsgBox "Please scan the receipt and add it to this transaction", vbInformation + vbOKOnly, "Add the Receipt"
Me.AddReceiptScanned.SetFocus
Exit Sub
End If
If Me.TransactionDate > Date Then
MsgBox "Future Transaction Date not allowed", vbCritical + vbOKOnly, "Transaction not Saved"
Me.TransactionDate.SetFocus
Exit Sub
Else
Dim Result As Long
Result = MsgBox("Saving Transaction!" & vbNewLine & vbNewLine & _
"Attention!" & vbNewLine & vbNewLine & _
"You will not be able to delete nor modify any detail of this transaction!" & vbNewLine & vbNewLine & _
"Are you sure you would like to save this transaction?" & vbNewLine & vbNewLine & _
"Click ""YES"" to save, or click ""NO"" to return.", vbExclamation + vbYesNo, "Save Transaction?")
If Result = 6 Then
DoCmd.Save
DoCmd.Requery
Form_frmPCHeader.TransactionDate.SetFocus
DoCmd.GoToRecord , , acNewRec
MsgBox "Transaction added successfully to the Petty Cash Register", _
vbInformation + vbOKOnly, "Petty Cash transaction added"
End If
End If
End Sub
Private Sub btnClose_Click()
Dim LastID As Long
Dim Result As Long
If IsNull(TransactionID) Then
DoCmd.Close
Exit Sub
End If
LastID = TransactionID
MsgBox("Closing without saving!" & vbNewLine & vbNewLine & _
"If you want to discard the entries, click ""YES"", or click ""NO"" to return.", vbCritical + vbYesNo, "Discard Entries?")
If Result = 6 Then
Application.Echo False
Me.Painting = False
DoCmd.SetWarnings False
Me.TransactionDate = "31/12/2099"
Me.SupplierPayee = "Discarded"
Me.TotalReceipt = 0
DoCmd.Save
DoCmd.Requery
DoCmd.RunSQL "DELETE * FROM tblPCHeader WHERE TransactionID = " & LastID & ""
DoCmd.Close
Application.Echo True
DoCmd.SetWarnings True
End If
End Sub
Private Sub Form_Load()
DoCmd.GoToRecord , , acNewRec
End Sub

For a Delete (-) button, consider code:
DoCmd.SetWarnings False
DoCmd.RunCommand acCmdDeleteRecord
DoCmd.SetWarnings True
For Add (+) button, just move to new record row with code you already know.
Docmd.GoToRecord , , acNewRec

Related

Required Fields Before Update in Conjunction with Close Button

Novie VBA programmer here, looking for the ABC's direction.
I have 3 fields that I need to require in a form before the form is close.
I had previously require the fields within the Table; However, when I create a new contact to add to this Form I need to Requery the results of the added contact. When I Requery and the required fields are Required from the Table I reach a stuck, since I need to add the new contact but cannot Requery until the fields are filled (and I cannot do that until I have the right contact).
I know this is in the wrong place but this is the code I have thus far attached to my "Close" button.
Private Sub SaveCloseBtn_Click()
If Nz([PlaintiffName], "") = "" Then
MsgBox "Plaintiff Name is required.", vbExclamation, "Required Field"
Cancel = True
End If
If Nz([LawFirm], "") = "" Then
MsgBox "Law Firm is required.", vbExclamation, "Required Field"
Cancel = True
Me.[LawFirm].SetFocus
End If
If [PlaintiffName] = True Then
If [LawFirm] = True Then
If MsgBox("You are about to exit this case. Are you sure?", vbYesNo, "Warning!") = vbYes Then
DoCmd.Close
Else
DoCmd.CancelEvent
End If
End Sub
I tried creating Required fields in "BeforeUpdate" but for whatever reason, it does not work in conjunction with the docmd.close form function.

MS-ACCESS Write Conflict Issue

I am having a write conflict issue. Not sure what the cause could be. The issue occurs when multiple users are using the Access database.
Below is a schematic of whats happening. The (Main) Form displays a Subform (with some data displayed from the Table). The user can double click a name to pull up that individuals details, via the (Details) Form that pops up. If changes are made, the user can save the details (actually, a prompt pops up asking if you would like to save when closing the window).
When saving, then going to open up another individuals details, the write conflict pops up.
The code when closing the (Details) Form:
Private Sub Form_BeforeUpdate(Cancel As Integer)
' This procedure checks to see if the data on the form has
' changed. If the data has changed, the procedure prompts the
' user to continue with the save operation or to cancel it. Then
' the action that triggered the BeforeUpdate event is completed.
Dim ctl As Control
On Error GoTo Err_BeforeUpdate
' The Dirty property is True if the record has been changed.
If Me.Dirty Then
' Prompt to confirm the save operation.
If MsgBox("Do you want to save?", vbYesNo + vbQuestion, _
"Save Record") = vbNo Then
Me.Undo
End If
End If
Exit_BeforeUpdate:
Exit Sub
Err_BeforeUpdate:
MsgBox Err.Number & " " & Err.Description
Resume Exit_BeforeUpdate
End Sub
Which was taken from the Microsoft website (I think).
Is there something wrong with my data structure that is causing this?
If more information is needed, I can edit the post to include more code etc.
EDIT 1: Reversed Subform arrow. The Subform simply displays data from the Table.
EDIT 2: Backend is Access. To open the (Details) Form from the Subform:
Private Sub First_Name_DblClick(Cancel As Integer)
DoCmd.OpenForm "Details", , , "[First Name] = '" & Me![First Name] & "' and [Last Name] = '" & Me![Last Name] & "'"
End Sub
EDIT 3:
While looking at the (Main) Form, the Subform has a Source Object set to the name of the Subform, which I call QuerySF. The QuerySF Form has a Record Source set to the Table. The pop-up (Details) Form has a Record Source set to the Table. I can instead set the Record Source for the (Details) Form to my Query, but I tried this and it did not help.
Would inserting a Requery somewhere help?
I have noticed the pop-up occurs every third time the (Details) Form is opened via the Subform.
If you don't want to save, use Cancel = True. BeforeUpdate would not trigger if form is not dirty so don't bother with If Me.Dirty.
' Prompt to confirm the save operation.
If MsgBox("Do you want to save?", vbYesNo + vbQuestion, "Save Record") = vbNo Then
Cancel = True
Me.Undo
End If

Subform blocking Recordset.AddNew (Error 3027)

Having a subform displaying the same table used in recordset makes the table read only (error 3027). Getting rid of the subform fixes the problem. Similar setup works in my other form interface.
I tried to delete the subform, which fix the accessibility problem. But that would defeat the original UX purpose.
Option Explicit
Public UnitRS As DAO.Recordset
Public ModelRS As DAO.Recordset
Public NameUnitRecords As String
Public NameModelRecords As String
Public Sub Form_Load()
'Initialization
NameUnitRecords = "tblBatteriesMainRecordsUnits"
NameModelRecords = "tblBatteriesRecordsModels"
End Sub
Private Sub SetUnitRecordsets()
'Set the path to the Battery Records table
Set UnitRS = CurrentDb.OpenRecordset(NameUnitRecords, dbOpenDynaset)
End Sub
Private Sub txtBatteryID_AfterUpdate()
'Set the recordset path for unit records
Call SetUnitRecordsets
'do a findfirst search for the Battery ID, using value from textbox txtBatteryID
UnitRS.FindFirst "[Battery ID]=" & txtBatteryID
'If no matching record, leave the other fields empty
If UnitRS.NoMatch Then
'If there is a matching record, then, grab the model number
Else
'as there is an existing record with model number, run a search on the model number and grab the model info
End If
'close recordset
UnitRS.Close
'check if the button can be enabled
End Sub
Private Sub cmbModelNumber_AfterUpdate()
'Set the recordset path for model records
'do a findfirst search for the Model Number, using value from combobox cmbModelNumber
'If no matching record, leave the other fields empty
If ModelRS.NoMatch Then
'If there is a matching record, then, grab the Model Info
Else
End If
'close recordset
'check if the button can be enabled
End Sub
Private Sub btnSaveAndCLear_Click()
Dim Response, strOldModelNumber
'Set the recordset path for unit records
Call SetUnitRecordsets
'Set the recordset path for model records
'close all related tables, queries and forms
DoCmd.Close acTable, NameUnitRecords, acSaveYes
DoCmd.Close acForm, "frmSubBatteriesMainRecordsUnits", acSaveYes
'If a new model record is required
ModelRS.FindFirst "[Model Number]='" & cmbModelNumber & "'"
If ModelRS.NoMatch Then
'msg box confirm model information
'If user chooses yes
If Response = vbYes Then
'create new model record
'this block could be done with "With...End" format for less error vulerability?
'nah, unless it is repetitively called, it's too much work to inplement just the .addnew part
'requery the two combobox to reflect newest changes
'User chooses no
Else
'popup a message telling the user the battery and model record is not logged, but don't clear the field.
Exit Sub
End If
End If
'need to find the record first, otherwise it will edit the first one
UnitRS.FindFirst "[Battery ID]=" & txtBatteryID
'store the old model number value
strOldModelNumber = UnitRS("Model Number")
'New record
If UnitRS.NoMatch Then
'create a new battery record
UnitRS.AddNew
UnitRS("Battery ID") = Me.txtBatteryID
'If this is an edit on existing record
Else
'if the new value is the same as the old one
If strOldModelNumber = cmbModelNumber Then
'msgbox the same value, no change is done to the database
MsgBox "the data is the same as the old record, no change is made to the record", vbInformation, "Same data"
Exit Sub
Else
'msg box confirm edit
Response = MsgBox("Please confirm edit on existing record: " & Chr(13) & Chr(10) & "BatteryID: " & txtBatteryID & Chr(13) & Chr(10) & "Model Number: " & cmbModelNumber, vbYesNo, "New Model Record Dectected")
'If user chooses yes
If Response = vbYes Then
'goto edit mode
UnitRS.Edit
'if user chooses no
Else
'msgbox notify the user nothing is changed in the database
MsgBox "Battery and Model record not logged, you may retry logging", vbInformation, "Record not logged"
Exit Sub
End If
End If
End If
'both changes the model number and comment field anyway
UnitRS("Model Number") = Me.cmbModelNumber
UnitRS("Comment") = Me.txtComment
'commit update
UnitRS.Update
UnitRS.Close
'clear all flieds
'reset all locks
'requery the sub form to reflect the newest changes
Me.subFrmBatteryRecords.Requery
End Sub
What I would like to achieve is to have a display in the form interface to show the content of the actual record table, so that the user knows what is in the table.
There is nothing wrong with my code. There is a property option in the form object, it's called "record lock", somehow mine had "All Records" selected. By making it "No locks", the accessibility problem is gone.

MS Access '16 - Query not pulling a value from a field

[Disclaimer: I'm self-taught and a total novice!]
I have a FORM with which a QUERY pulls data and uses it to populate a REPORT. As the end user finishes the report and clicks a button, the following is supposed to happen:
1) The FORM saves all the data in a new record on the TABLE 2) A query pulls that record by the ID (which is autonumbered) from the FORM 3) The QUERY populates a REPORT with the data from the TABLE 4) The FORM and QUERY close - no need to save.
The QUERY pulls all the data from the corresponding TABLE with the following criteria: [Forms]![Data_Input_Form]![ID]
However, my REPORT comes up blank! Eek!
I have a similar QUERY that pulls data from the same TABLE and populates it to a similar REPORT with the following criteria: Like Nz([Forms]![Home_Form]![Incident_ID_Lookup_text],"*")
Unsurprisingly, when I added this to the QUERY that wasn't working how I wanted it to, it reported ALL the previous records.
'------------------------------------------------------------
' Add Report [and Open Report] Button Click
'
'
'------------------------------------------------------------
Private Sub Add_Rpt_Btn_Click()
If MsgBox("Are you sure? No backsies.", vbYesNo, "Add Report?") = vbNo Then
Exit Sub
End If
'Check for Necessary Fields and Add New Record
If (IsNull(Me.Person_Filing) Or IsNull(Me.Nature_Lst) Or IsNull(Me.Location_Cmb) Or IsNull(Me.Summary) Or IsNull(Me.Narrative)) = True Then
MsgBox "Looks like you left some important information out. Please fill out all fields with an asterisk.", vbOKOnly, Whoops
Exit Sub
Else
DoCmd.GoToRecord , , acNewRec
End If
'Run Query to Open Report
DoCmd.OpenQuery "Form_to_Report_Qry"
DoCmd.OpenReport "Incident_Report_1", acViewReport, , [ID] = [Forms]![Data_Input_Form]![ID]
'Close Query without Saving
DoCmd.Close acQuery, "Form_to_Report_Qry", acSaveNo
'Close Form without Saving
DoCmd.Close acForm, "Data_Input_Form", acSaveNo
End Sub
The REPORT needs to populate with the most recent record, but it keeps coming up blank.
That's because you move a new (empty) record - having no ID.
I guess, all you need is to use the current ID of the form - and use the correct syntax for the filter:
Private Sub Add_Rpt_Btn_Click()
If MsgBox("Are you sure? No backsies.", vbYesNo, "Add Report?") = vbNo Then
Exit Sub
End If
' Check for Necessary Fields and Add New Record
If (IsNull(Me.Person_Filing) Or IsNull(Me.Nature_Lst) Or IsNull(Me.Location_Cmb) Or IsNull(Me.Summary) Or IsNull(Me.Narrative)) = True Then
MsgBox "Looks like you left some important information out. Please fill out all fields with an asterisk.", vbOKOnly, Whoops
Exit Sub
End If
' If not saved, save the current record.
If Me.Dirty = True Then
Me.Dirty = False
End If
DoCmd.OpenReport "Incident_Report_1", acViewReport, , "[ID] = " & Me![ID].Value & ""
' Close Form without Saving
DoCmd.Close acForm, Me.Name, acSaveNo
End Sub

More info when you click a field in a subform table in pop up form

I am trying to get a window to popup when you click that small box beside a field in my subform table, and then click the button "more info". This form would pop up with all the extra information about that record; in text boxes so you can edit it. On this form it pops out, it only has a button that has back and when you click back, it saves all the changes made in the text boxes in the record you were viewing more information about... and updates it. It then brings back up the previous form, and updates the subform on that form so it shows all changes made to the table. I am not looking for the More Info button to show more info for all records. I am looking for it to bring up more info for that one record you selected only, and to get more info on another, you must click back and select a new one. How would I go about setting up the More Info button so it pops up the new form with the information?
For an example of what I mean by pressing the more info button, here is a picture:
In the picture, it is a table. Clicking that small space highlights the whole record, and you are able to push DEL on your keyboard to delete the record (though that doesn't matter in the case, just pointing out it's a table.)
Code I have in my More Info button:
Private Sub CustomerMoreInfoBtn_Click()
On Error Resume Next
DoCmd.Close acForm, "CustomerInfoF"
DoCmd.DeleteObject acQuery, "CustomerMoreInfoQ"
On Error GoTo Err_CustomerMoreInfoBtn_Click
Dim qdef As DAO.QueryDef
Set qdef = CurrentDb.CreateQueryDef("CustomerMoreInfoQ", _
"SELECT * " & _
"FROM CustomersT " & _
"WHERE CustomerID = " & txtCustID.Value)
DoCmd.OpenForm "CustomerInfoF"
Exit_CustomerMoreInfoBtn_Click:
Exit Sub
Err_CustomerMoreInfoBtn_Click:
MsgBox Err.Description
Resume Exit_CustomerMoreInfoBtn_Click
End Sub
Code in the form it self:
Private Sub Form_Close()
On Error Resume Next
DoCmd.DeleteObject acQuery, "CustomerMoreInfoQ"
End Sub
In the below example your button is named cmdMoreInfo. When you click on the button it creates the MyCustDetail query based on the value of the customer id. Then it opens the frm_CustDetailEdit form. Set the forms Record Source to MyCustDetail.
In order to be able to create the query it must not already exist. Therefore, I check beforehand and delete it if it exists. Also, when closing the main form it also will delete the query since it would no longer be needed anyway.
Private Sub cmdMoreInfo_Click()
On Error Resume Next
DoCmd.Close acForm, "frm_CustDetailEdit"
DoCmd.DeleteObject acQuery, "MyCustDetail"
On Error GoTo Err_cmdMoreInfo_Click
Dim qdef As DAO.QueryDef
Set qdef = CurrentDb.CreateQueryDef("MyCustDetail", _
"SELECT * " & _
"FROM tbl_Customer " & _
"WHERE CustomerID = " & txtCustID.value)
DoCmd.OpenForm "frm_CustDetailEdit"
Exit_cmdMoreInfo_Click:
Exit Sub
Err_cmdMoreInfo_Click:
MsgBox Err.Description
Resume Exit_cmdMoreInfo_Click
End Sub
Private Sub Form_Close()
On Error Resume Next
DoCmd.DeleteObject acQuery, "MyCustDetail"
End Sub