├── docs ├── .nojekyll ├── CNAME ├── link.png ├── favicon.ico ├── assets │ ├── balance.png │ ├── swagger.jpg │ └── transactions.png └── sitemap.xml ├── version ├── source ├── Gnomeshade.WebApi │ ├── Pages │ │ ├── _ViewStart.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── .editorconfig │ │ ├── Shared │ │ │ └── _ValidationScriptsPartial.cshtml │ │ ├── Index.cshtml │ │ └── Index.cshtml.cs │ ├── Areas │ │ ├── Admin │ │ │ └── Pages │ │ │ │ ├── _ViewStart.cshtml │ │ │ │ ├── Index.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── Index.cshtml.cs │ │ ├── Identity │ │ │ └── Pages │ │ │ │ ├── _ViewStart.cshtml │ │ │ │ ├── Account │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ ├── Manage │ │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ │ ├── _ViewStart.cshtml │ │ │ │ │ ├── DownloadPersonalData.cshtml │ │ │ │ │ ├── _StatusMessage.cshtml │ │ │ │ │ ├── _Layout.cshtml │ │ │ │ │ ├── Disable2fa.cshtml │ │ │ │ │ ├── ShowRecoveryCodes.cshtml.cs │ │ │ │ │ └── ShowRecoveryCodes.cshtml │ │ │ │ ├── AccessDenied.cshtml │ │ │ │ ├── Lockout.cshtml │ │ │ │ ├── ResetPasswordConfirmation.cshtml │ │ │ │ ├── _StatusMessage.cshtml │ │ │ │ ├── AccessDenied.cshtml.cs │ │ │ │ ├── Lockout.cshtml.cs │ │ │ │ ├── ResetPasswordConfirmation.cshtml.cs │ │ │ │ ├── Logout.cshtml │ │ │ │ └── RegisterConfirmation.cshtml │ │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── Error.cshtml.cs │ │ └── .editorconfig │ ├── key.snk │ ├── wwwroot │ │ └── favicon.ico │ ├── Node │ │ └── package.json │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── Configuration │ │ ├── Roles.cs │ │ ├── Policies.cs │ │ ├── HttpRequestExtensions.cs │ │ └── Options │ │ │ ├── DatabaseOptions.cs │ │ │ ├── DatabaseProvider.cs │ │ │ ├── OAuthProviderOptions.cs │ │ │ └── AdminOptions.cs │ ├── Properties │ │ └── launchSettings.json │ ├── V1 │ │ ├── Accounts │ │ │ └── ReservedNames.cs │ │ ├── Importing │ │ │ ├── Paperless │ │ │ │ ├── Rimi │ │ │ │ │ └── Constants.cs │ │ │ │ └── IPaperlessDocumentParser.cs │ │ │ └── ImportableTransaction.cs │ │ └── Authorization │ │ │ ├── ApplicationUserRequirement.cs │ │ │ └── AdministratorRoleRequirement.cs │ ├── Services │ │ └── ApplicationVersionService.cs │ ├── Logging │ │ └── LoggingSerializerContext.cs │ └── OpenApi │ │ ├── ProducesStatus404NotFoundAttribute.cs │ │ ├── ProducesStatus409ConflictAttribute.cs │ │ └── InstantSchemaFilter.cs ├── Gnomeshade.Data │ ├── key.snk │ ├── Migrations │ │ ├── 00000033_inverse_multiplier.sql │ │ ├── 00000026_linked_product.sql │ │ ├── 00000032_system_data_access.sql │ │ └── IDatabaseMigrator.cs │ ├── Repositories │ │ ├── Queries │ │ │ ├── User │ │ │ │ ├── SelectAll.sql │ │ │ │ ├── Update.sql │ │ │ │ └── Insert.sql │ │ │ ├── Ownership │ │ │ │ ├── Insert.sql │ │ │ │ ├── SelectAll.sql │ │ │ │ ├── Select.sql │ │ │ │ └── Delete.sql │ │ │ ├── Link │ │ │ │ ├── Insert.sql │ │ │ │ └── SelectAll.sql │ │ │ ├── Owner │ │ │ │ ├── Insert.sql │ │ │ │ ├── SelectAll.sql │ │ │ │ └── Select.sql │ │ │ ├── AccountInCurrency │ │ │ │ ├── Insert.sql │ │ │ │ ├── SelectAll.sql │ │ │ │ └── Select.sql │ │ │ ├── Counterparty │ │ │ │ ├── Insert.sql │ │ │ │ ├── SelectAll.sql │ │ │ │ └── Merge.sql │ │ │ ├── Product │ │ │ │ ├── Insert.sql │ │ │ │ └── SelectAll.sql │ │ │ ├── Category │ │ │ │ ├── Insert.sql │ │ │ │ └── SelectAll.sql │ │ │ ├── Unit │ │ │ │ ├── Insert.sql │ │ │ │ └── SelectAll.sql │ │ │ ├── LoanPayment │ │ │ │ ├── Insert.sql │ │ │ │ ├── SelectAll.sql │ │ │ │ └── Delete.sql │ │ │ ├── Currency │ │ │ │ └── SelectAll.sql │ │ │ ├── Project │ │ │ │ ├── SelectAll.sql │ │ │ │ ├── Insert.sql │ │ │ │ ├── Select.sql │ │ │ │ └── Update.sql │ │ │ ├── Loan │ │ │ │ ├── Insert.sql │ │ │ │ └── SelectAll.sql │ │ │ ├── Transaction │ │ │ │ ├── Insert.sql │ │ │ │ └── SelectAll.sql │ │ │ ├── Loan2 │ │ │ │ ├── Insert.sql │ │ │ │ ├── Delete.sql │ │ │ │ └── SelectAll.sql │ │ │ ├── Purchase │ │ │ │ └── Insert.sql │ │ │ ├── Account │ │ │ │ ├── Insert.sql │ │ │ │ └── Delete.sql │ │ │ └── Transfer │ │ │ │ └── Insert.sql │ │ ├── .editorconfig │ │ ├── AccessLevelExtensions.cs │ │ ├── AccessLevel.cs │ │ └── Extensions │ │ │ └── IdContainer.cs │ ├── Entities │ │ ├── Abstractions │ │ │ ├── ISortableEntity.cs │ │ │ ├── IOwnableEntity.cs │ │ │ ├── INamedEntity.cs │ │ │ ├── Entity.cs │ │ │ └── IModifiableEntity.cs │ │ ├── AccessEntity.cs │ │ ├── OwnerEntity.cs │ │ ├── BalanceEntity.cs │ │ └── UserEntity.cs │ ├── Gnomeshade.Data.csproj │ └── Identity │ │ └── ApplicationRole.cs ├── Gnomeshade.Data.PostgreSQL │ ├── Migrations │ │ ├── 00000001_database_extensions.sql │ │ ├── 00000005_product_sku.sql │ │ ├── 00000011_rename_tags.sql │ │ ├── 00000024_refunded_by.sql │ │ ├── 00000019_unit_symbol.sql │ │ ├── 00000010_cleanup.sql │ │ ├── 00000014_account_currency_uniqueness.sql │ │ ├── 00000025_bank_reference.sql │ │ ├── 00000021_order.sql │ │ ├── 00000027_remove_disabled.sql │ │ ├── 00000006_account_name_uniqueness.sql │ │ ├── 00000022_related_transactions.sql │ │ ├── 00000020_sek.sql │ │ ├── 00000013_hrd.sql │ │ ├── 00000016_name_uniqueness.sql │ │ ├── 00000012_category_ids.sql │ │ ├── 00000029_transfer_dates.sql │ │ ├── 00000030_ownership_name.sql │ │ └── 00000004_transaction_dates.sql │ ├── key.snk │ └── Dapper │ │ ├── NullableInstantTypeHandler.cs │ │ └── UnsignedIntegerTypeHandler.cs ├── Gnomeshade.Data.Sqlite │ ├── key.snk │ ├── Migrations │ │ ├── 00000009_refunded_by.sql │ │ ├── 00000010_bank_reference.sql │ │ ├── 00000006_order.sql │ │ ├── 00000007_related_transactions.sql │ │ ├── 00000005_sek.sql │ │ └── 00000004_unit_symbol.sql │ └── Dapper │ │ ├── GuidHandler.cs │ │ └── DecimalHandler.cs ├── Gnomeshade.WebApi.Client │ ├── key.snk │ ├── packageIcon.png │ ├── ImportResult.cs │ ├── Results │ │ ├── ExternalLoginResult.cs │ │ ├── LoggedIn.cs │ │ └── RequiresRegistration.cs │ ├── LoginResult.cs │ ├── SuccessfulLogin.cs │ ├── FailedLogin.cs │ ├── GnomeshadeOptions.cs │ ├── NewRequisition.cs │ └── RefreshTokenChangedEventArgs.cs ├── Gnomeshade.WebApi.Models │ ├── key.snk │ ├── .editorconfig │ ├── Creation.cs │ ├── Owners │ │ ├── User.cs │ │ ├── OwnerCreation.cs │ │ ├── Owner.cs │ │ ├── Access.cs │ │ ├── OwnershipCreation.cs │ │ └── Ownership.cs │ ├── Gnomeshade.WebApi.Models.csproj │ ├── LinkCreation.cs │ ├── Accounts │ │ ├── CounterpartyCreation.cs │ │ ├── AccountInCurrencyCreation.cs │ │ └── Balance.cs │ ├── Transactions │ │ └── TransactionItemCreation.cs │ ├── Projects │ │ └── ProjectCreation.cs │ ├── Importing │ │ ├── AccountReference.cs │ │ ├── TransferReference.cs │ │ ├── TransactionReference.cs │ │ └── Iso20022Report.cs │ └── Authentication │ │ ├── UserModel.cs │ │ ├── Login.cs │ │ └── LoginResponse.cs ├── Gnomeshade.Desktop │ ├── .gitattributes │ ├── Assets │ │ ├── gnomeshade.ico │ │ ├── Hack-Regular.ttf │ │ ├── Hack-RegularOblique.ttf │ │ └── Inconsolata-Regular.ttf │ ├── appsettings.json │ └── Views │ │ ├── DashboardView.axaml.cs │ │ ├── Loans │ │ ├── LoanView.axaml.cs │ │ ├── LoanUpsertionView.axaml.cs │ │ └── Migration │ │ │ └── LoanMigrationView.axaml.cs │ │ ├── Projects │ │ ├── ProjectView.axaml.cs │ │ └── ProjectUpsertionView.axaml.cs │ │ ├── Help │ │ ├── AboutView.axaml.cs │ │ └── LicensesView.axaml.cs │ │ ├── DialogWindow.axaml.cs │ │ ├── Accesses │ │ ├── OwnerView.axaml.cs │ │ └── OwnerUpsertionView.axaml.cs │ │ ├── Imports │ │ └── ImportView.axaml.cs │ │ ├── Products │ │ ├── CategoryFilterView.axaml.cs │ │ ├── UnitView.axaml.cs │ │ ├── ProductView.axaml.cs │ │ ├── CategoryView.axaml.cs │ │ ├── ProductFilterView.axaml.cs │ │ ├── UnitCreationView.axaml.cs │ │ ├── ProductUpsertionView.axaml.cs │ │ └── CategoryUpsertionView.axaml.cs │ │ ├── DialogWindow.axaml │ │ ├── Accounts │ │ ├── AccountView.axaml.cs │ │ ├── AccountFilterView.axaml.cs │ │ └── AccountUpsertionView.axaml.cs │ │ ├── Authentication │ │ └── LoginView.axaml.cs │ │ ├── Transactions │ │ ├── Links │ │ │ ├── LinkView.axaml.cs │ │ │ └── LinkUpsertionView.axaml.cs │ │ ├── Loans │ │ │ ├── LoanView.axaml.cs │ │ │ └── LoanUpsertionView.axaml.cs │ │ ├── TransactionView.axaml.cs │ │ ├── Purchases │ │ │ ├── PurchaseView.axaml.cs │ │ │ └── PurchaseUpsertionView.axaml.cs │ │ ├── Transfers │ │ │ ├── TransferView.axaml.cs │ │ │ └── TransferUpsertionView.axaml.cs │ │ ├── Controls │ │ │ ├── TransactionFilterView.axaml.cs │ │ │ ├── TransactionSummaryView.axaml.cs │ │ │ └── TransactionPropertiesView.axaml.cs │ │ └── TransactionUpsertionView.axaml.cs │ │ ├── Counterparties │ │ ├── CounterpartyFilter.axaml.cs │ │ ├── CounterpartyView.axaml.cs │ │ ├── CounterpartyMergeView.axaml.cs │ │ └── CounterpartyUpsertionView.axaml.cs │ │ ├── Configuration │ │ ├── PreferencesView.axaml.cs │ │ ├── ApplicationSettingsView.axaml.cs │ │ ├── ConfigurationWizardView.axaml.cs │ │ └── GnomeshadeConfigurationView.axaml.cs │ │ ├── MainWindow.axaml.cs │ │ └── Reports │ │ ├── BalanceReportView.axaml.cs │ │ ├── ProductReportView.axaml.cs │ │ └── CategoryReportView.axaml.cs ├── Gnomeshade.Avalonia.Core │ ├── Help │ │ ├── licenses.json │ │ └── PackageContext.cs │ ├── Configuration │ │ └── UserConfigurationSerializationContext.cs │ ├── Delays.cs │ ├── Reports │ │ ├── Splits │ │ │ └── SplitProvider.cs │ │ ├── Aggregates │ │ │ ├── Sum.cs │ │ │ ├── IAggregateFunction.cs │ │ │ ├── Maximum.cs │ │ │ ├── Minimum.cs │ │ │ └── Average.cs │ │ ├── Calculations │ │ │ ├── CalculableValue.cs │ │ │ ├── ICalculationFunction.cs │ │ │ ├── TotalPrice.cs │ │ │ └── PricePerUnit.cs │ │ └── DateAxis.cs │ ├── LocalDateConverter.cs │ ├── UpsertedEventArgs.cs │ ├── ActivityScope.cs │ ├── LocalDateTimeConverter.cs │ ├── Commands │ │ └── CommandBase.cs │ ├── Authentication │ │ └── IGnomeshadeProtocolHandler.cs │ └── Counterparties │ │ └── CounterpartyComparer.cs └── Gnomeshade.Desktop.Installer │ ├── packages.lock.json │ └── Gnomeshade.Desktop.Installer.wixproj ├── .dockerignore ├── .github ├── pull_request_template.md └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── deployment ├── debian │ ├── prerm │ ├── changelog │ ├── copyright │ ├── control │ └── postinst ├── ansible │ ├── .gitattributes │ └── gnomeshade │ │ ├── molecule │ │ └── default │ │ │ ├── prepare.yml │ │ │ ├── converge.yml │ │ │ └── molecule.yml │ │ ├── handlers │ │ └── main.yml │ │ ├── meta │ │ └── main.yml │ │ └── tasks │ │ └── main.yml ├── docker │ └── Dockerfile ├── build.sh ├── restore.sh ├── docker_push.sh ├── nuget_vulnerabilities.sh ├── test.sh ├── licenses.ps1 ├── build.ps1 └── test-docker.sh ├── .gitattributes ├── global.json ├── tests ├── .editorconfig ├── Gnomeshade.Desktop.Tests │ └── Gnomeshade.Desktop.Tests.csproj ├── Gnomeshade.WebApi.Client.Tests │ ├── Gnomeshade.WebApi.Client.Tests.csproj │ └── RoutesTests.cs ├── Gnomeshade.WebApi.Tests.Integration │ ├── packages.lock.json │ ├── Gnomeshade.WebApi.Tests.Integration.csproj │ └── Fixtures │ │ └── DatabaseFixtureSource.cs ├── coverlet.runsettings ├── Gnomeshade.Avalonia.Core.Tests │ ├── Gnomeshade.Avalonia.Core.Tests.csproj │ └── Accounts │ │ └── AccountViewModelTests.cs ├── Gnomeshade.WebApi.Tests │ ├── Gnomeshade.WebApi.Tests.csproj │ └── V1 │ │ └── Transactions │ │ └── OptionalTimeRangeTests.cs ├── Gnomeshade.WebApi.Tests.Integration.Oidc │ ├── OidcFixtureSource.cs │ ├── Gnomeshade.WebApi.Tests.Integration.Oidc.csproj │ └── WebserverSetup.cs ├── Gnomeshade.TestingHelpers │ ├── Models │ │ ├── UnitExtensions.cs │ │ ├── CategoryExtensions.cs │ │ ├── TransactionExtensions.cs │ │ ├── LoanPaymentExtensions.cs │ │ ├── ProductExtensions.cs │ │ ├── LoanExtensions.cs │ │ └── PurchaseExtensions.cs │ └── Gnomeshade.TestingHelpers.csproj ├── Gnomeshade.Data.Tests.Integration │ └── Fakers │ │ ├── CounterpartyFaker.cs │ │ ├── UnitFaker.cs │ │ └── CategoryFaker.cs └── Gnomeshade.WebApi.Tests.Integration.PostgreSQL │ └── Fixtures │ └── UnloggedTableScriptPreprocessor.cs ├── nuget.config ├── codecov.yml └── docker-compose.yml /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | 0.8.7 2 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | gnomeshade.org -------------------------------------------------------------------------------- /docs/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/docs/link.png -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/bin/* 2 | **/obj/* 3 | **/node_modules/* 4 | **/.github/* 5 | **/.idea/* 6 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # . 2 | 3 | Changes proposed in this pull request: 4 | * 5 | -------------------------------------------------------------------------------- /deployment/debian/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | deb-systemd-invoke stop gnomeshade.service 5 | -------------------------------------------------------------------------------- /docs/assets/balance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/docs/assets/balance.png -------------------------------------------------------------------------------- /docs/assets/swagger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/docs/assets/swagger.jpg -------------------------------------------------------------------------------- /deployment/ansible/.gitattributes: -------------------------------------------------------------------------------- 1 | # Include Ansible files in language statistics 2 | *.yml linguist-detectable 3 | -------------------------------------------------------------------------------- /docs/assets/transactions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/docs/assets/transactions.png -------------------------------------------------------------------------------- /source/Gnomeshade.Data/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.Data/key.snk -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Admin/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "/Pages/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "/Pages/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.WebApi/key.snk -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.sql linguist-detectable=true 4 | *.sql linguist-language=sql 5 | *.sh text eol=lf 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000001_database_extensions.sql: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; 2 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.Sqlite/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.Data.Sqlite/key.snk -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Gnomeshade.WebApi.Areas.Identity.Pages.Account 2 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.411", 4 | "rollForward": "disable", 5 | "allowPrerelease": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000005_product_sku.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.products 2 | ADD COLUMN "sku" text NULL; 3 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.WebApi.Client/key.snk -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.WebApi.Models/key.snk -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000011_rename_tags.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE IF EXISTS "public"."tags" 2 | RENAME TO "categories"; 3 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.Data.PostgreSQL/key.snk -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/.gitattributes: -------------------------------------------------------------------------------- 1 | # Include Avalonia views 2 | *.axaml linguist-detectable=true 3 | *.axaml linguist-language=xml 4 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Migrations/00000033_inverse_multiplier.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE units 2 | ADD COLUMN inverse_multiplier bool DEFAULT false; 3 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/User/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT id, created_at CreatedAt, counterparty_id CounterpartyId 2 | FROM users 3 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Gnomeshade.WebApi.Areas.Identity.Pages.Account.Manage 2 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.WebApi/wwwroot/favicon.ico -------------------------------------------------------------------------------- /deployment/debian/changelog: -------------------------------------------------------------------------------- 1 | gnomeshade (${FULL_VERSION}) stable; urgency=low 2 | 3 | * Placeholder 4 | 5 | -- ${MAINTAINER} ${CHANGELOG_TIME} 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Assets/gnomeshade.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.Desktop/Assets/gnomeshade.ico -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/packageIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.WebApi.Client/packageIcon.png -------------------------------------------------------------------------------- /tests/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS1591: Missing XML comment for publicly visible type or member 4 | dotnet_diagnostic.CS1591.severity = none 5 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.Sqlite/Migrations/00000009_refunded_by.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE transactions 2 | ADD COLUMN refunded_by uuid NULL REFERENCES transactions; 3 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Migrations/00000026_linked_product.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "categories" 2 | ADD COLUMN "linked_product_id" uuid NULL REFERENCES products; 3 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Assets/Hack-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.Desktop/Assets/Hack-Regular.ttf -------------------------------------------------------------------------------- /deployment/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:8.0.411 2 | ENV CI=true 3 | RUN apt-get update && apt-get install -y \ 4 | npm \ 5 | zip 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Help/licenses.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.Avalonia.Core/Help/licenses.json -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000024_refunded_by.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE transactions 2 | ADD COLUMN refunded_by uuid NULL REFERENCES transactions; 3 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Manage/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "/Areas/Identity/Pages/Account/Manage/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /deployment/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ./deployment/restore.sh 5 | dotnet build --configuration Release --no-restore /warnAsError /nologo /clp:NoSummary 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Assets/Hack-RegularOblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.Desktop/Assets/Hack-RegularOblique.ttf -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Assets/Inconsolata-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VMelnalksnis/Gnomeshade/HEAD/source/Gnomeshade.Desktop/Assets/Inconsolata-Regular.ttf -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS1591: Property summary documentation must match accessors 4 | dotnet_diagnostic.SA1623.severity = none 5 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Gnomeshade.WebApi 2 | @using Gnomeshade.WebApi.Models 3 | 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS1591: Missing XML comment for publicly visible type or member 4 | dotnet_diagnostic.CS1591.severity = none 5 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/User/Update.sql: -------------------------------------------------------------------------------- 1 | UPDATE users 2 | SET modified_at = CURRENT_TIMESTAMP, 3 | counterparty_id = @CounterpartyId 4 | WHERE id = @Id; 5 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Ownership/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO ownerships 2 | (id, owner_id, user_id, access_id) 3 | VALUES 4 | (@Id, @OwnerId, @UserId, @AccessId) 5 | RETURNING id; 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000019_unit_symbol.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE units 2 | ADD COLUMN symbol text NULL, 3 | ADD CONSTRAINT "units_symbol_unique" UNIQUE (owner_id, symbol, deleted_at); 4 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # SA1615: Element return value should be documented 4 | dotnet_diagnostic.SA1615.severity = none 5 | dotnet_diagnostic.CS1591.severity = none 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Admin/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Gnomeshade.WebApi.Areas.Admin.Pages.Index 3 | 4 | @{ 5 | ViewData["Title"] = "Admin"; 6 | } 7 | 8 |

@ViewData["Title"]

9 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Pages/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # SA1615: Element return value should be documented 4 | dotnet_diagnostic.SA1615.severity = none 5 | dotnet_diagnostic.CS1591.severity = none 6 | -------------------------------------------------------------------------------- /deployment/ansible/gnomeshade/molecule/default/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: all 4 | tasks: 5 | - name: Copy deb 6 | copy: 7 | src: gnomeshade.deb 8 | dest: /tmp/gnomeshade.deb 9 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000010_cleanup.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS "public"."transaction_item_tags"; 2 | DROP TABLE IF EXISTS "public"."transaction_items"; 3 | DROP TABLE IF EXISTS "public"."tag_tags"; 4 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Gnomeshade": { 3 | "BaseAddress": "" 4 | }, 5 | "Oidc": { 6 | "Authority": "", 7 | "ClientId": "", 8 | "ClientSecret": "", 9 | "RedirectUri": "" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000014_account_currency_uniqueness.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.accounts_in_currency 2 | ADD CONSTRAINT accounts_in_currency_unique_account_id_currency_id 3 | UNIQUE (account_id, currency_id); 4 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.Sqlite/Migrations/00000010_bank_reference.sql: -------------------------------------------------------------------------------- 1 | UPDATE transfers 2 | SET external_reference = bank_reference, 3 | bank_reference = NULL 4 | WHERE bank_reference IS NOT NULL 5 | AND external_reference IS NULL; 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000025_bank_reference.sql: -------------------------------------------------------------------------------- 1 | UPDATE transfers 2 | SET external_reference = bank_reference, 3 | bank_reference = NULL 4 | WHERE bank_reference IS NOT NULL 5 | AND external_reference IS NULL; 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Link/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO links 2 | (id, owner_id, created_by_user_id, modified_by_user_id, uri) 3 | VALUES 4 | (@Id, @OwnerId, @CreatedByUserId, @ModifiedByUserId, @Uri) 5 | RETURNING id; 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Admin/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Identity 2 | @using Gnomeshade.WebApi.Areas.Admin 3 | @using Gnomeshade.WebApi.Areas.Admin.Pages 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Owner/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO owners 2 | (id, name, normalized_name, created_by_user_id, created_at) 3 | VALUES 4 | (@Id, @Name, upper(@Name), @CreatedByUserId, CURRENT_TIMESTAMP) 5 | RETURNING id; 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Identity 2 | @using Gnomeshade.WebApi.Areas.Identity 3 | @using Gnomeshade.WebApi.Areas.Identity.Pages 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gnomeshade", 3 | "dependencies": { 4 | "bootstrap": "5.3.3", 5 | "jquery": "3.7.1", 6 | "jquery-validation": "1.21.0", 7 | "jquery-validation-unobtrusive": "4.0.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000021_order.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE purchases 2 | ADD COLUMN "order" int NULL CHECK ("order" IS NULL OR "order" >= 0); 3 | 4 | ALTER TABLE transfers 5 | ADD COLUMN "order" int NULL CHECK ("order" IS NULL OR "order" >= 0); 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.Sqlite/Migrations/00000006_order.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE purchases 2 | ADD COLUMN "order" int NULL CHECK ("order" IS NULL OR "order" >= 0); 3 | 4 | ALTER TABLE transfers 5 | ADD COLUMN "order" int NULL CHECK ("order" IS NULL OR "order" >= 0); 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Migrations/00000032_system_data_access.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO ownerships (owner_id, user_id, access_id, created_by_user_id) 2 | SELECT get_system_user_id(), users.id, (SELECT id FROM access WHERE normalized_name = 'READ'), get_system_user_id() 3 | FROM users; 4 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000027_remove_disabled.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "accounts" 2 | DROP COLUMN "disabled_at", 3 | DROP COLUMN "disabled_by_user_id"; 4 | 5 | ALTER TABLE "accounts_in_currency" 6 | DROP COLUMN "disabled_at", 7 | DROP COLUMN "disabled_by_user_id"; 8 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/AccountInCurrency/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO accounts_in_currency 2 | (owner_id, created_by_user_id, modified_by_user_id, account_id, currency_id) 3 | VALUES (@OwnerId, @CreatedByUserId, @ModifiedByUserId, @AccountId, @CurrencyId) 4 | RETURNING id; 5 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Counterparty/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO counterparties 2 | (id, owner_id, created_by_user_id, modified_by_user_id, name, normalized_name) 3 | VALUES 4 | (@Id, @OwnerId, @CreatedByUserId, @ModifiedByUserId, @Name, upper(@Name)) 5 | RETURNING id; 6 | -------------------------------------------------------------------------------- /deployment/debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: gnomeshade 3 | Upstream-Contact: ${MAINTAINER_EMAIL} 4 | Source: https://github.com/VMelnalksnis/Gnomeshade 5 | 6 | Files: * 7 | Copyright: 2021 ${MAINTAINER} 8 | License: APGL-3.0-or-later 9 | -------------------------------------------------------------------------------- /deployment/restore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [ -z "$1" ]; then 5 | echo "Restoring solution" 6 | dotnet restore --locked-mode /p:Configuration="Release" 7 | else 8 | echo "Restoring project $1" 9 | dotnet restore ./source/"$1"/"$1".csproj --locked-mode /p:Configuration="Release" 10 | fi 11 | -------------------------------------------------------------------------------- /deployment/ansible/gnomeshade/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: Reload gnomeshade 2 | become: true 3 | systemd: 4 | name: gnomeshade 5 | state: reloaded 6 | daemon_reload: true 7 | 8 | - name: Restart gnomeshade 9 | become: true 10 | systemd: 11 | name: gnomeshade 12 | state: restarted 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000006_account_name_uniqueness.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.accounts 2 | DROP CONSTRAINT accounts_normalized_name; 3 | 4 | ALTER TABLE public.accounts 5 | ADD CONSTRAINT accounts_normalized_name_counterparty_id 6 | UNIQUE (counterparty_id, normalized_name); 7 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop.Installer/packages.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "dependencies": { 4 | "native,Version=v0.0": {}, 5 | "native,Version=v0.0/win": {}, 6 | "native,Version=v0.0/win-arm64": {}, 7 | "native,Version=v0.0/win-x64": {}, 8 | "native,Version=v0.0/win-x86": {} 9 | } 10 | } -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/AccessDenied.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AccessDenied 3 | @{ 4 | ViewData["Title"] = "Access denied"; 5 | } 6 | 7 |
8 |

@ViewData["Title"]

9 |

You do not have access to this resource.

10 |
11 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Lockout.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Lockout 3 | @{ 4 | ViewData["Title"] = "Locked out"; 5 | } 6 | 7 |
8 |

@ViewData["Title"]

9 |

This account has been locked out, please try again later.

10 |
11 | -------------------------------------------------------------------------------- /deployment/docker_push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | build_number=$1 5 | version=${2-$(tr -d '[:space:]' @ViewData["Title"] 8 |

9 | Your password has been reset. Please click here to log in. 10 |

11 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Gnomeshade.WebApi.Pages.Index 3 | 4 | @{ 5 | ViewData["Title"] = "Home Page"; 6 | } 7 | 8 |
9 |

Welcome

10 |

Open the application

11 |
12 | -------------------------------------------------------------------------------- /tests/Gnomeshade.Desktop.Tests/Gnomeshade.Desktop.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Product/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO products 2 | (id, owner_id, created_by_user_id, modified_by_user_id, name, normalized_name, sku, description, unit_id, category_id) 3 | VALUES 4 | (@Id, @OwnerId, @CreatedByUserId, @ModifiedByUserId, @Name, upper(@Name), @Sku, @Description, @UnitId, @CategoryId) 5 | RETURNING id; 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.Sqlite/Migrations/00000007_related_transactions.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE related_transactions 2 | ( 3 | first uuid NOT NULL, 4 | second uuid NOT NULL, 5 | 6 | CONSTRAINT related_transactions_first_fkey FOREIGN KEY (first) REFERENCES transactions, 7 | CONSTRAINT related_transactions_second_fkey FOREIGN KEY (second) REFERENCES transactions 8 | ); 9 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Category/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO categories 2 | (id, owner_id, created_by_user_id, modified_by_user_id, name, normalized_name, description, category_id, linked_product_id) 3 | VALUES 4 | (@Id, @OwnerId, @CreatedByUserId, @ModifiedByUserId, @Name, upper(@Name), @Description, @CategoryId, @LinkedProductId) 5 | RETURNING id; 6 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model DownloadPersonalData 3 | @{ 4 | ViewData["Title"] = "Download Your Data"; 5 | ViewData["ActivePage"] = ManageNavPages.PersonalData; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 10 | @section Scripts { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Client.Tests/Gnomeshade.WebApi.Client.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Unit/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO units 2 | (id, owner_id, created_by_user_id, modified_by_user_id, name, normalized_name, symbol, parent_unit_id, multiplier, inverse_multiplier) 3 | VALUES 4 | (@Id, @OwnerId, @CreatedByUserId, @ModifiedByUserId, @Name, upper(@Name), @Symbol, @ParentUnitId, @Multiplier, @InverseMultiplier) 5 | RETURNING id; 6 | -------------------------------------------------------------------------------- /deployment/ansible/gnomeshade/molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | vars: 5 | gnomeshade_binary_url: '/tmp/gnomeshade.deb' 6 | gnomeshade_oauth2_providers: [] 7 | gnomeshade_admin_password: 'Password1!' 8 | tasks: 9 | - name: 'Include gnomeshade.gnomeshade' 10 | include_role: 11 | name: 'gnomeshade.gnomeshade' 12 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000022_related_transactions.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE related_transactions 2 | ( 3 | first uuid NOT NULL, 4 | second uuid NOT NULL, 5 | 6 | CONSTRAINT related_transactions_first_fkey FOREIGN KEY (first) REFERENCES transactions, 7 | CONSTRAINT related_transactions_second_fkey FOREIGN KEY (second) REFERENCES transactions 8 | ) WITH (OIDS = FALSE); 9 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.Sqlite/Migrations/00000005_sek.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO currencies 2 | (name, 3 | normalized_name, 4 | numeric_code, 5 | alphabetic_code, 6 | minor_unit, 7 | official, 8 | crypto, 9 | historical, 10 | active_from, 11 | active_until) 12 | VALUES 13 | ('Swedish krona', 'SWEDISH KRONA', 752, 'SEK', 2, TRUE, FALSE, FALSE, '1876-01-01 00:00:00', NULL); 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AllowedHosts": "*", 3 | "Kestrel": { 4 | "EndpointDefaults": { 5 | "Protocols": "Http1AndHttp2" 6 | } 7 | }, 8 | "Logging": { 9 | "LogLevel": { 10 | "Default": "Information", 11 | "Microsoft.AspNetCore": "Warning", 12 | "Microsoft.EntityFrameworkCore": "Warning", 13 | "NpgSql": "Warning" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000020_sek.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO currencies 2 | (name, 3 | normalized_name, 4 | numeric_code, 5 | alphabetic_code, 6 | minor_unit, 7 | official, 8 | crypto, 9 | historical, 10 | active_from, 11 | active_until) 12 | VALUES 13 | ('Swedish krona', 'SWEDISH KRONA', 752, 'SEK', 2, TRUE, FALSE, FALSE, '1876-01-01 00:00:00+00', NULL); 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/User/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO users 2 | (id, modified_by_user_id, counterparty_id) 3 | VALUES 4 | (@Id, @ModifiedByUserId, @CounterpartyId) 5 | RETURNING id; 6 | 7 | INSERT INTO ownerships (owner_id, user_id, access_id, created_by_user_id) 8 | VALUES (get_system_user_id(), @Id, (SELECT id FROM access WHERE normalized_name = 'READ'), get_system_user_id()); 9 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/ImportResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.WebApi.Client; 6 | 7 | /// The result of account transaction import from Nordigen. 8 | public abstract class ImportResult; 9 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Configuration/Roles.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.WebApi.Configuration; 6 | 7 | internal static class Roles 8 | { 9 | internal const string Administrator = "Administrator"; 10 | } 11 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Gnomeshade.WebApi": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "launchUrl": "http://localhost:5000", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development", 9 | "GNOMESHADE_DEMO": "true" 10 | }, 11 | "dotnetRunMessages": "true" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /deployment/nuget_vulnerabilities.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ./deployment/restore.sh 5 | 6 | packages=$(dotnet list package --include-transitive --vulnerable --format json || true) 7 | echo "$packages" 8 | 9 | if [[ "$packages" == *"vulnerabilities"* ]]; then 10 | echo "Vulnerable packages" 11 | exit 1 12 | elif [[ "$packages" != *"projects"* ]]; then 13 | echo "Unexpected output" 14 | exit 1 15 | fi 16 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/AccountInCurrency/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT a.id, 2 | a.owner_id OwnerId, 3 | a.created_at CreatedAt, 4 | a.created_by_user_id CreatedByUserId, 5 | a.modified_at ModifiedAt, 6 | a.modified_by_user_id ModifiedByUserId, 7 | a.account_id AccountId, 8 | a.currency_id CurrencyId 9 | FROM accounts_in_currency a 10 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/V1/Accounts/ReservedNames.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.WebApi.V1.Accounts; 6 | 7 | internal static class ReservedNames 8 | { 9 | internal const string Unidentified = nameof(Unidentified); 10 | } 11 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/LoanPayment/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO loan_payments 2 | (id, 3 | created_by_user_id, 4 | owner_id, 5 | modified_by_user_id, 6 | loan_id, 7 | transaction_id, 8 | amount, 9 | interest) 10 | VALUES 11 | (@Id, 12 | @CreatedByUserId, 13 | @OwnerId, 14 | @ModifiedByUserId, 15 | @LoanId, 16 | @TransactionId, 17 | @Amount, 18 | @Interest) 19 | RETURNING id; 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/Results/ExternalLoginResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.WebApi.Client.Results; 6 | 7 | /// Result of logging in with an external provider. 8 | public abstract class ExternalLoginResult; 9 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Tests.Integration/packages.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "dependencies": { 4 | "net8.0": { 5 | "Microsoft.NET.ILLink.Tasks": { 6 | "type": "Direct", 7 | "requested": "[8.0.17, )", 8 | "resolved": "8.0.17", 9 | "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw==" 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: 2 | layout: "header, diff, components" 3 | 4 | component_management: 5 | individual_components: 6 | - component_id: api 7 | name: API 8 | paths: 9 | - source/Gnomeshade.Data*/** 10 | - source/Gnomeshade.WebApi*/** 11 | 12 | - component_id: ui 13 | name: UI 14 | paths: 15 | - source/Gnomeshade.Avalonia*/** 16 | - source/Gnomeshade.Desktop*/** 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/Results/LoggedIn.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.WebApi.Client.Results; 6 | 7 | /// User is already registered and is successfully logged in. 8 | public sealed class LoggedIn : ExternalLoginResult; 9 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/_StatusMessage.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | @if (!string.IsNullOrEmpty(Model)) 4 | { 5 | var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; 6 | 10 | } 11 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | @if (!string.IsNullOrEmpty(Model)) 4 | { 5 | var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; 6 | 10 | } 11 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/LoginResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using JetBrains.Annotations; 6 | 7 | namespace Gnomeshade.WebApi.Client; 8 | 9 | /// Base type for all login results. 10 | [PublicAPI] 11 | public abstract record LoginResult; 12 | -------------------------------------------------------------------------------- /tests/coverlet.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | [Gnomeshade.*]* 8 | Obsolete 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Currency/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | created_at CreatedAt, 3 | name, 4 | normalized_name NormalizedName, 5 | numeric_code NumericCode, 6 | alphabetic_code AlphabeticCode, 7 | minor_unit MinorUnit, 8 | official, 9 | crypto, 10 | historical, 11 | active_from ActiveFrom, 12 | active_until ActiveUntil 13 | FROM currencies 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/SuccessfulLogin.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using JetBrains.Annotations; 6 | 7 | namespace Gnomeshade.WebApi.Client; 8 | 9 | /// Indicates a successful login. 10 | [PublicAPI] 11 | public sealed record SuccessfulLogin : LoginResult; 12 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000013_hrd.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO "currencies" 2 | ("name", 3 | "normalized_name", 4 | "numeric_code", 5 | "alphabetic_code", 6 | "minor_unit", 7 | "official", 8 | "crypto", 9 | "historical", 10 | "active_from", 11 | "active_until") 12 | VALUES 13 | ('Croatian dinar', 'CROATIAN DINAR', 191, 'HRD', 2, TRUE, FALSE, TRUE, '1991-12-23 00:00:00+00', '1994-05-30 00:00:00+00'); 14 | -------------------------------------------------------------------------------- /tests/Gnomeshade.Avalonia.Core.Tests/Gnomeshade.Avalonia.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Link/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT links.id, 2 | links.created_at CreatedAt, 3 | links.created_by_user_id CreatedByUserId, 4 | links.owner_id OwnerId, 5 | links.modified_at ModifiedAt, 6 | links.modified_by_user_id ModifiedByUserId, 7 | links.uri AS Uri, 8 | links.deleted_at AS DeletedAt, 9 | links.deleted_by_user_id AS DeletedByUserId 10 | FROM links 11 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Tests/Gnomeshade.WebApi.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /deployment/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ./deployment/build.sh 5 | dotnet test \ 6 | -p:CollectCoverage=true \ 7 | -p:BuildInParallel=true \ 8 | -p:ContinuousIntegrationBuild=false \ 9 | -p:DebugType=portable \ 10 | -p:CopyLocalLockFileAssemblies=true \ 11 | -m:8 \ 12 | --configuration Release \ 13 | --collect:"XPlat Code Coverage" \ 14 | --settings ./tests/coverlet.runsettings \ 15 | --logger:"junit;LogFilePath=TestResults/test-result.junit.xml" \ 16 | --no-build 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Project/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT projects.id, 2 | projects.created_at CreatedAt, 3 | projects.owner_id OwnerId, 4 | projects.created_by_user_id CreatedByUserId, 5 | projects.modified_at ModifiedAt, 6 | projects.modified_by_user_id ModifiedByUserId, 7 | projects.name, 8 | projects.normalized_name NormalizedName, 9 | projects.parent_project_id ParentProjectId 10 | FROM projects 11 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace Gnomeshade.WebApi.Areas.Identity.Pages.Account; 8 | 9 | public sealed class AccessDenied : PageModel 10 | { 11 | public void OnGet() 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Gnomeshade.WebApi.Pages; 9 | 10 | [AllowAnonymous] 11 | public sealed class Index : PageModel 12 | { 13 | public PageResult OnGet() => Page(); 14 | } 15 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Category/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT c.id, 2 | c.created_at CreatedAt, 3 | c.owner_id OwnerId, 4 | c.created_by_user_id CreatedByUserId, 5 | c.modified_at ModifiedAt, 6 | c.modified_by_user_id ModifiedByUserId, 7 | c.name AS Name, 8 | c.normalized_name NormalizedName, 9 | c.description, 10 | c.category_id CategoryId, 11 | c.linked_product_id LinkedProductId 12 | FROM categories c 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Loan/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO loans 2 | (id, 3 | owner_id, 4 | created_by_user_id, 5 | modified_by_user_id, 6 | transaction_id, 7 | issuing_counterparty_id, 8 | receiving_counterparty_id, 9 | amount, 10 | currency_id) 11 | VALUES 12 | (@Id, 13 | @OwnerId, 14 | @CreatedByUserId, 15 | @ModifiedByUserId, 16 | @TransactionId, 17 | @IssuingCounterpartyId, 18 | @ReceivingCounterpartyId, 19 | @Amount, 20 | @CurrencyId) 21 | RETURNING id; 22 | -------------------------------------------------------------------------------- /deployment/ansible/gnomeshade/molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: docker 6 | platforms: 7 | - name: instance 8 | image: "geerlingguy/docker-${MOLECULE_DISTRO:-centos7}-ansible:latest" 9 | command: ${MOLECULE_DOCKER_COMMAND:-""} 10 | volumes: 11 | - /sys/fs/cgroup:/sys/fs/cgroup:rw 12 | cgroupns_mode: host 13 | privileged: true 14 | pre_build_image: true 15 | provisioner: 16 | name: ansible 17 | verifier: 18 | name: ansible 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Counterparty/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT c.id, 2 | c.created_at CreatedAt, 3 | c.owner_id OwnerId, 4 | c.created_by_user_id CreatedByUserId, 5 | c.modified_at ModifiedAt, 6 | c.modified_by_user_id ModifiedByUserId, 7 | c.name AS Name, 8 | c.normalized_name NormalizedName, 9 | c.deleted_at AS DeletedAt, 10 | c.deleted_by_user_id AS DeletedByUserId 11 | FROM counterparties c 12 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Configuration/Policies.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.WebApi.Configuration; 6 | 7 | internal static class Policies 8 | { 9 | internal const string Administrator = "Administrator"; 10 | internal const string ApplicationUser = "ApplicationUser"; 11 | internal const string Identity = "Identity"; 12 | } 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Configuration/HttpRequestExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace Gnomeshade.WebApi.Configuration; 8 | 9 | internal static class HttpRequestExtensions 10 | { 11 | internal static bool IsApiRequest(this HttpRequest request) => request.Path.StartsWithSegments(new("/api")); 12 | } 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000016_name_uniqueness.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.accounts 2 | DROP CONSTRAINT accounts_normalized_name_counterparty_id; 3 | 4 | ALTER TABLE public.accounts 5 | ADD CONSTRAINT accounts_normalized_name_counterparty_id 6 | UNIQUE (counterparty_id, normalized_name) NOT DEFERRABLE; 7 | 8 | ALTER TABLE public.accounts 9 | DROP CONSTRAINT IF EXISTS counterparties_normalized_name; 10 | 11 | ALTER TABLE public.counterparties 12 | DROP CONSTRAINT IF EXISTS counterparties_normalized_name; 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Creation.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.WebApi.Models; 8 | 9 | /// Base class for all resource creation models. 10 | public abstract record Creation 11 | { 12 | /// The id of the owner of the resource. 13 | public Guid? OwnerId { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Migrations/IDatabaseMigrator.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.Data.Migrations; 6 | 7 | /// Migrates database to the latest version. 8 | public interface IDatabaseMigrator 9 | { 10 | /// Ensures that the database is created and upgraded to the latest version. 11 | void Migrate(); 12 | } 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Project/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO projects 2 | (id, 3 | created_at, 4 | created_by_user_id, 5 | deleted_at, 6 | deleted_by_user_id, 7 | owner_id, 8 | modified_at, 9 | modified_by_user_id, 10 | name, 11 | normalized_name, 12 | parent_project_id) 13 | VALUES (@Id, 14 | @CreatedAt, 15 | @CreatedByUserId, 16 | @DeletedAt, 17 | @DeletedByUserId, 18 | @OwnerId, 19 | @ModifiedAt, 20 | @ModifiedByUserId, 21 | @Name, 22 | upper(@Name), 23 | @ParentProjectId) 24 | RETURNING id; 25 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Transaction/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO transactions 2 | (id, 3 | owner_id, 4 | created_by_user_id, 5 | modified_by_user_id, 6 | description, 7 | imported_at, 8 | reconciled_at, 9 | reconciled_by_user_id, 10 | refunded_by) 11 | VALUES 12 | (@Id, 13 | @OwnerId, 14 | @CreatedByUserId, 15 | @ModifiedByUserId, 16 | @Description, 17 | @ImportedAt, 18 | @ReconciledAt, 19 | @ReconciledByUserId, 20 | @RefundedBy) 21 | RETURNING id; 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Owners/User.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Owners; 10 | 11 | /// An application user. 12 | [PublicAPI] 13 | public sealed record User 14 | { 15 | /// The id of the user. 16 | public Guid Id { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /deployment/ansible/gnomeshade/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: [ ] 3 | galaxy_info: 4 | author: Valters Melnalksnis 5 | namespace: gnomeshade 6 | description: Installs Gnomeshade 7 | company: Gnomeshade 8 | license: AGPL-3.0-or-later 9 | 10 | min_ansible_version: '2.1' 11 | 12 | platforms: 13 | - name: Debian 14 | versions: 15 | - buster 16 | - bullseye 17 | - name: Ubuntu 18 | versions: 19 | - bionic 20 | - focal 21 | - jammy 22 | 23 | galaxy_tags: 24 | - gnomeshade 25 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000012_category_ids.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "public"."products" 2 | ADD COLUMN "category_id" uuid NULL; 3 | 4 | ALTER TABLE "public"."products" 5 | ADD CONSTRAINT products_category_id_fkey 6 | FOREIGN KEY (category_id) REFERENCES categories (id) NOT DEFERRABLE; 7 | 8 | ALTER TABLE "public"."categories" 9 | ADD COLUMN "category_id" uuid NULL; 10 | 11 | ALTER TABLE "public"."categories" 12 | ADD CONSTRAINT categories_category_id_fkey 13 | FOREIGN KEY (category_id) REFERENCES categories (id) NOT DEFERRABLE; 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Lockout.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Gnomeshade.WebApi.Areas.Identity.Pages.Account; 9 | 10 | [AllowAnonymous] 11 | public sealed class Lockout : PageModel 12 | { 13 | public void OnGet() 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Entities/Abstractions/ISortableEntity.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.Data.Entities.Abstractions; 6 | 7 | /// Represents an entity that can have an explicit order. 8 | public interface ISortableEntity : IEntity 9 | { 10 | /// Gets or sets the order of the entity. 11 | public uint? Order { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /deployment/debian/control: -------------------------------------------------------------------------------- 1 | Package: gnomeshade 2 | Version: ${FULL_VERSION} 3 | Section: misc 4 | Priority: optional 5 | Architecture: amd64 6 | Maintainer: ${MAINTAINER} 7 | Description: Server for Gnomeshade 8 | Personal finance tracker server 9 | Homepage: https://www.gnomeshade.org 10 | Depends: adduser, libgcc1, libicu | libicu74 | libicu72 | libicu71 | libicu70 | libicu69 | libicu68 | libicu67 | libicu66 | libicu65 | libicu63 | libicu60 | libicu57 | libicu55 | libicu52, libc6, libssl1.0.0 | libssl1.0.2 | libssl1.1 | libssl3, zlib1g, libstdc++6, libgssapi-krb5-2 11 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Tests.Integration/Gnomeshade.WebApi.Tests.Integration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Gnomeshade.WebApi.Areas.Identity.Pages.Account; 9 | 10 | [AllowAnonymous] 11 | public sealed class ResetPasswordConfirmation : PageModel 12 | { 13 | public void OnGet() 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Tests.Integration.Oidc/OidcFixtureSource.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections; 6 | 7 | namespace Gnomeshade.WebApi.Tests.Integration.Oidc; 8 | 9 | internal sealed class OidcFixtureSource : IEnumerable 10 | { 11 | public IEnumerator GetEnumerator() 12 | { 13 | yield return new TestFixtureData(WebserverSetup.KeycloakFixture).SetArgDisplayNames("Keycloak"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://www.gnomeshade.org/ 5 | 2024-05-10 6 | 7 | 8 | https://www.gnomeshade.org/getting-started 9 | 2024-05-10 10 | 11 | 12 | https://www.gnomeshade.org/configuration 13 | 2023-09-03 14 | 15 | 16 | https://www.gnomeshade.org/changelog 17 | 2024-10-21 18 | 19 | 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Loan2/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO loans2 2 | (id, 3 | created_by_user_id, 4 | owner_id, 5 | modified_by_user_id, 6 | name, 7 | normalized_name, 8 | issuing_counterparty_id, 9 | receiving_counterparty_id, 10 | principal, 11 | currency_id) 12 | VALUES 13 | (@Id, 14 | @CreatedByUserId, 15 | @OwnerId, 16 | @ModifiedByUserId, 17 | @Name, 18 | upper(@Name), 19 | @IssuingCounterpartyId, 20 | @ReceivingCounterpartyId, 21 | @Principal, 22 | @CurrencyId) 23 | RETURNING id; 24 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Purchase/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO purchases 2 | (id, 3 | owner_id, 4 | created_by_user_id, 5 | modified_by_user_id, 6 | transaction_id, 7 | price, 8 | currency_id, 9 | product_id, 10 | amount, 11 | delivery_date, 12 | "order") 13 | VALUES 14 | (@Id, 15 | @OwnerId, 16 | @CreatedByUserId, 17 | @ModifiedByUserId, 18 | @TransactionId, 19 | @Price, 20 | @CurrencyId, 21 | @ProductId, 22 | @Amount, 23 | @DeliveryDate, 24 | @Order) 25 | RETURNING id; 26 | -------------------------------------------------------------------------------- /deployment/ansible/gnomeshade/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: apt update 2 | ansible.builtin.apt: 3 | update_cache: yes 4 | cache_valid_time: 3600 5 | 6 | - name: Install Gnomeshade 7 | apt: 8 | deb: '{{ gnomeshade_binary_url }}' 9 | notify: 10 | - Restart gnomeshade 11 | 12 | - name: Create configuration 13 | become: true 14 | template: 15 | src: 'templates/appsettings.json.j2' 16 | dest: '{{ gnomeshade_config_filepath }}' 17 | owner: '{{ gnomeshade_user }}' 18 | group: '{{ gnomeshade_group }}' 19 | mode: '0640' 20 | notify: 21 | - Restart gnomeshade 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/AccessLevelExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.Data.Repositories; 6 | 7 | internal static class AccessLevelExtensions 8 | { 9 | internal static string? ToParam(this AccessLevel accessLevel) => accessLevel switch 10 | { 11 | AccessLevel.Read => "READ", 12 | AccessLevel.Write => "WRITE", 13 | AccessLevel.Delete => "DELETE", 14 | _ => null, 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Gnomeshade.WebApi.Models.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | true 7 | false 8 | true 9 | key.snk 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/LinkCreation.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models; 10 | 11 | /// Information needed to create a link. 12 | /// 13 | [PublicAPI] 14 | public sealed record LinkCreation : Creation 15 | { 16 | /// 17 | public Uri? Uri { get; set; } 18 | } 19 | -------------------------------------------------------------------------------- /deployment/debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | GNOMESHADE_USER=gnomeshade 5 | 6 | if [[ -z "$(getent group ${GNOMESHADE_USER})" ]]; then 7 | addgroup --system ${GNOMESHADE_USER} 8 | fi 9 | 10 | if [[ -z "$(getent passwd ${GNOMESHADE_USER})" ]]; then 11 | adduser \ 12 | --system --ingroup ${GNOMESHADE_USER} ${GNOMESHADE_USER} \ 13 | --no-create-home --home /nonexistent \ 14 | --gecos "Gnomeshade default user" 15 | fi 16 | 17 | chown gnomeshade /etc/opt/gnomeshade --recursive 18 | chown gnomeshade /opt/gnomeshade --recursive 19 | 20 | deb-systemd-invoke enable gnomeshade.service 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Configuration/UserConfigurationSerializationContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Text.Json.Serialization; 6 | 7 | namespace Gnomeshade.Avalonia.Core.Configuration; 8 | 9 | /// 10 | [JsonSerializable(typeof(UserConfiguration))] 11 | internal partial class UserConfigurationSerializationContext : JsonSerializerContext; 12 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Account/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO accounts 2 | (id, 3 | owner_id, 4 | created_by_user_id, 5 | modified_by_user_id, 6 | name, 7 | normalized_name, 8 | counterparty_id, 9 | preferred_currency_id, 10 | bic, 11 | iban, 12 | account_number) 13 | VALUES 14 | (@Id, 15 | @OwnerId, 16 | @CreatedByUserId, 17 | @ModifiedByUserId, 18 | @Name, 19 | upper(@Name), 20 | @CounterpartyId, 21 | @PreferredCurrencyId, 22 | @Bic, 23 | @Iban, 24 | @AccountNumber) 25 | RETURNING id; 26 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.Sqlite/Migrations/00000004_unit_symbol.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE units 2 | ADD COLUMN symbol text NULL; 3 | 4 | -- Update raw SQL statements, since SQLite does not support adding constraints 5 | -- https://stackoverflow.com/a/42970982 6 | 7 | SELECT sql 8 | FROM sqlite_master 9 | WHERE type = 'table' 10 | AND name = 'units'; 11 | 12 | PRAGMA writable_schema=1; 13 | 14 | UPDATE sqlite_master 15 | SET sql=REPLACE(sql, '\r\n)', '\tCONSTRAINT "units_symbol_unique" UNIQUE (owner_id, symbol, deleted_at)\r\n)') 16 | WHERE type = 'table' 17 | AND name = 'units'; 18 | 19 | PRAGMA writable_schema=0; 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/V1/Importing/Paperless/Rimi/Constants.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.WebApi.V1.Importing.Paperless.Rimi; 8 | 9 | internal static class Constants 10 | { 11 | internal const StringComparison Comparison = StringComparison.OrdinalIgnoreCase; 12 | 13 | internal static readonly string[] DiscountIdentifiers = 14 | [ 15 | "Atl. ", 16 | "Atl ", 17 | "ati ", 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Configuration/Options/DatabaseOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.WebApi.Configuration.Options; 6 | 7 | /// Options relating to databases, except for connection string. 8 | public sealed class DatabaseOptions 9 | { 10 | /// Gets the name of the . 11 | public string Provider { get; init; } = DatabaseProvider.PostgreSQL.Name; 12 | } 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Product/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT p.id, 2 | p.created_at CreatedAt, 3 | p.owner_id OwnerId, 4 | p.created_by_user_id CreatedByUserId, 5 | p.modified_at ModifiedAt, 6 | p.modified_by_user_id ModifiedByUserId, 7 | p.name AS Name, 8 | p.normalized_name NormalizedName, 9 | p.deleted_at DeletedAt, 10 | p.deleted_by_user_id DeletedByUserId, 11 | p.sku AS Sku, 12 | p.description, 13 | p.unit_id UnitId, 14 | p.category_id CategoryId 15 | FROM products p 16 | -------------------------------------------------------------------------------- /tests/Gnomeshade.TestingHelpers/Models/UnitExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Products; 6 | 7 | namespace Gnomeshade.TestingHelpers.Models; 8 | 9 | public static class UnitExtensions 10 | { 11 | public static UnitCreation ToCreation(this Unit unit) => new() 12 | { 13 | OwnerId = unit.OwnerId, 14 | Name = unit.Name, 15 | ParentUnitId = unit.ParentUnitId, 16 | Multiplier = unit.Multiplier, 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Ownership/Select.sql: -------------------------------------------------------------------------------- 1 | SELECT o.id AS Id, 2 | o.created_at CreatedAt, 3 | o.created_by_user_id AS CreatedByUserId, 4 | o.owner_id OwnerId, 5 | o.user_id UserId, 6 | o.access_id AccessId 7 | FROM ownerships o 8 | INNER JOIN owners on owners.id = o.owner_id 9 | INNER JOIN ownerships on ownerships.owner_id = owners.id 10 | INNER JOIN access on access.id = ownerships.access_id 11 | WHERE ownerships.user_id = @userId 12 | AND (access.normalized_name = @access OR access.normalized_name = 'OWNER') 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/V1/Authorization/ApplicationUserRequirement.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.Data.Identity; 6 | 7 | using Microsoft.AspNetCore.Authorization; 8 | 9 | namespace Gnomeshade.WebApi.V1.Authorization; 10 | 11 | /// An that indicates that an is required. 12 | public sealed class ApplicationUserRequirement : IAuthorizationRequirement; 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Transaction/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT t.id, 2 | t.owner_id OwnerId, 3 | t.created_at CreatedAt, 4 | t.created_by_user_id CreatedByUserId, 5 | t.modified_at ModifiedAt, 6 | t.modified_by_user_id ModifiedByUserId, 7 | t.deleted_at DeletedAt, 8 | t.deleted_by_user_id DeletedByUserId, 9 | t.description, 10 | t.imported_at ImportedAt, 11 | t.reconciled_at ReconciledAt, 12 | t.reconciled_by_user_id ReconciledByUserId, 13 | t.refunded_by RefundedBy 14 | FROM transactions t 15 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Logout.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Logout 3 | @{ 4 | ViewData["Title"] = "Log out"; 5 | } 6 | 7 |
8 |

@ViewData["Title"]

9 | @{ 10 | if (User.Identity?.IsAuthenticated ?? false) 11 | { 12 |
13 | 14 |
15 | } 16 | else 17 | { 18 |

You have successfully logged out of the application.

19 | } 20 | } 21 |
22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/V1/Authorization/AdministratorRoleRequirement.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Configuration; 6 | 7 | using Microsoft.AspNetCore.Authorization.Infrastructure; 8 | 9 | namespace Gnomeshade.WebApi.V1.Authorization; 10 | 11 | /// Requires the user to have the role. 12 | public sealed class AdministratorRoleRequirement() : RolesAuthorizationRequirement([Roles.Administrator]); 13 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000029_transfer_dates.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE transfers 2 | ADD COLUMN booked_at timestamptz NULL, 3 | ADD COLUMN valued_at timestamptz NULL; 4 | 5 | UPDATE transfers 6 | SET booked_at = transactions.booked_at, 7 | valued_at = transactions.valued_at 8 | FROM transactions 9 | WHERE transactions.id = transfers.transaction_id; 10 | 11 | ALTER TABLE transactions 12 | DROP CONSTRAINT transactions_one_of_dates_required, 13 | DROP COLUMN booked_at, 14 | DROP COLUMN valued_at; 15 | 16 | ALTER TABLE transfers 17 | ADD CONSTRAINT transfers_one_of_dates_required CHECK (num_nonnulls(booked_at, valued_at) > 0); 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Entities/AccessEntity.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.Data.Entities.Abstractions; 6 | 7 | namespace Gnomeshade.Data.Entities; 8 | 9 | /// Represents an access level to another entity. 10 | public sealed record AccessEntity : Entity, INamedEntity 11 | { 12 | /// 13 | public string Name { get; set; } = null!; 14 | 15 | /// 16 | public string NormalizedName { get; set; } = null!; 17 | } 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Owners/OwnerCreation.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Owners; 10 | 11 | /// Information needed to create an owner. 12 | [PublicAPI] 13 | public sealed record OwnerCreation : Creation 14 | { 15 | /// 16 | [Required] 17 | public string Name { get; set; } = null!; 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Services/ApplicationVersionService.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Reflection; 6 | 7 | namespace Gnomeshade.WebApi.Services; 8 | 9 | internal sealed class ApplicationVersionService 10 | { 11 | private static readonly string _version = Assembly.GetExecutingAssembly().GetName().Version is { } version 12 | ? $"v{version.Major}.{version.Minor}.{version.Build}" 13 | : string.Empty; 14 | 15 | internal string Version => _version; 16 | } 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Delays.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.Avalonia.Core; 8 | 9 | internal static class Delays 10 | { 11 | internal static readonly TimeSpan ActivityDelay = TimeSpan.FromMilliseconds(_activityMillis); 12 | internal static readonly TimeSpan UserInputDelay = TimeSpan.FromMilliseconds(_userInputMillis); 13 | 14 | private const double _activityMillis = 100; 15 | private const double _userInputMillis = 350; 16 | } 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/AccessLevel.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.Data.Repositories; 6 | 7 | /// Defines the level of access needed for the particular entity. 8 | public enum AccessLevel 9 | { 10 | /// Allows read-only access. 11 | Read, 12 | 13 | /// Allows to update an existing entity. 14 | Write, 15 | 16 | /// Allows to delete an existing entity. 17 | Delete, 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Owner/Select.sql: -------------------------------------------------------------------------------- 1 | SELECT o.id AS Id, 2 | o.created_at AS CreatedAt, 3 | o.created_by_user_id AS CreatedByUserId, 4 | o.deleted_at AS DeletedAt, 5 | o.deleted_by_user_id AS DeletedByUserId, 6 | o.name, 7 | o.normalized_name AS NormalizedName 8 | FROM owners o 9 | INNER JOIN ownerships on o.id = ownerships.owner_id 10 | INNER JOIN users on users.id = ownerships.user_id 11 | INNER JOIN access on access.id = ownerships.access_id 12 | WHERE ownerships.user_id = @userId 13 | AND (access.normalized_name = @access OR access.normalized_name = 'OWNER') 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Unit/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT u.id, 2 | u.created_at CreatedAt, 3 | u.owner_id OwnerId, 4 | u.created_by_user_id CreatedByUserId, 5 | u.modified_at ModifiedAt, 6 | u.modified_by_user_id ModifiedByUserId, 7 | u.name AS Name, 8 | u.normalized_name NormalizedName, 9 | u.symbol AS Symbol, 10 | u.deleted_at AS DeletedAt, 11 | u.deleted_by_user_id AS DeletedByUserId, 12 | u.parent_unit_id ParentUnitId, 13 | u.multiplier, 14 | u.inverse_multiplier AS InverseMultiplier 15 | FROM units u 16 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Owners/Owner.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Owners; 10 | 11 | /// A group of resources. 12 | [PublicAPI] 13 | public sealed record Owner 14 | { 15 | /// The id of the owner. 16 | public Guid Id { get; set; } 17 | 18 | /// The name of the owner. 19 | public string Name { get; set; } = null!; 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Logging/LoggingSerializerContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Text.Json.Serialization; 6 | 7 | using VMelnalksnis.NordigenDotNet.Accounts; 8 | 9 | namespace Gnomeshade.WebApi.Logging; 10 | 11 | /// 12 | [JsonSourceGenerationOptions(IgnoreReadOnlyProperties = true)] 13 | [JsonSerializable(typeof(BookedTransaction))] 14 | internal sealed partial class LoggingSerializerContext : JsonSerializerContext; 15 | -------------------------------------------------------------------------------- /tests/Gnomeshade.TestingHelpers/Models/CategoryExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Products; 6 | 7 | namespace Gnomeshade.TestingHelpers.Models; 8 | 9 | public static class CategoryExtensions 10 | { 11 | public static CategoryCreation ToCreation(this Category category) => new() 12 | { 13 | OwnerId = category.OwnerId, 14 | Name = category.Name, 15 | CategoryId = category.CategoryId, 16 | Description = category.Description, 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Splits/SplitProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.Avalonia.Core.Reports.Splits; 6 | 7 | internal static class SplitProvider 8 | { 9 | internal static readonly MonthlySplit MonthlySplit = new(); 10 | internal static readonly DailySplit DailySplit = new(); 11 | 12 | internal static readonly IReportSplit[] Splits = 13 | [ 14 | new YearlySplit(), 15 | MonthlySplit, 16 | new WeeklySplit(), 17 | DailySplit, 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Extensions/IdContainer.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | using JetBrains.Annotations; 9 | 10 | namespace Gnomeshade.Data.Repositories.Extensions; 11 | 12 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 13 | [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] 14 | internal sealed class IdContainer 15 | { 16 | public Guid Id { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/FailedLogin.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Net; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Client; 10 | 11 | /// Indicates a failed login. 12 | /// The response status code. 13 | /// Message describing the failure reason. 14 | [PublicAPI] 15 | public sealed record FailedLogin(HttpStatusCode? StatusCode, string Message) : LoginResult; 16 | -------------------------------------------------------------------------------- /tests/Gnomeshade.TestingHelpers/Models/TransactionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Transactions; 6 | 7 | namespace Gnomeshade.TestingHelpers.Models; 8 | 9 | public static class TransactionExtensions 10 | { 11 | public static TransactionCreation ToCreation(this Transaction transaction) => new() 12 | { 13 | OwnerId = transaction.OwnerId, 14 | ReconciledAt = transaction.ReconciledAt, 15 | Description = transaction.Description, 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Aggregates/Sum.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Gnomeshade.Avalonia.Core.Reports.Aggregates; 9 | 10 | /// Sums all values within the range. 11 | public sealed class Sum : IAggregateFunction 12 | { 13 | /// 14 | public string Name => nameof(Sum); 15 | 16 | decimal IAggregateFunction.Aggregate(IEnumerable source) => source.Sum(); 17 | } 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Help/PackageContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | using System.Text.Json.Serialization; 7 | 8 | namespace Gnomeshade.Avalonia.Core.Help; 9 | 10 | /// 11 | [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] 12 | [JsonSerializable(typeof(List))] 13 | internal sealed partial class PackageContext : JsonSerializerContext; 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Accounts/CounterpartyCreation.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Accounts; 10 | 11 | /// The information needed to create a new counterparty. 12 | [PublicAPI] 13 | public sealed record CounterpartyCreation : Creation 14 | { 15 | /// 16 | [Required] 17 | public string? Name { get; set; } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Gnomeshade.TestingHelpers/Gnomeshade.TestingHelpers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | d2cd7231-768d-4a4d-94ec-71ff3372aaf1 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Aggregates/IAggregateFunction.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace Gnomeshade.Avalonia.Core.Reports.Aggregates; 8 | 9 | /// A function that aggregates a collection of values to a single value. 10 | public interface IAggregateFunction 11 | { 12 | /// Gets the name of the aggregation. 13 | string Name { get; } 14 | 15 | internal decimal Aggregate(IEnumerable source); 16 | } 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000030_ownership_name.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE owners 2 | ADD COLUMN name TEXT NULL, 3 | ADD COLUMN normalized_name TEXT NULL; 4 | 5 | UPDATE owners o 6 | SET name = c.name, 7 | normalized_name = c.normalized_name 8 | FROM owners 9 | INNER JOIN ownerships ON ownerships.id = owners.id 10 | INNER JOIN users u ON u.id = ownerships.user_id 11 | INNER JOIN counterparties c ON c.id = u.counterparty_id 12 | INNER JOIN access a ON a.id = ownerships.access_id 13 | WHERE a.normalized_name = 'OWNER'; 14 | 15 | ALTER TABLE owners 16 | ALTER COLUMN name SET NOT NULL, 17 | ALTER COLUMN normalized_name SET NOT NULL; 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.Sqlite/Dapper/GuidHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.Data; 7 | 8 | using Dapper; 9 | 10 | namespace Gnomeshade.Data.Sqlite.Dapper; 11 | 12 | internal sealed class GuidHandler : SqlMapper.TypeHandler 13 | { 14 | /// 15 | public override void SetValue(IDbDataParameter parameter, Guid value) => parameter.Value = value.ToString(); 16 | 17 | /// 18 | public override Guid Parse(object value) => new((string)value); 19 | } 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Loan2/Delete.sql: -------------------------------------------------------------------------------- 1 | WITH accessable AS 2 | (SELECT loans2.id 3 | FROM loans2 4 | INNER JOIN owners ON owners.id = loans2.owner_id 5 | INNER JOIN ownerships ON owners.id = ownerships.owner_id 6 | INNER JOIN access ON access.id = ownerships.access_id 7 | WHERE ownerships.user_id = @userId 8 | AND (access.normalized_name = 'DELETE' OR access.normalized_name = 'OWNER') 9 | AND loans2.deleted_at IS NULL 10 | AND loans2.id = @id) 11 | 12 | UPDATE loans2 13 | SET deleted_at = CURRENT_TIMESTAMP, 14 | deleted_by_user_id = @userId 15 | FROM accessable 16 | WHERE loans2.id IN (SELECT id FROM accessable); 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/DashboardView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | 7 | using Gnomeshade.Avalonia.Core; 8 | 9 | namespace Gnomeshade.Desktop.Views; 10 | 11 | /// 12 | public sealed partial class DashboardView : UserControl, IView 13 | { 14 | /// Initializes a new instance of the class. 15 | public DashboardView() => InitializeComponent(); 16 | } 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Admin/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Gnomeshade.WebApi.Areas.Admin.Pages; 9 | 10 | /// Main administration page. 11 | public sealed class Index : PageModel 12 | { 13 | /// Handles requests made by the user. 14 | public ActionResult OnGet() 15 | { 16 | return RedirectToPage("Users/Index", new { area = "Admin" }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Migrations/00000004_transaction_dates.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.transactions 2 | RENAME COLUMN "date" TO booked_at; 3 | 4 | ALTER TABLE public.transactions 5 | ALTER COLUMN booked_at DROP NOT NULL; 6 | 7 | ALTER TABLE public.transactions 8 | ADD COLUMN valued_at timestamptz NULL; 9 | 10 | ALTER TABLE public.transactions 11 | ADD CONSTRAINT transactions_one_of_dates_required CHECK (num_nonnulls(booked_at, transactions.valued_at) > 0); 12 | 13 | ALTER TABLE public.transactions 14 | RENAME COLUMN validated_at TO reconciled_at; 15 | 16 | ALTER TABLE public.transactions 17 | RENAME COLUMN validated_by_user_id TO reconciled_by_user_id; 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Entities/Abstractions/IOwnableEntity.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.Data.Entities.Abstractions; 8 | 9 | /// Represents an entity that is owned by a . 10 | public interface IOwnableEntity : IEntity 11 | { 12 | /// Gets or sets the id of the owner of this entity. 13 | /// 14 | /// 15 | public Guid OwnerId { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/OpenApi/ProducesStatus404NotFoundAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | using static Microsoft.AspNetCore.Http.StatusCodes; 8 | 9 | namespace Gnomeshade.WebApi.OpenApi; 10 | 11 | /// A filter that specifies that the action returns with . 12 | internal sealed class ProducesStatus404NotFoundAttribute() 13 | : ProducesResponseTypeAttribute(Status404NotFound); 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/OpenApi/ProducesStatus409ConflictAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | using static Microsoft.AspNetCore.Http.StatusCodes; 8 | 9 | namespace Gnomeshade.WebApi.OpenApi; 10 | 11 | /// A filter that specifies that the action returns with . 12 | internal sealed class ProducesStatus409ConflictAttribute() 13 | : ProducesResponseTypeAttribute(Status409Conflict); 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.Sqlite/Dapper/DecimalHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.Data; 7 | 8 | using Dapper; 9 | 10 | namespace Gnomeshade.Data.Sqlite.Dapper; 11 | 12 | internal sealed class DecimalHandler : SqlMapper.TypeHandler 13 | { 14 | /// 15 | public override void SetValue(IDbDataParameter parameter, decimal value) => parameter.Value = value; 16 | 17 | /// 18 | public override decimal Parse(object value) => Convert.ToDecimal(value); 19 | } 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Owners/Access.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Owners; 10 | 11 | /// The level of access a user can have to a group of resources. 12 | [PublicAPI] 13 | public sealed record Access 14 | { 15 | /// The id of the access. 16 | public Guid Id { get; set; } 17 | 18 | /// The name of the access. 19 | public string Name { get; set; } = null!; 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Manage/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | if (ViewData.TryGetValue("ParentLayout", out var parentLayout) && parentLayout is not null) 3 | { 4 | Layout = parentLayout.ToString(); 5 | } 6 | else 7 | { 8 | Layout = "~/Pages/Shared/_Layout.cshtml"; 9 | } 10 | } 11 | 12 |

Manage your account

13 | 14 |
15 |

Change your account settings

16 |
17 |
18 |
19 | 20 |
21 |
22 | @RenderBody() 23 |
24 |
25 |
26 | 27 | @section Scripts { 28 | @await RenderSectionAsync("Scripts", false) 29 | } 30 | -------------------------------------------------------------------------------- /tests/Gnomeshade.TestingHelpers/Models/LoanPaymentExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Loans; 6 | 7 | namespace Gnomeshade.TestingHelpers.Models; 8 | 9 | public static class LoanPaymentExtensions 10 | { 11 | public static LoanPaymentCreation ToCreation(this LoanPayment payment) => new() 12 | { 13 | OwnerId = payment.OwnerId, 14 | LoanId = payment.LoanId, 15 | TransactionId = payment.TransactionId, 16 | Amount = payment.Amount, 17 | Interest = payment.Interest, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: type:bug 6 | assignees: VMelnalksnis 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /deployment/licenses.ps1: -------------------------------------------------------------------------------- 1 | $dotnetArgs = @() 2 | $dotnetArgs = $dotnetArgs + "tool" 3 | $dotnetArgs = $dotnetArgs + "install" 4 | $dotnetArgs = $dotnetArgs + "-g" 5 | $dotnetArgs = $dotnetArgs + "dotnet-project-licenses" 6 | $dotnetArgs = $dotnetArgs + "--prerelease" 7 | 8 | & dotnet $dotnetArgs 9 | 10 | $licenseArgs = @() 11 | $licenseArgs = $licenseArgs + "-i" + ".\Gnomeshade.sln" 12 | $licenseArgs = $licenseArgs + "-o" + "JsonPretty" 13 | $licenseArgs = $licenseArgs + "-override" + ".\source\Gnomeshade.Avalonia.Core\Help\override.json" 14 | $licenseArgs = $licenseArgs + "-t" 15 | 16 | & dotnet-project-licenses $licenseArgs | Out-File -FilePath ".\source\Gnomeshade.Avalonia.Core\Help\licenses.json" 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Account/Delete.sql: -------------------------------------------------------------------------------- 1 | WITH accessable AS 2 | (SELECT accounts.id 3 | FROM accounts 4 | INNER JOIN owners ON owners.id = accounts.owner_id 5 | INNER JOIN ownerships ON owners.id = ownerships.owner_id 6 | INNER JOIN access ON access.id = ownerships.access_id 7 | WHERE ownerships.user_id = @userId 8 | AND (access.normalized_name = 'WRITE' OR access.normalized_name = 'OWNER') 9 | AND accounts.deleted_at IS NULL 10 | AND accounts.id = @Id) 11 | 12 | UPDATE accounts 13 | SET deleted_at = CURRENT_TIMESTAMP, 14 | deleted_by_user_id = @userId 15 | FROM accessable 16 | WHERE accounts.id IN (SELECT id FROM accessable); 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Loans/LoanView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | 7 | using Gnomeshade.Avalonia.Core; 8 | using Gnomeshade.Avalonia.Core.Loans; 9 | 10 | namespace Gnomeshade.Desktop.Views.Loans; 11 | 12 | /// 13 | public sealed partial class LoanView : UserControl, IView 14 | { 15 | /// Initializes a new instance of the class. 16 | public LoanView() => InitializeComponent(); 17 | } 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Owners/OwnershipCreation.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Owners; 10 | 11 | /// Information needed to create an ownership. 12 | [PublicAPI] 13 | public sealed record OwnershipCreation : Creation 14 | { 15 | /// 16 | public Guid UserId { get; set; } 17 | 18 | /// 19 | public Guid AccessId { get; set; } 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Configuration/Options/DatabaseProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Ardalis.SmartEnum; 6 | 7 | namespace Gnomeshade.WebApi.Configuration.Options; 8 | 9 | internal sealed class DatabaseProvider : SmartEnum 10 | { 11 | public static readonly DatabaseProvider PostgreSQL = new(nameof(PostgreSQL), 1); 12 | public static readonly DatabaseProvider Sqlite = new(nameof(Sqlite), 2); 13 | 14 | private DatabaseProvider(string name, int value) 15 | : base(name, value) 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Gnomeshade.TestingHelpers/Models/ProductExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Products; 6 | 7 | namespace Gnomeshade.TestingHelpers.Models; 8 | 9 | public static class ProductExtensions 10 | { 11 | public static ProductCreation ToCreation(this Product product) => new() 12 | { 13 | OwnerId = product.OwnerId, 14 | Name = product.Name, 15 | CategoryId = product.CategoryId, 16 | UnitId = product.UnitId, 17 | Description = product.Description, 18 | Sku = product.Sku, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/LoanPayment/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT loan_payments.id, 2 | loan_payments.created_at CreatedAt, 3 | loan_payments.created_by_user_id CreatedByUserId, 4 | loan_payments.deleted_at DeletedAt, 5 | loan_payments.deleted_by_user_id DeletedByUserId, 6 | loan_payments.owner_id OwnerId, 7 | loan_payments.modified_at ModifiedAt, 8 | loan_payments.modified_by_user_id ModifiedByUserId, 9 | loan_payments.loan_id LoanId, 10 | loan_payments.transaction_id TransactionId, 11 | loan_payments.amount AS Amount, 12 | loan_payments.interest AS Interest 13 | FROM loan_payments 14 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Transactions/TransactionItemCreation.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.ComponentModel.DataAnnotations; 7 | 8 | namespace Gnomeshade.WebApi.Models.Transactions; 9 | 10 | /// Creation model of a sub-item of a . 11 | public abstract record TransactionItemCreation : Creation 12 | { 13 | /// The id of the transaction of which this item is a part of. 14 | [Required] 15 | public abstract Guid? TransactionId { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Counterparty/Merge.sql: -------------------------------------------------------------------------------- 1 | WITH accessable AS 2 | (SELECT accounts.id 3 | FROM accounts 4 | INNER JOIN owners ON owners.id = accounts.owner_id 5 | INNER JOIN ownerships ON owners.id = ownerships.owner_id 6 | INNER JOIN access ON access.id = ownerships.access_id 7 | WHERE accounts.counterparty_id = @sourceId 8 | AND ownerships.user_id = @userId 9 | AND (access.normalized_name = 'WRITE' OR access.normalized_name = 'OWNER')) 10 | 11 | UPDATE accounts 12 | SET modified_at = CURRENT_TIMESTAMP, 13 | modified_by_user_id = @userId, 14 | counterparty_id = @targetId 15 | FROM accessable 16 | WHERE accounts.id IN (SELECT id FROM accessable); 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model RegisterConfirmation 3 | @{ 4 | ViewData["Title"] = "Register confirmation"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | @{ 9 | if (@Model.DisplayConfirmAccountLink) 10 | { 11 |

12 | This app does not currently have a real email sender registered, see these docs for how to configure a real email sender. 13 | Normally this would be emailed: Click here to confirm your account 14 |

15 | } 16 | else 17 | { 18 |

19 | Please check your email to confirm your account. 20 |

21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Gnomeshade.Data.Tests.Integration/Fakers/CounterpartyFaker.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using Gnomeshade.Data.Entities; 8 | 9 | namespace Gnomeshade.Data.Tests.Integration.Fakers; 10 | 11 | public sealed class CounterpartyFaker : NamedEntityFaker 12 | { 13 | public CounterpartyFaker(Guid userId) 14 | : base(userId) 15 | { 16 | RuleFor(party => party.Name, faker => faker.Company.CompanyName()); 17 | RuleFor(party => party.NormalizedName, (_, party) => party.Name.ToUpperInvariant()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Dapper/NullableInstantTypeHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Data; 6 | 7 | using Dapper; 8 | 9 | using NodaTime; 10 | 11 | namespace Gnomeshade.Data.PostgreSQL.Dapper; 12 | 13 | internal sealed class NullableInstantTypeHandler : SqlMapper.TypeHandler 14 | { 15 | /// 16 | public override void SetValue(IDbDataParameter parameter, Instant? value) => parameter.Value = value; 17 | 18 | /// 19 | public override Instant? Parse(object value) => (Instant?)value; 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Entities/Abstractions/INamedEntity.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | namespace Gnomeshade.Data.Entities.Abstractions; 6 | 7 | /// 8 | /// Represents an entity that can be named by the user. 9 | /// 10 | public interface INamedEntity : IEntity 11 | { 12 | /// 13 | /// Gets or sets the name of the entity. 14 | /// 15 | string Name { get; set; } 16 | 17 | /// 18 | /// Gets or sets the normalized name of the entity. 19 | /// 20 | string NormalizedName { get; set; } 21 | } 22 | -------------------------------------------------------------------------------- /deployment/build.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [Parameter(Mandatory = $true, Position = 0)] 3 | [System.String] 4 | $Project 5 | ) 6 | 7 | $publish_dir = "source\$Project\bin\Release\" 8 | $archive_name = "${Project}.msi" 9 | 10 | $dotnetArgs = @() 11 | $dotnetArgs = $dotnetArgs + "build" 12 | $dotnetArgs = $dotnetArgs + ".\source\$Project\$Project.wixproj" 13 | $dotnetArgs = $dotnetArgs + "--configuration" + "Release" 14 | $dotnetArgs = $dotnetArgs + "--no-restore" 15 | $dotnetArgs = $dotnetArgs + "/nologo" 16 | 17 | & dotnet $dotnetArgs 18 | 19 | "artifact-name=$archive_name" >> $env:GITHUB_OUTPUT 20 | "artifact<> $env:GITHUB_OUTPUT 21 | "$publish_dir\$archive_name" >> $env:GITHUB_OUTPUT 22 | "EOF" >> $env:GITHUB_OUTPUT 23 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Projects/ProjectView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | 7 | using Gnomeshade.Avalonia.Core; 8 | using Gnomeshade.Avalonia.Core.Projects; 9 | 10 | namespace Gnomeshade.Desktop.Views.Projects; 11 | 12 | /// 13 | public sealed partial class ProjectView : UserControl, IView 14 | { 15 | /// Initializes a new instance of the class. 16 | public ProjectView() => InitializeComponent(); 17 | } 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Accounts/AccountInCurrencyCreation.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.ComponentModel.DataAnnotations; 7 | 8 | using JetBrains.Annotations; 9 | 10 | namespace Gnomeshade.WebApi.Models.Accounts; 11 | 12 | /// The information needed to add a currency to an account. 13 | [PublicAPI] 14 | public sealed record AccountInCurrencyCreation : Creation 15 | { 16 | /// The id of the currency to add to an account. 17 | [Required] 18 | public Guid? CurrencyId { get; set; } 19 | } 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/OpenApi/InstantSchemaFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using JetBrains.Annotations; 6 | 7 | using Microsoft.OpenApi.Models; 8 | 9 | using NodaTime; 10 | 11 | namespace Gnomeshade.WebApi.OpenApi; 12 | 13 | [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] 14 | internal sealed class InstantSchemaFilter : SchemaFilter 15 | { 16 | /// 17 | protected override void ApplyFilter(OpenApiSchema schema) 18 | { 19 | schema.Type = "string"; 20 | schema.Format = "date-time"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Gnomeshade.TestingHelpers/Models/LoanExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Loans; 6 | 7 | namespace Gnomeshade.TestingHelpers.Models; 8 | 9 | public static class LoanExtensions 10 | { 11 | public static LoanCreation ToCreation(this Loan loan) => new() 12 | { 13 | OwnerId = loan.OwnerId, 14 | Name = loan.Name, 15 | IssuingCounterpartyId = loan.IssuingCounterpartyId, 16 | ReceivingCounterpartyId = loan.ReceivingCounterpartyId, 17 | Principal = loan.Principal, 18 | CurrencyId = loan.CurrencyId, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: type:enhancement 6 | assignees: VMelnalksnis 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Projects/ProjectCreation.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.ComponentModel.DataAnnotations; 7 | 8 | namespace Gnomeshade.WebApi.Models.Projects; 9 | 10 | /// The information needed to create or update a project. 11 | public sealed record ProjectCreation : Creation 12 | { 13 | /// 14 | [Required] 15 | public string Name { get; set; } = null!; 16 | 17 | /// 18 | public Guid? ParentProjectId { get; set; } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Tests.Integration.PostgreSQL/Fixtures/UnloggedTableScriptPreprocessor.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using DbUp.Engine; 6 | 7 | namespace Gnomeshade.WebApi.Tests.Integration.Fixtures; 8 | 9 | /// 10 | /// 11 | internal sealed class UnloggedTableScriptPreprocessor : IScriptPreprocessor 12 | { 13 | /// 14 | public string Process(string contents) => contents.Replace("CREATE TABLE", "CREATE UNLOGGED TABLE"); 15 | } 16 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Loan/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT loans.id, 2 | loans.created_at CreatedAt, 3 | loans.owner_id OwnerId, 4 | loans.created_by_user_id CreatedByUserId, 5 | loans.modified_at ModifiedAt, 6 | loans.modified_by_user_id ModifiedByUserId, 7 | loans.transaction_id TransactionId, 8 | loans.issuing_counterparty_id IssuingCounterpartyId, 9 | loans.receiving_counterparty_id ReceivingCounterpartyId, 10 | loans.amount AS Amount, 11 | loans.currency_id CurrencyId, 12 | loans.deleted_at DeletedAt, 13 | loans.deleted_by_user_id DeletedByUserId 14 | FROM loans 15 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Aggregates/Maximum.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Gnomeshade.Avalonia.Core.Reports.Aggregates; 9 | 10 | /// Aggregates the collection to the largest value. 11 | /// 12 | public sealed class Maximum : IAggregateFunction 13 | { 14 | /// 15 | public string Name => nameof(Maximum); 16 | 17 | decimal IAggregateFunction.Aggregate(IEnumerable source) => source.Max(); 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Aggregates/Minimum.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Gnomeshade.Avalonia.Core.Reports.Aggregates; 9 | 10 | /// Aggregates the collection to the smallest value. 11 | /// 12 | public sealed class Minimum : IAggregateFunction 13 | { 14 | /// 15 | public string Name => nameof(Minimum); 16 | 17 | decimal IAggregateFunction.Aggregate(IEnumerable source) => source.Min(); 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Aggregates/Average.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Gnomeshade.Avalonia.Core.Reports.Aggregates; 9 | 10 | /// Aggregates the collection to an average value. 11 | /// 12 | public sealed class Average : IAggregateFunction 13 | { 14 | /// 15 | public string Name => nameof(Average); 16 | 17 | decimal IAggregateFunction.Aggregate(IEnumerable source) => source.Average(); 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Loans/LoanUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | 7 | using Gnomeshade.Avalonia.Core; 8 | using Gnomeshade.Avalonia.Core.Loans; 9 | 10 | namespace Gnomeshade.Desktop.Views.Loans; 11 | 12 | /// 13 | public sealed partial class LoanUpsertionView : UserControl, IView 14 | { 15 | /// Initializes a new instance of the class. 16 | public LoanUpsertionView() => InitializeComponent(); 17 | } 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Loan2/SelectAll.sql: -------------------------------------------------------------------------------- 1 | SELECT loans2.id, 2 | loans2.created_at CreatedAt, 3 | loans2.created_by_user_id CreatedByUserId, 4 | loans2.deleted_at DeletedAt, 5 | loans2.deleted_by_user_id DeletedByUserId, 6 | loans2.owner_id OwnerId, 7 | loans2.modified_at ModifiedAt, 8 | loans2.modified_by_user_id ModifiedByUserId, 9 | loans2.name, 10 | loans2.normalized_name NormalizedName, 11 | 12 | loans2.issuing_counterparty_id IssuingCounterpartyId, 13 | loans2.receiving_counterparty_id ReceivingCounterpartyId, 14 | loans2.principal, 15 | loans2.currency_id CurrencyId 16 | FROM loans2 17 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Help/AboutView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Help; 10 | 11 | namespace Gnomeshade.Desktop.Views.Help; 12 | 13 | /// 14 | public sealed partial class AboutView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public AboutView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/DialogWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | #if DEBUG 6 | using Avalonia; 7 | #endif 8 | using Avalonia.Controls; 9 | using Avalonia.Markup.Xaml; 10 | 11 | namespace Gnomeshade.Desktop.Views; 12 | 13 | /// The root element of a dialog window. 14 | public sealed partial class DialogWindow : Window 15 | { 16 | /// Initializes a new instance of the class. 17 | public DialogWindow() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | 21 | #if DEBUG 22 | this.AttachDevTools(); 23 | #endif 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Entities/OwnerEntity.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.Data.Entities.Abstractions; 6 | 7 | namespace Gnomeshade.Data.Entities; 8 | 9 | /// Represents a collection of other entities (users, roles, groups, etc.) that can own other entities. 10 | /// 11 | /// 12 | public sealed record OwnerEntity : Entity, INamedEntity 13 | { 14 | /// 15 | public string Name { get; set; } = null!; 16 | 17 | /// 18 | public string NormalizedName { get; set; } = null!; 19 | } 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Accesses/OwnerView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Accesses; 10 | 11 | namespace Gnomeshade.Desktop.Views.Accesses; 12 | 13 | /// 14 | public sealed partial class OwnerView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public OwnerView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Gnomeshade.Data.Tests.Integration/Fakers/UnitFaker.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using Gnomeshade.Data.Entities; 8 | 9 | namespace Gnomeshade.Data.Tests.Integration.Fakers; 10 | 11 | public sealed class UnitFaker : NamedEntityFaker 12 | { 13 | public UnitFaker(UserEntity user) 14 | : this(user.Id) 15 | { 16 | } 17 | 18 | public UnitFaker(Guid userId) 19 | : base(userId) 20 | { 21 | RuleFor(unit => unit.Name, faker => faker.Commerce.ProductMaterial()); 22 | RuleFor(unit => unit.NormalizedName, (_, unit) => unit.Name.ToUpperInvariant()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Gnomeshade.TestingHelpers/Models/PurchaseExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Transactions; 6 | 7 | namespace Gnomeshade.TestingHelpers.Models; 8 | 9 | public static class PurchaseExtensions 10 | { 11 | public static PurchaseCreation ToCreation(this Purchase purchase) => new() 12 | { 13 | OwnerId = purchase.OwnerId, 14 | TransactionId = purchase.TransactionId, 15 | ProductId = purchase.ProductId, 16 | Amount = purchase.Amount, 17 | CurrencyId = purchase.CurrencyId, 18 | Price = purchase.Price, 19 | DeliveryDate = purchase.DeliveryDate, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data.PostgreSQL/Dapper/UnsignedIntegerTypeHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Data; 6 | 7 | using Dapper; 8 | 9 | namespace Gnomeshade.Data.PostgreSQL.Dapper; 10 | 11 | internal sealed class UnsignedIntegerTypeHandler : SqlMapper.TypeHandler 12 | { 13 | /// 14 | public override void SetValue(IDbDataParameter parameter, uint? value) 15 | { 16 | parameter.DbType = DbType.Int32; 17 | parameter.Value = (int?)value; 18 | } 19 | 20 | /// 21 | public override uint? Parse(object? value) => value is int number ? (uint)number : null; 22 | } 23 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Project/Select.sql: -------------------------------------------------------------------------------- 1 | SELECT projects.id, 2 | projects.created_at CreatedAt, 3 | projects.owner_id OwnerId, 4 | projects.created_by_user_id CreatedByUserId, 5 | projects.modified_at ModifiedAt, 6 | projects.modified_by_user_id ModifiedByUserId, 7 | projects.name, 8 | projects.normalized_name NormalizedName, 9 | projects.parent_project_id ParentProjectId 10 | FROM projects 11 | INNER JOIN owners ON owners.id = projects.owner_id 12 | INNER JOIN ownerships ON owners.id = ownerships.owner_id 13 | INNER JOIN access ON access.id = ownerships.access_id 14 | WHERE ownerships.user_id = @userId 15 | AND (access.normalized_name = @access OR access.normalized_name = 'OWNER') 16 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Imports/ImportView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Imports; 10 | 11 | namespace Gnomeshade.Desktop.Views.Imports; 12 | 13 | /// 14 | public sealed partial class ImportView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public ImportView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Products/CategoryFilterView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | 7 | using Gnomeshade.Avalonia.Core; 8 | using Gnomeshade.Avalonia.Core.Products; 9 | 10 | namespace Gnomeshade.Desktop.Views.Products; 11 | 12 | /// 13 | public sealed partial class CategoryFilterView : UserControl, IView 14 | { 15 | /// Initializes a new instance of the class. 16 | public CategoryFilterView() 17 | { 18 | InitializeComponent(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Products/UnitView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Products; 10 | 11 | namespace Gnomeshade.Desktop.Views.Products; 12 | 13 | /// An overview of of all units. 14 | public sealed partial class UnitView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public UnitView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Importing/AccountReference.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Accounts; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Importing; 10 | 11 | /// A reference to an account that was used during import. 12 | [PublicAPI] 13 | public sealed record AccountReference 14 | { 15 | /// Whether or not the account was created during import. 16 | public bool Created { get; set; } 17 | 18 | /// The referenced account. 19 | public Account Account { get; set; } = null!; 20 | } 21 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Tests.Integration.Oidc/Gnomeshade.WebApi.Tests.Integration.Oidc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /deployment/test-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | tag="ghcr.io/vmelnalksnis/gnomeshade:test" 5 | name="test" 6 | 7 | function printLogs() { 8 | docker logs $name 9 | } 10 | 11 | trap printLogs EXIT 12 | 13 | docker build --tag $tag --build-arg "BUILD_NUMBER=$1" ./ 14 | docker run --name $name -d -p 8000:8080 -e "Admin__Password=$2" $tag 15 | 16 | wget --tries=10 --retry-connrefused --waitretry=1 --timeout=15 "http://localhost:8000/api/v1.0/health" 17 | 18 | if [[ $(cat health) != "Healthy" ]]; then 19 | exit 1 20 | fi 21 | 22 | curl --header "Content-Type: application/json" \ 23 | --request POST \ 24 | --data '{"username":"john.doe", "password": "Password123!", "email": "john.doe@example.com", "fullName": "John Doe" }' \ 25 | http://localhost:8000/Authorization/Register 26 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Calculations/CalculableValue.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Transactions; 6 | 7 | using NodaTime; 8 | 9 | namespace Gnomeshade.Avalonia.Core.Reports.Calculations; 10 | 11 | internal struct CalculableValue 12 | { 13 | internal CalculableValue(Purchase purchase, ZonedDateTime date, decimal multiplier) 14 | { 15 | Purchase = purchase; 16 | Date = date; 17 | Multiplier = multiplier; 18 | } 19 | 20 | internal Purchase Purchase { get; } 21 | 22 | internal ZonedDateTime Date { get; } 23 | 24 | internal decimal Multiplier { get; } 25 | } 26 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/DialogWindow.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Help/LicensesView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Help; 10 | 11 | namespace Gnomeshade.Desktop.Views.Help; 12 | 13 | /// 14 | public sealed partial class LicensesView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public LicensesView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Accounts/AccountView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Accounts; 10 | 11 | namespace Gnomeshade.Desktop.Views.Accounts; 12 | 13 | /// 14 | public sealed partial class AccountView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public AccountView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Loans/Migration/LoanMigrationView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | 7 | using Gnomeshade.Avalonia.Core; 8 | using Gnomeshade.Avalonia.Core.Loans.Migration; 9 | 10 | namespace Gnomeshade.Desktop.Views.Loans.Migration; 11 | 12 | /// 13 | public sealed partial class LoanMigrationView : UserControl, IView 14 | { 15 | /// Initializes a new instance of the class. 16 | public LoanMigrationView() => InitializeComponent(); 17 | } 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Products/ProductView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Products; 10 | 11 | namespace Gnomeshade.Desktop.Views.Products; 12 | 13 | /// 14 | public sealed partial class ProductView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public ProductView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Authentication/UserModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using JetBrains.Annotations; 6 | 7 | namespace Gnomeshade.WebApi.Models.Authentication; 8 | 9 | /// Information about a user. 10 | [PublicAPI] 11 | public sealed record UserModel 12 | { 13 | /// The id of the user. 14 | public string Id { get; set; } = null!; 15 | 16 | /// The username of the user. 17 | public string Username { get; set; } = null!; 18 | 19 | /// The primary email address of the user. 20 | public string Email { get; set; } = null!; 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Importing/TransferReference.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Transactions; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Importing; 10 | 11 | /// A reference to an transfer that was used during import. 12 | [PublicAPI] 13 | public sealed record TransferReference 14 | { 15 | /// Whether or not the transfer was created during import. 16 | public bool Created { get; set; } 17 | 18 | /// The referenced transfer. 19 | public Transfer Transfer { get; set; } = null!; 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Authentication/LoginView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Authentication; 10 | 11 | namespace Gnomeshade.Desktop.Views.Authentication; 12 | 13 | /// 14 | public sealed partial class LoginView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public LoginView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Authentication/Login.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Authentication; 10 | 11 | /// The information needed to log in. 12 | [PublicAPI] 13 | public sealed record Login 14 | { 15 | /// The username to log in with. Required. 16 | [Required] 17 | public string Username { get; set; } = null!; 18 | 19 | /// The password to log in with. Required. 20 | [Required] 21 | public string Password { get; set; } = null!; 22 | } 23 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Authentication/LoginResponse.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using JetBrains.Annotations; 6 | 7 | using NodaTime; 8 | 9 | namespace Gnomeshade.WebApi.Models.Authentication; 10 | 11 | /// Information about the started session. 12 | [PublicAPI] 13 | public sealed record LoginResponse(string Token, Instant ValidTo) 14 | { 15 | /// A JWT for authenticating the session. 16 | public string Token { get; set; } = Token; 17 | 18 | /// The point in time until which the session is valid. 19 | public Instant ValidTo { get; set; } = ValidTo; 20 | } 21 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Tests.Integration/Fixtures/DatabaseFixtureSource.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | namespace Gnomeshade.WebApi.Tests.Integration.Fixtures; 10 | 11 | public sealed class DatabaseFixtureSource : IEnumerable 12 | { 13 | internal static readonly List Fixtures = WebserverSetup.WebserverFixtures; 14 | 15 | public IEnumerator GetEnumerator() 16 | { 17 | return Fixtures 18 | .Select(fixture => new TestFixtureData(fixture).SetArgDisplayNames(fixture.Name)) 19 | .GetEnumerator(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Links/LinkView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Links; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Links; 12 | 13 | /// 14 | public sealed partial class LinkView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public LinkView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/GnomeshadeOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.ComponentModel.DataAnnotations; 7 | 8 | using JetBrains.Annotations; 9 | 10 | namespace Gnomeshade.WebApi.Client; 11 | 12 | /// Settings for accessing gnomeshade API. 13 | [UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.Members)] 14 | public sealed record GnomeshadeOptions 15 | { 16 | internal const string SectionName = "Gnomeshade"; 17 | 18 | /// Gets or sets the gnomeshade API base address. 19 | [Required] 20 | public Uri? BaseAddress { get; set; } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Gnomeshade.Data.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | false 6 | 7 | CS8002 8 | true 9 | key.snk 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/AccountInCurrency/Select.sql: -------------------------------------------------------------------------------- 1 | SELECT a.id, 2 | a.owner_id OwnerId, 3 | a.created_at CreatedAt, 4 | a.created_by_user_id CreatedByUserId, 5 | a.modified_at ModifiedAt, 6 | a.modified_by_user_id ModifiedByUserId, 7 | a.account_id AccountId, 8 | a.currency_id CurrencyId 9 | FROM accounts_in_currency a 10 | INNER JOIN accounts on accounts.id = a.account_id 11 | INNER JOIN owners ON owners.id = accounts.owner_id 12 | INNER JOIN ownerships ON owners.id = ownerships.owner_id 13 | INNER JOIN access ON access.id = ownerships.access_id 14 | WHERE (ownerships.user_id = @userId AND (access.normalized_name = @access OR access.normalized_name = 'OWNER') 15 | AND accounts.deleted_at IS NULL) 16 | -------------------------------------------------------------------------------- /tests/Gnomeshade.Avalonia.Core.Tests/Accounts/AccountViewModelTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | 7 | using Gnomeshade.Avalonia.Core.Accounts; 8 | using Gnomeshade.Avalonia.Core.DesignTime; 9 | 10 | namespace Gnomeshade.Avalonia.Core.Tests.Accounts; 11 | 12 | public class AccountViewModelTests 13 | { 14 | [Test] 15 | public async Task DataGridView_ShouldBeGrouped() 16 | { 17 | var viewModel = new AccountViewModel(new StubbedActivityService(), new DesignTimeGnomeshadeClient()); 18 | await viewModel.RefreshAsync(); 19 | 20 | viewModel.DataGridView.Groups.Should().ContainSingle(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Gnomeshade.Data.Tests.Integration/Fakers/CategoryFaker.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using Gnomeshade.Data.Entities; 8 | 9 | namespace Gnomeshade.Data.Tests.Integration.Fakers; 10 | 11 | public sealed class CategoryFaker : NamedEntityFaker 12 | { 13 | public CategoryFaker(Guid userId) 14 | : base(userId) 15 | { 16 | RuleFor(category => category.Name, faker => faker.Commerce.ProductName()); 17 | RuleFor(category => category.NormalizedName, (_, product) => product.Name.ToUpperInvariant()); 18 | RuleFor(category => category.Description, faker => faker.Lorem.Sentence()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/LocalDateConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Globalization; 6 | 7 | using NodaTime; 8 | using NodaTime.Text; 9 | 10 | namespace Gnomeshade.Avalonia.Core; 11 | 12 | /// Converts a binding value of type . 13 | public sealed class LocalDateConverter : NodaTimeValueConverter 14 | { 15 | /// 16 | protected override LocalDate TemplateValue { get; } = new(2000, 12, 31); 17 | 18 | /// 19 | protected override LocalDatePattern GetPattern(CultureInfo culture) => 20 | LocalDatePattern.Create("d", culture); 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Calculations/ICalculationFunction.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | 7 | using LiveChartsCore.Defaults; 8 | 9 | namespace Gnomeshade.Avalonia.Core.Reports.Calculations; 10 | 11 | /// A function that calculates the value to display for a purchase. 12 | public interface ICalculationFunction 13 | { 14 | /// Gets the name of the calculation. 15 | string Name { get; } 16 | 17 | internal decimal Calculate(CalculableValue value); 18 | 19 | internal IEnumerable Update(IReadOnlyCollection points); 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/UpsertedEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.Avalonia.Core; 8 | 9 | /// Event arguments for event. 10 | public sealed class UpsertedEventArgs : EventArgs 11 | { 12 | /// Initializes a new instance of the class. 13 | /// The id of the upserted entity. 14 | public UpsertedEventArgs(Guid id) 15 | { 16 | Id = id; 17 | } 18 | 19 | /// Gets the id of the upserted entity. 20 | public Guid Id { get; } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Products/CategoryView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Products; 10 | 11 | namespace Gnomeshade.Desktop.Views.Products; 12 | 13 | /// An overview of of all categories. 14 | public sealed partial class CategoryView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public CategoryView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Importing/TransactionReference.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Gnomeshade.WebApi.Models.Transactions; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Importing; 10 | 11 | /// A reference to an transaction that was used during import. 12 | [PublicAPI] 13 | public sealed record TransactionReference 14 | { 15 | /// Whether or not the transaction was created during import. 16 | public bool Created { get; set; } 17 | 18 | /// The referenced transaction. 19 | public Transaction Transaction { get; set; } = null!; 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Accounts/AccountFilterView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Accounts; 10 | 11 | namespace Gnomeshade.Desktop.Views.Accounts; 12 | 13 | /// 14 | public sealed partial class AccountFilterView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public AccountFilterView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Products/ProductFilterView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Products; 10 | 11 | namespace Gnomeshade.Desktop.Views.Products; 12 | 13 | /// 14 | public sealed partial class ProductFilterView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public ProductFilterView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Loans/LoanView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Loans; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Loans; 12 | 13 | /// 14 | public sealed partial class LoanView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public LoanView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/DateAxis.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using LiveChartsCore.SkiaSharpView; 8 | 9 | namespace Gnomeshade.Avalonia.Core.Reports; 10 | 11 | internal static class DateAxis 12 | { 13 | private static double DateStep { get; } = TimeSpan.FromDays(30.4375).Ticks; 14 | 15 | internal static Axis GetXAxis() => new() { Labeler = Date, UnitWidth = DateStep, MinStep = DateStep }; 16 | 17 | private static string Date(double value) 18 | { 19 | var ticks = Math.Clamp((long)value, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks); 20 | return new DateTime(ticks).ToString("yyyy MM"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Counterparties/CounterpartyFilter.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | 7 | using Gnomeshade.Avalonia.Core; 8 | using Gnomeshade.Avalonia.Core.Counterparties; 9 | 10 | namespace Gnomeshade.Desktop.Views.Counterparties; 11 | 12 | /// 13 | public sealed partial class CounterpartyFilterView : UserControl, IView 14 | { 15 | /// Initializes a new instance of the class. 16 | public CounterpartyFilterView() 17 | { 18 | InitializeComponent(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/ActivityScope.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | 8 | namespace Gnomeshade.Avalonia.Core; 9 | 10 | internal readonly struct ActivityScope : IDisposable 11 | { 12 | private readonly ICollection _scopes; 13 | 14 | public ActivityScope(ICollection scopes, string name) 15 | { 16 | _scopes = scopes; 17 | Name = name; 18 | 19 | _scopes.Add(this); 20 | } 21 | 22 | /// Gets the name of the activity. 23 | public string Name { get; } 24 | 25 | /// 26 | public void Dispose() => _scopes.Remove(this); 27 | } 28 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Calculations/TotalPrice.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | 7 | using LiveChartsCore.Defaults; 8 | 9 | namespace Gnomeshade.Avalonia.Core.Reports.Calculations; 10 | 11 | /// Calculates the purchase price as is. 12 | public sealed class TotalPrice : ICalculationFunction 13 | { 14 | /// 15 | public string Name => "Total Price"; 16 | 17 | decimal ICalculationFunction.Calculate(CalculableValue value) => value.Purchase.Price; 18 | 19 | IEnumerable ICalculationFunction.Update(IReadOnlyCollection points) => points; 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Products/UnitCreationView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Products; 10 | 11 | namespace Gnomeshade.Desktop.Views.Products; 12 | 13 | /// 14 | public sealed partial class UnitCreationView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public UnitCreationView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Tests.Integration.Oidc/WebserverSetup.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | 7 | [assembly: Parallelizable(ParallelScope.Children)] 8 | 9 | namespace Gnomeshade.WebApi.Tests.Integration.Oidc; 10 | 11 | [SetUpFixture] 12 | public static class WebserverSetup 13 | { 14 | internal static readonly KeycloakFixture KeycloakFixture = new(); 15 | 16 | [OneTimeSetUp] 17 | public static async Task OneTimeSetUp() 18 | { 19 | await KeycloakFixture.Initialize(); 20 | } 21 | 22 | [OneTimeTearDown] 23 | public static async Task OneTimeTearDown() 24 | { 25 | await KeycloakFixture.DisposeAsync(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Configuration/PreferencesView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Configuration; 10 | 11 | namespace Gnomeshade.Desktop.Views.Configuration; 12 | 13 | /// 14 | public sealed partial class PreferencesView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public PreferencesView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | #if DEBUG 6 | using Avalonia; 7 | #endif 8 | using Avalonia.Controls; 9 | using Avalonia.Markup.Xaml; 10 | 11 | using Gnomeshade.Avalonia.Core; 12 | 13 | namespace Gnomeshade.Desktop.Views; 14 | 15 | /// 16 | public sealed partial class MainWindow : Window, IView 17 | { 18 | /// Initializes a new instance of the class. 19 | public MainWindow() 20 | { 21 | AvaloniaXamlLoader.Load(this); 22 | 23 | #if DEBUG 24 | this.AttachDevTools(); 25 | #endif 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Projects/ProjectUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Projects; 10 | 11 | namespace Gnomeshade.Desktop.Views.Projects; 12 | 13 | /// 14 | public sealed partial class ProjectUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public ProjectUpsertionView() => AvaloniaXamlLoader.Load(this); 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Reports/BalanceReportView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Reports; 10 | 11 | namespace Gnomeshade.Desktop.Views.Reports; 12 | 13 | /// 14 | public sealed partial class BalanceReportView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public BalanceReportView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Reports/ProductReportView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Reports; 10 | 11 | namespace Gnomeshade.Desktop.Views.Reports; 12 | 13 | /// 14 | public sealed partial class ProductReportView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public ProductReportView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/TransactionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions; 12 | 13 | /// 14 | public sealed partial class TransactionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public TransactionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Client.Tests/RoutesTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using NodaTime; 8 | 9 | namespace Gnomeshade.WebApi.Client.Tests; 10 | 11 | public class RoutesTests 12 | { 13 | [Test] 14 | public void AccountUri_ShouldFormatGuidWithoutSeparators() 15 | { 16 | Routes.Accounts.IdUri(Guid.Empty).Should().Be("v1.0/Accounts/00000000000000000000000000000000"); 17 | } 18 | 19 | [TestCaseSource(typeof(TransactionUriTestCaseSource))] 20 | public void TransactionUri_ShouldReturnExpected(Interval interval, string expectedUri) 21 | { 22 | Routes.Transactions.DateRangeUri(interval).Should().Be(expectedUri); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Entities/Abstractions/Entity.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using NodaTime; 8 | 9 | namespace Gnomeshade.Data.Entities.Abstractions; 10 | 11 | /// Base class for all entities. 12 | public abstract record Entity : IEntity 13 | { 14 | /// 15 | public Guid Id { get; init; } 16 | 17 | /// 18 | public Instant CreatedAt { get; init; } 19 | 20 | /// 21 | public Guid CreatedByUserId { get; init; } 22 | 23 | /// 24 | public Instant? DeletedAt { get; set; } 25 | 26 | /// 27 | public Guid? DeletedByUserId { get; set; } 28 | } 29 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/LoanPayment/Delete.sql: -------------------------------------------------------------------------------- 1 | WITH accessable AS 2 | (SELECT loan_payments.id 3 | FROM loan_payments 4 | INNER JOIN loans2 ON loan_payments.loan_id = loans2.id 5 | LEFT JOIN owners ON loans2.owner_id = owners.id 6 | LEFT JOIN ownerships ON owners.id = ownerships.owner_id 7 | LEFT JOIN access ON ownerships.access_id = access.id 8 | WHERE (ownerships.user_id = @userId 9 | AND (access.normalized_name = 'DELETE' OR access.normalized_name = 'OWNER') 10 | AND loans2.deleted_at IS NULL) 11 | AND loan_payments.deleted_at IS NULL 12 | AND loan_payments.id = @id) 13 | 14 | UPDATE loan_payments 15 | SET deleted_at = CURRENT_TIMESTAMP, 16 | deleted_by_user_id = @userId 17 | FROM accessable 18 | WHERE loan_payments.id IN (SELECT id FROM accessable); 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Reports/CategoryReportView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Reports; 10 | 11 | namespace Gnomeshade.Desktop.Views.Reports; 12 | 13 | /// 14 | public sealed partial class CategoryReportView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public CategoryReportView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Project/Update.sql: -------------------------------------------------------------------------------- 1 | WITH accessable AS 2 | (SELECT projects.id 3 | FROM projects 4 | INNER JOIN owners ON owners.id = projects.owner_id 5 | INNER JOIN ownerships ON owners.id = ownerships.owner_id 6 | INNER JOIN access ON access.id = ownerships.access_id 7 | WHERE ownerships.user_id = @ModifiedByUserId 8 | AND (access.normalized_name = 'WRITE' OR access.normalized_name = 'OWNER') 9 | AND projects.deleted_at IS NULL 10 | AND projects.id = @Id) 11 | 12 | UPDATE projects 13 | SET modified_at = CURRENT_TIMESTAMP, 14 | modified_by_user_id = @ModifiedByUserId, 15 | name = @Name, 16 | normalized_name = upper(@Name), 17 | parent_project_id = @ParentProjectId 18 | FROM accessable 19 | WHERE projects.id IN (SELECT id FROM accessable); 20 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Transfer/Insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO transfers 2 | (id, 3 | owner_id, 4 | created_by_user_id, 5 | modified_by_user_id, 6 | transaction_id, 7 | source_amount, 8 | source_account_id, 9 | target_amount, 10 | target_account_id, 11 | bank_reference, 12 | external_reference, 13 | internal_reference, 14 | "order", 15 | booked_at, 16 | valued_at) 17 | VALUES 18 | (@Id, 19 | @OwnerId, 20 | @CreatedByUserId, 21 | @ModifiedByUserId, 22 | @TransactionId, 23 | @SourceAmount, 24 | @SourceAccountId, 25 | @TargetAmount, 26 | @TargetAccountId, 27 | @BankReference, 28 | @ExternalReference, 29 | @InternalReference, 30 | @Order, 31 | @BookedAt, 32 | @ValuedAt) 33 | RETURNING id; 34 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Accesses/OwnerUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Accesses; 10 | 11 | namespace Gnomeshade.Desktop.Views.Accesses; 12 | 13 | /// 14 | public sealed partial class OwnerUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public OwnerUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Counterparties/CounterpartyView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Counterparties; 10 | 11 | namespace Gnomeshade.Desktop.Views.Counterparties; 12 | 13 | /// 14 | public sealed partial class CounterpartyView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public CounterpartyView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Purchases/PurchaseView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Purchases; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Purchases; 12 | 13 | /// 14 | public sealed partial class PurchaseView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public PurchaseView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Transfers/TransferView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Transfers; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Transfers; 12 | 13 | /// 14 | public sealed partial class TransferView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public TransferView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/LocalDateTimeConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Globalization; 6 | 7 | using NodaTime; 8 | using NodaTime.Text; 9 | 10 | namespace Gnomeshade.Avalonia.Core; 11 | 12 | /// Converts a binding value of type . 13 | public sealed class LocalDateTimeConverter : NodaTimeValueConverter 14 | { 15 | /// 16 | protected override LocalDateTime TemplateValue { get; } = new(2000, 12, 31, 13, 20); 17 | 18 | /// 19 | protected override LocalDateTimePattern GetPattern(CultureInfo culture) => 20 | LocalDateTimePattern.Create("g", culture); 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Repositories/Queries/Ownership/Delete.sql: -------------------------------------------------------------------------------- 1 | WITH accessable AS 2 | (SELECT ownerships.id AS Id, 3 | ownerships.created_at CreatedAt, 4 | ownerships.created_by_user_id AS CreatedByUserId, 5 | ownerships.owner_id OwnerId, 6 | ownerships.user_id UserId, 7 | ownerships.access_id AccessId 8 | FROM ownerships ownerships 9 | INNER JOIN owners on owners.id = ownerships.owner_id 10 | INNER JOIN ownerships o on o.owner_id = owners.id 11 | INNER JOIN access on access.id = o.access_id 12 | WHERE (o.user_id = @userId AND (access.normalized_name = 'DELETE' OR access.normalized_name = 'OWNER')) 13 | AND ownerships.id = @id) 14 | 15 | DELETE 16 | FROM ownerships 17 | WHERE ownerships.id in (SELECT id from accessable); 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/NewRequisition.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.WebApi.Client; 8 | 9 | /// User approval is needed in order to access the account. 10 | public sealed class NewRequisition : ImportResult 11 | { 12 | /// Initializes a new instance of the class. 13 | /// The URI for the approval page. 14 | public NewRequisition(Uri requisitionUri) 15 | { 16 | RequisitionUri = requisitionUri; 17 | } 18 | 19 | /// Gets the URI for the approval page. 20 | public Uri RequisitionUri { get; } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Accounts/AccountUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Accounts; 10 | 11 | namespace Gnomeshade.Desktop.Views.Accounts; 12 | 13 | /// 14 | public sealed partial class AccountUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public AccountUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Products/ProductUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Products; 10 | 11 | namespace Gnomeshade.Desktop.Views.Products; 12 | 13 | /// 14 | public sealed partial class ProductUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public ProductUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Accounts/Balance.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.WebApi.Models.Accounts; 8 | 9 | /// Account balance in a single currency. 10 | public sealed record Balance 11 | { 12 | /// The id of the for which the balance was calculated for. 13 | public Guid AccountInCurrencyId { get; set; } 14 | 15 | /// The total amount withdrawn from the account. 16 | public decimal SourceAmount { get; set; } 17 | 18 | /// The total amount deposited to the account. 19 | public decimal TargetAmount { get; set; } 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Entities/Abstractions/IModifiableEntity.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using NodaTime; 8 | 9 | namespace Gnomeshade.Data.Entities.Abstractions; 10 | 11 | /// Represents an entity that can be modified. 12 | /// 13 | public interface IModifiableEntity : IEntity 14 | { 15 | /// Gets or sets the timestamp of the last modification of this entity. 16 | Instant ModifiedAt { get; set; } 17 | 18 | /// Gets or sets the id of the user which last modified this entity. 19 | /// 20 | public Guid ModifiedByUserId { get; set; } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/Results/RequiresRegistration.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.WebApi.Client.Results; 8 | 9 | /// User is not registered. 10 | public sealed class RequiresRegistration : ExternalLoginResult 11 | { 12 | /// Initializes a new instance of the class. 13 | /// The uri the the registration page. 14 | public RequiresRegistration(Uri redirectUri) 15 | { 16 | RedirectUri = redirectUri; 17 | } 18 | 19 | /// Gets the uri the the registration page. 20 | public Uri RedirectUri { get; } 21 | } 22 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | gnomeshade: 3 | image: ghcr.io/vmelnalksnis/gnomeshade:0.8.7 4 | restart: unless-stopped 5 | ports: 6 | - "8080:8080" 7 | volumes: 8 | - gnomeshade_data:/data 9 | environment: 10 | Admin__Password: "Password1!" 11 | Database__Provider: "PostgreSQL" 12 | ConnectionStrings__Gnomeshade: "Server = database; Port = 5432; Database = database; User Id = postgres; Password = Password2!; Maximum Pool Size = 20" 13 | depends_on: 14 | database: 15 | condition: service_started 16 | 17 | database: 18 | image: postgres:15.8-bookworm 19 | restart: unless-stopped 20 | volumes: 21 | - postgresql_data:/var/lib/postgresql/data 22 | environment: 23 | POSTGRES_PASSWORD: Password2! 24 | 25 | volumes: 26 | gnomeshade_data: 27 | postgresql_data: 28 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Commands/CommandBase.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.Windows.Input; 7 | 8 | namespace Gnomeshade.Avalonia.Core.Commands; 9 | 10 | /// 11 | public abstract class CommandBase : ICommand 12 | { 13 | /// 14 | public event EventHandler? CanExecuteChanged; 15 | 16 | /// 17 | public abstract bool CanExecute(object? parameter); 18 | 19 | /// 20 | public abstract void Execute(object? parameter); 21 | 22 | /// Invokes . 23 | public void InvokeExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty); 24 | } 25 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Products/CategoryUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Products; 10 | 11 | namespace Gnomeshade.Desktop.Views.Products; 12 | 13 | /// 14 | public sealed partial class CategoryUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public CategoryUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Links/LinkUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Links; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Links; 12 | 13 | /// 14 | public sealed partial class LinkUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public LinkUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Configuration/Options/OAuthProviderOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | using Microsoft.AspNetCore.Authentication.OAuth; 8 | 9 | namespace Gnomeshade.WebApi.Configuration.Options; 10 | 11 | /// Options for configuring an OAuth provider. 12 | public sealed class OAuthProviderOptions 13 | { 14 | internal const string ProviderSectionName = "OAuth"; 15 | 16 | /// 17 | [Required] 18 | public string ClientId { get; init; } = null!; 19 | 20 | /// 21 | public string? ClientSecret { get; init; } 22 | } 23 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Entities/BalanceEntity.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.Data.Entities; 8 | 9 | /// Account balance in a single currency. 10 | public sealed class BalanceEntity 11 | { 12 | /// Gets the id of the for which the balance was calculated for. 13 | public Guid AccountInCurrencyId { get; init; } 14 | 15 | /// Gets the total amount withdrawn from the account. 16 | public decimal SourceAmount { get; init; } 17 | 18 | /// Gets the total amount deposited to the account. 19 | public decimal TargetAmount { get; init; } 20 | } 21 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Importing/Iso20022Report.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | using JetBrains.Annotations; 8 | 9 | using Microsoft.AspNetCore.Http; 10 | 11 | namespace Gnomeshade.WebApi.Models.Importing; 12 | 13 | /// An ISO20022 report. 14 | [PublicAPI] 15 | public sealed record Iso20022Report 16 | { 17 | /// The ISO20022 report content. 18 | [Required] 19 | public IFormFile Report { get; set; } = null!; 20 | 21 | /// The timezone which will be assumed for all unspecified dates in the report. 22 | [Required] 23 | public string TimeZone { get; set; } = null!; 24 | } 25 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Diagnostics; 6 | 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.AspNetCore.Mvc.RazorPages; 10 | 11 | namespace Gnomeshade.WebApi.Areas.Identity.Pages; 12 | 13 | [AllowAnonymous] 14 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 15 | public sealed class Error : PageModel 16 | { 17 | public string? RequestId { get; set; } 18 | 19 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 20 | 21 | public void OnGet() => RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 22 | } 23 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Entities/UserEntity.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using Gnomeshade.Data.Entities.Abstractions; 8 | 9 | using NodaTime; 10 | 11 | namespace Gnomeshade.Data.Entities; 12 | 13 | /// A user within the context of this application. 14 | public sealed record UserEntity : Entity, IModifiableEntity 15 | { 16 | /// 17 | public Instant ModifiedAt { get; set; } 18 | 19 | /// 20 | public Guid ModifiedByUserId { get; set; } 21 | 22 | /// Gets or sets the id of the which represents this user in transactions. 23 | public Guid CounterpartyId { get; set; } 24 | } 25 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Disable2Fa 3 | @{ 4 | ViewData["Title"] = "Disable two-factor authentication (2FA)"; 5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; 6 | } 7 | 8 | 9 |

@ViewData["Title"]

10 | 11 | 20 | 21 |
22 |
23 | 24 |
25 |
26 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Gnomeshade.WebApi.Areas.Identity.Pages.Account.Manage; 9 | 10 | public sealed class ShowRecoveryCodes : PageModel 11 | { 12 | [TempData] 13 | public string[]? RecoveryCodes { get; set; } 14 | 15 | [TempData] 16 | public string? StatusMessage { get; set; } 17 | 18 | public IActionResult OnGet() 19 | { 20 | if (RecoveryCodes is null || RecoveryCodes.Length == 0) 21 | { 22 | return RedirectToPage("./TwoFactorAuthentication"); 23 | } 24 | 25 | return Page(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Configuration/Options/AdminOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | namespace Gnomeshade.WebApi.Configuration.Options; 8 | 9 | /// Options for configuring the initial admin user. 10 | public sealed class AdminOptions 11 | { 12 | internal const string SectionName = "Admin"; 13 | 14 | /// Gets or sets the name of the initial admin user. Defaults to 'Admin'. 15 | [Required] 16 | public string Username { get; set; } = "Admin"; 17 | 18 | /// Gets or sets the password of the initial admin user. 19 | [Required] 20 | public string Password { get; set; } = null!; 21 | } 22 | -------------------------------------------------------------------------------- /tests/Gnomeshade.WebApi.Tests/V1/Transactions/OptionalTimeRangeTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | using Gnomeshade.WebApi.V1.Transactions; 8 | 9 | namespace Gnomeshade.WebApi.Tests.V1.Transactions; 10 | 11 | public class OptionalTimeRangeTests 12 | { 13 | [TestCaseSource(typeof(ValidateTestCaseSource))] 14 | public void Validate_ShouldReturnExpected( 15 | OptionalTimeRange optionalTimeRange, 16 | int expectedResultCount) 17 | { 18 | var validationContext = new ValidationContext(optionalTimeRange); 19 | 20 | optionalTimeRange 21 | .Validate(validationContext) 22 | .Should() 23 | .HaveCount(expectedResultCount); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Counterparties/CounterpartyMergeView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Counterparties; 10 | 11 | namespace Gnomeshade.Desktop.Views.Counterparties; 12 | 13 | /// 14 | public sealed partial class CounterpartyMergeView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public CounterpartyMergeView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Loans/LoanUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Loans; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Loans; 12 | 13 | /// 14 | public sealed partial class LoanUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public LoanUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/V1/Importing/Paperless/IPaperlessDocumentParser.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | 7 | using VMelnalksnis.PaperlessDotNet.Documents; 8 | 9 | namespace Gnomeshade.WebApi.V1.Importing.Paperless; 10 | 11 | /// Parses information from Paperless s. 12 | public interface IPaperlessDocumentParser 13 | { 14 | /// Parses purchase texts from a paperless . 15 | /// The document from which to parse purchases. 16 | /// All purchases from . 17 | List ParsePurchases(Document document); 18 | } 19 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Controls/TransactionFilterView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Controls; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Controls; 12 | 13 | /// 14 | public sealed partial class TransactionFilterView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public TransactionFilterView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Controls/TransactionSummaryView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Controls; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Controls; 12 | 13 | /// 14 | public sealed partial class TransactionSummaryView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public TransactionSummaryView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Configuration/ApplicationSettingsView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Configuration; 10 | 11 | namespace Gnomeshade.Desktop.Views.Configuration; 12 | 13 | /// 14 | public sealed partial class ApplicationSettingsView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public ApplicationSettingsView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Configuration/ConfigurationWizardView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Configuration; 10 | 11 | namespace Gnomeshade.Desktop.Views.Configuration; 12 | 13 | /// 14 | public sealed partial class ConfigurationWizardView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public ConfigurationWizardView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Authentication/IGnomeshadeProtocolHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Gnomeshade.Avalonia.Core.Authentication; 9 | 10 | /// Handles requests made in the gnomeshade protocol. 11 | public interface IGnomeshadeProtocolHandler 12 | { 13 | /// Gets the content of a gnomeshade protocol request. 14 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. 15 | /// The request content. 16 | Task GetRequestContent(CancellationToken cancellationToken = default); 17 | } 18 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/TransactionUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions; 12 | 13 | /// 14 | public sealed partial class TransactionUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public TransactionUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Client/RefreshTokenChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | namespace Gnomeshade.WebApi.Client; 8 | 9 | /// for the event. 10 | public sealed class RefreshTokenChangedEventArgs : EventArgs 11 | { 12 | /// Initializes a new instance of the class. 13 | /// The new refresh token value. 14 | public RefreshTokenChangedEventArgs(string token) 15 | { 16 | Token = token; 17 | } 18 | 19 | /// Gets the new refresh token value. 20 | public string Token { get; } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi.Models/Owners/Ownership.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using JetBrains.Annotations; 8 | 9 | namespace Gnomeshade.WebApi.Models.Owners; 10 | 11 | /// Access rights for a user to a group of resources. 12 | [PublicAPI] 13 | public sealed record Ownership 14 | { 15 | /// The id of the ownership. 16 | public Guid Id { get; set; } 17 | 18 | /// The id of the owner. 19 | public Guid OwnerId { get; set; } 20 | 21 | /// The id of the user that has the access. 22 | public Guid UserId { get; set; } 23 | 24 | /// The id of the access level. 25 | public Guid AccessId { get; set; } 26 | } 27 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ShowRecoveryCodes 3 | @{ 4 | ViewData["Title"] = "Recovery codes"; 5 | ViewData["ActivePage"] = "TwoFactorAuthentication"; 6 | } 7 | 8 | 9 |

@ViewData["Title"]

10 | 18 |
19 |
20 | @for (var row = 0; row < (Model.RecoveryCodes?.Length ?? 0); row += 2) 21 | { 22 | @Model.RecoveryCodes?[row] 23 |   24 | @Model.RecoveryCodes?[row + 1] 25 |
26 | } 27 |
28 |
29 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Purchases/PurchaseUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Purchases; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Purchases; 12 | 13 | /// 14 | public sealed partial class PurchaseUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public PurchaseUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Transfers/TransferUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Transfers; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Transfers; 12 | 13 | /// 14 | public sealed partial class TransferUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public TransferUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Counterparties/CounterpartyUpsertionView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Counterparties; 10 | 11 | namespace Gnomeshade.Desktop.Views.Counterparties; 12 | 13 | /// 14 | public sealed partial class CounterpartyUpsertionView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public CounterpartyUpsertionView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Transactions/Controls/TransactionPropertiesView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Transactions.Controls; 10 | 11 | namespace Gnomeshade.Desktop.Views.Transactions.Controls; 12 | 13 | /// 14 | public sealed partial class TransactionPropertiesView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public TransactionPropertiesView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.WebApi/V1/Importing/ImportableTransaction.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using NodaTime; 6 | 7 | using VMelnalksnis.ISO20022DotNet.MessageSets.BankToCustomerCashManagement.V2.AccountReport; 8 | 9 | namespace Gnomeshade.WebApi.V1.Importing; 10 | 11 | internal sealed record ImportableTransaction( 12 | string? BankReference, 13 | string? ExternalReference, 14 | decimal Amount, 15 | string CurrencyCode, 16 | CreditDebitCode CreditDebitCode, 17 | Instant BookingDate, 18 | Instant? ValueDate, 19 | string? Description, 20 | string OtherCurrencyCode, 21 | decimal OtherAmount, 22 | string? OtherAccountIban, 23 | string? OtherAccountName, 24 | string? DomainCode, 25 | string? FamilyCode, 26 | string? SubFamilyCode); 27 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Counterparties/CounterpartyComparer.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | 9 | namespace Gnomeshade.Avalonia.Core.Counterparties; 10 | 11 | internal sealed class CounterpartyComparer : IComparer, IComparer 12 | { 13 | private static readonly StringComparer _comparer = StringComparer.Ordinal; 14 | 15 | /// 16 | public int Compare(object? x, object? y) 17 | { 18 | return Compare(x as CounterpartyRow, y as CounterpartyRow); 19 | } 20 | 21 | /// 22 | public int Compare(CounterpartyRow? x, CounterpartyRow? y) 23 | { 24 | return _comparer.Compare(x?.Name, y?.Name); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop/Views/Configuration/GnomeshadeConfigurationView.axaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | using Gnomeshade.Avalonia.Core; 9 | using Gnomeshade.Avalonia.Core.Configuration; 10 | 11 | namespace Gnomeshade.Desktop.Views.Configuration; 12 | 13 | /// 14 | public sealed partial class GnomeshadeConfigurationView : UserControl, IView 15 | { 16 | /// Initializes a new instance of the class. 17 | public GnomeshadeConfigurationView() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/Gnomeshade.Avalonia.Core/Reports/Calculations/PricePerUnit.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | 7 | using LiveChartsCore.Defaults; 8 | 9 | namespace Gnomeshade.Avalonia.Core.Reports.Calculations; 10 | 11 | /// Normalizes purchase price by amount of base unit. 12 | public sealed class PricePerUnit : ICalculationFunction 13 | { 14 | /// 15 | public string Name => "Price Per Unit"; 16 | 17 | decimal ICalculationFunction.Calculate(CalculableValue value) 18 | { 19 | return value.Purchase.Price / (value.Purchase.Amount * value.Multiplier); 20 | } 21 | 22 | IEnumerable ICalculationFunction.Update(IReadOnlyCollection points) => points; 23 | } 24 | -------------------------------------------------------------------------------- /source/Gnomeshade.Data/Identity/ApplicationRole.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Valters Melnalksnis 2 | // Licensed under the GNU Affero General Public License v3.0 or later. 3 | // See LICENSE.txt file in the project root for full license information. 4 | 5 | using System; 6 | 7 | using Microsoft.AspNetCore.Identity; 8 | 9 | namespace Gnomeshade.Data.Identity; 10 | 11 | /// Application identity role. 12 | public sealed class ApplicationRole : IdentityRole 13 | { 14 | /// Initializes a new instance of the class. 15 | public ApplicationRole() 16 | { 17 | Id = Guid.NewGuid(); 18 | } 19 | 20 | /// Initializes a new instance of the class. 21 | /// The role name. 22 | public ApplicationRole(string roleName) 23 | : this() 24 | { 25 | Name = roleName; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/Gnomeshade.Desktop.Installer/Gnomeshade.Desktop.Installer.wixproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | ICE38; ICE91 4 | 0204 5 | 6 | 7 | 8 | Debug 9 | True 10 | 11 | 12 | 13 | 14 | Gnomeshade.Desktop 15 | {d2ed2f80-3638-405e-baa0-2788a47d853d} 16 | True 17 | True 18 | Binaries;Content;Satellites 19 | INSTALLFOLDER 20 | 21 | 22 | 23 | --------------------------------------------------------------------------------