docs: correct stale product facts (arkivplan, architecture, agents, self-hosting, extensions, database map) (#1931)
Every statement was verified against the code on main before editing; the docs had drifted from the product in ways a customer or agent would act on. - public/docs/arkivplan-mall.md: product named erp-base; magic-link login; BAS 2025/2026; eu-central/eu-west region; US subprocessors for AI. Now Accounted, e-mail + password + TOTP (BankID optional), BAS 2026, eu-north-1 Stockholm, Bedrock in EU with Resend as the only US subprocessor; adds rättelselogg, Peppol inbound, skattekonto imports and the säkerhetsbackup ZIP to the räkenskapsinformation tables. - ARCHITECTURE.md: adds the inline-rättelse correction path, OAuth 2.1 and lazy MCP auth, accounted-mcp and claude-plugin, 150+ tools. - AGENTS.md: defers to CLAUDE.md instead of a drifted copy; keeps the Codex-only constraints with the Supabase project name fixed (erp-base). - README.md: drops LangChain/OpenAI (not dependencies), Node 20/22 facts, 150+ tools, adds betalfil, Peppol, skattekonto and the Claude plugin. - docs/PEPPOL_FOUNDATION.md: the two sentences denying network delivery and inbound support now describe the live Qvalia path. - docs/SELF-HOSTING.md, docs/DOCKER.md: clone URLs and directory names, Sentry DSNs are not read by the app, image pinning uses the 7-char SHA tags the workflow actually publishes (no semver tag has been cut). - docs/EXTENSIONS.md: replaces the fictional sector tree with the 19 real extensions/general directories; lib/reports/sru-encoding.ts. - .claude/rules/database.md: 680+ migrations, ~170 live tables, adds the tables and RPCs that matter since July, drops sandbox_users. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5
parent
188816652d
commit
9396e54965
+14
-12
@@ -17,15 +17,15 @@ You do **not** need Node.js, npm, or anything else installed locally. The pre-bu
|
||||
mkdir Accounted && cd Accounted
|
||||
|
||||
# Compose file + env template
|
||||
curl -fsSLO https://raw.githubusercontent.com/gnubok/gnubok/main/docker-compose.yml
|
||||
curl -fsSLO https://raw.githubusercontent.com/gnubok/gnubok/main/.env.docker.example
|
||||
curl -fsSLO https://raw.githubusercontent.com/erp-mafia/accounted/main/docker-compose.yml
|
||||
curl -fsSLO https://raw.githubusercontent.com/erp-mafia/accounted/main/.env.docker.example
|
||||
|
||||
# Cron sidecar (Dockerfile + schedule)
|
||||
mkdir -p docker
|
||||
curl -fsSL -o docker/cron.Dockerfile \
|
||||
https://raw.githubusercontent.com/gnubok/gnubok/main/docker/cron.Dockerfile
|
||||
https://raw.githubusercontent.com/erp-mafia/accounted/main/docker/cron.Dockerfile
|
||||
curl -fsSL -o docker/crontab.self-hosted \
|
||||
https://raw.githubusercontent.com/gnubok/gnubok/main/docker/crontab.self-hosted
|
||||
https://raw.githubusercontent.com/erp-mafia/accounted/main/docker/crontab.self-hosted
|
||||
```
|
||||
|
||||
### 2. Configure your environment
|
||||
@@ -146,10 +146,10 @@ NEXT_PUBLIC_APP_URL=https://gnubok.example.com
|
||||
### 3. Download the overlay + Caddyfile
|
||||
|
||||
```bash
|
||||
curl -fsSLO https://raw.githubusercontent.com/gnubok/gnubok/main/docker-compose.caddy.yml
|
||||
curl -fsSLO https://raw.githubusercontent.com/erp-mafia/accounted/main/docker-compose.caddy.yml
|
||||
mkdir -p docker
|
||||
curl -fsSL -o docker/Caddyfile \
|
||||
https://raw.githubusercontent.com/gnubok/gnubok/main/docker/Caddyfile
|
||||
https://raw.githubusercontent.com/erp-mafia/accounted/main/docker/Caddyfile
|
||||
```
|
||||
|
||||
### 4. Start with the overlay
|
||||
@@ -216,19 +216,21 @@ No env vars needed: always available.
|
||||
|
||||
## Updating
|
||||
|
||||
The default `IMAGE_TAG=latest` follows `main` and updates on every `docker compose pull`. For production, **pin to a specific release** so updates are deliberate:
|
||||
The default `IMAGE_TAG=latest` follows `main` and updates on every `docker compose pull`. For production, **pin to a specific build** so updates are deliberate. Every merge to `main` publishes the image under two tags: `latest` and the bare 7-character commit SHA (for example `3e4b5dd`), so a pin looks like:
|
||||
|
||||
```env
|
||||
# .env
|
||||
IMAGE_TAG=1.2.3
|
||||
IMAGE_TAG=3e4b5dd
|
||||
```
|
||||
|
||||
Browse available tags at https://github.com/erp-mafia/gnubok/pkgs/container/gnubok. For maximum integrity, pin by digest:
|
||||
Browse available tags at https://github.com/erp-mafia/accounted/pkgs/container/gnubok (the image name keeps the historical `gnubok` package name on purpose). For maximum integrity, pin by digest; the digest is printed in the `docker-publish` workflow run and by `docker buildx imagetools inspect ghcr.io/erp-mafia/gnubok:<sha>`:
|
||||
|
||||
```env
|
||||
IMAGE_TAG=1.2.3@sha256:abcdef...
|
||||
IMAGE_TAG=3e4b5dd@sha256:abcdef...
|
||||
```
|
||||
|
||||
Semver tags (`1.2.3`, `1.2`, `1`) are published only when a `v*.*.*` git tag is cut. No such tag exists yet, so until the first tagged release the commit SHA is the only immutable pin.
|
||||
|
||||
Apply updates:
|
||||
|
||||
```bash
|
||||
@@ -246,8 +248,8 @@ If you prefer to build locally instead of pulling the pre-built image:
|
||||
|
||||
```bash
|
||||
# Clone the repo
|
||||
git clone https://github.com/gnubok/gnubok.git
|
||||
cd Accounted
|
||||
git clone https://github.com/erp-mafia/accounted.git
|
||||
cd accounted
|
||||
cp .env.docker.example .env
|
||||
# Fill in .env
|
||||
|
||||
|
||||
+58
-89
@@ -42,12 +42,14 @@ These are configured via `extensions.config.json` and only loaded when explicitl
|
||||
|
||||
Sector extensions are tied to a specific market sector. They're only relevant to businesses operating in that sector. A restaurant owner wants "Food Cost %" but an IT consultant does not.
|
||||
|
||||
Examples:
|
||||
Examples of what a sector extension could be (design intent, none of these exist):
|
||||
- **Restaurant:** Food Cost %, Earnings Per Alcohol Liter, POS Z-Report Import, Tip Tracking
|
||||
- **Construction:** ROT Calculator, Project Cost Tracking
|
||||
- **Hotel:** RevPAR, Occupancy Tracking
|
||||
- **IT/Consulting:** Billable Hours Ratio, Project Billing Metrics
|
||||
- **E-commerce:** Shopify Order Import, Multi-channel Revenue Analytics
|
||||
- **E-commerce:** Multi-channel Revenue Analytics
|
||||
|
||||
**Current state (2026-08-26):** no sector extension has been built. Every shipped extension lives under `extensions/general/`, and `SectorSlug` in `lib/extensions/types.ts` is currently just `'general'`. The sector concept, the marketplace routes (`app/(dashboard)/extensions/[sector]/...`) and the `sector/slug` extension-id format are in place for when the first one arrives. Shopify and WooCommerce order import shipped as general extensions, not e-commerce sector ones.
|
||||
|
||||
### The Unified Model
|
||||
|
||||
@@ -55,39 +57,30 @@ Both general and sector extensions live in the same system:
|
||||
|
||||
```
|
||||
extensions/
|
||||
general/ ← General extensions (any business)
|
||||
document-extraction/
|
||||
invoice-inbox/
|
||||
mcp-server/
|
||||
skatteverket/
|
||||
push-notifications/
|
||||
enable-banking/
|
||||
calendar/
|
||||
email/
|
||||
restaurant/ ← Restaurant sector extensions
|
||||
food-cost/
|
||||
earnings-per-liter/
|
||||
pos-import/
|
||||
tip-tracking/
|
||||
construction/ ← Construction sector extensions
|
||||
rot-calculator/
|
||||
project-cost/
|
||||
hotel/ ← Hotel sector extensions
|
||||
revpar/
|
||||
occupancy/
|
||||
tech/ ← IT/Consulting sector extensions
|
||||
billable-hours/
|
||||
project-billing/
|
||||
ecommerce/ ← E-commerce sector extensions
|
||||
shopify-import/
|
||||
multichannel-revenue/
|
||||
export/ ← Export & international trade extensions
|
||||
eu-sales-list/
|
||||
intrastat/
|
||||
vat-monitor/
|
||||
currency-receivables/
|
||||
general/ ← General extensions (any business); the only sector today
|
||||
arcim-migration/ ← Systemmigration: import from Fortnox, Visma, Bokio, Björn Lundén, Briox
|
||||
bolagsverket/ ← Digital årsredovisning filing to Bolagsverket (iXBRL)
|
||||
calendar/ ← Kalender: month/week/day views
|
||||
cloud-backup/ ← Molnsynkronisering: sync the säkerhetsbackup to the user's own cloud storage
|
||||
document-extraction/ ← AI-extrahering av underlag: reads receipts and invoices, fills supplier/amount/VAT/date
|
||||
email/ ← E-post (Resend): invoices and reminders by e-mail
|
||||
enable-banking/ ← Bankintegration (PSD2): automatic bank transaction sync
|
||||
invoice-inbox/ ← Dokumentinkorg: forward supplier invoices to a unique address
|
||||
mail/ ← Brevlådor: lets Kvittojakten (receipt hunt) search the user's mailboxes
|
||||
mcp-server/ ← MCP-server (API): bookkeeping via Claude, Cursor or any MCP client
|
||||
push-notifications/ ← Push-notiser: event notifications
|
||||
shopify/ ← Shopify: paid orders and refunds into the Ordersidan
|
||||
skatteverket/ ← Skatteverket: VAT declaration submission via BankID, skattekonto
|
||||
stripe/ ← Stripe-betalningar: payment links on invoices, automatic avprickning
|
||||
tic/ ← Bolagsuppgifter: company data from public registers via TIC
|
||||
whatsapp-inbox/ ← WhatsApp-inkorg: receipts as photo or PDF to Accounted's WhatsApp number
|
||||
woocommerce/ ← WooCommerce: paid orders and refunds into the transaction inbox
|
||||
_example-branding/ ← Whitelabel branding starter (disabled by default)
|
||||
example-logger/ ← Minimal example extension (index.ts only, no manifest)
|
||||
```
|
||||
|
||||
Which of these are compiled in is decided by `extensions.config.json`; today that is everything except `bolagsverket`, `push-notifications`, `_example-branding` and `example-logger`.
|
||||
|
||||
Each extension directory contains a `manifest.json` declaring metadata, entry point, workspace component path, required env vars, and npm dependencies.
|
||||
|
||||
In the marketplace:
|
||||
@@ -283,8 +276,8 @@ type ExtensionCategory = 'accounting' | 'reports' | 'import' | 'operations'
|
||||
| Earnings Per Alcohol Liter | Restaurant | A + B (both) | Liters sold per day/week | Alcohol revenue from BAS 3001 | Calculates revenue/liter, trends over time |
|
||||
| Food Cost % | Restaurant | A (core) | None | Food purchases (4000-series), food revenue (3000-series) | Calculates food_cost/food_revenue %, trends |
|
||||
| Tip Tracking | Restaurant | B (manual) | Tip amounts per shift | Optionally reads staff cost accounts | Total tips, tips/employee, tip % of revenue |
|
||||
| POS Z-Report Import | Restaurant | B (manual) | Uploads Z-report CSV/Excel | None | Parses POS data, stores in extension, shows daily sales analytics |
|
||||
| Shopify Order Import | E-commerce | B (manual) | Uploads order export | None | Imports orders into extension, shows revenue by product, trends |
|
||||
| POS Z-Report Import (not built) | Restaurant | B (manual) | Uploads Z-report CSV/Excel | None | Parses POS data, stores in extension, shows daily sales analytics |
|
||||
| Shopify / WooCommerce (shipped, general) | General | B (manual) | Store connection | None | Pulls paid orders and refunds into `webshop_orders` for the order and transaction inboxes |
|
||||
| ROT Calculator | Construction | A + B (both) | Labor hours, material costs per job | Invoice data for customer billing | ROT deduction amounts (30% of labor, max 50k/year per customer) |
|
||||
| RevPAR | Hotel | A + B (both) | Room count and occupancy | Room revenue accounts | Revenue Per Available Room, occupancy rate |
|
||||
| Billable Hours Ratio | IT/Consulting | A + B (both) | Hours worked per project | Invoice data for billed amounts | Billable/total hours, effective hourly rate |
|
||||
@@ -297,7 +290,7 @@ type ExtensionCategory = 'accounting' | 'reports' | 'import' | 'operations'
|
||||
|
||||
```
|
||||
extensions/ ← Extension source code (opt-in via config)
|
||||
general/ ← General extensions
|
||||
general/ ← General extensions (the only sector that exists)
|
||||
invoice-inbox/
|
||||
manifest.json ← Metadata, entry point, env vars, workspace path
|
||||
index.ts ← Extension definition + logic (exports Extension)
|
||||
@@ -313,11 +306,14 @@ extensions/ ← Extension source code (opt-in via conf
|
||||
push-notifications/
|
||||
manifest.json
|
||||
index.ts
|
||||
lib/
|
||||
api-routes.ts
|
||||
notification-scheduler.ts
|
||||
notification-sender.ts
|
||||
enable-banking/
|
||||
manifest.json
|
||||
index.ts
|
||||
lib/
|
||||
components/
|
||||
email/ ← Email service extension (registers Resend impl)
|
||||
manifest.json
|
||||
index.ts
|
||||
@@ -325,45 +321,22 @@ extensions/ ← Extension source code (opt-in via conf
|
||||
calendar/
|
||||
manifest.json
|
||||
index.ts
|
||||
restaurant/ ← Restaurant sector
|
||||
food-cost/
|
||||
manifest.json
|
||||
earnings-per-liter/
|
||||
manifest.json
|
||||
pos-import/
|
||||
manifest.json
|
||||
tip-tracking/
|
||||
manifest.json
|
||||
construction/ ← Construction sector
|
||||
rot-calculator/
|
||||
manifest.json
|
||||
project-cost/
|
||||
manifest.json
|
||||
hotel/ ← Hotel sector
|
||||
revpar/
|
||||
manifest.json
|
||||
occupancy/
|
||||
manifest.json
|
||||
tech/ ← IT/Consulting sector
|
||||
billable-hours/
|
||||
manifest.json
|
||||
project-billing/
|
||||
manifest.json
|
||||
ecommerce/ ← E-commerce sector
|
||||
shopify-import/
|
||||
manifest.json
|
||||
multichannel-revenue/
|
||||
manifest.json
|
||||
export/ ← Export & international trade sector
|
||||
eu-sales-list/
|
||||
components/
|
||||
mcp-server/ ← MCP server: server.ts, tools, prompts, resources, skills, widgets
|
||||
manifest.json
|
||||
index.ts
|
||||
intrastat/
|
||||
manifest.json
|
||||
vat-monitor/
|
||||
manifest.json
|
||||
currency-receivables/
|
||||
manifest.json
|
||||
server.ts
|
||||
arcim-migration/ ← Provider migration (Fortnox, Visma, Bokio, BL, Briox)
|
||||
bolagsverket/ ← Digital årsredovisning (iXBRL)
|
||||
cloud-backup/ ← Cloud sync of the säkerhetsbackup
|
||||
mail/ ← Mailbox connections for Kvittojakten
|
||||
shopify/ ← Shopify order import (api-routes.ts, components/, lib/)
|
||||
woocommerce/ ← WooCommerce order import (api-routes.ts, components/, lib/)
|
||||
stripe/ ← Stripe payment links and avprickning
|
||||
tic/ ← Company data lookup (TIC)
|
||||
whatsapp-inbox/ ← WhatsApp receipt intake
|
||||
_example-branding/ ← Whitelabel starter, disabled by default
|
||||
example-logger/ ← Minimal example, index.ts only
|
||||
|
||||
extensions.config.json ← Which extensions are enabled (empty = core-only)
|
||||
extensions.schema.json ← JSON Schema for extensions.config.json
|
||||
@@ -384,7 +357,7 @@ lib/
|
||||
email/
|
||||
service.ts ← EmailService interface + no-op default + getEmailService()
|
||||
reports/
|
||||
sru-export/ ← SRU file export (core, not an extension)
|
||||
sru-encoding.ts ← SRU file encoding (core, not an extension)
|
||||
ne-bilaga/ ← NE tax form attachment (core, not an extension)
|
||||
|
||||
scripts/
|
||||
@@ -403,22 +376,18 @@ components/
|
||||
DateRangeFilter.tsx
|
||||
EmptyExtensionState.tsx
|
||||
ExtensionLoadingSkeleton.tsx
|
||||
general/ ← General extension workspaces
|
||||
general/ ← General extension workspaces (the only ones that exist)
|
||||
ReceiptOcrWorkspace.tsx
|
||||
AiCategorizationWorkspace.tsx
|
||||
AiChatWorkspace.tsx
|
||||
restaurant/ ← Restaurant extension workspaces
|
||||
EarningsPerLiterWorkspace.tsx
|
||||
FoodCostWorkspace.tsx
|
||||
PosImportWorkspace.tsx
|
||||
construction/
|
||||
RotCalculatorWorkspace.tsx
|
||||
hotel/
|
||||
RevparWorkspace.tsx
|
||||
tech/
|
||||
BillableHoursWorkspace.tsx
|
||||
ecommerce/
|
||||
ShopifyImportWorkspace.tsx
|
||||
InvoiceInboxWorkspace.tsx
|
||||
EnableBankingWorkspace.tsx
|
||||
CalendarWorkspace.tsx
|
||||
CloudBackupWorkspace.tsx
|
||||
ArcimMigrationWorkspace.tsx
|
||||
PushNotificationsWorkspace.tsx
|
||||
TicWorkspace.tsx
|
||||
MailConnectionsPanel.tsx
|
||||
WhatsAppLinkPanel.tsx
|
||||
|
||||
app/(dashboard)/
|
||||
extensions/ ← Marketplace
|
||||
|
||||
@@ -5,7 +5,7 @@ Issue #546 requires two separable capabilities:
|
||||
1. Produce a correctly structured Peppol BIS Billing 3 invoice from Accounted data.
|
||||
2. Deliver and receive documents through the Peppol network.
|
||||
|
||||
The first slice implemented the invoice profile. The second slice adds an immutable staged-delivery and audit foundation. Neither slice claims network delivery.
|
||||
The first slice implemented the invoice profile. The second slice added an immutable staged-delivery and audit foundation. Neither of those two slices claimed network delivery; since 2026-08-21 both sending and receiving run over the Peppol network through Qvalia (see the Qvalia and Receiving (PR2) sections below). The sections up to "Access point: Qvalia" describe the foundation those slices left behind and the requirements the Qvalia work had to meet.
|
||||
|
||||
## Implemented scope
|
||||
|
||||
@@ -74,7 +74,7 @@ No invoice status should change merely because XML was generated or accepted by
|
||||
|
||||
### Receiving
|
||||
|
||||
Inbound invoices are a separate acceptance slice. It requires provider webhook authentication, raw XML retention, duplicate detection, supplier matching, safe attachment handling, and mapping into the supplier-invoice inbox without treating received content as trusted. Nothing in this foundation claims inbound support.
|
||||
Inbound invoices are a separate acceptance slice. It requires provider webhook authentication, raw XML retention, duplicate detection, supplier matching, safe attachment handling, and mapping into the supplier-invoice inbox without treating received content as trusted. The original foundation did not claim inbound support; that slice has since shipped (`peppol_registrations`, `peppol_inbound_documents`, the inbound cron and inbox delivery) and is described under "Receiving (PR2)" below.
|
||||
|
||||
### UI and API
|
||||
|
||||
|
||||
+7
-12
@@ -65,8 +65,8 @@ These are all available on Supabase hosted. `pg_cron` requires a paid plan: if y
|
||||
**Option A: Setup script (recommended):**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/erp-mafia/gnubok.git
|
||||
cd Accounted
|
||||
git clone https://github.com/erp-mafia/accounted.git
|
||||
cd accounted
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
@@ -75,8 +75,8 @@ The script checks prerequisites, prompts for your Supabase credentials, auto-gen
|
||||
**Option B: Manual:**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/erp-mafia/gnubok.git
|
||||
cd Accounted
|
||||
git clone https://github.com/erp-mafia/accounted.git
|
||||
cd accounted
|
||||
cp .env.docker.example .env
|
||||
```
|
||||
|
||||
@@ -313,14 +313,9 @@ VAPID_SUBJECT=mailto:you@example.com
|
||||
|
||||
Generate VAPID keys with `npx web-push generate-vapid-keys`. Push notifications require HTTPS.
|
||||
|
||||
### Error Tracking (Sentry)
|
||||
### Error Tracking
|
||||
|
||||
```bash
|
||||
SENTRY_DSN=https://...@sentry.io/...
|
||||
NEXT_PUBLIC_SENTRY_DSN=https://...@sentry.io/...
|
||||
```
|
||||
|
||||
Sentry is disabled if these are not set. No errors are thrown.
|
||||
There is no Sentry integration. `SENTRY_DSN` and `NEXT_PUBLIC_SENTRY_DSN` are not read by the app: setting them changes nothing. Error-level events go to the container logs (structured JSON on stdout/stderr); `lib/observability/sink.ts` is a provider-agnostic seam that stays a no-op until an adapter is registered with `registerObservabilitySink()`, so a self-hosted build carries no third-party error-tracking dependency. If you want alerting, ship the container logs to your log system and alert there. See [docs/security/logging-and-observability.md](security/logging-and-observability.md).
|
||||
|
||||
## Storage Buckets
|
||||
|
||||
@@ -341,7 +336,7 @@ If a new release includes database migrations, apply them before restarting:
|
||||
supabase db push
|
||||
```
|
||||
|
||||
Check the [release notes](https://github.com/erp-mafia/gnubok/releases) for migration instructions.
|
||||
Check the [release notes](https://github.com/erp-mafia/accounted/releases) for migration instructions.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
|
||||
Reference in New Issue
Block a user