Introduction.
Last week, we created a new Wrapper Class named ClsTiles, which used the ClsArea Class twice within the same Class Module—one instance to store the room’s floor dimensions and another to store the tile dimensions—to calculate the number of tiles needed for the room.
In this new Wrapper Class Module, we will take the existing ClsVolume2 Class and transform it into a ClsSales Class. With a few cosmetic changes, we will give it a complete facelift inside the Wrapper Class, concealing its original purpose as a volume calculation class and repurposing it to calculate the selling price of products with a discount.
Interestingly, the ClsVolume2 Class already has all the necessary properties to store sales-related data: strDesc, dblLength, dblWidth, and dblHeight can be repurposed to represent Description, Quantity, Unit Price, and Discount Percentage, respectively.
Remember, the ClsVolume2 Class itself is a derived class, originally built using ClsArea as its base class.ClsVolume2 Class Re-Visited.
But, first, the VBA Code of ClsVolume2 Class Module (the Base Class for our new ClsSales Class Module) is reproduced below for reference:
Option Compare Database Option Explicit Private p_Height As Double Private p_Area As ClsArea Public Property Get dblHeight() As Double dblHeight = p_Height End Property Public Property Let dblHeight(ByVal dblNewValue As Double) p_Height = dblNewValue End Property Public Function Volume() As Double Volume = CArea.dblLength * CArea.dblWidth * Me.dblHeight End Function Public Property Get CArea() As ClsArea Set CArea = p_Area End Property Public Property Set CArea(ByRef AreaValue As ClsArea) Set p_Area = AreaValue End Property Private Sub Class_Initialize() Set p_Area = New ClsArea End Sub Private Sub Class_Terminate() Set p_Area = Nothing End Sub
The only obstacle preventing us from using the ClsVolume2 Class directly for sales data entry is that its Property Procedure names—dblLength, dblWidth, and dblHeight—do not correspond to the required sales-related properties: Quantity, Unit Price, and Discount Percentage.
Fortunately, all these properties in the ClsVolume2 Class are of the Double data type, which is perfectly suitable for our sales calculations, so no data type changes are needed.
Similarly, the public function names Area() and Volume() are not meaningful in the context of sales, but their underlying calculation logic can be repurposed for sales-related computations without modification.
a) Area = dblLength * dblWidth is suitable for TotalPrice = Quantity * UnitPrice
b) Volume = Area * dblHeight is good for DiscountAmount = TotalPrice * DiscountPercentage
Here, we have two choices to use the ClsVolume2 as the ClsSales Class.
The easiest approach is to create a copy of the ClsVolume2 Class and save it as a new Class Module named ClsSales. Then, update the Property Procedure names and public Function names to reflect sales-related values and calculations. You can also add any additional functions needed for handling sales operations directly within this new class module.
Create a Wrapper Class based on the
ClsVolume2
Base Class. In the Wrapper Class, implement corresponding property procedures and public functions that encapsulate and rename the Base Class’s property procedures and functions, effectively masking their original names. Add new functions, if any, to the Wrapper Class as needed.
The first option is relatively straightforward and easy to implement. However, we will choose the second option to learn how to access the Base Class’s properties within the new Wrapper Class and how to mask their original names with new property names.
The Transformed ClsVolume2 Class.
Open your Database and display the VBA Editing Window (Alt+F11).
Select the Class Module from the Insert Menu to insert a new Class Module.
Change the Class Module’s Name property Value from Class1 to ClsSales.
Copy and Paste the following VBA Code into the Module and Save the Code:
Option Compare Database Option Explicit Private m_Sales As ClsVolume2 Private Sub Class_Initialize() 'instantiate the Base Class in Memory Set m_Sales = New ClsVolume2 End Sub Private Sub Class_Terminate() 'Clear the Base Class from Memory Set m_Sales = Nothing End Sub Public Property Get Description() As String Description = m_Sales.CArea.strDesc 'Get from Base Class End Property Public Property Let Description(ByVal strValue As String) m_Sales.CArea.strDesc = strValue ' Assign to Base Class End Property Public Property Get Quantity() As Double Quantity = m_Sales.CArea.dblLength End Property Public Property Let Quantity(ByVal dblValue As Double) If dblValue > 0 Then m_Sales.CArea.dblLength = dblValue ' Assign to clsArea, Base Class of ClsVolume2 Else MsgBox "Quantity: " & dblValue & " Invalid.", vbExclamation, "ClsSales" Do While m_Sales.CArea.dblLength <= 0 m_Sales.CArea.dblLength = InputBox("Quantity:, Valid Value >0") Loop End If End Property Public Property Get UnitPrice() As Double UnitPrice = m_Sales.CArea.dblWidth End Property Public Property Let UnitPrice(ByVal dblValue As Double) If dblValue > 0 Then m_Sales.CArea.dblWidth = dblValue ' Assign to clsArea, Base Class of ClsVolume2 Else MsgBox "UnitPrice: " & dblValue & " Invalid.", vbExclamation, "ClsSales" Do While m_Sales.CArea.dblWidth <= 0 m_Sales.CArea.dblWidth = InputBox("UnitPrice:, Valid Value >0") Loop End If End Property Public Property Get DiscountPercent() As Double DiscountPercent = m_Sales.dblHeight End Property Public Property Let DiscountPercent(ByVal dblValue As Double) ' Assign to Class .dblHeight of ClsVolume2 Select Case dblValue Case Is <= 0 MsgBox "Discount % -ve Value" & dblValue & " Invalid!", vbExclamation, "ClsSales" Do While m_Sales.dblHeight <= 0 m_Sales.dblHeight = InputBox("Discount %, Valid Value >0") Loop Case Is >= 1 m_Sales.dblHeight = dblValue / 100 Case 0.01 To 0.75 m_Sales.dblHeight = dblValue End Select End Property Public Function TotalPrice() As Double Dim Q As Double, U As Double Q = m_Sales.CArea.dblLength U = m_Sales.CArea.dblWidth If (Q * U) = 0 Then MsgBox "Quantity / UnitPrice Value(s) 0", vbExclamation, "ClsVolume" Else TotalPrice = m_Sales.CArea.Area 'Get from Base Class ClsArea End If End Function Public Function DiscountAmount() As Double DiscountAmount = TotalPrice * DiscountPercent End Function Public Function PriceAfterDiscount() PriceAfterDiscount = TotalPrice - DiscountAmount End Function
So far in the Wrapper Class, we have created an instance of the ClsVolume2
class, renamed its property and function members to more suitable names, and added validation checks with appropriate error messages. We also ensured that invalid input does not trigger the Base Class’s own validation routines, which could otherwise display error messages like “Value of the dblLength property is invalid” from the ClsVolume2
class.
Review the highlighted lines in the code above; they should help you understand how the property values are assigned to and retrieved from the Base Class ClsVolume2
.
First, review the ClsArea
Class Module, and then examine the ClsVolume2
Class Module, which is derived from ClsArea
. After understanding both of these, revisit the code in this Wrapper Class for a better perspective.
Test Program for ClsSales Class in Standard Module.
Let us write a Test Program to try out the Wrapper Class.
Copy and Paste the following VBA Code into a Standard Module.
Public Sub SalesTest() Dim S As ClsSales Set S = New ClsSales S.Description = "Micro Drive" S.Quantity = 12 S.UnitPrice = 25 S.DiscountPercent = 0.07 Debug.Print "Desccription", "Quantity", "UnitPrice", "Total Price", "Disc. Amt", "To Pay" With S Debug.Print .Description, .Quantity, .UnitPrice, .TotalPrice, .DiscountAmount, .PriceAfterDiscount End With End Sub
Run The Code.
Keep the Debug Window open (Ctrl+G).
Click somewhere in the middle of the Code and press the F5 key to run the Code and to print the output in the Debug Window.
You can further test the code by entering negative numbers for any of the input values and running it to trigger the new error messages. You may also try disabling one or more input lines by placing a comment symbol (
'
) at the beginning of the line, then rerun the code and observe the results.
Calculate Price/Discount for an Array of Products.
The following test code creates an array of three Products and Sales Values by entering directly from the Keyboard.
Copy and paste the following Code into a Standard Module and run to test the Wrapper Class further.
Public Sub SalesTest2() Dim S() As ClsSales Dim tmp As ClsSales Dim j As Long For j = 1 To 3 Set tmp = New ClsSales tmp.Description = InputBox(j & ") Description") tmp.Quantity = InputBox(j & ") Quantity") tmp.UnitPrice = InputBox(j & ") UnitPrice") tmp.DiscountPercent = InputBox(j & ") Discount Percentage") ReDim Preserve S(1 To j) As ClsSales Set S(j) = tmp Set tmp = Nothing Next 'Output Section Debug.Print "Desccription", "Quantity", "UnitPrice", "Total Price", "Disc. Amt", "To Pay" For j = 1 To 3 With S(j) Debug.Print .Description, .Quantity, .UnitPrice, .TotalPrice, .DiscountAmount, .PriceAfterDiscount End With Next For j = 1 To 3 Set S(j) = Nothing Next End Sub
Once the correct values have been successfully entered into the array, the product names and corresponding sales values are displayed in the Debug Window.
CLASS MODULES.
Demo Database Download
- MS-Access Class Module and VBA
- MS-Access VBA Class Object Arrays
- MS-Access Base Class and Derived Objects
- VBA Base Class and Derived Objects-2
- Base Class and Derived Object Variants
- Ms-Access Recordset and Class Module
- Access Class Module and Wrapper Classes
- Wrapper Class Functionality Transformation
COLLECTION OBJECT.
- Ms-Access and Collection Object Basics
- Ms-Access Class Module and Collection Object
- Table Records in Collection Object and Form
I have been following the series of articles since its beginning and I implemented everything mentioned in it step by step and the work is going well until I reached this article, so I created the clsSales class, but when I try to execute SalesTest and SalesTest2 procedures, the cursor stops on the line (S.Description = "Micro Drive") and it appears to me The following error message
ReplyDeleteRun-time error '91':
Object variable or With block variable not set
I tried to review the code of the clsSales class and review everything that was previously explained in the series of articles, and I did not find anything wrong. Is the problem an error that I did not recognize in the code? or Is it a problem specific to my work environment?
I have been following the series of articles since its beginning and I implemented everything mentioned in it step by step and the work is going well until I reached this article, so I created the clsSales class, but when I try to execute SalesTest and SalesTest2 procedures, the cursor stops on the line (S.Description = "Micro Drive") and it appears to me The following error message
ReplyDeleteRun-time error '91':
Object variable or With block variable not set
I tried to review the code of the clsSales class and review everything that was previously explained in the series of articles, and I did not find anything wrong. Is the problem an error that I did not recognize in the code? or Is it a problem specific to my work environment?
I have been following the series of articles since its beginning and I implemented everything mentioned in it step by step and the work is going well until I reached this article, so I created the clsSales class, but when I try to execute SalesTest and SalesTest2 procedures, the cursor stops on the line (S.Description = "Micro Drive") and it appears to me The following error message
ReplyDeleteRun-time error '91':
Object variable or With block variable not set
I tried to review the code of the clsSales class and review everything that was previously explained in the series of articles, and I did not find anything wrong. Is the problem an error that I did not recognize in the code? or Is it a problem specific to my work environment?
This comment has been removed by the author.
ReplyDeleteI created a Database with the VBA Code given in this Page and run them successfully in the attached sample WrapperClass.accdb Database. The Database is attached to this Page in ZIP File Format with the name WrapperClass.Zip. Please download and try it out in your Access Verion.
ReplyDeleteIf it runs successfully, then compare your VBA Code with the Demo Database Code to find out what went wrong.
Regards,
It works successfully, thank you
DeleteI will try to compare the codes and look for the reason for the error.
The reason for the error is that the Class_Initialize and Class_Terminate property procedures were not created for the ClsVolume2 class.
ReplyDelete