<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Harshaa Vardhini R</title>
    <description>The latest articles on DEV Community by Harshaa Vardhini R (@harshaa).</description>
    <link>https://dev.to/harshaa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4172959%2F6cfc82c8-9d0c-4b60-ad37-77d7bb45dccd.png</url>
      <title>DEV Community: Harshaa Vardhini R</title>
      <link>https://dev.to/harshaa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harshaa"/>
    <language>en</language>
    <item>
      <title>Receipt Sorter: a private, offline bookkeeping helper for freelancers, powered by Qwen2.5-VL</title>
      <dc:creator>Harshaa Vardhini R</dc:creator>
      <pubDate>Sat, 17 Oct 2026 11:55:59 +0000</pubDate>
      <link>https://dev.to/harshaa/receipt-sorter-a-private-offline-bookkeeping-helper-for-freelancers-powered-by-qwen25-vl-19jf</link>
      <guid>https://dev.to/harshaa/receipt-sorter-a-private-offline-bookkeeping-helper-for-freelancers-powered-by-qwen25-vl-19jf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;receipt-sorter&lt;/strong&gt; is a command-line tool that turns a messy folder of receipts and invoices (PDFs and phone photos) into something an accountant can use. It renames each file (&lt;code&gt;2026-10-14_Brew-Bean-Cafe_441.0.png&lt;/code&gt;), writes an accountant-ready &lt;code&gt;receipts.csv&lt;/code&gt; (date, vendor, total, tax, currency, category), and produces a &lt;code&gt;summary.md&lt;/code&gt; with totals per category.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who would pay for it:&lt;/strong&gt; freelancers, consultants and tiny agencies who dread tax season and handle other people's financial documents. Their problem is twofold: the sorting is tedious, and they don't want client invoices and bank details uploaded to a third-party cloud. A tool that runs entirely on their own laptop solves both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ICEBWeKbYTk" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9xff2lyofzkotlfplzso.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9xff2lyofzkotlfplzso.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hv8499-svg" rel="noopener noreferrer"&gt;
        hv8499-svg
      &lt;/a&gt; / &lt;a href="https://github.com/hv8499-svg/receipt-sorter" rel="noopener noreferrer"&gt;
        receipt-sorter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Private, offline receipt and invoice sorter powered by a local open-weight vision model
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;receipt-sorter&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Drop a folder of receipts and invoices (PDFs and phone photos) in, get back renamed files
an accountant-ready &lt;code&gt;receipts.csv&lt;/code&gt;, and a category summary. A local open-weight vision model
does the reading, so &lt;strong&gt;client invoices and bank details never leave your laptop&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;sorted_output/
  sorted/          2026-03-04_Figma_15.0.pdf ...   (confident results)
  needs_review/    anything unreadable, low-confidence or possibly duplicated
  receipts.csv     date, vendor, total, tax, currency, category, notes ...
  summary.md       totals per currency and category + a review list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Originals are copied, never moved or modified.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 1. Install Ollama (https://ollama.com) and pull an open vision model&lt;/span&gt;
ollama pull qwen2.5vl:7b        &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; ~6 GB; use gemma3:4b or qwen2.5vl:3b on smaller machines&lt;/span&gt;

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 2. Install deps&lt;/span&gt;
pip install -r requirements.txt

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 3. Run&lt;/span&gt;
python -m sorter &lt;span class="pl-k"&gt;~&lt;/span&gt;/Documents/receipts -o &lt;span class="pl-k"&gt;~&lt;/span&gt;/Documents/receipts_sorted
python -m sorter ./receipts --model gemma3:4b --dry-run   &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; preview, writes nothing&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Add &lt;code&gt;--verify&lt;/code&gt; to read every document twice (second pass at a different…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hv8499-svg/receipt-sorter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Qwen2.5-VL 7B, an open-weight vision-language model, served locally through &lt;strong&gt;Ollama&lt;/strong&gt;. A vision model reads tilted phone photos far better than classic OCR plus rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline (Python):&lt;/strong&gt; PyMuPDF renders PDFs, Pillow normalises photos, and the model is asked for one JSON object per document using Ollama's schema-constrained output. Parsing is tolerant, with up to two retries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation instead of trust:&lt;/strong&gt; amounts like &lt;code&gt;₹1,234.50&lt;/code&gt; and &lt;code&gt;1.234,50&lt;/code&gt; are normalised, dates are converted to ISO, tax greater than total is flagged, and unknown categories are caught. Anything uncertain goes to a &lt;code&gt;needs_review/&lt;/code&gt; folder rather than being silently accepted. Exact duplicates are skipped by SHA-256 without spending any inference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customisation:&lt;/strong&gt; &lt;code&gt;categories.json&lt;/code&gt; holds your own categories and vendor rules (which always override the model), and &lt;code&gt;examples.json&lt;/code&gt; holds corrected examples used as few-shot guidance. &lt;code&gt;--model&lt;/code&gt; swaps in any Ollama vision model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests:&lt;/strong&gt; the whole pipeline is tested end to end against a fake model, so it runs with no GPU and no network.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing on synthetic receipts
&lt;/h3&gt;

&lt;p&gt;I generated seven fake receipts to test with: a café bill in INR, a SaaS invoice in USD, a tilted Uber photo, a German hardware-store receipt with comma decimals, an internet bill, a deliberately unreadable one, and an exact duplicate. On my first run, the model got the first five right: vendor, date, total, currency and category, including the tilted photos and the German number format. The duplicate was skipped.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where it failed (and what I changed)
&lt;/h3&gt;

&lt;p&gt;The deliberately blurry receipt &lt;strong&gt;slipped through&lt;/strong&gt;. The real image read &lt;code&gt;1?/09/2026&lt;/code&gt; and &lt;code&gt;3?5.00&lt;/code&gt;, but the model confidently returned &lt;code&gt;2026-09-17&lt;/code&gt; and &lt;code&gt;375.0&lt;/code&gt;, with no warning. For a bookkeeping tool, a quiet wrong number is worse than a flagged one.&lt;/p&gt;

&lt;p&gt;I added two safeguards:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Evidence check:&lt;/strong&gt; the model must also quote the date and total exactly as printed. A &lt;code&gt;?&lt;/code&gt;, an unparseable quote, or a quote that disagrees with the extracted value gets flagged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--verify&lt;/code&gt; mode:&lt;/strong&gt; each document is read twice at different resolutions, and any disagreement on date or total is flagged for review.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Performance:&lt;/strong&gt; [YOUR LAPTOP: RAM, CPU/GPU] processed the 7 documents in about [X] minutes without &lt;code&gt;--verify&lt;/code&gt; and [Y] minutes with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy is the product.&lt;/strong&gt; The only network call in the code is to &lt;code&gt;localhost&lt;/code&gt; (Ollama). Client invoices never leave the laptop, which is something a cloud API can't honestly promise. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The economics work.&lt;/strong&gt; There is no per-document or per-token cost. Processing a thousand receipts costs the same as processing ten, so a one-time or flat yearly price is realistic. [Optional: say what price you'd charge.]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lock-in.&lt;/strong&gt; The model is a one-flag swap: a smaller model for an old laptop, a bigger one for accuracy, or a fine-tune for a specific niche. A closed API can change its pricing, its behaviour or its availability whenever it wants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could fix a failure at the right layer.&lt;/strong&gt; When the model guessed on a blurry receipt, I could change the prompt, the schema and the validation, and add a second pass, because every layer is mine to modify.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
