Skip to content

Commit f9a75e0

Browse files
committed
Fix installed data path and refresh user documentation
1 parent 5e73a62 commit f9a75e0

8 files changed

Lines changed: 253 additions & 51 deletions

File tree

BrassLedger.Infrastructure.Tests/WorkspaceInitializationTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,27 @@ public async Task InitializeBrassLedgerAsync_CreatesSqliteDatabaseInAppData()
5252
Assert.True(File.Exists(databasePath));
5353
}
5454

55+
[Fact]
56+
public async Task InitializeBrassLedgerAsync_UsesConfiguredDataRootWhenProvided()
57+
{
58+
var configuredDataRoot = Path.Combine(_contentRootPath, "CustomDataRoot");
59+
var configuration = new ConfigurationBuilder()
60+
.AddInMemoryCollection(new Dictionary<string, string?>
61+
{
62+
["Storage:DataRoot"] = configuredDataRoot
63+
})
64+
.Build();
65+
66+
var serviceCollection = new ServiceCollection();
67+
serviceCollection.AddBrassLedgerInfrastructure(configuration, _contentRootPath, seedSampleData: true);
68+
69+
using var services = serviceCollection.BuildServiceProvider();
70+
await services.InitializeBrassLedgerAsync();
71+
72+
Assert.True(File.Exists(Path.Combine(configuredDataRoot, "brassledger.db")));
73+
Assert.True(Directory.Exists(Path.Combine(configuredDataRoot, "keys")));
74+
}
75+
5576
[Fact]
5677
public async Task InitializeBrassLedgerAsync_SeedsAuthenticationCredentials()
5778
{

BrassLedger.Infrastructure/Persistence/ServiceCollectionExtensions.cs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public static IServiceCollection AddBrassLedgerInfrastructure(
2323
string contentRootPath,
2424
bool seedSampleData = false)
2525
{
26-
var dataDirectory = BuildDataDirectory(contentRootPath);
26+
var dataDirectory = BuildDataDirectory(configuration, contentRootPath);
2727
var keysDirectory = Path.Combine(dataDirectory, "keys");
2828
Directory.CreateDirectory(keysDirectory);
2929

@@ -84,11 +84,64 @@ private static BootstrapOptions BuildBootstrapOptions(IConfiguration configurati
8484
return options;
8585
}
8686

87-
private static string BuildDataDirectory(string contentRootPath)
87+
private static string BuildDataDirectory(IConfiguration configuration, string contentRootPath)
8888
{
89-
var dataDirectory = Path.Combine(contentRootPath, "App_Data");
90-
Directory.CreateDirectory(dataDirectory);
91-
return dataDirectory;
89+
var configuredDataRoot =
90+
configuration["Storage:DataRoot"]
91+
?? configuration["BrassLedger:DataRoot"];
92+
93+
if (!string.IsNullOrWhiteSpace(configuredDataRoot))
94+
{
95+
var explicitDataDirectory = Path.GetFullPath(Environment.ExpandEnvironmentVariables(configuredDataRoot));
96+
Directory.CreateDirectory(explicitDataDirectory);
97+
return explicitDataDirectory;
98+
}
99+
100+
var contentRootDataDirectory = Path.Combine(contentRootPath, "App_Data");
101+
if (TryEnsureWritableDirectory(contentRootDataDirectory))
102+
{
103+
return contentRootDataDirectory;
104+
}
105+
106+
var localApplicationDataRoot = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
107+
if (string.IsNullOrWhiteSpace(localApplicationDataRoot))
108+
{
109+
localApplicationDataRoot = Path.GetTempPath();
110+
}
111+
112+
var fallbackDataDirectory = Path.Combine(localApplicationDataRoot, "BrassLedger", "App_Data");
113+
Directory.CreateDirectory(fallbackDataDirectory);
114+
return fallbackDataDirectory;
115+
}
116+
117+
private static bool TryEnsureWritableDirectory(string directoryPath)
118+
{
119+
try
120+
{
121+
Directory.CreateDirectory(directoryPath);
122+
123+
var probeFilePath = Path.Combine(directoryPath, $".write-test-{Guid.NewGuid():N}.tmp");
124+
using var probeStream = new FileStream(
125+
probeFilePath,
126+
FileMode.CreateNew,
127+
FileAccess.Write,
128+
FileShare.None,
129+
bufferSize: 1,
130+
options: FileOptions.DeleteOnClose);
131+
132+
probeStream.WriteByte(0);
133+
probeStream.Flush();
134+
135+
return true;
136+
}
137+
catch (UnauthorizedAccessException)
138+
{
139+
return false;
140+
}
141+
catch (IOException)
142+
{
143+
return false;
144+
}
92145
}
93146

94147
private static string BuildDefaultSqliteConnectionString(string dataDirectory)

BrassLedger.Web/Components/Pages/HelpAdministration.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<article class="panel">
3030
<h2>Data and source control</h2>
3131
<ul class="feature-list">
32-
<li>Local fallback data in <code>App_Data</code> stays out of Git.</li>
32+
<li>Local application data and fallback SQLite storage stay out of Git.</li>
3333
<li>Source-controlled static assets belong under <code>BrassLedger.Web/wwwroot</code>.</li>
3434
<li>Publish output under <code>artifacts</code> is disposable and should be regenerated when needed.</li>
3535
<li>Package builds as releases rather than committing generated files to the main repository.</li>

BrassLedger.Web/Components/Pages/Publish.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
<li>Windows produces an `.exe` plus supporting publish output.</li>
5050
<li>Linux and macOS builds publish platform-native executables rather than `.exe` files.</li>
5151
<li>The UI stays the same across platforms because it is rendered by ASP.NET Core and opened in a browser.</li>
52-
<li>Without a PostgreSQL connection string, the app boots on a local SQLite database in `App_Data`.</li>
52+
<li>Without a PostgreSQL connection string, the app boots on a local SQLite database in a writable application data directory.</li>
5353
<li>A desktop shell can be added later if you want a packaged window around this web app.</li>
5454
</ul>
5555
</article>

README.md

Lines changed: 92 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,99 @@
11
# BrassLedger
22

3-
This repository contains a compileable cross-platform .NET application shell an open-source accounting platform.
3+
BrassLedger is an open-source cross-platform accounting and business management system for organizations that need general ledger, receivables, payables, payroll, projects, reporting, tax workflows, and printable business forms in one application.
4+
5+
The current public prerelease is `v0.1.0-pre.1`.
46

57
License: `GPL-3.0-only`. See [LICENSE](LICENSE).
68

7-
Projects:
9+
## Why BrassLedger
10+
11+
BrassLedger is intended for teams that want a modern, open-source financial operations platform without locking their business into a proprietary desktop stack. The application is built to support daily accounting work, period close, payroll review, operational documents, tax-facing output, labels, checks, and other fixed-layout business forms.
12+
13+
The current end-user application is `BrassLedger.Web`, delivered as a cross-platform .NET application with installers for Windows, macOS, and Linux.
14+
15+
## What the application includes
16+
17+
- General ledger workspaces for journal activity, balances, and month-end review
18+
- Receivables support for customer invoices, statements, balances, and cash application
19+
- Payables support for vendor bills, approvals, due-date review, and disbursement preparation
20+
- Operations workspaces for inventory, order flow, purchasing, fulfillment, and operational forms
21+
- Payroll support for employee records, pay processing, liability review, and year-end forms
22+
- Project and job tracking for cost visibility and work-in-progress review
23+
- Reporting support for financial statements, checks, paychecks, labels, shipment forms, and management output
24+
- Tax workspaces for federal, state, and employer-specific tax profile review
25+
26+
## Download and quick start
27+
28+
- Quick start and installation: [docs/quick-start.md](docs/quick-start.md)
29+
- Current prerelease downloads: [GitHub Releases](https://github.com/rhamenator/BrassLedger/releases)
30+
31+
Current prerelease installers are available for:
32+
33+
- Windows `x64`
34+
- macOS Intel
35+
- macOS Apple silicon
36+
- Linux `amd64`
37+
38+
## How to use BrassLedger
39+
40+
Start with these guides:
41+
42+
- Main user guide: [docs/user-guide.md](docs/user-guide.md)
43+
- Reporting, checks, labels, forms, and print output: [docs/reporting-guide.md](docs/reporting-guide.md)
44+
- Administration, publishing, and support practices: [docs/administration-guide.md](docs/administration-guide.md)
845

9-
- `BrassLedger.Domain`: core business concepts and legacy inventory records
10-
- `BrassLedger.Application`: product catalog contracts and planning models
11-
- `BrassLedger.Infrastructure`: concrete catalog and platform service implementations
12-
- `BrassLedger.Api`: JSON endpoints for assessment and source inventories
13-
- `BrassLedger.Web`: primary end-user web application shell
46+
Module-by-module usage links:
1447

15-
The current user-facing application is `BrassLedger.Web`.
48+
- Overview and daily review: [docs/user-guide.md#overview](docs/user-guide.md#overview)
49+
- General ledger: [docs/user-guide.md#ledger](docs/user-guide.md#ledger)
50+
- Receivables: [docs/user-guide.md#receivables](docs/user-guide.md#receivables)
51+
- Payables: [docs/user-guide.md#payables](docs/user-guide.md#payables)
52+
- Operations: [docs/user-guide.md#operations](docs/user-guide.md#operations)
53+
- Payroll: [docs/user-guide.md#payroll](docs/user-guide.md#payroll)
54+
- Projects: [docs/user-guide.md#projects](docs/user-guide.md#projects)
55+
- Reporting and forms: [docs/user-guide.md#reporting-and-forms](docs/user-guide.md#reporting-and-forms)
56+
- Taxes: [docs/user-guide.md#taxes](docs/user-guide.md#taxes)
57+
- Publish workspace: [docs/user-guide.md#publish](docs/user-guide.md#publish)
58+
- Month-end review: [docs/user-guide.md#month-end-review](docs/user-guide.md#month-end-review)
59+
- Security and data handling: [docs/user-guide.md#security-and-data-handling](docs/user-guide.md#security-and-data-handling)
1660

17-
Why this shape:
61+
Specialized documentation:
1862

19-
- it publishes as a Windows `.exe`
20-
- it can also be published for Linux and macOS
21-
- it keeps the UI cross-platform without locking the product into Windows-only desktop technology
63+
- Financial statements: [docs/reporting-guide.md#financial-statements](docs/reporting-guide.md#financial-statements)
64+
- Receivables output: [docs/reporting-guide.md#receivables-output](docs/reporting-guide.md#receivables-output)
65+
- Payables output: [docs/reporting-guide.md#payables-output](docs/reporting-guide.md#payables-output)
66+
- Payroll and year-end output: [docs/reporting-guide.md#payroll-and-year-end-output](docs/reporting-guide.md#payroll-and-year-end-output)
67+
- Tax-facing output: [docs/reporting-guide.md#tax-facing-output](docs/reporting-guide.md#tax-facing-output)
68+
- Operations documents and labels: [docs/reporting-guide.md#operations-documents-and-labels](docs/reporting-guide.md#operations-documents-and-labels)
69+
- Administrative data handling: [docs/administration-guide.md#data-handling](docs/administration-guide.md#data-handling)
70+
- Administrative publishing guidance: [docs/administration-guide.md#publishing](docs/administration-guide.md#publishing)
2271

23-
Helpful commands:
72+
## Security baseline
73+
74+
The current application includes:
75+
76+
- authenticated access before accounting data loads in the web app or API
77+
- password hashing through ASP.NET Core Identity primitives
78+
- protection of sensitive fields at rest with ASP.NET Core Data Protection
79+
- persisted application key material under `App_Data\keys`
80+
- security headers in the web application and API
81+
- bootstrap administrator requirements for first non-development startup
82+
83+
Before live production use, administrators should still review operational backup, recovery, secrets management, access control, and deployment procedures.
84+
85+
## Build, test, and publish
86+
87+
Helpful maintainer commands:
2488

2589
```powershell
2690
dotnet build .\BrassLedger.slnx
2791
```
2892

93+
```powershell
94+
dotnet test .\BrassLedger.slnx -nr:false
95+
```
96+
2997
```powershell
3098
.\publish-brassledger.ps1 -Runtime win-x64
3199
```
@@ -50,36 +118,19 @@ dotnet build .\BrassLedger.slnx
50118
.\run-ui-tests.ps1 -UpdateBaselines
51119
```
52120

53-
See `docs/publish-guide.md` for platform-specific publish commands.
54-
UI visual baselines live in `BrassLedger.Web.E2E.Tests\Snapshots` so they can be reviewed and committed with the test suite.
55-
The Playwright matrix auto-detects Microsoft Edge on Windows and will include it in browser-based test runs when available.
56-
Firefox is also supported in the local Playwright browser matrix when installed.
57-
58-
Current prerelease:
59-
60-
- The current consumer-facing prerelease is `v0.1.0-pre.1`.
61-
- Release assets are published for Windows, macOS Intel, macOS Apple silicon, and Linux.
62-
- GitHub Actions workflow `.github/workflows/release-installers.yml` builds the unsigned installers and attaches them to tagged prereleases.
63-
- Installer signing is intentionally disabled for now and can be added later once certificate material is available.
64-
65-
Current security baseline:
66-
67-
- authenticated access is required before accounting data loads in the web app or API
68-
- password hashes use ASP.NET Core Identity hashing
69-
- sensitive fields are protected at rest with ASP.NET Core Data Protection
70-
- Data Protection keys are persisted under `App_Data\keys`
71-
- security headers are applied to both the web app and API
72-
- a first non-development startup requires `Bootstrap:AdminPassword` so the system creates a real administrator account instead of demo users
121+
Additional maintainer documentation:
73122

74-
Bootstrap configuration:
123+
- Platform-specific publish notes: [docs/publish-guide.md](docs/publish-guide.md)
75124

76-
- development starts with sample data automatically
77-
- non-development first run requires `Bootstrap:AdminPassword`
78-
- optional bootstrap settings include `Bootstrap:CompanyName`, `Bootstrap:LegalName`, `Bootstrap:AdminUserName`, `Bootstrap:AdminDisplayName`, and `Bootstrap:AdminEmail`
125+
Release automation is handled by `.github/workflows/release-installers.yml`, which builds installer assets for Windows, macOS Intel, macOS Apple silicon, and Linux and attaches them to tagged prereleases and releases.
79126

80-
Help and operator guidance:
127+
## Repository layout
81128

82-
- In-app help pages live under `BrassLedger.Web/Components/Pages/Help*.razor`.
83-
- Written guides live in `docs/user-guide.md`, `docs/reporting-guide.md`, and `docs/administration-guide.md`.
84-
- Source-controlled web assets belong under `BrassLedger.Web/wwwroot`; generated publish output stays under `artifacts` and remains ignored by Git.
129+
- `BrassLedger.Domain`: core business entities
130+
- `BrassLedger.Application`: application-facing contracts and models
131+
- `BrassLedger.Infrastructure`: authentication, persistence, security, and service implementations
132+
- `BrassLedger.Api`: API endpoints
133+
- `BrassLedger.Web`: end-user application
134+
- `docs`: installation, usage, reporting, administration, and publishing guides
85135

136+
Source-controlled web assets belong under `BrassLedger.Web/wwwroot`. Generated publish output stays under `artifacts` and remains ignored by Git.

docs/administration-guide.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ Before using live confidential books in production, plan for more:
2525
Use these rules consistently:
2626

2727
- the database is the source of truth for accounting data
28-
- `App_Data` is local environment data and should stay out of Git
28+
- local SQLite data and key material belong in a writable application data directory and should stay out of Git
29+
- `Storage:DataRoot` can be set explicitly if you need to control where local application data is stored
2930
- `BrassLedger.Web/wwwroot` is the source location for committed static assets
3031
- `artifacts` is a publish output folder and should be disposable
3132

@@ -55,7 +56,7 @@ Ignore:
5556
- `artifacts`
5657
- `bin`
5758
- `obj`
58-
- `App_Data`
59+
- local application data directories such as `App_Data`
5960
- local IDE state
6061

6162
## Support checklist

docs/quick-start.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# BrassLedger Quick Start
2+
3+
This guide helps first-time users install BrassLedger, sign in, and start exploring the main work areas.
4+
5+
## Download an installer
6+
7+
Get the installer that matches your operating system from the current prerelease on GitHub Releases:
8+
9+
- Windows: `BrassLedger-setup-win-x64.exe`
10+
- macOS Intel: `BrassLedger-<version>-osx-x64.pkg`
11+
- macOS Apple silicon: `BrassLedger-<version>-osx-arm64.pkg`
12+
- Linux: `brassledger_<version>_amd64.deb`
13+
14+
## Install BrassLedger
15+
16+
### Windows
17+
18+
1. Download `BrassLedger-setup-win-x64.exe`.
19+
2. Run the installer.
20+
3. Keep the default install directory unless you have a deployment reason to change it.
21+
4. Start BrassLedger from the Start menu or desktop shortcut.
22+
23+
### macOS
24+
25+
1. Download the `.pkg` for your Mac architecture.
26+
2. Open the package.
27+
3. Follow the installer prompts.
28+
4. Start BrassLedger from Applications.
29+
30+
### Linux
31+
32+
1. Download the `.deb` package.
33+
2. Install it with your package manager or:
34+
35+
```bash
36+
sudo dpkg -i brassledger_<version>_amd64.deb
37+
```
38+
39+
3. Start BrassLedger from your desktop launcher or by running `brassledger`.
40+
41+
## First launch
42+
43+
BrassLedger runs as a local web application. On first launch:
44+
45+
1. Open the sign-in page.
46+
2. Sign in with the administrator account created during setup.
47+
3. Confirm the company, tax identifier, base currency, and fiscal settings are correct.
48+
4. Review the dashboard totals before entering live work.
49+
50+
## Initial setup notes
51+
52+
For a brand-new non-development installation, the deployer must provide:
53+
54+
- `Bootstrap:AdminPassword`
55+
- optionally `Bootstrap:CompanyName`
56+
- optionally `Bootstrap:LegalName`
57+
- optionally `Bootstrap:AdminUserName`
58+
- optionally `Bootstrap:AdminDisplayName`
59+
- optionally `Bootstrap:AdminEmail`
60+
61+
## First-day walkthrough
62+
63+
1. Review `Overview`.
64+
2. Confirm the chart of accounts in `Ledger`.
65+
3. Review open customer and invoice activity in `Receivables`.
66+
4. Review vendor balances and due items in `Payables`.
67+
5. Review inventory and order flow in `Operations`.
68+
6. Review employee and payroll setup in `Payroll`.
69+
7. Review jobs in `Projects`.
70+
8. Review output options in `Reporting` and `Taxes`.
71+
72+
## Next guides
73+
74+
- Main operator guide: [user-guide.md](user-guide.md)
75+
- Reporting, checks, forms, and labels: [reporting-guide.md](reporting-guide.md)
76+
- Administration guidance: [administration-guide.md](administration-guide.md)

docs/user-guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ Current expectations:
178178

179179
- authenticated access is required before users can load accounting data
180180
- confidential data should live in the database, not copied publish folders
181-
- local fallback data under `App_Data` should not be committed to Git
181+
- local fallback data directories should not be committed to Git
182182
- static site assets should come from `BrassLedger.Web/wwwroot`
183183
- published output under `artifacts` should be treated as disposable packaging
184184

0 commit comments

Comments
 (0)