CONTRIBUTING.md
Rama main · réplica de lectura del repositorio original.
# Contributing to OpenBooks
OpenBooks welcomes accounting expertise, code, tests, documentation, design,
translations, deployment improvements, and reproducible bug reports.
The project is an accounting system before it is a collection of screens.
Changes that affect money, posting, permissions, audit evidence, or tenant data
must preserve the controls described below.
## Before starting
- Search existing issues and pull requests.
- Open an issue before a large feature, schema redesign, or behavior change so
the accounting treatment and migration path can be agreed first.
- Never include customer records, production exports, credentials, private
infrastructure details, or generated build/test output in a contribution.
- Report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
## Development setup
Requirements:
- Node.js 24 or newer
- npm
- PostgreSQL 16
- Redis 7 for queue-backed functionality
```bash
npm ci
cp .env.example .env
# Configure a disposable development database in .env.
npx tsx scripts/bootstrap.ts
npm run dev -w web
```
The application listens on `http://localhost:4780` in development.
For a packaged deployment, use the root `compose.yaml` through:
```bash
./scripts/compose-up.sh
```
Do not run integration tests, migration experiments, simulations, or destructive
maintenance commands against production or shared data.
## Financial integrity requirements
Every accounting change must preserve:
- strict organization and legal-entity isolation;
- balanced, deterministic, idempotent posting;
- exact decimal and currency handling without floating-point financial math;
- closed-period immutability and controlled correction evidence;
- explicit lifecycle transitions, approvals, permissions, and concurrency
behavior;
- complete audit evidence for material configuration and transaction changes;
- effective-dated financial configuration where changing a rule could
reinterpret history;
- domain- and API-level enforcement rather than UI-only controls;
- backward-compatible migrations and preserved tenant data; and
- one authoritative source for each policy or feature setting.
Organization feature gates belong on **Company Settings → Features**. A
module-specific page may explain effective state and link there, but it must not
persist a second gate. Child capabilities must fail closed when their parent
feature is disabled. Disabling a feature must preserve its data and audit
history.
## Code and product conventions
- Use TypeScript and keep strict type checking clean.
- Use the money helpers and decimal strings for financial values. Do not convert
financial amounts through JavaScript `number`.
- Scope tenant-owned records by `org_id`; add and test row-level security for new
tenant tables.
- Add permissions and server-side enforcement for every mutation.
- Make retries safe. External callbacks, imports, posting, and workers require
stable idempotency behavior.
- Posted corrections require explicit lineage and audit evidence.
- Add generated and handwritten migrations as required. Never edit an applied
migration to reinterpret existing databases.
- Keep user-visible copy in all seven locale catalogs: `en`, `fr`, `es`, `de`,
`pt-BR`, `zh`, and `ja`.
- Keep optional organization features in the centralized registry at
`web/lib/features.ts`.
- Add or update in-product documentation for material user workflows.
## Tests
Run the release gate before requesting review:
```bash
npm run verify:release
```
That command type-checks workspaces, runs the test suite, and creates a
production build. Database-backed tests execute when `OPENBOOKS_DB_URL` points
to a bootstrapped scratch database; they skip when no database is configured.
CI includes a canary that fails if database tests silently skip.
For browser smoke tests:
```bash
npx playwright install chromium
npx playwright test
```
New accounting behavior should include:
- nominal examples;
- boundary and rounding cases;
- invalid-state rejection;
- idempotent retry behavior;
- concurrency behavior where applicable;
- tenant-isolation checks; and
- proof that every generated journal entry balances.
### What a test in this repository must be
Tests are product code: someone reads them to learn what the system promises,
and they fail only when that promise breaks. Every test must:
- **Exercise behaviour through a real interface.** Call the function, the HTTP
route, or the database path a caller uses. Assert on what that caller observes:
return values, response status and body, rows written, journal lines posted.
- **Use an independent expected value.** Compute it by hand, take it from a
published source (a statute, a tax table, a file-format specification), or
derive it from the inputs. Never compute it with the code under test or a
copy of its formula. Asserting what a mock was told to return proves nothing.
- **Fail for one reason, and say so.** One behaviour per test. The name states
the behaviour ("posting into a closed period is refused with the period
named"), not the ticket that prompted it.
- **Mock only what you don't own.** External services, the clock and
randomness are fine to double. Doubling our own modules turns the test into a
description of today's call graph.
- **Be deterministic.** Pin the clock, and seed anything random. A test that
passes only before a calendar date, or only in one time zone, is broken.
These are not tests, and review refuses them:
- **Source-text assertions.** A regex, `includes` or `indexOf` over a `.ts`,
`.tsx`, `.sql` or `.yml` file ("the guard call exists", "the view uses
`table({`"). They break on reformatting and pass while the behaviour
regresses. `check:test-source-pins` refuses new ones. The rare file that pins
a genuine external contract (a CI or release workflow's trigger policy, a
published migration's bytes) declares it with a
`// source-pin-contract: <which contract>` header.
- **Inventory and fingerprint pins.** Asserting that a catalog has exactly N
keys, or that some content hashes to a fixed digest, so every legitimate
addition fails until someone "re-pins". Assert the rule instead: every English
message key exists in every locale with the same placeholders.
- **A regression test with no behavioural gap behind it.** When fixing a bug,
first look for an existing test of that behaviour and extend it. Add a new
test only when nothing covers the behaviour, and make it fail on the
behaviour before the fix, not on the exact bug example or the fixed line.
- **Trivial or duplicate tests.** "The function exists", constants compared
with themselves, or a second test of a case another test already covers.
## Pull requests
Keep pull requests focused. Describe:
- the user or accounting problem;
- the accounting treatment and affected invariants;
- schema and migration impact;
- permission, audit, and feature-gate impact;
- rollback or operational considerations; and
- the exact checks performed.
By contributing, you agree that your contribution is licensed under the
repository's GNU Affero General Public License v3.0 or later.