Skip to content

gprakosa/kideco-sharing-session

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

1 Commit
ย 
ย 

Repository files navigation

Daily Spending Tracker (๐Ÿ‡ฎ๐Ÿ‡ฉ Indonesian Version)

Prerequisites

  1. A Google account (the free one).
  2. An iPhone (no iPhone 17 Pro Max 2 TB required).
  3. Some money to be spent (spending it wisely, thank me later).

TL;DR

  • Levels 1 - 3 are follow-along instructions while level 4-5 only cover the key highlights.
  • Levels 1 - 3 are designed to you hands-on and comfortable playing around with iOS Shortcuts.
  • Setup a Google Form, create a Gemini API Key, download the ready-to-use shortcut and here we go โšฝ๏ธ!

Level 1 - Basic

This stage covers initial setup and foundational knowledge you need to get started ๐Ÿš€.

Create a Google Form

  1. Go to https://docs.google.com/forms/u/0/.
  2. Choose Blank Form and rename to Daily Spending Tracker Indo.
  3. Leave Form description blank.
  4. Rename Untitled Question to Kategori and change to Short answer.
  5. Click Copy icon to Duplicate Question and rename to Deskripsi.
  6. Click Copy icon to Duplicate Question and rename to Nominal. Click Triple dots icon and choose Response validation. Choose Number and Is number.
  7. Move to Responses Tab and click Link to Sheets. Stay on Create a new spreadsheet option and rename to Daily Spending Tracker Indo (Responses). Click Create.
  8. Click Publish button. Click Publish button again.
  9. Click Triple dots icon on the right side of Published button and choose Pre-fill form. Fill the form with the random values and click the Get link button.
  10. Click the COPY LINK button on the bottom pop-up later on. Paste it somewhere:

For example, here is the clipboard (your Form ID maybe different):

https://docs.google.com/forms/d/e/1FAIpQLSfgd-AefHbwUojiaFUEnrnMSWaeXqMxoumpn40CALxNksfC5g/viewform?usp=pp_url&entry.108078765=food&entry.799982393=satay&entry.778257956=25000

so the Form URL will look like this:

https://docs.google.com/forms/d/e/1FAIpQLSfgd-AefHbwUojiaFUEnrnMSWaeXqMxoumpn40CALxNksfC5g/formResponse

also jotted down the entries reflected to our questions (Your Entry ID maybe different):

Question Type Response validation Entry ID
Kategori Short answer entry.108078765
Deskripsi Short answer entry.799982393
Nominal Short answer Number; Is number; Custom error text entry.778257956

Create an iOS Shortcut

  1. Open Shortcuts app in iPhone. Tap + to create a new shortcut. Rename New Shortcut to Daily Spending Tracker Indo.
  2. In Search Actions bar, find and choose Ask for Input. Fill Prompt with Kategori?. Tap Chevron icon and untick Allow Multiple Line option.
  3. Tap Ask for Input icon action and choose Duplicate. Rename it into Deskripsi?.
  4. Duplicate once more. Tap Text and choose Number. Rename it into Nominal?.
  5. Tap Chevron icon and untick Allow Decimal Numbers and Allow Negative Numbers options.
  6. In Search Actions bar, find and choose Get contents of URL. Fill URL with the Form URL then tap Chevron icon.
  7. Tap GET and change the Method into POST. Tap JSON and change the Request Body into Form.
  8. Tap Add new field and choose Text. Fill Key with Entry ID. Repeat for all Entry ID.
  9. Tap Text on each field and choose Select Variable. Swipe up and select Ask for Input, matching it accordingly.
  10. Tap Add new field and choose Text. Fill Key with submit and Text with Submit.

For the first time, pop-up will be shown to ask permission for accessing the form URL. Simply choose Always Allow.

Level 2 - Aware

This stage adds context to your data. Now we don't just track what you spent, but also where you spent it ๐Ÿ‘๏ธ.

Update the Google Form

  1. Go to https://docs.google.com/forms/u/0/.
  2. Open Daily Spending Tracker Indo form and click Amount question. Click Copy icon twice to Duplicate Question two times.
  3. Rename the duplicated questions to each Longitude and Latitude respectively.
  4. Click Triple dots icon on the right side of Published button and choose Pre-fill form. Fill the form with the random values and click the Get link button.
  5. Click the COPY LINK button on the bottom pop-up later on. Paste it somewhere:

Take a look only for the new Entry ID:

https://docs.google.com/forms/d/e/1FAIpQLSfgd-AefHbwUojiaFUEnrnMSWaeXqMxoumpn40CALxNksfC5g/viewform?usp=pp_url&entry.108078765=food&entry.799982393=satay&entry.778257956=25000&entry.214181402=-1.5&entry.714053152=1.5
Question Type Response validation Entry ID
Latitude Short answer Number; Is number; Custom error text entry.214181402
Longitude Short answer Number; Is number; Custom error text entry.714053152

Update the iOS Shortcut

  1. In Search Actions bar, find and choose Get Current Location.
  2. In Search Actions bar, find and choose Get Details of Locations. Tap Detail and choose Latitude.
  3. Tap Get Details of Locations icon and choose Duplicate. Tap Latitude and change it to Longitude.
  4. Tap and hold Get contents of URL action then drag it to the very bottom. Next, Tap Chevron icon.
  5. Tap Add new field and choose Text. Fill Key with the new Entry ID. Repeat for all new Entry ID.
  6. Tap Text on each field and choose Select Variable. Swipe up and choose Longitude and Latitude respectively.
  7. Tap and hold Hamburger icon on submit field then drag it to the very bottom.

Level 3 - Organized

This stage makes your data cleaner and your shortcut smarter. No more random in Kategori and let's start make things tidy and programmatic ๐ŸŽฏ.

Update the iOS Shortcut

  1. In Search Actions bar, find and choose List.
  2. Change One to ๐Ÿฑ๐Ÿœ Makan/Minum and change Two to ๐Ÿฅค๐Ÿฉ Jajan.
  3. Tap Add New Item and fill Text to ๐Ÿ›’๐ŸŽ Belanja. Continue for the rest or feel free to adjust as needed.
  4. Tap and hold List action then drag it to the very top.
  5. In Search Actions bar, find and choose Choose from List. Tap and hold then drag it to down below the List action.
  6. Tap Chevron icon and fill Prompt with Kategori? and remove Ask for Input action for the Kategori? we've created previously.
  7. In Search Actions bar, find and choose Set Variable. Tap and hold then drag it to down below the Choose from List action. Fill Variable Name with category.
  8. In Search Actions bar, find and choose Set Variable. Tap and hold then drag it to down below the Ask for Input action for Deskripsi?. Fill Variable Name with description.
  9. In Search Actions bar, find and choose Set Variable. Tap and hold then drag it to down below the Ask for Input action for Nominal?. Fill Variable Name with amount.
  10. Go to the Get contents of URL action and tap Chevron icon. You'll notice the Kategori? value field within Request Body turn into red.
  11. Tap on it and click Clear variable. Tap Text and click Select Variable. Swipe up and choose category variable.
  12. Do the same for the two next fields for clear the previous variables and choose our newly created variables, description and amount.

For the first time, pop-up will be shown to ask permission for accessing our current loction. Simply choose Always Allow.

Full Category List

No. Category Examples
1. ๐Ÿฑ๐Ÿœ Makan/Minum warung, resto, food delivery, nasi padang
2. ๐Ÿฅค๐Ÿฉ Jajan boba, donut, snack, cafe ringan, bakery
3. ๐Ÿ›’๐ŸŽ Belanja raw groceries: sayur, daging, bumbu, beras
4. ๐Ÿงด๐Ÿชฅ Rumah Tangga sabun, odol, deterjen, tissue, perlengkapan rumah
5. ๐Ÿ“ถ๐Ÿ›œ Tagihan PLN, PDAM, Indihome, pulsa, BPJS
6. ๐Ÿš—โ›ฝ Transport bensin, ojol, parking, tol, tiket
7. ๐ŸŽฌ๐ŸŽฎ Hiburan Spotify, Netflix, Vidio, bioskop, game
8. ๐Ÿ‘•๐Ÿ‘Ÿ Fashion baju, sepatu, salon, skincare premium, parfum
9. ๐Ÿ’Š๐Ÿ’ช๐Ÿป Kesehatan apotek, dokter, vitamin, gym, klinik
10. ๐Ÿง ๐Ÿ“š Level Up course, buku, sertifikasi, ChatGPT, Udemy
11. ๐ŸŽ๐Ÿ’Œ Sosial hadiah, kondangan, sumbangan, kasih ortu
12. ๐Ÿ’ฐ๐Ÿ“ˆ Finansial cicilan, asuransi premi, investasi

Level 4 - Intelligent

This stage is where AI takes over! Just upload a receipt and let the magic happen ๐Ÿง .

Create a Gemini API Key

  1. Go to https://aistudio.google.com/api-keys.
  2. At the right top, click Create API Key button.
  3. Rename Gemini API Key to something more descriptive.
  4. Assign it to a specific project to stay organized.
  5. You can also choose + Create Project directly if you prefer.

The API Key will be used later on for the headers value of x-goog-api-key key.

iOS Shortcut Action Flow

%%{init: {'flowchart': {'curve': 'basis'}}}%%
flowchart TD
    Start([Start])
    Menu{Choose 
    from Menu}
    Start --> Menu 
    Menu --> | Manual Input | ChooseList
    Menu --> | Scan Receipt | SelectPhoto
    
    ChooseList[/Choose 
        from List/]
    AskDesc[/Ask for Input/]
    AskAmt[/Ask for Input/]
    SetCat1["Set Variable 
        (category)"]
    SetDesc1["Set Variable 
        (description)"]
    SetAmt1["Set Variable 
        (amount)"]
    ChooseList -.-> List --> SetCat1 --> AskDesc --> SetDesc1 --> AskAmt --> SetAmt1
    
    SelectPhoto[/Select 
        Photo/]
    subgraph AIModel["Send to Gemini AI Model"]
        Encode[Encode
            Base64]
        Prompt[/"Text
            (AI Prompt)"/]
        API[/"Text
            (API Key)"/]
        AIPost["Get Contents 
            of URL (POST)"]
    end    
    Parse[[Parsing Response]]
    SetCat2["Set Variable 
        (category)"]
    SetDesc2["Set Variable 
        (description)"]
    SetAmt2["Set Variable 
        (amount)"]
    API -.-> | Headers | AIPost
    SelectPhoto --> Encode -.-> | Request Body | AIPost
    Prompt -.-> | Request Body | AIPost
    AIPost --> | Response Body | Parse --> SetCat2 --> SetDesc2 --> SetAmt2

    subgraph GForm["Send to Google Form"]
        Variables["
            category, description, amount, latitude, longitude"]
        FinalPost["Get Contents 
            of URL (POST)"]
    end
    Location[Get Current
        Location]
    LocationDtl[Get Details of
        Locations]
    SetAmt1 --> Variables
    Location --> LocationDtl --> Variables
    SetAmt2 --> Variables
    Variables --> | Request Body | FinalPost
    FinalPost --> End([End])
Loading

Headers & Request Body Structure

Headers
โ”œโ”€โ”€ x-goog-api-key: "AIzaSy...your_api_key"
โ””โ”€โ”€ Content-Type: "application/json"
Request Body: JSON
โ”œโ”€โ”€ contents (Array, 1 item)
โ”‚   โ””โ”€โ”€ [Item 1: Dictionary]
โ”‚       โ””โ”€โ”€ parts (Array, 2 items)
โ”‚           โ”œโ”€โ”€ [Item 1: Dictionary - IMAGE]
โ”‚           โ”‚   โ””โ”€โ”€ inline_data (Dictionary)
โ”‚           โ”‚       โ”œโ”€โ”€ mime_type: "image/jpeg"
โ”‚           โ”‚       โ””โ”€โ”€ data: [Base64 Encoded] โ† variable
โ”‚           โ””โ”€โ”€ [Item 2: Dictionary - TEXT]
โ”‚               โ””โ”€โ”€ text: `"You are a receipt parser..."`
โ””โ”€โ”€ generationConfig (Dictionary)
    โ””โ”€โ”€ responseMimeType: "application/json"
Here's the prompt or feel free to adjust as needed.
You are a receipt parser for Indonesian receipts. Look at the receipt image and return ONLY a valid JSON object.

OUTPUT FORMAT:
{
  "ai_description": "string in Proper Case",
  "ai_amount": number,
  "ai_suggestedCategory": "string"
}

RULES:

1. AMOUNT: Indonesian format, dots/commas are THOUSAND separators (Rp16.000 = 16000). Pick FINAL TOTAL paid (look for "Grand Total" or "Total"). Ignore DPP, PPN, Harga Jual, Subtotal, "Number of Items".

2. DESCRIPTION (MUST be in Proper Case โ€” first letter of each significant word capitalized):
   - Find MERCHANT NAME. Usually at top header, but for small businesses may appear at FOOTER (after "Terima Kasih").
   - IGNORE address/location-only lines (e.g., "Batukajang", "Jakarta Selatan", "Jl. Sudirman").
   - Strip legal prefixes: PT, CV, UD.
   - Format: "Merchant: Item1, Item2 (xN), Item3"
   - For QTY > 1: append "(xN)" after item name.
   - For QTY = 1: write item name ONLY. DO NOT add "(x1)".
   - EXCEPT For FUEL, write item name and its numbers of Liter, add "L (without prefix space)"
   - Convert ALL CAPS items to Proper Case (e.g., "ROTI TAWAR GANDUM" โ†’ "Roti Tawar Gandum").
   - Max 90 chars total.
   - If no items, use merchant name only.
   
   Examples:
   - "Julie The Half Plate: Roti Tawar Gandum, Abon Roll (x2), Roti Keju, Snack Bolen Pisang, Roti Pisang (x2), Box Medium"
   - "Indomaret: Aqua (x3), Sabun, Tissue (x2)"
   - "Solusi Mart QRIS BNI"

3. SUGGESTED_CATEGORY must be EXACTLY one of (case-sensitive):
   Makan/Minum (warung, resto, food delivery, nasi padang)
   Jajan (boba, donut, snack, cafe ringan, bakery)
   Belanja (raw groceries: sayur, daging, bumbu, beras)
   Rumah Tangga (sabun, odol, deterjen, tissue, perlengkapan rumah)
   Tagihan (PLN, PDAM, Indihome, pulsa, BPJS)
   Transport (bensin, ojol, parking, tol, tiket)
   Hiburan (Spotify, Netflix, Vidio, bioskop, game, konser)
   Fashion (baju, sepatu, salon, skincare premium, parfum)
   Kesehatan (apotek, dokter, vitamin, gym, klinik)
   Level Up (course, buku, sertifikasi, ChatGPT, Udemy)
   Sosial (hadiah, kondangan, sumbangan, kasih ortu)
   Finansial (cicilan, asuransi premi, investasi)

4. PRIORITY RULES (if ambiguous):
   - Premium personal care (>Rp100K, aesthetic-driven) โ†’ Fashion
   - Basic hygiene (<Rp100K, daily use) โ†’ Rumah Tangga
   - Streaming subscription โ†’ Hiburan (not Tagihan)
   - AI/productivity tools (ChatGPT, Claude) โ†’ Level Up
   - One-time course/exam โ†’ Level Up
   - BPJS / mandatory insurance โ†’ Tagihan
   - Optional insurance/investment โ†’ Finansial
   - Bensin + minor item (e.g., aqua) โ†’ Transport (dominant cost rule)
   - Convenience store mostly food โ†’ Belanja
   - Convenience store mostly toiletries โ†’ Rumah Tangga
   - Bakery/cake shop โ†’ Jajan

5. Use null if cannot extract.

Level 5 - PRO!

This stage unlocks direct camera scanning and resilient error handling. Congratulations, King ๐Ÿ‘‘.

iOS Shortcut Action Flow

%%{init: {'flowchart': {'curve': 'basis'}}}%%
flowchart TD
    Start([Start])
    Menu{Choose 
    from Menu}
    Menu2{Choose 
    from Menu}
    Start --> Menu 
    Menu --> | Manual Input | ChooseList
    Menu --> | Scan Receipt | Menu2

    ChooseList[/Choose 
        from List/]
    AskDesc[/Ask for Input/]
    AskAmt[/Ask for Input/]
    SetCat1["Set Variable 
        (category)"]
    SetDesc1["Set Variable 
        (description)"]
    SetAmt1["Set Variable 
        (amount)"]
    ChooseList -.-> List --> SetCat1 --> AskDesc --> SetDesc1 --> AskAmt --> SetAmt1
    
    SelectPhoto[/Select 
        Photo/]
    subgraph AIModel["Send to Gemini AI Model"]
        Encode[Encode
            Base64]
        Prompt[/"Text
            (AI Prompt)"/]
        API[/"Text
            (API Key)"/]
        AIPost["Get Contents 
            of URL (POST)"]
    end    
    TakePhoto[/Take 
        Photo/]
    subgraph Result[" "]
        direction LR
        error[/Error/]
        candidates[/Candidates/]
        error -.- candidates
    end
    subgraph Result2[" "]
        direction LR
        error2[[Error Handling]]
        candidates2[[Parsing Response]]
        error2 -.- candidates2
    end
    SetAmt2["Set Variable 
        (category, description, amount)"]
    API -.-> | Headers | AIPost
    Menu2 --> | Open Camera | TakePhoto --> Encode -.-> | Request Body | AIPost
    Menu2 --> | Open Galery | SelectPhoto --> Encode    
    Prompt -.-> | Request Body | AIPost
    AIPost --> | Response Body | Result --> Result2 --> SetAmt2
    
    subgraph GForm["Send to Google Form"]
        Variables["
            category, description, amount, latitude, longitude"]
        FinalPost["Get Contents 
            of URL (POST)"]
    end
    Location[Get Current
        Location]
    LocationDtl[Get Details of
        Locations]
    SetAmt1 --> Variables
    Location --> LocationDtl --> Variables
    SetAmt2 --> Variables
    Variables --> | Request Body | FinalPost
    FinalPost --> End([End])
Loading

About

Track your daily spending like a PRO!

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors