Sometimes Excel is overkill. You just need a grid, a few numbers, and a sum. Opening a full spreadsheet application, creating a file, saving it somewhere — it’s too much friction for a quick calculation.
I built Mini Spreadsheet for exactly that. It sits in your system tray, out of the way, until you need it. One click and you have a clean 25-row grid ready to go. No file to save, no ribbon to navigate.
Anyone who has worked with Excel knows the tax it charges just to open it. The OLE initialization, the random pauses, the ticks and stutters before you can type a single number. Half the time you just need a column of numbers added up — you don’t need all of that. Mini Spreadsheet opens instantly, no loading screens, no file dialogs, no surprises. And when you do need to take it further — share it, format it, or run a pivot — one click sends everything straight to Excel, formulas and all.
What It Does
Mini Spreadsheet is a lightweight Windows desktop app with a 5-column, 25-row grid and real formula support. It is not trying to replace Excel — it is trying to replace the calculator.
Formulas: =A1+B2, =A1-B2, =SUM(A1:A25)
Click a cell while typing a formula to insert its reference
Drag across cells to select a range (e.g. A1:C10)
Shift+Arrow to extend a range from the keyboard
Live column totals in the SUM footer — always visible
Numbers formatted with commas automatically
One-click Export to Excel if you need to take it further
Minimises to the system tray — always one click away
Optional: launch automatically at Windows startup
Who It’s For
Anyone who finds themselves opening Excel just to add up a column of numbers. Accountants doing a quick sanity check. Project managers rough-costing something on the fly. Anyone who wants a persistent scratchpad that is always one click away and never asks you to save a file.
Download the zip file below. It includes a standalone Windows executable (no Python required), a Python script version for technical users, and a full README.
I built this tool to solve a specific problem: searching through tens of thousands of emails to find the ones that matter — and exporting them cleanly as PDFs for legal or compliance use.
If you’ve ever had to respond to a litigation hold, conduct an internal investigation, or review a contract dispute, you know the pain. Gmail’s search is fine for day-to-day use. It is not designed for discovery.
There are other MBOX viewer tools out there. I tried them. The problem is that real-world MBOX files — especially Gmail Takeout exports from active accounts — can be multi-gigabyte files with tens of thousands of emails. Most tools choke on them. They freeze, crash, or simply never finish loading. I needed something that could handle the size without drama. So I built it myself.
This tool is lightweight, fast, and built for exactly this one job. It indexes your MBOX file once, then searches in seconds — no matter how large the archive. No bloat, no subscription, no cloud upload. Everything stays on your machine.
What Is an MBOX File?
An MBOX file is the standard format used by Gmail Takeout, Thunderbird, Apple Mail, and most other email clients to export or archive email. If you have ever downloaded your Gmail data through Google Takeout, you have an MBOX file. The problem is that these files can contain tens of thousands of emails, and there is no easy built-in way to search through them on Windows. You cannot just open them and Ctrl+F your way to the email you need.
This tool solves exactly that. Point it at your MBOX file, type a keyword, a name, a date range — and it finds the emails instantly.
What It Does
The Legal Email Discovery Tool searches one or more MBOX files — Gmail Takeout exports, Thunderbird archives, or any standard .mbox file — for emails matching your criteria, then exports each matching email as a properly formatted PDF.
Filter by sender, recipient, subject, body text, or date range
Combine multiple conditions: AND within a group, OR between groups
Handles archives of any size — tested on multi-GB files
Builds a persistent SQLite index so repeat searches take seconds
Crash-safe: saves progress every 50 messages and resumes where it left off
Outputs one PDF per matching email, plus a CSV log with full metadata
Works out of the box with Gmail Takeout exports
Who It’s For
Free for personal use. If you’re a law firm, litigation support provider, or using it professionally, a commercial license is available for $20/year — which also gets you a stamped, verifiable license on every PDF you produce.
How to Get It
Download the zip file below. It includes a standalone Windows executable (no Python required), a Python script version for technical users, and a full README with step-by-step instructions.
A lot of people ask me about automation. Specifically, when do you automate something and when do you just fix it?
Honestly, I wasn’t sure I had a clean answer. But then I thought about how we actually make these decisions and I think there’s a framework buried in there. Let me walk through it using a real example.
The Invoice Problem
We have a vendor that sends us hundreds of invoices. For a long time, the AP team was entering them manually. Tedious, error-prone, inconsistent descriptions in the GL (i.e. the same product can be described as “muffler” or “chrome muffler” or “Muffler” or “MUFFLER” which made searching a minefield) and a waste of a good person’s time.
Most companies in this situation would do one of two things. They either keep entering them manually, or they buy some kind of OCR or AI system to read the PDF invoices and extract the data automatically. That second option sounds smart. It isn’t.
Here’s why. PDF extraction is not accurate. You have to train the system. It still makes mistakes. And it costs money and sometimes a lot of it. At the end of the day, no one is 100% certain it is right and the person reviewing it has to be extra careful.
We did something different. We went to the vendor directly and asked them to export a flat Excel file of our invoices instead of just emailing us PDFs. That’s it. That was the fix.
They have sophisticated systems just like we do. The data already exists in a clean, structured format on their end. We just asked them to share it. It took one conversation. It costs nothing. And it works perfectly every time.
From there, we built a simple Power Automate flow that takes that file and creates the purchase invoices directly in Business Central — matched against the PO automatically. The AP person still gets the PDF in their email, but their job is now to verify what’s already in the system, not to enter it. That’s a fundamentally different job.
Result: about 80% reduction in data entry time. Zero typos. Zero cost. A few hours of setup.
That’s low hanging fruit.
So How Do You Decide?
Working through that example, here’s what I think matters:
Go upstream, not around the problem.
The obvious solution was to build technology around the PDF. The better solution was to eliminate the PDF problem entirely by going to the source. Before you automate anything, ask yourself: where did this mess come from? Can you fix it there instead of building around it?
Most of the time, you can. And it’s almost always simpler and cheaper.
Only automate what is stable, repetitive, and rule-based.
Our invoice flow works because the input is consistent. Same vendor, same format, same fields every time. The rules don’t change.
Things that aren’t worth automating: one-off invoices; processes that change constantly; anything that requires judgment. If a human needs to think about it every time, a computer probably should not be doing it.
Fix the process before you automate it.
This one is important. You cannot automate a broken process. If you try, you just get a faster broken process.
If something isn’t working, put humans back in the loop. Figure out what’s wrong. Clean it up. Run it for a while. Then automate. In that order.
Keep humans in the loop deliberately.
We don’t auto-post the invoices. A person reviews them first. That’s intentional.
The goal isn’t to remove humans from the process, it’s to change what they’re doing. Instead of entering data, our AP person is now catching operational issues, flagging discrepancies, asking questions like “why did we buy so much of this?” That’s an AP Financial Analyst, not an AP clerk. Same person, better job.
Automation should elevate the role, not just reduce the hours.
You need to own it yourself.
Here’s the part most people don’t talk about. When the automation breaks, and at some point it will break, you need someone on the team who can fix it. Not a consultant. Not a ticket to IT. Someone who actually understands what the flow does and can get it running again the same day. Some of the people on our team join as accountants first, but end up being accountants with a tech side job. They become real wizards with the tools that are already accessible but never used. Just think how many people have access to Power Automate already, but never even try to open it.
I happen to build some of these automations myself, so that’s not an issue for me. But not every team has that. I think it should be a core skill for any CFO. If you can’t fix your own automation, you don’t really own it. Once your team gets a whiff of the automations, a few of the team members will really latch on to it. They will come up with ideas. They will bring forward new automations and some might even try it on their own and become champions of their own. Your job is to show them a path, and give them room to breathe. The one thing you need to do is to know what the processes and automations are, just in case something breaks; you will know where to look and how to fix it.
Not All Automation Has to Be Smart
At home, I run a mix of motion sensor switches and Z-wave switches controlled through Home Assistant. Friends walk in and inevitably ask: why not put Z-wave switches everywhere?
The answer is simple. I put motion sensor switches in the places where I want the light to come on when I walk in. That’s all I need them to do. Why add a Z-wave switch, set up automations in Home Assistant, configure scenes and schedules — when a $20 motion sensor switch does exactly the same job without any of that?
From the outside, the house seems equally automated either way. But some of that automation is just a dumb motion sensor doing one thing reliably. And there’s nothing wrong with that. In fact, that’s the whole point.
I bring the same logic to work. The invoice automation we built isn’t sophisticated. It’s a flat Excel file fed into Power Automate. No AI, no machine learning, no expensive software. Just a file that gets picked up and turned into purchase invoices. It looks like automation. It is automation. But it’s not complicated.
Don’t reach for the fancy switch when the motion sensor will do.
The Simple Test
Before I automate anything, I ask three questions:
Is this high volume, consistent, and rule-based?
Have I fixed the process first, or am I automating around a problem?
Can someone on my team maintain this without me?
If the answer to all three is yes, it’s worth automating. If not, fix it first, or don’t automate it at all.
The best automations are the ones that are almost boring in how simple they are. Elegant, really. If it feels complicated to explain, it’s probably not ready.
This is part of an ongoing series on how I think about finance systems, automation, and building companies that run well. I’m a CFO and CA CPA who likes to tinker — and occasionally write about it.
If you are using PowerBI Service and the PowerBI Data Gateway but are having troubles authenticating to your local machine (i.e. you have an excel or csv file that you want refreshed) this may be the post for you.
I use Office365 and have an @yourcompany.onmicrosoft.com account. That’s also the username to login to my computer. But if you pass xxx@yourcompany.onmicrosoft.com as the username it will not work because it doesn’t know what domain to use.
Instead, you must use this format for the username: azuread\xxx@yourcompany.onmicrosoft.com
Power Automate has Business Central connectors, however, they are very limited. What Power Automate does not have are connectors to Business Central for the Web Services. You can locate these web services in Business Central under Web Services.
Services listed under Web Services in Business Central
So how can you get access to the Web Services in Power Automate? Not that easy. But I figured it out with some sleuthing and piecing things together. This is a multi-step process and if you do not have admin rights to Azure/Business Central, you may need to contact your IT department to get them.
So the steps (read difficulties) I ran into getting this to work are the following:
You need to authenticate against Business Central (Microsoft) using oAuth2.0 and get a bearer token
You use the bearer token to call the web service that you need
You get a JSON response
You need to fiddle with the response and the Parse JSON did not work properly for me so I did need to MacGyver some of it
You get variables and you can use them anywhere, in my case, I just wanted to populate an Excel table
Select Certificates & Secrets and then click on New Client Secret
Remember, you MUST keep the secret (password) as it is shown only this once. But it’s not a big deal, as you can always create another one
Now click on API permissions and make sure these permissions are added. So click on Add a permission and on the next screen select Microsoft APIs and look for Business Central. Select Business Central and then select DELEGATED PERMISSIONS. I selected app_access/Automation.ReadWrite.All/user_impersonation. I do not believe that you need to Financials.ReadWrite.All.
After you are done that, you need to add another permission but this time, you need to choose Microsoft Graph. The select Delegated Permission and then add USER.READ.
After you have done all that Don’t forget to GRANT ADMIN CONSENT after you add it!
Click on the Overview tab, and make a note of the Client ID and Tenant ID. We will need those in a bit.
Now, go to Power Automate where we will create the create the steps required to access Business Central
This is a fairly simple process and here is the Power Automate steps that we will require:
Initialize a variable and assign it a name Username or UN. It is a STRING variable and paste the CLIENT ID that you got from Azure into the value field.
Initialize another variable and assign it a name Password or PW. It is also a STRING variable and paste the SECRET (password) that you got from Azure into the value field
Add a new step using COMPOSE. Add this line into the Expression (with all the quotes, but replace XXXX with the Client ID from above and YYYY with the Secret (password) from above:
Authorization value to assign Bearer and choose the variable “access token” from our previous step
Accept: value to assign odata=nometadata
Under ADVANCED OPTIONS, choose None for authentication
The step above will return the information to you in a JSON format. In my case, I got the CUSTOMER CARD information from Business Central (this is Page 21)
Add another step and use the Parse JSON function. Use the Body from the HTTP request above under CONTENT. Use this as the schema (note you may have additional fields for your customization):
Add a new step and initialize 1 variable for each field that you will require data for. In this case, we will just obtain the Name of the client. So initialize 1 variable and call it Name, type is STRING, and assign it any value for now
Add an Apply to Each step under SELECT AN OUTPUT FROM PREVIOUS STEPS I added this expression: outputs(‘Parse_JSON_DetailCustomer’)?[‘body’]?[‘value’]
Note that I called my Parse JSON step Parse JSON DetailCustomer so remember to add an underscore for all the spaces. You might have named that step something else, so replace the title of that step with yours. If you are at a loss, you can get the title just by looking at the Parse JSON step
Now assign the value to the variable by using the set variable step and using this expression: items(‘Apply_to_each’)?[‘Name’] Note that the Apply_to_each is the title for this step
Once you have assigned the variable, you can use it anywhere!
This entire process was frustrating on many levels. Microsoft documentation is about as good if it were written in an alien language (why is that?). There is great information about oAuth2.0 authentication (but only after spending hours looking) and not a great deal on Parse JSON. I ran into days of frustration when I kept on getting object not array errors, or null errors. In any event, this worked for me, and I hope it works for you.
If you are like me and do a lot of importing and sorting and just want data at your fingertips without having to go through Business Central, you like to have data either in SQL or in Excel. Why? Because you can do xlookups or other necessary evils when creating import tables with configuration packages or other automation.
So how do you go about getting the data into Excel? You can use PowerAutomate with Business Central.
I will show you the steps that I had to take to do this.
Step 1: Use the Business Central Connector in PowerAutomate and use the FIND RECORDS (V3). You will need to input the Environment Name (usually Production) and then the company name. Use the API v2.0, and for my case, I used the customers table.
Step 2: Add another step, APPLY TO EACH and use the output from the previous step. Choose the one that says VALUE (List of items). Then select EXCEL ONLINE BUSINESS > ADD A ROW INTO A TABLE and work your way through to the Excel file that you created for this. Remember, your data MUST sit in a table in Excel, so you have to formulate this first. Your flow should look like this, but your table may look different from mine.
Simply go through the different headers in your excel file and simply plop down the the data that you need for each one. You will have an excel file that looks like this in the end (I have obfuscated the confidential data)
The Master Calendar is super important in PowerBI, especially if you want to run reports using relative dates or a series in time. Essentially all tables, should be linked to your master calendar via the date field.
This is what PowerBI looks like when the dates are all joined. You should use the date field to connect to other date fields. It is usually a 1 to many relationship.
Master Calendar
The Master Calendar is important, especially for financial reporting because you can create columns that indicate fiscal year, fiscal period, week number, year, year to date flags, next month, last twelve months and so forth. Here is a Master Calendar that I created:
This Master Calendar includes the day relative to today, month relative to today, day of the week in words, fiscal year (which is different from the calendar year), fiscal quarter, accounting period (as a number), week number (relative to the calendar), weekday or weekend and so forth.
The Master Calendar allows you to use slicers to filter out data automatically. This is very important when creating financial reports that are a point in time, such as the balance sheet vs income statement which is a period of time. It is exceptionally useful when running trial balance data. If you are short on time, you can just use the steps below to create the Master Calendar in PowerBI:
Click on NEW TABLE (name the table Master_Calendar)
Make sure you mark the table as a date table by clicking on DATE TABLE
Then start by adding this column
Your first column is a column of dates automatically generated by PowerBI:
DATE=(CALENDAR(START DATE,TODAY())
Note, you can add to the number of days beyond today() by simply putting a + and the number of days you want to go beyond today. Like this:
You should have a Master_Calendar table similar to the one I have. This is a very important step to creating financial reporting (or other time based reporting in PowerBI.
Please note that the fiscal year in this examples starts on Aug 1. If you want to change the fiscal year start date simply change items 5, 6 and 7 to your corresponding fiscal year to the period.
We wanted to see all of the foreign exchange rates in Power BI from Business Central for various reasons: 1) To make sure the exchange rates were being updated daily and, 2) to run reports on the exchange rates
Business Central stores the FX rates in table
To create the API, go to WEB SERVICES in Business Central and select NEW. The object type is page, and use Object ID 483 and publish. The URL will be produced and you use that as the oData source in PowerBI.
This piece of wood was in pretty bad shape. I managed to stabilize it with PC Wood Hardener and epoxy. The rotting portion was very much a sponge. It sucked up about 3 bottles of wood hardener and lots of epoxy and varnish.
Here is an over exposed picture of the piece of wood.
I used a hand plane to plane the surface of the table flat. I then realized the right side of the wood was going to fall off due to the rot.
Close up of the rotting woodAfter planing over several days
I don’t typically like the epoxy effects that some people do, so I strictly wanted the epoxy to be structural and not a feature. I mixed in some deep red dye so that it would not be entirely noticeable.
I sanded down the epoxy so it would only show up where it was needed and not on the rest of the table top itself.
I had to make legs for this thing. I noticed some cool joins that welders do, so I tried one myself.
Figuring out the measurements is relatively easy. It’s a quarter circle, so the distance of the cuts is 3.14 x the height (or width of the steel – let’s say it is about 2″ x 4″, then the diameter is 8″ because 4″ x 2) all of that divided by 4. So Pi x D / 4.
Because the bottom of the wood was not planed to the same thickness, I had to improvise on the legs. I made one of the legs adjustable for level.
I wanted a really simply way to access the tables in Business Central and to write the information of those tables into SQL database so that my team could access them directly from SQL.
Since I am just an accountant, I always find it very difficult when I find snippets of code but nothing is explained. So I will try my best to break everything down for other fellow accountants so that they can follow through.
Sure, there are many ways to do this, but I just wanted to try this. So here is my script. You can copy this script directly, and there are just a few things for you to change. Any thing that is underlined you will need to change.
# References.
# https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code
# https://www.reddit.com/r/PowerShell/comments/9clts3/powershell_automation_with_oauth2/
#
#
# https://login.windows.net/2383c526-aef3-47e9-8487-37712e689168/oauth2/token?resource=https://api.businesscentral.dynamics.com
# Class to get an OAuth 2.0 authentication token from BC using a Password Grant.
#
class AADPasswordGrant {
[string]$token #Token that we need to get to Authenticate with later
[string]$tenantId #BC Tenant ID
[string]$clientId #The ApplicationId that was registed for BC in AAD.
[string]$username #BC username
[string]$password #BC Password
[System.Security.SecureString]$securePasswordStr #BC Password we will convert to a secure string later
[string]$securePasswordBStr #BSTR version of the secure password
[string]$clientSecret #Key that was generated when registring BC in AAD
[string]$grantType = "password" #This must be password so we are not challenged or have to use a form.
[string]$callbackUrl = "https://127.0.0.1/auth-response" #The same callback registered for BC in AAD
[string]$accessTokenUrl = "https://login.windows.net/{tenantId}/oauth2/token" #Url to request the token from
[string]$resourceUrl = "https://api.businesscentral.dynamics.com" #The resource we want to talk to
[string]$scopeUrl = "https://api.businesscentral.dynamics.com" #The resource we want to talk to
AADPasswordGrant([string]$tenandId, [string]$clientId, [string]$clientSecret, [string]$userName, [string]$password) {
$this.tenantId = $tenandId
$this.clientId = $clientId
$this.clientSecret = $clientSecret
$this.userName = $userName
$this.securePasswordStr = (ConvertTo-SecureString -String $password -AsPlainText -Force)
$this.securePasswordBStr = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($this.securePasswordStr))
$this.accessTokenUrl = $this.accessTokenUrl.Replace('{tenantId}', $tenandId)
}
[void]TryGetAuthorisationToken () {
$body = @{
grant_type = $this.grantType
username = $this.userName
password = $this.securePasswordBStr
client_id = $this.clientId
client_secret = $this.clientSecret
scope = $this.scopeUrl
redirect_uri = $this.callbackUrl
resource = $this.resourceUrl
}
$authResult = Invoke-RestMethod -Method Post -Uri $this.accessTokenUrl -Body $body
$this.token =$authResult.access_token
}
}
#
# Test the Class Here
#
$tenantId = '1447d526-aee2-47e9-8418-27732e6848546' #Your BC tennantID
$clientId = 'af912c1d-d67c-407a-8f5f-2bd8dbb1cda9' #The id that BC is registered with in AAD
$clientSecret = 'ETZ9Nqy--Wi9-zjF9B97-.oeCLyA0.AEfd' #The secret key that was created when registering BC for AAD Auth
$username = 'xx@xxx.com' #Username for the password grant -- this is usually your Office365 credentials
$password = 'your office365 password' #Password in plain text note.. this is not advised and there are ways of producing a key in PowerShell so your password is not stored in plain text
[AADPasswordGrant]$aadPasswordGrant = [AADPasswordGrant]::new($tenantId, $clientId, $clientSecret, $username, $password)
$aadPasswordGrant.TryGetAuthorisationToken()
$companiesUrl = "https://api.businesscentral.dynamics.com/v2.0/Production/api/v2.0"
$requestHeaders = @{ 'Authorization' = 'Bearer ' + $aadPasswordGrant.token }
$result = Invoke-RestMethod -Uri $companiesUrl -Headers $requestHeaders -Method Get
$result.value | Export-Csv -Path c:\output\bc-environment.csv
A few things to note about our environment:
We are running Business Central (SAAS/Online – it’s in Office365 not our own servers
We use AAD (Azure Active Directory) to authenticate – that is we use our Office365 credentials to login to Business Central (I am using the OAuth2.0 token to gain access to Business Central
The code above is accessing the PRODUCTION platform.
Most of the code above is just to get the OAuth 2.0 token to get access to Business Central. Why is it not simply a username and password? I’m not sure, and this is what Microsoft requires to access Business Central. Like I said, I’m not a coder, just an accountant.
There are 5 pieces of information that you need to change in the above code (and you might not be able to get all of them yourself):
Tenant ID – You can get this by going to Business Central with your browser and clicking on Help and Support. You will find your Tenant ID. If you don’t see it, you can always get it from your URL.