Calling a macro from Excel ribbon control - VBA - vba

So, I have a macro, LoadData, that I want to call in two separate macros, TransmitNewAndOldRecords and refresh, which are in turn called from a button on a custom tab of the Excel ribbon.
The TransmitNewAndOldRecords works fine. Here is the code for it. You can see the call at the second to last line of the macro.
Sub TransmitNewAndOldRecords(control As IRibbonControl)
'Reference Microsoft ActiveX Data Objects 6.1 Library
Dim conn As ADODB.Connection
Dim cmd As ADODB.Command
Dim pgconn As String
Dim Year As String
Dim SQLDelete As String
Dim SQLInsert As String
Set conn = New ADODB.Connection
Set cmd = New ADODB.Command
Year = ActiveWorkbook.Sheets("SQL Creator").Range("Year").Value
pgconn = "Driver={PostgreSQL};" & _
"Server = me;" & _
"Port = 1234;" & _
"Database = database;" & _
"Uid = me;" & _
"Pwd = me;"
conn.ConnectionString = pgconn
conn.Open "PostgreSQL35W", "me", "me"
cmd.CommandType = adCmdText
cmd.ActiveConnection = conn
'Tests if there are any records to delete. If so, define SQLDelete variable and execute SQL string that is assigned to SQLDelete variable.
If WorksheetFunction.CountIf(Range("DELETE"), "Y") <> 0 Then
SQLDelete = DeleteRecords(ActiveWorkbook.Sheets("Budget").Range("DELETE"), Year)
cmd.CommandText = SQLDelete
cmd.Execute
End If
'Tests if there are any records to insert. If so, define SQLInsert variable and execute SQL string that is assgined to SQLInsert variable.
If WorksheetFunction.CountIf(Range("INSERT"), "Y") <> 0 Then
SQLInsert = InsertRecords(ActiveWorkbook.Sheets("Budget").Range("INSERT"), Year)
cmd.CommandText = SQLInsert
cmd.Execute
End If
conn.Close
'If there are no new/old records to insert/delete, then display message box stating that. IOF
If WorksheetFunction.CountIf(Range("DELETE"), "Y") = 0 And WorksheetFunction.CountIf(Range("INSERT"), "Y") = 0 Then
MsgBox "No data to insert/delete into/from table.", vbOKOnly
Else
MsgBox "Data saved successfully!", vbOKOnly
End If
Set conn = Nothing
Set cmd = Nothing
Call LoadData
End Sub
I also call the LoadData macro in a very simple macro, refresh (seen below), that just calls LoadData. I get an error (Wrong number of arguments or invalid property assignment) when I run refresh. However, if I add the "control as iRibbonControl" parameter to LoadData, it works just fine when called from refresh, but then I get an "Argument not optional" error in the TransmitNewAndOldRecords macro above when calling LoadData.
Sub refresh(control As IRibbonControl)
Call LoadData
End Sub
I'm really confused, because if I add the "control as IRibbonControl" parameter to LoadData, then the refresh macro works, but the TransmitNewAndOldRecords macro errors out. But, if I don't add the "control as IRibbonControl" parameter to LoadData, then the refresh macro errors out and the TransmitNewAndOldRecords works.
Does anybody know why this is happening?
Here is the LoadData macro in case it's needed:
Sub LoadData()
Dim conn As ADODB.Connection
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset
Dim pgconn As String
Dim Team As Range
Dim FC As Range
Dim SQL As String
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
Set cmd = New ADODB.Command
Set Team = ActiveWorkbook.Sheets("Info").Range("Info_Team")
Set FC = ActiveWorkbook.Sheets("Info").Range("Info_FundingCategory")
SQL = ActiveWorkbook.Sheets("SQL Creator").Range("BudgetSQL")
pgconn = "Driver={PostgreSQL};" & _
"Server = me;" & _
"Port = 1234;" & _
"Database = database;" & _
"Uid = me;" & _
"Pwd = me;"
conn.ConnectionString = pgconn
conn.Open "PostgreSQL35W", "me", "me"
cmd.CommandType = adCmdText
cmd.ActiveConnection = conn
cmd.CommandText = SQL
rs.Open cmd
ActiveWorkbook.Sheets("Budget").Range("A11:Y500").ClearContents
ActiveWorkbook.Sheets("Budget").Range("A11").CopyFromRecordset rs
rs.Close
conn.Close
End Sub

Related

Runtime Error '13' Type Mismatch clicking Cancel on VBA Input Box

I am using an input box to pass a value to a stored procedure. However, when I click cancel I get a Runtime Error '13' Type Mismatch error. I tried to include an exit sub command but it hasn't worked. Code below. Any help would be appreciated. Thanks.
Option Explicit
Sub reverse_posted()
Const PROC = "ashcourt_concrete_balfour_reverse_posting"
Dim con As ADODB.Connection, cmd As ADODB.Command, i As Long
i = InputBox("Invoice Number to be re-posted")
If Len(i) < 1 Then Exit Sub
Set con = New ADODB.Connection
con.Open "Provider=SQLOLEDB;Data Source=ashcourt_app1;" & _
"Initial Catalog=ASHCOURT_Weighsoft5;" & _
"Integrated Security=SSPI;Trusted_Connection=Yes;"
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = con
.CommandType = adCmdStoredProc
.CommandText = PROC
.Parameters.Append .CreateParameter("P1", adInteger, adParamInput)
.Execute , i
End With
con.Close
Set con = Nothing
MsgBox ("Invoice Number " & i & " reversed")
End Sub
InputBox returns a string.
When you press Cancel it returns "" which isn't a number, so you get the error (you'd get the same error if you entered text and hit OK).
Maybe use two variables - i As String and lngI As Long.
Dim i As String, lngI As Long
i = InputBox("Invoice Number to be re-posted")
If IsNumeric(i) Then
lngI = CLng(i)
Else
Exit Sub
End If
An InputBox always returns text, thus an empty string when cancelled, so use Val:
i = Val(InputBox("Invoice Number to be re-posted"))
If i = 0 Then Exit Sub

Is it possible to send off info row by row from Access to QuickBooks?

Currently I have the following code that allows me to insert values into specific fields in QuickBooks.
I am trying to add fields from a table into QuickBooks row by row:
See picture ex:
Example:
At the end of each row there is a column for sending off the entries to QuickBooks. How can I modify my code to have this function work?
Public Sub exampleInsert()
Const adOpenStatic = 3
Const adLockOptimistic = 3
Dim oConnection
Dim oRecordset
Dim sMsg
Dim sConnectString
Dim sSQL
sConnectString = "DSN=Quickbooks Data;OLE DB Services=-2;"
sSQL = "Insert into customer (Name, FullName, CompanyName) values ('Testing VB', 'Full Name', 'Test Company Name')"
Set oConnection = CreateObject("ADODB.Connection")
Set oRecordset = CreateObject("ADODB.Recordset")
oConnection.Open sConnectString
oConnection.Execute (sSQL)
sMsg = sMsg & "Record Added"
MsgBox sMsg
Set oRecordset = Nothing
Set oConnection = Nothing
End Sub
UPDATE:
I added:
sConnectString = "DSN=Quickbooks Data;OLE DB Services=-2;"
sSQL = "Insert into customer (Name, CompanyName) Select Num, Description From TestTable"
Set oConnection = CreateObject("ADODB.Connection")
Set oRecordset = CreateObject("ADODB.Recordset")
oConnection.Open sConnectString
oConnection.Execute (sSQL)
sMsg = sMsg & "Record Added"
MsgBox sMsg
But I get the error "Invalid table name: TestTable" how can I get this SQL script to see my Access table?
To add the form's current record values to your queries, you just pull the value (e.g. Me.txtDescription). I would recommend you use the ADODB.Command object, so you can parameterize your SQL and avoid SQL injection:
Option Explicit
Const adOpenStatic As Integer = 3
Const adLockOptimistic As Integer = 3
Const CONNECTION_STRING As String = "DSN=Quickbooks Data;OLE DB Services=-2;"
Private Sub Command10_Click()
Dim cn As ADODB.Connection
Dim cmd As ADODB.Command
Dim param As ADODB.Parameter
Dim sMsg As String
' set up ADODOB connection
Set cn = New ADODB.Connection
cn.Open CONNECTION_STRING
' set up ADODB command object
Set cmd = New ADODB.Command
cmd.ActiveConnection = cn
' note that we're using unnamed parameters,
' with the ? symbol
cmd.CommandText = _
"INSERT INTO customer " & _
"(Name, CompanyName) " & _
"VALUES " & _
"(?, ?)"
' add form values as command parameters
cmd.Parameters.Append cmd.CreateParameter( _
Type:=adVarChar, _
Size:=255, _
value:=Me.txtNumber)
cmd.Parameters.Append cmd.CreateParameter( _
Type:=adVarChar, _
Size:=255, _
value:=Me.txtDescription)
' now that we have the command set up with its params,
' we can just execute it:
cmd.Execute
sMsg = "Record Added"
MsgBox sMsg
Set param = Nothing
Set cmd = Nothing
cn.Close: Set cn = Nothing
End Sub
Of course, you'll have to use the actual names of your textboxes.
Also, please notice a couple of additional modifications I a made to your original code:
I have Option Explicit defined. You may already have this in your code, but if not, you need it. That way, any variables used have to be declared. For more information, see the Microsoft Docs
I moved your ADODB constants outside your sub. Ideally, you'd either use early binding and add the ADODB library reference (so you don't need to define these yourself), or add them in a separate module, so you can use them in any of your forms.
I also added your connection string as a constant outside your sub. Again, this should probably be in a separate module (e.g. modConstants) you can easily refer to from anywhere in your project.
I improved the indentation of your code.
I explicitly added the types for your declarations (Dim sSQL as String rather than just Dim sSQL). Note that if you declare a variable without a type, it defaults to the Variant type, instead of String (which you want). See Microsoft Docs for more information.

How to export SQL statement results to an Excel File

I have an Access DataBase and a form in Excel VBA. All the data I input into the DB is input through the VBA form.
This DB contains all the benefits cards we already received this year in the company. But the same employee can ask for the card twice or more, so we'll have more than one record on the DB for him.
What I need is when the number of records is greater than one, the SQL statement result should appear in a Excel report.
I use the SELECT (*) COUNT statement to know when there is more than one record that is compatible with the search criterion. But I can't make the result appear in an Excel file.
Here is my code:
Public Function Relatorio()
Dim sql As String
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim rel As String
Set cn = New ADODB.Connection
cn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & enderecoDB & ";Jet OLEDB:Database"
cn.Open
Set rs = New ADODB.Recordset
sql = "INSERT INTO OPENROWSET('Microsoft.ACE.OLEDB.12.0', 'Excel 12.0;Database=" & enderecoDB & ";', 'SELECT * FROM [Planilha1$]') SELECT * FROM controle WHERE BP = " & controlectform.nmbpbox.Value & ";"
rs.Open sql, cn
End Function
When I run this code it gives me a message saying something like:
Can't locate the OPENROWSET Table exit
I'm not able to install new programs, so I need to do this using only Excel VBA and the Access DB.
How can I make this work?
I don't believe Access supports the OPENROWSET, dynamic table you're working with there. I have a lot of old projects that do this though, so here's my method
Public Function Relatorio()
Dim sql As String
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim rel As String
Set cn = New ADODB.Connection
cn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & enderecoDB & ";Jet OLEDB:Database"
cn.Open
Set rs = New ADODB.Recordset
dim path_To_XLSX
dim name_of_sheet
path_To_XLSX = "c:\temp\output.xlsx"
name_of_sheet = "Planilha1"
sql = sql = "SELECT * INTO [Excel 12.0;Database=" & path_To_XLSX & "]." & name_of_sheet & " FROM controle WHERE BP = '" & controlectform.nmbpbox.Value & "';"
rs.Open sql, cn
'If this application is in an unsecure environment, use the following code instead! This is to prevent a SQL injection, security concern here.
'As it is an Access Database, this is likely overkill for this project
'Create Command Object.
Set Cmd1 = New ADODB.Command
Cmd1.ActiveConnection = cn
cmd1.CommandText = "SELECT * FROM controle INTO [Excel 12.0;Database=" & path_To_XLSX & "]." & name_of_sheet & " WHERE BP = ?"
' Create Parameter Object.
Set Param1 = Cmd1.CreateParameter(, adInteger, adParamInput, 5) 'use adVarchar for strings(versus adInteger), https://www.w3schools.com/asp/met_comm_createparameter.asp
Param1.Value = controlectform.nmbpbox.Value
Cmd1.Parameters.Append Param1
Set Param1 = Nothing
Set Rs = Cmd1.Execute()
End Function
I had this challenge so many years ago that I cant remember but this link ring the bell. check if it help.
https://stackoverflow.com/a/28889774/382588
try { connw.Open(); OleDbCommand command; command = new OleDbCommand( "Update Deliveries " + "SET Deliveries.EmployeeID = ?, Deliveries.FIN = ?, Deliveries.TodaysOrders = ? , connw); command.Parameters.Add(new OleDbParameter("#EMPID", Convert.ToDecimal(empsplitIt[1]))); command.Parameters.Add(new OleDbParameter("#FIN", truckSplit[1].ToString())); command.Parameters.Add(new OleDbParameter("#TodaysOrder", "R")); catchReturnedRows = command.ExecuteNonQuery();//Commit connw.Close(); } catch (OleDbException exception) { MessageBox.Show(exception.Message, "OleDb Exception"); }
you can use this, to print the actual SQL.
Private Sub Command2_Click()
Dim db As Database
Dim qr As QueryDef
Set db = CurrentDb
For Each qr In db.QueryDefs
TextOut (qr.Name)
TextOut (qr.SQL)
TextOut (String(100, "-"))
Next
End Sub
Public Sub TextOut(OutputString As String)
Dim fh As Long
fh = FreeFile
Open "C:\Users\rs17746\Desktop\Text_Files\sample.txt" For Append As fh
Print #fh, OutputString
Close fh
End Sub
Here is one more version for you. This will export the results of each query, each to a separate text file.
Private Sub Command0_Click()
Dim qdf As QueryDef
Dim strFileName As String
For Each qdf In CurrentDb.QueryDefs
If Left(qdf.Name, 1) <> "~" Then
'you need to figure out TransferText command. Maybe
'you won't be lazy and expect people to read it to
'you and tutor you on how it works.
strFileName = qdf.Name
'Docmd.TransferText ....
DoCmd.TransferText transferType:=acExportDelim, TableName:=strFileName, FileName:="C:\test\" & strFileName & ".txt", hasfieldnames:=True
End If
Next qdf
MsgBox "Done"
End Sub

How to update Sql table from excel directly?

I have an sql database and I am able to connect with excel spreadsheet. But when I update the table from excel directly it's not updating the database and once I click refresh all the entered data is no longer in the excel table
Is it possible to update sql database from excel without using any queries?
There are many ways to do this. I'd recommend something like this, to push data from Excel to SQL Server.
Sub ButtonClick()
'TRUSTED CONNECTION
On Error GoTo errH
Dim con As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strPath As String
Dim intImportRow As Integer
Dim strFirstName, strLastName As String
Dim server, username, password, table, database As String
With Sheets("Sheet1")
server = .TextBox1.Text
table = .TextBox4.Text
database = .TextBox5.Text
If con.State <> 1 Then
con.Open "Provider=SQLOLEDB;Data Source=" & server & ";Initial Catalog=" & database & ";Integrated Security=SSPI;"
'con.Open
End If
'this is the TRUSTED connection string
Set rs.ActiveConnection = con
'delete all records first if checkbox checked
If .CheckBox1 Then
con.Execute "delete from tbl_demo"
End If
'set first row with records to import
'you could also just loop thru a range if you want.
intImportRow = 10
Do Until .Cells(intImportRow, 1) = ""
strFirstName = .Cells(intImportRow, 1)
strLastName = .Cells(intImportRow, 2)
'insert row into database
con.Execute "insert into tbl_demo (firstname, lastname) values ('" & strFirstName & "', '" & strLastName & "')"
intImportRow = intImportRow + 1
Loop
MsgBox "Done importing", vbInformation
con.Close
Set con = Nothing
End With
Exit Sub
errH:
MsgBox Err.Description
End Sub
You can also try this, which uses a Where Clause.
Sub InsertInto()
'Declare some variables
Dim cnn As adodb.Connection
Dim cmd As adodb.Command
Dim strSQL As String
'Create a new Connection object
Set cnn = New adodb.Connection
'Set the connection string
cnn.ConnectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Initial Catalog=Northwind;Data Source=Excel-PC\SQLEXPRESS"
'cnn.ConnectionString = "DRIVER=SQL Server;SERVER=Excel-PC\SQLEXPRESS;DATABASE=Northwind;Trusted_Connection=Yes"
'Create a new Command object
Set cmd = New adodb.Command
'Open the Connection to the database
cnn.Open
'Associate the command with the connection
cmd.ActiveConnection = cnn
'Tell the Command we are giving it a bit of SQL to run, not a stored procedure
cmd.CommandType = adCmdText
'Create the SQL
strSQL = "UPDATE TBL SET JOIN_DT = '2013-01-22' WHERE EMPID = 2"
'Pass the SQL to the Command object
cmd.CommandText = strSQL
'Execute the bit of SQL to update the database
cmd.Execute
'Close the connection again
cnn.Close
'Remove the objects
Set cmd = Nothing
Set cnn = Nothing
End Sub
Yes, you can directly via VBA or with other tools.
via VBA (via qry)
via SSIS (https://www.simple-talk.com/sql/ssis/moving-data-from-excel-to-sql-server-10-steps-to-follow/)
via managament studio (https://www.mssqltips.com/sqlservertutorial/203/simple-way-to-import-data-into-sql-server/)
via MS ACCESS (with ODBC connection to server)
...

VBA in Access 2010 - Run-time Error 430

I’m getting a Run-time error '430': Class does not support Automation or does not support expected interface" on this line of code Set Me.lstResults.Recordset = rs or this Set Me![frmM_SearchForDocumentsSubForm].Form.Recordset = rs. I am trying to get the ADO Recordset based on a SQL stored procedure to appear in an unbound Listbox or Subform of an Access form. I’m on Win 7 Machine using Access 2010 connecting to SQL Server 2008:
On_Click event:
Private Sub cmdRun_Click()
'On Error Resume Next
Dim strSQL As String
'Stored procedure + parameters called from form
strSQL = "Exec sqlsp_searchalltables " & Me.txtTables & _
", " & "'%" & Me.txtSearchTerm & "%'"
OpenMyRecordset rs, strSQL
'debug - view procedure
Me.lblQuery.Caption = strSQL
Me.Repaint
Set Me.lstResults.Recordset = rs
'or this
'Set Me![frmM_SearchForDocumentsSubForm].Form.Recordset = rs
End Sub
I found some solutions for this error on the web and tried all of them to no avail. Most suggested checking the references which I did and verified.
I am able to successfully connect to the SQL server and have the results display in both a Listbox and Subform when I use DAO Querydef and a passthrough query or if I use this .listbox method:
With Me.lstResults
Do
strItem = rs.Fields("CLIENT_ID").Value
.AddItem strItem
rs.MoveNext
Loop Until rs.EOF
End With
I would prefer not to use the DAO method because I found I need the coding flexibility of ADO especially with connecting to multiple Recordsets in SQL. Thoughts?
FYI: My OpenMyRecordset public function in Module:
Option Compare Database
Option Explicit
Global con As New ADODB.Connection
Global rs As ADODB.Recordset
Global NoRecords As Boolean
Public Enum rrCursorType
rrOpenDynamic = adOpenDynamic
rrOpenForwardOnly = adOpenForwardOnly
rrOpenKeyset = adOpenKeyset
rrOpenStatic = adOpenStatic
End Enum
Public Enum rrLockType
rrLockOptimistic = adLockOptimistic
rrLockReadOnly = adLockReadOnly
End Enum
Public Function OpenMyRecordset(rs As ADODB.Recordset, strSQL As String, Optional rrCursor As rrCursorType, _
Optional rrLock As rrLockType, Optional bolClientSide As Boolean) As ADODB.Recordset
If con.STATE = adStateClosed Then
con.ConnectionString = "ODBC;Driver={SQL Server};Server=mysqlsvr;DSN=RecordsMgmt_SQLDB;UID=XXX;Trusted_Connection=Yes;DATABASE=RecordsManagementDB;"
con.Open
End If
Set rs = New ADODB.Recordset
With rs
.ActiveConnection = con
.CursorLocation = adUseClient
.CursorType = IIf((rrCursor = 0), adOpenDynamic, rrCursor)
.LockType = IIf((rrLock = 0), adLockOptimistic, rrLock)
.Open strSQL
If .EOF And .BOF Then
NoRecords = True
Exit Function
End If
End With
End Function
You definitely do not have to do the looping method to just to populate the listbox. I'm not familiar with the OpenMyRecordset command you used, but I suspect that something in its functionality is what is causing this error (i.e., it's not opening the recordset in a manner compatible with the listbox). This is how I connected to a local instance of SQL Server Express and was able to populate a listbox.
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
With cn
.ConnectionString = _
"Provider=SQLOLEDB;Data Source=localhost\SQLEXPRESS;" & _
"Initial Catalog=Northwind;Trusted_Connection=yes"
.Open
End With
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Source = "SELECT FirstName, LastName FROM Employees"
.LockType = adLockOptimistic
.CursorType = adOpenKeyset
.Open
End With
Set Me.lstTest.Recordset = rs
Set rs = Nothing
Set cn = Nothing
You will have to make sure that you have the Microsoft ActiveX Data Objects Library reference enabled in your project.