{"id":15541393,"url":"https://github.com/ivov/monotax","last_synced_at":"2026-05-06T14:31:28.142Z","repository":{"id":103105736,"uuid":"241487399","full_name":"ivov/monotax","owner":"ivov","description":"Colorful dashboard to display financial stats for a side business, built with TypeScript/React, TypeScript/Node and SQLite.","archived":false,"fork":false,"pushed_at":"2020-04-18T18:33:20.000Z","size":11807,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-02T06:16:20.701Z","etag":null,"topics":["material-ui","nodejs","reactjs","sqlite","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-18T23:11:12.000Z","updated_at":"2020-06-11T02:16:57.000Z","dependencies_parsed_at":null,"dependency_job_id":"bd15731b-0743-459e-9de7-8ad7d62b8281","html_url":"https://github.com/ivov/monotax","commit_stats":{"total_commits":39,"total_committers":1,"mean_commits":39.0,"dds":0.0,"last_synced_commit":"5b57739180d67361627f064670c9c49ab402ac17"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ivov/monotax","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivov%2Fmonotax","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivov%2Fmonotax/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivov%2Fmonotax/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivov%2Fmonotax/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivov","download_url":"https://codeload.github.com/ivov/monotax/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivov%2Fmonotax/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32698092,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-06T08:33:17.875Z","status":"ssl_error","status_checked_at":"2026-05-06T08:33:17.221Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["material-ui","nodejs","reactjs","sqlite","typescript"],"created_at":"2024-10-02T12:16:56.157Z","updated_at":"2026-05-06T14:31:28.127Z","avatar_url":"https://github.com/ivov.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# monotax\r\n\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n\r\nColorful dashboard to display financial stats for a side business, built with TypeScript/React, TypeScript/Node and SQLite.\r\n\r\n\u003cbr\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"demo/ts.png\" width=\"140\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"demo/react.png\" width=\"150\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"demo/node.png\" width=\"145\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"demo/sqlite.png\" width=\"240\"\u003e\r\n\u003c/p\u003e\r\n\r\n\u003cbr\u003e\r\n\r\n## Overview\r\n\r\nQuick app to display a client's earnings, expenses, savings and invoices in material design, including a PDF invoice parser.\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"demo/overview.gif\"\u003e\r\n\u003c/p\u003e\r\n\r\nFeatures:\r\n\r\n- Views for all-time, quarterly, yearly and monthly stats\r\n- Views for earnings, expenses, savings and invoices\r\n- CLI utility for quick PDF-to-SQLite conversion\r\n- Colorful animated charts and tabbed tables\r\n- Expanding panels, pagination and sorting\r\n\r\n## Screenshots\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"demo/2019.png\" width=\"\"\u003e\r\n\u003c/p\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"demo/earnings.png\"\u003e\r\n\u003c/p\u003e\r\n\r\n## Operation\r\n\r\nPDF invoices are placed at `data/pdf` and parsed with `npm run parse`. The invoices must be formatted as those issued under the monotax scheme of Argentina's Federal Administration of Public Revenue.\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"demo/cli.gif\"\u003e\r\n\u003c/p\u003e\r\n\r\nEvery PDF is converted to a JSON object and is persisted in the database at `data/sql/monotax.db`. The db is pre-populated with earnings, expenses and ARS-USD exchange rate data, queriable through 35+ SQL views, many of which are used by the backend. A demo db and its full schema are included.\r\n\r\nPDF invoice sample:\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"demo/invoice.png\" width=\"630\"\u003e\r\n\u003c/p\u003e\r\n\r\nJSON object sample:\r\n\r\n```js\r\n{\r\n\tdate: \"Mon Dec 23 2019 12:41:30 GMT-0300 (Argentina Standard Time)\",\r\n\tid: \"692\",\r\n\tname: \"Lorem Ipsum\",\r\n\taddress: {\r\n\t\tstreet: \"Dolor Sit\",\r\n\t\tprovince: \"Amet\",\r\n\t}\r\n\tclient_id: {\r\n\t\ttype: \"DNI\",\r\n\t\tnumber: \"12934612\",\r\n\t}\r\n\tvatStatus: \"Consumidor Final\",\r\n\tamount: 1900.00\r\n}\r\n```\r\n\r\nThe frontend relies on [React Admin](https://github.com/marmelab/react-admin) to auto-fetch data for views corresponding to earnings, expenses, savings and invoices. The client also makes requests custom endpoints to fetch data for the global overview and the yearly stats views.\r\n\r\n```ts\r\n// setAutomaticRoutes.ts → four React Admin endpoints\r\nconst setAutomaticRoutes = (app: express.Application) =\u003e {\r\n\tconst autoRoutes = [\"invoices\", \"earnings\", \"expenses\", \"savings\"];\r\n\tautoRoutes.forEach(route =\u003e\r\n\t\tapp.get(\r\n\t\t\t\"/api/\" + route,\r\n\t\t\t(request: express.Request, response: express.Response) =\u003e\r\n\t\t\t\tcreateAutomaticRoute(request, response, route)\r\n\t\t)\r\n\t);\r\n};\r\n\r\nconst createAutomaticRoute = (\r\n\trequest: express.Request,\r\n\tresponse: express.Response,\r\n\troute: string\r\n) =\u003e {\r\n\tconst { offset, limit } = request.query;\r\n\tconst [field, order] = JSON.parse(request.query.sort);\r\n\tconst results = DatabaseService.getAllRecords(\r\n\t\tVIEWS[route],\r\n\t\toffset,\r\n\t\tlimit,\r\n\t\tfield,\r\n\t\torder\r\n\t);\r\n\tconst total = DatabaseService.getCount(VIEWS[route]);\r\n\treturn response\r\n\t\t.status(200)\r\n\t\t.set(\"Content-Range\", route + \"0-15/\" + total)\r\n\t\t.set(\"Access-Control-Expose-Headers\", \"Content-Range\")\r\n\t\t.json(results);\r\n};\r\n```\r\n\r\n```ts\r\n// dataProvider.ts → four API calls\r\ngetList: (resource: string, params: GetListParams) =\u003e {\r\n\tconst { page, perPage } = params.pagination;\r\n\tconst { field, order } = params.sort;\r\n\r\n\tconst query = {\r\n\t\tfilter: JSON.stringify(params.filter),\r\n\t\toffset: JSON.stringify((page - 1) * perPage),\r\n\t\tlimit: JSON.stringify(perPage),\r\n\t\tsort: JSON.stringify([field, order])\r\n\t};\r\n\tconst url = `${apiUrl}/${resource}?${stringify(query)}`;\r\n\r\n\treturn httpClient(url).then(({ json, headers }: any) =\u003e {\r\n\t\treturn {\r\n\t\t\tdata: json,\r\n\t\t\ttotal: parseInt(\r\n\t\t\t\theaders\r\n\t\t\t\t\t.get(\"Content-Range\")\r\n\t\t\t\t\t.split(\"/\")\r\n\t\t\t\t\t.pop(),\r\n\t\t\t\t10\r\n\t\t\t)\r\n\t\t};\r\n\t});\r\n};\r\n```\r\n\r\n```ts\r\n// setCustomRoutes.ts → four custom endpoints\r\nconst setCustomRoutes = (app: express.Application) =\u003e {\r\n\tapp.get(\"/api/overview/\", (request, response) =\u003e {\r\n\t\tconst quarterlyData = DatabaseService.getQuarterlyData();\r\n\t\tconst allTimeTotals = DatabaseService.getAllTimeTotals();\r\n\t\tconst yearlyTotals = DatabaseService.getYearlyTotals();\r\n\t\tconst monthlyAveragesPerYear = DatabaseService.getMonthlyAveragesPerYear();\r\n\t\tconst lastSixMonthsValues = DatabaseService.getLastSixMonthsValues();\r\n\r\n\t\tresponse.status(200).json({\r\n\t\t\tquarterlyData,\r\n\t\t\tallTimeTotals,\r\n\t\t\tyearlyTotals,\r\n\t\t\tmonthlyAveragesPerYear,\r\n\t\t\tlastSixMonthsValues\r\n\t\t});\r\n\t});\r\n\r\n\tconst years = [\"2017\", \"2018\", \"2019\"];\r\n\tyears.forEach(year =\u003e\r\n\t\tapp.get(\"/api/year/\" + year, (request, response) =\u003e {\r\n\t\t\tconst yearData = DatabaseService.getYearData(year);\r\n\t\t\tconst totalsForYear = calculateTotals(yearData);\r\n\r\n\t\t\tresponse.status(200).json({\r\n\t\t\t\tearningsForYear: yearData[\"earnings\"],\r\n\t\t\t\texpensesForYear: yearData[\"expenses\"],\r\n\t\t\t\tsavingsForYear: yearData[\"savings\"],\r\n\t\t\t\tinvoicedForYear: yearData[\"invoiced\"],\r\n\t\t\t\ttotalsForYear\r\n\t\t\t});\r\n\t\t})\r\n\t);\r\n};\r\n```\r\n\r\nThe dashboard borrows from [Material UI](https://material-ui.com/) icons and Material React components, to display data in colorful boxes, animated charts and tabbed tables. Material React components must be placed at `client/src/mdr` and are not included in this repo.\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"demo/line-graph.gif\"\u003e\r\n\u003c/p\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"demo/bar-graph.gif\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"demo/tabbed-table.gif\"\u003e\r\n\u003c/p\u003e\r\n\r\nA dev proxy is set up so that any request from the client at `http://localhost:3000/` made to an `/api/` endpoint is redirected to the server at `http://localhost:5000/`. See the CRA docs on [API request proxying](https://create-react-app.dev/docs/proxying-api-requests-in-development/).\r\n\r\n```js\r\nconst { createProxyMiddleware } = require(\"http-proxy-middleware\");\r\n\r\nmodule.exports = function(app) {\r\n\tapp.use(\r\n\t\t\"/api\",\r\n\t\tcreateProxyMiddleware({\r\n\t\t\ttarget: \"http://localhost:5000\",\r\n\t\t\tchangeOrigin: true\r\n\t\t})\r\n\t);\r\n};\r\n```\r\n\r\n## Author\r\n\r\n© 2020 Iván Ovejero\r\n\r\n## License\r\n\r\nDistributed under the MIT License. See [LICENSE.md](LICENSE.md)\r\n\r\n\u003c!-- Model: http://webapplayers.com/inspinia_admin-v2.9.3/\r\n\r\nBetter SQlite documentation: https://github.com/JoshuaWise/better-sqlite3/blob/master/docs/api.md\r\nReact Admin documentation: https://marmelab.com/react-admin/Readme.html\r\nMaterial Design React documentation: https://demos.creative-tim.com/material-dashboard-react/#/documentation/tutorial\r\nMaterial Design React demo: https://demos.creative-tim.com/material-dashboard-react/#/admin/dashboard\r\n\r\nPending:\r\n\r\n- Sixth item --\u003e\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivov%2Fmonotax","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivov%2Fmonotax","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivov%2Fmonotax/lists"}