Microsoft Access Components Database

Created Date:

1 Mar 2024

Last Updated / Completion Date:

Status:

Complete

Project Types:

in

For years I managed a list of the integrated circuits I own in an Excel spreadsheet. I wanted to add hierarchical information to the list, including specifications, attributes, pinout images, and links to datasheets. While this is possible in Excel, it is much better handled by a database.

I had a look at various free applications, including inventory management, but found nothing that would give me the functionality I wanted.

Having done a fair amount of Access database development in the 1990s, I decided to use Access as the platform for a Components Database. This would give me the flexibility to edit at the data table level and easily make changes to the front-end as required.

In addition to organising the data and making it searchable, I wanted to be able to physically locate a part quickly in on of my component drawers / trays, as I have far too many to simply search for a label visually (see photo below). I thought about using barcodes, as I have a scanner, but quickly rules that out when I discovered that a simple reference system would be better.

The drawers have limited space for a label. I wanted to see the part number on the label, which left too little space for a barcode. In addition to that is the fact that a human cannot read a barcode, so you would always need a scanner to check the drawer reference.

So instead I decided to give each set of drawers an alpha character, e.g. A, B, C, D. Then I numbered the drawers according to row and column. This meant that I could very quickly locate a drawer and thus a component, by a 3 digit alphanumeric code.

I have a Brother P-Touch P750W label printer, so I printed labels for each drawer (work in progress).

This code is then used in the database (top right pink box), with a number after the dot denoting the section of the drawer the component is in.

The files (images, datasheets) are organised under a single root folder, with subfolders for each category and type. Clicking the PDF icon opens the datasheet in the default PDF application (Adobe Reader for me).

Supporting forms:

What started out as a simple idea turned into a complicated beast very quickly once I remembered how to enhance the front-end with VBA! See below for the code I wrote just for the main components form above!

VBA code for the main components form.
VB
Option Compare Database
Option Explicit    
    
Private Sub cmbCategory_AfterUpdate()

    If (cmbCategory.Value = 0 Or IsNull(cmbCategory.Value)) Then
        cmbType.RowSource = ""
        cmbType.Value = 0
    Else
        cmbType.Value = 0
        cmbType.RowSource = "qryTypesForCategory"
        cmbType.Requery
    End If
    
    Call cmbType_AfterUpdate

End Sub

Private Sub cmbSubType_AfterUpdate()


 On Error GoTo ImageError
    
    If (Not IsNull(cmbSubType.Column(2)) And cmbSubType.Column(2) <> "") Then
        imgComponent.Picture = Left(Application.CurrentProject.Path, InStrRev(Application.CurrentProject.Path, "\") - 1) & cmbSubType.Column(2)
    Else
        imgComponent.Picture = Left(Application.CurrentProject.Path, InStrRev(Application.CurrentProject.Path, "\") - 1) & "\Files\DefaultTypeImage.jpg"
    End If
        
    Exit Sub
    
ImageError:

    MsgBox "Error loading the image: " & Left(Application.CurrentProject.Path, InStrRev(Application.CurrentProject.Path, "\") - 1) & cmbSubType.Column(2), vbCritical, "Error loading image"
    
End Sub

Private Sub cmbType_AfterUpdate()

    If (cmbType.Value = 0 Or IsNull(cmbType.Value)) Then
        cmbSubType.RowSource = ""
        cmbSubType.Value = 0
        
    Else
        cmbSubType.Value = 0
        cmbSubType.RowSource = "qrySubTypesForType"
        cmbSubType.Requery
    End If

End Sub

Private Sub cmbRemoveFilter_Click()

    Me.FilterOn = False
    Me.Filter = ""
    cmbRemoveFilter.Visible = False
    lblRecordCount.BackColor = &HFFFFFF
    lblRecordCount.ForeColor = &H666666
        
End Sub

Private Sub cmbCategory_GotFocus()

    cmbCategory.Requery

End Sub

Private Sub cmbCategory_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

    cmbCategory.Dropdown
    
End Sub

Private Sub cmbSection_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

    cmbSection.Dropdown
    
End Sub

Private Sub cmbContainer_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

    cmbContainer.Dropdown
    
End Sub

Private Sub cmbManufacturer_GotFocus()

    cmbManufacturer.Requery

End Sub

Private Sub cmbManufacturer_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

    cmbManufacturer.Dropdown

End Sub

Private Sub cmbMountType_GotFocus()

    cmbMountType.Requery
    
End Sub

Private Sub cmbMountType_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

    cmbMountType.Dropdown
    
End Sub

Private Sub cmbPackage_GotFocus()

    cmbPackage.Requery
    
End Sub

Private Sub cmbPackage_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
    cmbPackage.Dropdown
    
End Sub

Private Sub cmbSubType_GotFocus()

    cmbSubType.Requery
    
End Sub

Private Sub cmbSubType_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
    cmbSubType.Dropdown
    
End Sub


Private Sub cmbType_GotFocus()

    cmbType.Requery
    
End Sub

Private Sub cmbType_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

    cmbType.Dropdown
    
End Sub

Private Sub cmdClearDatasheet_Click()

    txtDatasheetPath.Value = Null
    imgDatasheet.Visible = False
    cmdDatasheet.Visible = False
    
End Sub

Private Sub cmdClose_Click()

    DoCmd.Close acForm, Me.Name, acSavePrompt

End Sub

Private Sub cmdCopyRecord_Click()

    Dim FieldList As String
    Dim SQL As String
    Dim CurrentRecordID As Integer
    Dim NewRecordID As Integer
    
    On Error GoTo ErrorMessage
    
        CurrentRecordID = txtComponentID.Value
        
        FieldList = "Qty, PartNumber, Description, CategoryID, TypeID, SubTypeID, PackageID, MountTypeID, ManufacturerID, Container, Tray, Section, Location, DatasheetPath"
            
        SQL = "INSERT INTO tblComponent ( " & FieldList & " ) SELECT " & FieldList & " FROM tblComponent WHERE tblComponent.ComponentID = " & CurrentRecordID
            
        CurrentDb.Execute SQL, dbFailOnError
        
        NewRecordID = CurrentDb.OpenRecordset("SELECT @@Identity")(0)

        SQL = "INSERT INTO tblSpec ( ComponentID, SpecDescription, UnitPrefixID, UnitID, UnitValue, Spec ) " & _
            "SELECT " & NewRecordID & ", tblSpec.SpecDescription, tblSpec.UnitPrefixID, tblSpec.UnitID, tblSpec.UnitValue, tblSpec.Spec " & _
            "FROM tblSpec WHERE ComponentID = " & CurrentRecordID & ";"
 
        CurrentDb.Execute SQL, dbFailOnError
        
        SQL = "INSERT INTO tblAttribute ( ComponentID, AttributeName, AttributeValue ) " & _
            "SELECT " & NewRecordID & ", AttributeName, AttributeValue " & _
            "FROM tblAttribute WHERE ComponentID = " & CurrentRecordID & ";"

        CurrentDb.Execute SQL, dbFailOnError
        
        SQL = "INSERT INTO tblImage ( ComponentID, ImageDescription, ImagePath ) " & _
            "SELECT " & NewRecordID & ", ImageDescription, ImagePath " & _
            "FROM tblImage  WHERE ComponentID = " & CurrentRecordID & ";"

        CurrentDb.Execute SQL, dbFailOnError
        
        Me.Requery
        DoCmd.GoToRecord , "", acLast

        Exit Sub

ErrorMessage:
    
    MsgBox err.Description, vbCritical, "Record Duplication Error"

End Sub

Private Sub cmdDatasheet_Click()

    Dim BasePath As String
    BasePath = Left(Application.CurrentProject.Path, InStrRev(Application.CurrentProject.Path, "\") - 1)
    
    'On Error GoTo DatasheetOpenError
    If Not IsNull(txtDatasheetPath.Value) Then
        Application.FollowHyperlink BasePath & txtDatasheetPath.Value
    End If
    Exit Sub
    
DatasheetOpenError:
    MsgBox "The datasheet could not be found."

End Sub

Private Sub cmdDecriptionBreakout_Click()

    'DoCmd.OpenForm "frmDescription", acNormal, , "ComponentID = " & Me.ComponentID
    DoCmd.OpenForm "frmDescription", acNormal
    'frmDescription.Forms.txtDescription = "Hello"
    Forms.frmDescription.txtDescription.Value = txtDescription.Value

End Sub

Private Sub cmdDelete_Click()

    Dim CurrentRecordID As Integer
    Dim SQL As String
    CurrentRecordID = txtComponentID.Value
            
    On Error GoTo SqlDeleteError
    
    If (Not Form.NewRecord) Then
        If MsgBox("Delete record?", vbYesNo + vbExclamation + vbDefaultButton2, "Warning") = vbYes Then
            DoCmd.SetWarnings False
            DoCmd.GoToControl Screen.PreviousControl.Name
            DoCmd.RunCommand acCmdDeleteRecord
            DoCmd.SetWarnings True
            
            SQL = "DELETE FROM tblSpec WHERE ComponentID = " & CurrentRecordID & ";"
            CurrentDb.Execute SQL, dbFailOnError
        
            SQL = "DELETE FROM tblAttribute WHERE ComponentID = " & CurrentRecordID & ";"
            CurrentDb.Execute SQL, dbFailOnError
                
            SQL = "DELETE FROM tblImage WHERE ComponentID = " & CurrentRecordID & ";"
            CurrentDb.Execute SQL, dbFailOnError
            
            If (Me.CurrentRecord > Me.Recordset.RecordCount) Then
                DoCmd.GoToRecord , "", acLast
            End If
            
        End If
        
    End If
    
    If (Form.NewRecord And Form.Dirty) Then
        DoCmd.RunCommand acCmdUndo
    End If
        
    Exit Sub

SqlDeleteError:
    
    MsgBox err.Description, vbCritical, "Record Deletion Error"

End Sub

Private Sub cmdDeleteAttribute_Click()

    On Error GoTo ErrorMessage
    If Not (IsNull(Forms!frmComponent!frmAttributeList!AttributeID)) Then
        Dim SQL As String
        SQL = "DELETE FROM tblAttribute WHERE AttributeID = " & Forms!frmComponent!frmAttributeList!AttributeID
        CurrentDb.Execute SQL, dbFailOnError
        frmAttributeList.Requery
    End If

    Exit Sub
    
ErrorMessage:
    
   MsgBox err.Description, vbCritical, "Record Delete Error"

End Sub

Private Sub cmdDeleteSpec_Click()
    
    On Error GoTo ErrorMessage
    If Not (IsNull(Forms!frmComponent!frmSpecList!SpecID)) Then
        Dim SQL As String
        SQL = "DELETE FROM tblSpec WHERE SpecID = " & Forms!frmComponent!frmSpecList!SpecID
        CurrentDb.Execute SQL, dbFailOnError
        frmSpecList.Requery
    End If
    
    Exit Sub
    
ErrorMessage:
    
   MsgBox err.Description, vbCritical, "Record Delete Error"

End Sub

Private Sub cmdEditLock_Click()

If (cmdEditLock.Caption = "Edit") Then
    cmdEditLock.Caption = "Lock"
    cmdEditLock.BackColor = RGB(192, 79, 21)
    Me.AllowEdits = True
    Me.AllowDeletions = True
    Me.AllowAdditions = True
    cmdDeleteSpec.Enabled = True
    cmdOpenFrmSpec.Enabled = True
    cmdSelectDatasheet.Enabled = True
    cmdClearDatasheet.Enabled = True
    cmdCopyRecord.Enabled = True
    cmdSave.Enabled = True
    cmdNewRecord.Enabled = True
    cmdDecriptionBreakout.Enabled = True
    Forms!frmComponent!frmImage!cmdSelectImageFile.Enabled = True
    Forms!frmComponent!frmImage!cmdRemoveImage.Enabled = True
    Forms!frmComponent!frmImage!cmdNewRecord.Enabled = True
    Forms!frmComponent!frmImage!cmdDeleteRecord.Enabled = True
    frmImage.Locked = False
    frmSpecList.Locked = False
    frmAttributeList.Locked = False
    cmdDelete.Enabled = True
Else
    If Me.Recordset.RecordCount > 0 Then
        cmdEditLock.Caption = "Edit"
        cmdEditLock.BackColor = &H929292
        Me.AllowEdits = False
        Me.AllowDeletions = False
        Me.AllowAdditions = False
        cmdDeleteSpec.Enabled = False
        cmdOpenFrmSpec.Enabled = False
        cmdSelectDatasheet.Enabled = False
        cmdClearDatasheet.Enabled = False
        cmdCopyRecord.Enabled = False
        cmdSave.Enabled = False
        cmdNewRecord.Enabled = False
        cmdDecriptionBreakout.Enabled = False
        Forms!frmComponent!frmImage!cmdSelectImageFile.Enabled = False
        Forms!frmComponent!frmImage!cmdRemoveImage.Enabled = False
        Forms!frmComponent!frmImage!cmdNewRecord.Enabled = False
        Forms!frmComponent!frmImage!cmdDeleteRecord.Enabled = False
        frmImage.Locked = True
        frmSpecList.Locked = True
        frmAttributeList.Locked = True
        cmdDelete.Enabled = False
    End If
End If

End Sub

Private Sub cmdFirstRecord_Click()
    
    DoCmd.GoToRecord , "", acFirst

End Sub

Private Sub cmdLastRecord_Click()

    DoCmd.GoToRecord , "", acLast

End Sub

Private Sub cmdNewRecord_Click()

    frmAttributeList.Enabled = False
    frmAttributeList.Form.AllowAdditions = False
    DoCmd.GoToRecord , "", acNewRec
    lblRecordCount.Caption = Me.Recordset.RecordCount + 1 & " of " & Me.Recordset.RecordCount + 1 & " * "

End Sub

Private Sub cmdPreviousRecord_Click()

    'If (Me.Recordset.AbsolutePosition + 1 > 1) Then
        
    If (Me.CurrentRecord > 1) Then
        DoCmd.GoToRecord , "", acPrevious
    End If

End Sub

Private Sub cmdNextRecord_Click()

    ' If you want to have the Next Record button create a new record, just use '<=' below instead.
    
    If (Me.CurrentRecord < Me.Recordset.RecordCount) Then
        DoCmd.GoToRecord , , acNext
    End If
    
    ' Me.NewRecord is 0 for existing records and -1 for new record.
    
    If (Me.NewRecord < 0) Then
        lblRecordCount.Caption = Me.Recordset.RecordCount + 1 & " of " & Me.Recordset.RecordCount + 1
    End If
   
End Sub


Private Sub cmdOpenFrmManufacturer_Click()

    DoCmd.OpenForm "frmManufacturer", acNormal, "", "", , acNormal

End Sub

Private Sub cmdOpenFrmSpec_Click()

    DoCmd.OpenForm "frmSpec", acNormal, "", "", , acDialog, Me.ComponentID
    frmSpecList.Requery
 
End Sub

Private Sub cmdOpenFrmCategory_Click()

    DoCmd.OpenForm "frmCategory", acNormal, "", "", , acNormal

End Sub

Private Sub cmdOpenFrmType_Click()

    DoCmd.OpenForm "frmType", acNormal, "", "", , acNormal

End Sub

Private Sub cmdOpenFrmSubType_Click()
 
    DoCmd.OpenForm "frmSubType", acNormal, "", "", , acNormal

End Sub

Private Sub cmdOpenFrmMountType_Click()

    DoCmd.OpenForm "frmMountType", acNormal, "", "", , acNormal
    
End Sub

Private Sub cmdOpenFrmPackage_Click()

     DoCmd.OpenForm "frmPackage", acNormal, "", "", , acNormal
    
End Sub

Private Sub cmdSave_Click()

    On Error GoTo SaveError
    DoCmd.RunCommand acCmdSaveRecord

    Exit Sub

SaveError:
    MsgBox err.Description, vbCritical, "Failed to save the record"
    
End Sub

Private Sub cmdSelectDatasheet_Click()

    Dim fp As Object
    Set fp = Application.FileDialog(msoFileDialogOpen)
    Dim BasePath As String
    BasePath = Left(Application.CurrentProject.Path, InStrRev(Application.CurrentProject.Path, "\") - 1)
    
    fp.Filters.Clear
    fp.Title = "Select Datasheet"
    fp.AllowMultiSelect = False
    fp.ButtonName = "Select"
    'fp.InitialFileName = BasePath & "\Files"
    fp.Filters.Add "PDF Files", "*.pdf"
    'fp.Filters.Add "All Files", "*.*"
    If (fp.Show) Then
         txtDatasheetPath.Value = Replace(fp.SelectedItems(1), BasePath, "")
    End If
    imgDatasheet.Visible = True
    cmdDatasheet.Visible = True

End Sub

Private Sub Form_ApplyFilter(Cancel As Integer, ApplyType As Integer)

    Me.Recordset.MoveLast

End Sub

Private Sub Form_Current()
      
    Me.Painting = False
    
    If (Me.FilterOn) Then
        cmbRemoveFilter.Visible = True
        lblRecordCount.BackColor = RGB(192, 79, 21)
        lblRecordCount.ForeColor = &HFFFFFF
    End If
    
    If (Not IsNull(Me.ComponentID)) Then
        If (cmdEditLock.Caption = "Lock") Then
            cmdOpenFrmSpec.Enabled = True
        End If
        frmAttributeList.Enabled = True
        frmAttributeList.Form.AllowAdditions = True
    Else
        cmdOpenFrmSpec.Enabled = False
        frmAttributeList.Enabled = False
        frmAttributeList.Form.AllowAdditions = False
    End If
    
    lblRecordCount.Caption = Me.CurrentRecord & " of " & Me.Recordset.RecordCount
    
    If (cmbCategory.Value = 0 Or IsNull(cmbCategory.Value)) Then
        cmbType.RowSource = ""
        cmbType.Value = 0
    Else
        cmbType.RowSource = "qryTypesForCategory"
        cmbType.Requery
    End If
    
    If (cmbType.Value = 0 Or IsNull(cmbType.Value)) Then
        cmbSubType.RowSource = ""
        cmbSubType.Value = 0
    Else
        cmbSubType.RowSource = "qrySubTypesForType"
        cmbSubType.Requery
    End If
    
    If (IsNull(txtDatasheetPath.Value) Or txtDatasheetPath.Value = "") Then
        imgDatasheet.Visible = False
        cmdDatasheet.Visible = False
    Else
        imgDatasheet.Visible = True
        cmdDatasheet.Visible = True
    End If
       
    On Error Resume Next

    Me.frmImage.Form.Painting = False
    Me.frmImage.Form.Recordset.MoveLast
    Me.frmImage.Form.Recordset.MoveFirst
    Me.frmImage.Form.Painting = True
    
    Me.Painting = True
    
     On Error GoTo ImageError
    
    If (Not IsNull(cmbSubType.Column(2)) And cmbSubType.Column(2) <> "") Then
        imgComponent.Picture = Left(Application.CurrentProject.Path, InStrRev(Application.CurrentProject.Path, "\") - 1) & cmbSubType.Column(2)
    Else
        imgComponent.Picture = Left(Application.CurrentProject.Path, InStrRev(Application.CurrentProject.Path, "\") - 1) & "\Files\DefaultTypeImage.jpg"
    End If
        
    Exit Sub
    
ImageError:

    MsgBox "Error loading the image: " & Left(Application.CurrentProject.Path, InStrRev(Application.CurrentProject.Path, "\") - 1) & cmbSubType.Column(2), vbCritical, "Error loading image"
    
End Sub

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)

    ' Disables the F5 (refresh) keypress, which performs a ReQuery on the form, resetting the psoition of the recordset.
    ' The form's Key Preview property must be set to Yes for this to work.
    
    If KeyCode = vbKeyF5 Then
        KeyCode = 0
    End If
      
End Sub

Private Sub Form_Load()

    Me.ShortcutMenu = False
    
    If Me.Recordset.RecordCount > 0 Then
        frmImage.Form.AllowAdditions = True
        frmImage.Form.AllowEdits = True
        frmImage.Form.AllowDeletions = True
        frmAttributeList.Form.AllowDeletions = True
        frmSpecList.Form.AllowDeletions = True
    End If
    
End Sub

Private Sub Form_Open(Cancel As Integer)
    
    On Error Resume Next
    If Me.Recordset.RecordCount > 0 Then
        DoCmd.GoToRecord , "", acLast
        DoCmd.GoToRecord , "", acFirst
    End If
    
End Sub

Private Sub txtTray_KeyUp(KeyCode As Integer, Shift As Integer)
    
    If (Len(txtTray.Text) > 2) Then
        txtTray.Text = Left(txtTray.Text, 2)
    End If

End Sub

Public Sub Form_MouseWheel(ByVal Page As Boolean, ByVal Count As Long)

    On Error Resume Next
    
    If chkMouseWheelScroll = True Then
        If (Count > 0) And (Me.CurrentRecord > 1) Then
            DoCmd.GoToRecord , , acPrevious
        ElseIf (Count < 0) And (Me.CurrentRecord < Me.Recordset.RecordCount) Then
            DoCmd.GoToRecord , , acNext
        End If
    End If

End Sub

Private Sub cmdSearch_Click()

    On Error Resume Next
    DoCmd.GoToControl Screen.PreviousControl.Name
    DoCmd.RunCommand acCmdFind

End Sub

I obviously didn’t need to go this far, but software development is also a hobby, and I really enjoyed developing this database! The good thing about this is that I have ‘access’ to the relational data stored in the tables, regardless of what I do with the front-end. For example I could migrate the data to MariaDB or any other database backend, and develop a web front-end.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *