diff --git a/apps/finance/services/api/main/handler.go b/apps/finance/services/api/main/handler.go index 6a1ab98..a5103ed 100644 --- a/apps/finance/services/api/main/handler.go +++ b/apps/finance/services/api/main/handler.go @@ -642,19 +642,20 @@ func (h *Handler) Transactions(w http.ResponseWriter, r *http.Request) { } render(w, txnsTmpl, map[string]interface{}{ - "UserID": a.UserID, - "Email": a.Email, - "Title": "Transactions", - "Route": "transactions", - "IsOwner": true, - "Txns": txns, - "Categories": cats, - "Accounts": accounts, - "AccountNames": accountNames, + "UserID": a.UserID, + "Email": a.Email, + "Title": "Transactions", + "Route": "transactions", + "IsOwner": true, + "Txns": txns, + "Categories": cats, + "Accounts": accounts, + "AccountNames": accountNames, "CategoryColors": catColors, - "Cat": cat, - "Search": search, - "Days": daysStr, + "Cat": cat, + "Search": search, + "Days": daysStr, + "Notice": r.URL.Query().Get("notice"), }) } @@ -2241,48 +2242,13 @@ func (h *Handler) Household(w http.ResponseWriter, r *http.Request) { func (h *Handler) AutoImport(w http.ResponseWriter, r *http.Request) { auth := getAuth(r) - ctx := r.Context() - - if r.Method == http.MethodPost { - _ = r.ParseForm() - sched := &ImportSchedule{ - ID: bson.NewObjectID().Hex(), - UserID: auth.UserID, - AccountID: r.FormValue("account_id"), - Label: r.FormValue("label"), - Format: r.FormValue("format"), - URL: r.FormValue("url"), - Active: r.FormValue("active") == "on", - CreatedAt: time.Now(), - } - if err := h.store.createImportSchedule(ctx, sched); err != nil { - http.Error(w, "failed to create schedule", http.StatusInternalServerError) - return - } - http.Redirect(w, r, "/auto-import", http.StatusSeeOther) - return - } - - if r.Method == http.MethodDelete { - id := r.PathValue("id") - if err := h.store.deleteImportSchedule(ctx, id, auth.UserID); err != nil { - http.Error(w, "failed to delete schedule", http.StatusInternalServerError) - return - } - w.WriteHeader(http.StatusNoContent) - return - } - - schedules, _ := h.store.getImportSchedules(ctx, auth.UserID) - accounts, _ := h.store.getAccounts(ctx, auth.UserID) - + accounts, _ := h.store.getAccounts(r.Context(), auth.UserID) render(w, autoImportTmpl, &AutoImportData{ - UserID: auth.UserID, - Email: auth.Email, - Title: "Auto Import", - Route: "/auto-import", - Accounts: accounts, - Schedules: schedules, + UserID: auth.UserID, + Email: auth.Email, + Title: "Import Guide", + Route: "/auto-import", + Accounts: accounts, }) } @@ -2320,8 +2286,6 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("POST /household", h.Household) mux.HandleFunc("DELETE /household", h.Household) mux.HandleFunc("GET /auto-import", h.AutoImport) - mux.HandleFunc("POST /auto-import", h.AutoImport) - mux.HandleFunc("DELETE /auto-import/{id}", h.AutoImport) } func sortStrings(s []string) { diff --git a/apps/finance/services/api/main/templates/auto_import.html b/apps/finance/services/api/main/templates/auto_import.html index 25c3077..3cb0f9e 100644 --- a/apps/finance/services/api/main/templates/auto_import.html +++ b/apps/finance/services/api/main/templates/auto_import.html @@ -3,110 +3,80 @@ {{define "content"}} {{$d := .}} -
- Configure a recurring CSV import source. The app will fetch and import it automatically on a daily schedule. -
- -How to get your bank transactions into the app
- You can also push a CSV file directly without scheduling. Use this endpoint from any automation tool (n8n, cron, etc.): -
-account_id, format, rows (JSON array)
+Log into your bank's online portal and download a transaction extract for the period you want. Most Portuguese banks offer this under Movimentos or Extratos.
+Date, Description, Amount. The importer auto-detects column order.Go to Import, pick the account and format, then select your file. You'll see a preview of every row with auto-suggested categories. Rows already in the database are shown greyed out and will be skipped automatically — safe to re-upload the same file.
+Adjust any auto-categorised rows using the dropdown selectors, then click Confirm Import. Only new transactions are saved.
++ If you export your CSV on a schedule (e.g. via a cron job or n8n), you can push it directly to the import endpoint without going through the UI: +
+curl -X POST https://<your-host>/import/preview \ + -H "X-Auth-User-Id: <user-id>" \ + -H "X-Auth-Email: <email>" \ + -F "account_id=<account-id>" \ + -F "format=cgd" \ + -F "file=@movements.csv"+
The preview endpoint returns an HTML page; for headless import pipe the confirmed rows to POST /import/confirm with the same account_id, format, raw_data, and categories[] fields.