It’s no surprise that Mama Funke’s problems are also mine to bear. That’s what I signed up for me when I said the words, “I do”, before the Priest 😊.
Every month in 2021, Mama Funke would remind me to help her create an invoice for one of her recurring service recipients. I simply had to edit a pdf template in Adobe Illustrator by changing a few texts and resaving it in a different name for her to send as email attachment to its recipient.
While it may not seem like much, it does take some effort even for her to remember to remind me of the task and we’ve had a few times where payments were delayed because invoices weren’t sent on time. With Funke in the picture, it seems like even the most mundane of tasks requires rethinking and re-planning for efficiency. The time was ripe to systemize the process.
Towards the end of last year, I resolved to include this as one of my immediate projects for the year 2022. I needed to automate the invoice-sending process, thus providing me and Mama Funke more family time. Yup, as I’ve come to discover and embrace, every minute counts when you have a child. My problem statement was thus,
How could I automate the Invoice-sending process on Mama Funke’s behalf so that recipients get them on time and she never has to miss payments?
The existing invoicing process had some pretty standard features that would make a solution relatively straight forward as follows. Monthly payments included in the invoices are fixed, having been agreed to prior to the start of every year. What that meant was we had only a few dynamic features for each invoice namely – the date, description of service which involved changing the month only, reference Id of the invoice for tracking purposes.
Solution – Azure Functions + Table Storage + Blob Storage
My solution involved a combination of cloud-based features namely Azure Table Storage, Azure Functions and Azure Blob Storage. No need for setting up any infrastructure. I simply make the most of Microsoft’s Azure pay-as-you-go PAAS (Platform As A Service) offering. It’s dirt cheap! I know so from my currently running Report Manager solution which has been running once a day for over 4 months, costing me less than $1 monthly! Yep, my last monthly bill was actually 0.02 Pounds.
- Given the standard features of the invoice, my first step in the solution was to develop all invoices for the year 2022 at a go then upload them to Azure Blob Storage Container, a service on Microsoft’s Azure Portal. The files are named in a format that includes the Month for which they are meant e.g. 01_January_2022, 02_February_2022…
- Next, I have an Azure Function Timer trigger that runs on the first Monday of every month and it does the following.
- It uses the Current Month as a search query string to find the invoice in the Blob Storage.
- It then downloads the invoice as a stream, attaches it to an email then sends it to the recipients with Mama Funke copied in (proof that the email is sent)
- Lastly, it updates a Table storage with the details for record purposes.
The implementation of this solution is described below. A slightly modified version of the project can be found in my GitHub Repository for reference purposes. You may also be able to adapt it for your use. Enjoy!
How?
Phase I – Azure Portal
While you could use the Azure Storage Explorer tool for this phase, I demonstrate it using the Azure portal website.
Step 1
In Azure Portal, create a Storage Accounts resource. Storage Accounts is an umbrella resource for related resources like containers, queues and table storage.
Step 2
Next, create a container in the Storage Accounts called invoices by selecting the plus symbol at the top. The concept of containers and blobs in Azure is synonymous with folders and files. In this case, think of a blob as a file but it could be any tangible resource like images, audios etc. The container can be likened to a folder in which you store the blobs. Make sure the Public access level is set to Container (anonymous….) as below so you can access it from your code.
With the container created, you can view or select it from the list of containers as below
Step 3
Next, create a Table called InvoiceManager. This is where we’ll be storing a reference to each sent invoice for record purposes. Azure Table Storage is a simply json nosql database. It’s not indexed meaning you can’t exactly define related tables and foreign keys like you would for relational databases. Considering the simplicity of our solution, SQL Server will be overkill not to mention the cost!
Lastly, grab the Access Keys as this will be used within the app for connecting to the Storage Accounts. Navigate to your Storage Accounts and select Access Keys under the Security + networking category on the lefthand menu.
Select Show Keys at the top to make the Keys and Connection strings visible then simply copy the Connection String for Key 1.
Step 4
Design the invoices for the year. Like I mentioned, the details are pre-known, hence relatively easier to do upfront. I ensure a logical consistent naming format – 2 digit number_monthyear_invoice – for ease of querying the files in code.
Step 5
Go back to the container created above and select the upload button at the top left, select the files and that’s it as below.
Phase II – Visual Studio
Step 6
In Visual Studio 2022, I create a blank solution named BabaFukeInvoiceManager
Add a new project to the solution. Select Azure Functions, name it same as above and select Timer trigger from the list of options. You can select the Storage Account directly linked to your Azure Portal from the dropdown for Storage account (AzureWebJobsStorage) or leave it as it is – ‘Storage emulator’ – especially if you plan on testing locally using the Azure Storage Explorer tool. I simply select my cloud account
First things first, install the necessary Nuget packages. In this case,
- Azure.Storage.Blobs – for managing blobs
- Microsoft.Azure.Cosmos.Table – for managing the table storage
- Microsoft.Azure.Functions.Extensions – for setting up Dependency Injection in an Azure Functions project
- SendGrid – my preferred email provider tool. Ensure you register with them for the necessary API keys.
- FluentEmail.SendGrid – for sending emails
Miscrosoft.NET.Sdk.Functions is automatically provided for all Azure Functions App projects so should be pre-installed already.
Step 7 – Create the Models
First one is for the Invoice. Half of its properties are initialized; Category name, Sent which is set to the date the invoice is created, IsArchived is usually a property to disable an invoice rather than deleting it, something I’ve found useful for record purposes.
The BlobDetail model will be used for handling the response from our blob manager when we query the storage account to schedule each monthly invoice. I prefer this approach of an object rather than passing a bunch of stuffs as parameters or returning a tuple. The pdf file is returned as a Stream, its location as the BlobUri, file name as BlobName and the month as CurrentMonth.
Step 8 – Create the Entity
As explained above, we’ll be relying on Azure Table Storage for record keeping. Azure Table Storage is a very simple resource. It’s basically a database without the complexities and sophistication of relational databases like Sql Server etc. It’s dirt cheap to maintain on Azure and its structure is pretty straightforward – a key-value/json structure. To use it in code, you simply create an object that inherits TableEntity. TableEntity has two default properties called Partition Key and Row Key. The Row Key should be a unique string for each record, hence think of it as a primary key even though it’s not in the sense of a traditional relational database. The Partition Key on the other hand is simply a string value for grouping records.
Create an InvoiceTableEntity which inherits the TableEntity. The InvoiceTableEntity will have a direct interaction with the Table Storage; data coming in and out will be mapped to the Invoice model. Add the properties of the Invoice model to this except the Reference Id and Category. The Reference Id maps to the RowKey while the Category property maps to the PartitionKey.
Setup the Startup.cs for the DI. This does not come out of the box for Azure Functions hence the extension nuget we installed earlier. With this, we can set one up. The values for Azure Storage Account (the connection string from step 3) and Email Settings are stored in my local.settings.json file.
Step 9 – Create the Repository
This is the interface and implementation for adding data to the table storage. In this case, I’m simply adding to the table. I’ve left out other CRUD operations in keeping with the focus of this project.
Step 10 – Create the Services
I use 3 services to separately handle the distinct parts of the functionalities required. Think Single Responsibility Principle. They are self-explanatory by the naming of each method. First, the interfaces below
And the implementations
Step 11 – Create the Timer Function
Finally, we can then revisit the default Timer Trigger function that was created by Visual Studio by default when we selected the project template. First rename the Function1.cs file to InvoiceScheduler.cs. The most notable change is that I’m having the function triggered monthly on the first Monday of each month at 9 A.M. The TimerTrigger uses what’s called NCRONTAB expression to define the schedule. Each item represented by a digit or * represents the following => {second} {minute} {hour} {day} {month} {day-of-week}. See this page for more examples.
Conclusion
That’s all. A slightly modified version of the complete project is available in my GitHub Repository. Mama Funke’s problem solved and world (family) peace achieved with more family time. In the near future, I should develop a user-friendly frontend app so that she can view the table for her records. This would be useful for exporting the data to say her accountant. For now, she can simply refer to her email thread.
References