dinary 1.2.0__tar.gz → 1.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dinary-1.2.0 → dinary-1.2.2}/PKG-INFO +1 -1
- dinary-1.2.2/src/dinary/__about__.py +1 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/__init__.py +2 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/import_tasks.py +15 -0
- dinary-1.2.2/tasks/imports/income_original_export.py +180 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/IncomeForm.vue +7 -4
- dinary-1.2.0/src/dinary/__about__.py +0 -1
- {dinary-1.2.0 → dinary-1.2.2}/.claudeignore +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.coveragerc +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.deploy.example/.env +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.deploy.example/README.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.deploy.example/import_sources.json +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.deploy.example/llm_providers.toml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.github/workflows/ci.yml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.github/workflows/docs.yml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.github/workflows/pip_publish.yml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.github/workflows/static.yml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.gitignore +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/.pre-commit-config.yaml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/AGENTS.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/CLAUDE.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/Dockerfile +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/LICENSE +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/README.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/activate.sh +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docker-compose.yml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/mkdocs.yml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/common/images/about.jpg +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/common/reference.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/en/cloudflare-setup.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/en/deploy-oracle.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/en/deploy-selfhost.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/en/development.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/en/google-sheets-setup.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/en/index.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/en/installation.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/en/operations.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/en/pwa-install.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/cloudflare-setup.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/deploy-oracle.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/deploy-selfhost.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/development.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/google-sheets-setup.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/index.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/installation.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/operations.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/pwa-install.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/docs/src/ru/taxonomy.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/invoke.yml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/pyproject.toml +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/pytest.ini +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/scripts/verup.sh +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/README.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/plans/analytics.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/architecture.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/catalog-api.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/classification-pipeline.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/currencies.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/frontend-cache.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/income-import.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/llm-providers.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/pwa-offline.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/receipt-fetching.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/sheets.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/sql-tool.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/stores.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/reference/timestamps.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/ui/README.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/ui/components.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/ui/design-language.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/ui/future-screens-guide.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/ui/patterns.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/specs/ui/screens.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/README.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/__init__.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/adapters/exchange_rates.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/adapters/llm_storage.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/adapters/llmbroker.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/adapters/nbp.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/adapters/nbs.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/adapters/rate_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/adapters/serbian_receipt_parser.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/adapters/sheets_client.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/catalog.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/catalog.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/catalog_writer.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/catalog_writer_categories.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/catalog_writer_errors.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/catalog_writer_events.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/catalog_writer_groups.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/expense_corrections.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/expenses.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/income.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/llm.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/qr_parser.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/controllers/rules.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/currencies.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/expense_corrections.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/expenses.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/income.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/llm.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/qr.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/receipts.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/api/rules.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/classification/item_normalizer.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/classification/persist.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/classification/receipt_classifier.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/classification/store_resolver.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/classification/task.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/rate_prefetch/task.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/sheet_logging/income_sheet_logging.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/sheet_logging/logging_jobs.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/sheet_logging/sheet_logging.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/sheet_logging/sheets_write.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/background/sheet_logging/task.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/config.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/catalog.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/classification_rules.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/currencies.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/db_migrations.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/expenses.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/income.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0001_initial_schema.rollback.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0001_initial_schema.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0002_exchange_rates_source_target.rollback.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0002_exchange_rates_source_target.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0003_app_currencies.rollback.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0003_app_currencies.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0004_receipt_pipeline.rollback.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0004_receipt_pipeline.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0005_income_logging.rollback.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0005_income_logging.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/README.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/receipts.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/__init__.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/get_category_by_name.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/get_existing_expense.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/get_month_expenses.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/insert_expense.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/insert_income.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/list_categories.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/list_incomes.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/logging_projection.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/resolve_mapping_for_year.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql/seed_load_categories.sql +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/sql_loader.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/storage.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/main.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/sheets/sheet_mapping.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/src/dinary/sheets/sheets.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/backups/backup_retention.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/backups/backup_snapshots.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/backups/backups_replica.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/backups/backups_restore.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/backups/backups_status.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/backups/backups_yandex.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/backups/restore_utils.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/db.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/deploy.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/devtools/build_docs.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/devtools/constants.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/devtools/dev.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/devtools/env.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/healthcheck.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/README.md +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/expense_import.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/income_import.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/report_2d_3d.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/seed.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/seed_config.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/seed_derivation.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/verify_equivalence.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/imports/verify_income.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/receipt.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/reports/expenses.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/reports/income.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/reports/report_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/reports/report_tasks.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/reports/verify_budget.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/reports/verify_income.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/server.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/setup.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/sql.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tasks/ssh_utils.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/_admin_catalog_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/_api_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_admin_catalog_add.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_admin_catalog_delete.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_admin_catalog_meta.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_admin_catalog_patch.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_admin_llm.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_catalog.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_concurrency.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_conflict.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_currencies.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_delete_expense.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_delete_receipt.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_expenses_recent_patch.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_get_expenses.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_income.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_post_expense.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_receipts.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_rules_approve.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_api_validation.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_receipt_pipeline_e2e.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_receipt_review.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/api/test_review_page_ux.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/conftest.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/currency/_currency_rates_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/currency/test_currency_rates_misc.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/currency/test_currency_rates_nbp.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/currency/test_currency_rates_resolve.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/currency/test_rate_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/currency/test_rate_prefetch_task.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/imports/test_expense_import.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/imports/test_seed_config.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/_catalog_writer_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/_ledger_repo_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_catalog_writer_invariants.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_catalog_writer_patch.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_income_db.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_ledger_repo_catalog.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_ledger_repo_expenses_insert.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_ledger_repo_expenses_lookup.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_ledger_repo_expenses_race.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_ledger_repo_jobs.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_ledger_repo_logging_projection.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/ledger/test_migrations.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/reports/_report_2d_3d_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/reports/test_report_2d_3d.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/reports/test_report_2d_3d_aggregate.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/reports/test_report_2d_3d_render.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/reports/test_report_2d_3d_resolve.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/reports/test_reports_expenses.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/reports/test_reports_income.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/reports/test_reports_verify_budget.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/reports/test_reports_verify_income.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_classification_rules.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_item_normalizer.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_llm_storage.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_llmbroker.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_qr_parser.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_receipt_classification.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_receipt_classifier.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_receipt_parser.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_sql_loader.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/services/test_store_resolver.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/_sheet_logging_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/_sheet_mapping_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/_sheets_helpers.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_income_drain.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_sheet_logging.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_sheet_logging_derive.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_sheet_logging_drain.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_sheet_logging_drain_one.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_sheet_mapping_parse.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_sheet_mapping_reload.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_sheet_mapping_resolve.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_sheets_read.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/sheets/test_sheets_rows.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_receipt_drain.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_receipt_pipeline.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_reclassify_receipts.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_backups_restore.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_backups_retention.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_backups_status.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_backups_yandex_setup.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_db.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_deploy.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_dev.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_imports.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_reports.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_restore_utils.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_server.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_server_receipt.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_setup_replica.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_ssh_utils.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tasks_ssh_utils_scripts.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/tasks/test_tools_sql.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/test_config.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/test_main.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/tests/test_webapp_api_contract.py +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/uv.lock +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/index.html +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/package-lock.json +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/package.json +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/public/apple-touch-icon-precomposed.png +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/public/apple-touch-icon.png +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/public/favicon.ico +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/public/icons/icon-180.png +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/public/icons/icon-192.png +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/public/icons/icon-512.png +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/public/manifest.json +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/App.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/api/_request.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/api/adminLlm.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/api/catalog.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/api/currencies.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/api/expenseCorrections.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/api/expenses.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/api/income.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/api/receipts.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/api/review.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/assets/base.css +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/BaseModal.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/BaseSheet.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/CatalogSelectField.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/CategoryQuickPicks.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/CategorySheet.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/ConfirmDeleteSheet.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/CorrectionSheet.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/CurrencyAmountRow.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/CurrencyPicker.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/ExpenseEditSheet.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/ExpenseForm.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/ExpenseRow.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/HeaderSegmented.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/HealthSummaryCard.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/IconBtn.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/IncomeEditSheet.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/IncomeRow.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/InlineCreateEvent.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/InlineCreateRow.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/KeyboardSaveBar.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/ManageList.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/ProviderCard.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/ProviderSheet.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/QrScanner.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/QueueModal.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/ReceiptCascadeCard.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/RuleRow.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/ScopeSelector.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/StatusDot.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/components/TagPicker.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/addResult.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/catalogManage.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/flushQueue.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/flushReceiptQueue.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/receipt.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/useExpenseDeleteFlow.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/useKeyboardVisible.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/useOnline.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/useStaleCache.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/useSwipeRow.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/composables/zbar.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/legacy-pwa-cleanup.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/main.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/modals/EditModal.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/stores/catalog.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/stores/currency.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/stores/frequentCategories.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/stores/income.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/stores/llm.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/stores/queue.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/stores/receiptQueue.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/stores/review.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/stores/toast.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/views/AddView.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/views/IncomeView.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/views/LLMView.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/src/views/ReviewView.vue +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/BaseSheet.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/CategoryQuickPicks.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/CategorySheet.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/CurrencyAmountRow.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/ExpenseEditSheet.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/ExpenseRow.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/ReceiptCascadeCard.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/ScopeSelector.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/api-adminLlm.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/api-catalog.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/api-currencies.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/api-expenses.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/api-income.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/api-receipts.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/api-request.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/api-review.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-app.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-catalog-select-field.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-correction-sheet.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-currency-picker.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-edit-modal.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-expense-edit-sheet.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-expense-form.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-header-segmented.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-health-summary-card.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-inline-create-event.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-inline-create-row.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-manage-list.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-provider-card.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-provider-sheet.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-queue-modal.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-review-view.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-rule-row.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-status-dot.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/component-tag-picker.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/composable-add-result.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/composable-catalog-manage.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/composable-flush-queue.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/composable-flush-receipt-queue.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/composable-keyboard-visible.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/composable-receipt.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/composable-stale-cache.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/composable-use-online.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/frequentCategories.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/legacy-pwa-cleanup.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/setup.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/store-catalog.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/store-currency.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/store-income.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/store-llm.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/store-queue.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/store-receipt-queue-durability.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/store-receipt-queue.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/store-review.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/store-toast.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/useExpenseDeleteFlow.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/tests/useSwipeRow.test.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/vite.config.js +0 -0
- {dinary-1.2.0 → dinary-1.2.2}/webapp/vitest.config.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dinary
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Server for [Dinary - your dinar diary](https://github.com/andgineer/dinary). Track expenses, scan receipts, analyze spending with AI
|
|
5
5
|
Project-URL: Homepage, https://andgineer.github.io/dinary/
|
|
6
6
|
Project-URL: Documentation, https://andgineer.github.io/dinary/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.2.2"
|
|
@@ -20,6 +20,7 @@ from .devtools.constants import ALLOWED_VERSION_TYPES
|
|
|
20
20
|
from .devtools.dev import build_static, dev, pre, reqs, test, uv, ver_task_factory, version
|
|
21
21
|
from .healthcheck import healthcheck
|
|
22
22
|
from .imports.import_tasks import (
|
|
23
|
+
export_income_original,
|
|
23
24
|
import_budget,
|
|
24
25
|
import_budget_all,
|
|
25
26
|
import_catalog,
|
|
@@ -47,6 +48,7 @@ __all__ = [
|
|
|
47
48
|
"dev",
|
|
48
49
|
"docs_task_factory",
|
|
49
50
|
"healthcheck",
|
|
51
|
+
"export_income_original",
|
|
50
52
|
"import_budget",
|
|
51
53
|
"import_budget_all",
|
|
52
54
|
"import_catalog",
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import json as _json
|
|
4
4
|
import sys
|
|
5
|
+
from pathlib import Path
|
|
5
6
|
from datetime import datetime as _dt
|
|
6
7
|
|
|
7
8
|
from invoke import task
|
|
8
9
|
|
|
10
|
+
from tasks.imports import income_original_export
|
|
9
11
|
from tasks.imports import report_2d_3d as _report_2d_3d_module
|
|
10
12
|
from tasks.reports import verify_budget, verify_income
|
|
11
13
|
from tasks.ssh_utils import remote_snapshot_cmd, ssh_capture_bytes, ssh_json, ssh_run
|
|
@@ -248,6 +250,19 @@ def verify_income_equivalence_all(c, json=False): # noqa: A002
|
|
|
248
250
|
sys.exit(verify_income.exit_code_for_batch(results))
|
|
249
251
|
|
|
250
252
|
|
|
253
|
+
@task(name="export-income-original")
|
|
254
|
+
def export_income_original(c, output=""):
|
|
255
|
+
"""Export income from all sheets in original currency (RUB/RSD) to a JSON file.
|
|
256
|
+
|
|
257
|
+
Reads every registered income source from .deploy/import_sources.json and
|
|
258
|
+
writes a flat list of {year, month, amount, currency} entries to JSON.
|
|
259
|
+
Default output path: data/income_original.json. Override with --output.
|
|
260
|
+
"""
|
|
261
|
+
dest = Path(output) if output else income_original_export.DEFAULT_OUTPUT
|
|
262
|
+
count = income_original_export.export_to_file(dest)
|
|
263
|
+
print(f"Wrote {count} entries to {dest}")
|
|
264
|
+
|
|
265
|
+
|
|
251
266
|
def _render_2d3d_locally(raw: bytes, *, as_csv: bool) -> None:
|
|
252
267
|
"""Render a JSON envelope produced by ``tasks.imports.report_2d_3d --json``.
|
|
253
268
|
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Extract monthly income from Google Sheets in original currency (no EUR conversion).
|
|
2
|
+
|
|
3
|
+
Reads every registered income source from ``.deploy/import_sources.json``
|
|
4
|
+
and aggregates amounts per (year, month) in the currency the sheet is
|
|
5
|
+
denominated in — RUB for years up to mid-2022, RSD from mid-2022 onward
|
|
6
|
+
(exact boundary is the ``transition_month`` in each year's ``IncomeLayout``).
|
|
7
|
+
|
|
8
|
+
The result is written as a JSON file with a flat list of entries:
|
|
9
|
+
|
|
10
|
+
.. code-block:: json
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
"generated_at": "2026-05-31",
|
|
14
|
+
"entries": [
|
|
15
|
+
{"year": 2019, "month": 1, "amount": "85000.00", "currency": "RUB"},
|
|
16
|
+
...
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
``inv export-income-original`` is the operator entry point. The module
|
|
21
|
+
can also be run with ``uv run python -m tasks.imports.income_original_export``.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import argparse
|
|
25
|
+
import json
|
|
26
|
+
import logging
|
|
27
|
+
import sys
|
|
28
|
+
from collections import defaultdict
|
|
29
|
+
from datetime import date
|
|
30
|
+
from decimal import Decimal
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
from dinary.adapters.sheets_client import get_sheet
|
|
34
|
+
from dinary.config import IMPORT_SOURCES_DOC_HINT, read_import_sources
|
|
35
|
+
from tasks.imports.expense_import import MONTHS_IN_YEAR
|
|
36
|
+
from tasks.imports.income_import import (
|
|
37
|
+
INCOME_LAYOUTS,
|
|
38
|
+
IncomeLayout,
|
|
39
|
+
_cell,
|
|
40
|
+
_parse_amount,
|
|
41
|
+
_parse_date,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
logger = logging.getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
DEFAULT_OUTPUT = Path("data/income_original.json")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def aggregate_original_currency(
|
|
50
|
+
year: int,
|
|
51
|
+
spreadsheet_id: str,
|
|
52
|
+
worksheet_name: str,
|
|
53
|
+
layout: IncomeLayout,
|
|
54
|
+
) -> dict[tuple[int, str], Decimal]:
|
|
55
|
+
"""Read one Income/Balance worksheet; return ``{(month, currency): total}``.
|
|
56
|
+
|
|
57
|
+
Does NOT convert to accounting currency. Each month carries exactly
|
|
58
|
+
one currency (the layout's ``currency`` before ``transition_month``,
|
|
59
|
+
``transition_currency`` from that month onward).
|
|
60
|
+
"""
|
|
61
|
+
ss = get_sheet(spreadsheet_id)
|
|
62
|
+
ws = ss.worksheet(worksheet_name)
|
|
63
|
+
all_values = ws.get_all_values()
|
|
64
|
+
|
|
65
|
+
monthly: dict[tuple[int, str], Decimal] = defaultdict(Decimal)
|
|
66
|
+
|
|
67
|
+
for row_idx in range(layout.header_rows, len(all_values)):
|
|
68
|
+
row = all_values[row_idx]
|
|
69
|
+
parsed = _parse_date(_cell(row, layout.col_date))
|
|
70
|
+
if parsed is None:
|
|
71
|
+
continue
|
|
72
|
+
row_year, month = parsed
|
|
73
|
+
if row_year != year:
|
|
74
|
+
continue
|
|
75
|
+
if not 1 <= month <= MONTHS_IN_YEAR:
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
amount = _parse_amount(_cell(row, layout.col_amount))
|
|
79
|
+
if amount is None:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
currency = layout.currency
|
|
83
|
+
if (
|
|
84
|
+
layout.transition_month is not None
|
|
85
|
+
and layout.transition_currency is not None
|
|
86
|
+
and month >= layout.transition_month
|
|
87
|
+
):
|
|
88
|
+
currency = layout.transition_currency
|
|
89
|
+
|
|
90
|
+
monthly[(month, currency)] += amount
|
|
91
|
+
|
|
92
|
+
return dict(monthly)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def extract_all_years() -> list[dict]:
|
|
96
|
+
"""Return a flat list of ``{year, month, amount, currency}`` dicts.
|
|
97
|
+
|
|
98
|
+
Iterates every import source that has an income worksheet. Years with
|
|
99
|
+
no registered source or no income worksheet are silently skipped.
|
|
100
|
+
Amounts are ``str`` (canonical decimal notation) to avoid float rounding.
|
|
101
|
+
"""
|
|
102
|
+
sources = read_import_sources()
|
|
103
|
+
entries: list[dict] = []
|
|
104
|
+
|
|
105
|
+
for source in sorted(sources, key=lambda s: s.year):
|
|
106
|
+
if not source.income_worksheet_name:
|
|
107
|
+
continue
|
|
108
|
+
if not source.income_layout_key or source.income_layout_key not in INCOME_LAYOUTS:
|
|
109
|
+
logger.warning(
|
|
110
|
+
"Year %d: unknown income_layout_key %r — skipping. %s",
|
|
111
|
+
source.year,
|
|
112
|
+
source.income_layout_key,
|
|
113
|
+
IMPORT_SOURCES_DOC_HINT,
|
|
114
|
+
)
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
layout = INCOME_LAYOUTS[source.income_layout_key]
|
|
118
|
+
logger.info(
|
|
119
|
+
"Extracting year %d from worksheet %r",
|
|
120
|
+
source.year,
|
|
121
|
+
source.income_worksheet_name,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
monthly = aggregate_original_currency(
|
|
126
|
+
source.year,
|
|
127
|
+
source.spreadsheet_id,
|
|
128
|
+
source.income_worksheet_name,
|
|
129
|
+
layout,
|
|
130
|
+
)
|
|
131
|
+
except Exception:
|
|
132
|
+
logger.exception("Failed to read year %d — skipping", source.year)
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
for (month, currency), total in sorted(monthly.items()):
|
|
136
|
+
entries.append(
|
|
137
|
+
{
|
|
138
|
+
"year": source.year,
|
|
139
|
+
"month": month,
|
|
140
|
+
"amount": format(total, "f"),
|
|
141
|
+
"currency": currency,
|
|
142
|
+
},
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return entries
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def export_to_file(output: Path) -> int:
|
|
149
|
+
"""Extract all income in original currency and write to ``output``.
|
|
150
|
+
|
|
151
|
+
Returns the number of entries written. Raises on I/O errors.
|
|
152
|
+
"""
|
|
153
|
+
entries = extract_all_years()
|
|
154
|
+
payload = {
|
|
155
|
+
"generated_at": str(date.today()),
|
|
156
|
+
"entries": entries,
|
|
157
|
+
}
|
|
158
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
|
159
|
+
output.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
160
|
+
return len(entries)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def main(argv: list[str] | None = None) -> int:
|
|
164
|
+
parser = argparse.ArgumentParser(
|
|
165
|
+
description="Extract income from all sheets in original currency to JSON.",
|
|
166
|
+
)
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
"--output",
|
|
169
|
+
default=str(DEFAULT_OUTPUT),
|
|
170
|
+
help=f"destination JSON file (default: {DEFAULT_OUTPUT})",
|
|
171
|
+
)
|
|
172
|
+
args = parser.parse_args(argv)
|
|
173
|
+
output = Path(args.output)
|
|
174
|
+
count = export_to_file(output)
|
|
175
|
+
print(f"Wrote {count} entries to {output}")
|
|
176
|
+
return 0
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
sys.exit(main())
|
|
@@ -20,14 +20,17 @@ function today() {
|
|
|
20
20
|
return new Date().toISOString().slice(0, 10);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function
|
|
23
|
+
function defaultIncomeMonth() {
|
|
24
24
|
const now = new Date();
|
|
25
|
-
|
|
25
|
+
// Salary for the previous month is paid on the 5th (or earlier if weekend),
|
|
26
|
+
// so on days 1–5 the income being entered is for the previous month.
|
|
27
|
+
const d = now.getDate() <= 5 ? new Date(now.getFullYear(), now.getMonth() - 1, 1) : now;
|
|
28
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
const amount = ref("");
|
|
29
32
|
const selectedCurrency = ref("");
|
|
30
|
-
const monthValue = ref(
|
|
33
|
+
const monthValue = ref(defaultIncomeMonth());
|
|
31
34
|
const dateValue = ref(today());
|
|
32
35
|
const comment = ref("");
|
|
33
36
|
const currencyPickerOpen = ref(false);
|
|
@@ -66,7 +69,7 @@ async function save() {
|
|
|
66
69
|
currency.setLastUsed(code);
|
|
67
70
|
amount.value = "";
|
|
68
71
|
comment.value = "";
|
|
69
|
-
monthValue.value =
|
|
72
|
+
monthValue.value = defaultIncomeMonth();
|
|
70
73
|
dateValue.value = today();
|
|
71
74
|
emit("saved");
|
|
72
75
|
} catch {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.2.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dinary-1.2.0 → dinary-1.2.2}/src/dinary/db/migrations/0002_exchange_rates_source_target.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|