lambda-erp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lambda_erp-0.1.0/.gitignore +29 -0
- lambda_erp-0.1.0/LICENSE +21 -0
- lambda_erp-0.1.0/PKG-INFO +454 -0
- lambda_erp-0.1.0/README.md +430 -0
- lambda_erp-0.1.0/api/__init__.py +0 -0
- lambda_erp-0.1.0/api/attachments.py +229 -0
- lambda_erp-0.1.0/api/auth.py +511 -0
- lambda_erp-0.1.0/api/bootstrap.py +498 -0
- lambda_erp-0.1.0/api/chat.py +2764 -0
- lambda_erp-0.1.0/api/demo_limits.py +400 -0
- lambda_erp-0.1.0/api/deps.py +7 -0
- lambda_erp-0.1.0/api/errors.py +56 -0
- lambda_erp-0.1.0/api/main.py +182 -0
- lambda_erp-0.1.0/api/pdf.py +151 -0
- lambda_erp-0.1.0/api/providers.py +116 -0
- lambda_erp-0.1.0/api/routers/__init__.py +0 -0
- lambda_erp-0.1.0/api/routers/accounting.py +63 -0
- lambda_erp-0.1.0/api/routers/admin.py +122 -0
- lambda_erp-0.1.0/api/routers/analytics.py +1009 -0
- lambda_erp-0.1.0/api/routers/bank_reconciliation.py +31 -0
- lambda_erp-0.1.0/api/routers/documents.py +100 -0
- lambda_erp-0.1.0/api/routers/masters.py +396 -0
- lambda_erp-0.1.0/api/routers/reports.py +735 -0
- lambda_erp-0.1.0/api/routers/setup.py +387 -0
- lambda_erp-0.1.0/api/services.py +372 -0
- lambda_erp-0.1.0/api/templates/document.html +197 -0
- lambda_erp-0.1.0/docs/agents/README.md +43 -0
- lambda_erp-0.1.0/frontend/README.md +84 -0
- lambda_erp-0.1.0/frontend/src/api/client.ts +399 -0
- lambda_erp-0.1.0/lambda_erp/__init__.py +3 -0
- lambda_erp-0.1.0/lambda_erp/accounting/__init__.py +0 -0
- lambda_erp-0.1.0/lambda_erp/accounting/bank_transaction.py +76 -0
- lambda_erp-0.1.0/lambda_erp/accounting/budget.py +117 -0
- lambda_erp-0.1.0/lambda_erp/accounting/chart_of_accounts.py +183 -0
- lambda_erp-0.1.0/lambda_erp/accounting/general_ledger.py +362 -0
- lambda_erp-0.1.0/lambda_erp/accounting/journal_entry.py +235 -0
- lambda_erp-0.1.0/lambda_erp/accounting/payment_entry.py +515 -0
- lambda_erp-0.1.0/lambda_erp/accounting/pos_invoice.py +342 -0
- lambda_erp-0.1.0/lambda_erp/accounting/purchase_invoice.py +504 -0
- lambda_erp-0.1.0/lambda_erp/accounting/revaluation.py +172 -0
- lambda_erp-0.1.0/lambda_erp/accounting/sales_invoice.py +523 -0
- lambda_erp-0.1.0/lambda_erp/accounting/subscription.py +132 -0
- lambda_erp-0.1.0/lambda_erp/buying/__init__.py +0 -0
- lambda_erp-0.1.0/lambda_erp/buying/purchase_order.py +165 -0
- lambda_erp-0.1.0/lambda_erp/controllers/__init__.py +0 -0
- lambda_erp-0.1.0/lambda_erp/controllers/currency.py +52 -0
- lambda_erp-0.1.0/lambda_erp/controllers/defaults.py +51 -0
- lambda_erp-0.1.0/lambda_erp/controllers/pricing_rule.py +103 -0
- lambda_erp-0.1.0/lambda_erp/controllers/taxes_and_totals.py +369 -0
- lambda_erp-0.1.0/lambda_erp/database.py +1543 -0
- lambda_erp-0.1.0/lambda_erp/exceptions.py +37 -0
- lambda_erp-0.1.0/lambda_erp/hooks.py +37 -0
- lambda_erp-0.1.0/lambda_erp/model.py +462 -0
- lambda_erp-0.1.0/lambda_erp/selling/__init__.py +0 -0
- lambda_erp-0.1.0/lambda_erp/selling/quotation.py +263 -0
- lambda_erp-0.1.0/lambda_erp/selling/sales_order.py +214 -0
- lambda_erp-0.1.0/lambda_erp/simulation.py +704 -0
- lambda_erp-0.1.0/lambda_erp/stock/__init__.py +0 -0
- lambda_erp-0.1.0/lambda_erp/stock/delivery_note.py +254 -0
- lambda_erp-0.1.0/lambda_erp/stock/purchase_receipt.py +356 -0
- lambda_erp-0.1.0/lambda_erp/stock/stock_entry.py +330 -0
- lambda_erp-0.1.0/lambda_erp/stock/stock_ledger.py +337 -0
- lambda_erp-0.1.0/lambda_erp/utils.py +167 -0
- lambda_erp-0.1.0/pyproject.toml +40 -0
- lambda_erp-0.1.0/terraform/README.md +293 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.so
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
*.egg
|
|
8
|
+
*.db
|
|
9
|
+
*.db-shm
|
|
10
|
+
*.db-wal
|
|
11
|
+
*.sqlite3
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
.env
|
|
15
|
+
.env.*
|
|
16
|
+
|
|
17
|
+
# JWT signing secret auto-generated by api/auth.py when JWT_SECRET_KEY is
|
|
18
|
+
# unset. Forging login cookies is trivial if this leaks.
|
|
19
|
+
.jwt_secret
|
|
20
|
+
*.log
|
|
21
|
+
.idea/
|
|
22
|
+
.vscode/
|
|
23
|
+
*.swp
|
|
24
|
+
*~
|
|
25
|
+
.DS_Store
|
|
26
|
+
Thumbs.db
|
|
27
|
+
frontend/node_modules/
|
|
28
|
+
frontend/dist/
|
|
29
|
+
*.tgz
|
lambda_erp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TORUS INVESTMENTS AG
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lambda-erp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Core ERP logic - accounting, sales, purchasing, inventory
|
|
5
|
+
Author: TORUS INVESTMENTS AG
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: anthropic>=0.40
|
|
10
|
+
Requires-Dist: bcrypt>=5
|
|
11
|
+
Requires-Dist: fastapi>=0.115
|
|
12
|
+
Requires-Dist: holidays>=0.40
|
|
13
|
+
Requires-Dist: httpx>=0.27
|
|
14
|
+
Requires-Dist: jinja2>=3.1
|
|
15
|
+
Requires-Dist: openai>=1.0
|
|
16
|
+
Requires-Dist: pydantic>=2.0
|
|
17
|
+
Requires-Dist: python-dateutil>=2.8
|
|
18
|
+
Requires-Dist: python-dotenv>=1.0
|
|
19
|
+
Requires-Dist: python-jose[cryptography]>=3.3
|
|
20
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
21
|
+
Requires-Dist: uvicorn[standard]>=0.30
|
|
22
|
+
Requires-Dist: weasyprint>=62.0
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# Lambda ERP
|
|
26
|
+
|
|
27
|
+
**Open-source ERP you can run through chat — configurable in plain language**
|
|
28
|
+
|
|
29
|
+
Lambda ERP is a working prototype of a simpler ERP: create invoices, check inventory, answer accounting questions, and change reports by asking for what you need in plain language.
|
|
30
|
+
|
|
31
|
+
https://github.com/user-attachments/assets/1b2749ef-10e7-42f5-9cce-df5628292667
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<a href="https://lambda-erp-demo.grayocean-53ec71ac.northeurope.azurecontainerapps.io/demo">
|
|
35
|
+
<img alt="Try the Live Demo" src="https://img.shields.io/badge/%E2%96%B6%20Try%20the%20Live%20Demo-4fc3f7?style=for-the-badge&labelColor=000000">
|
|
36
|
+
</a>
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
> Click **Enter Live Demo** for a 40-second scripted walkthrough and prompt freely after (rate-limited to ~$50/day of LLM spend across all visitors).
|
|
40
|
+
|
|
41
|
+
**Join the discussion** on Discord — report bugs, share prompts that broke (or surprised) the agent, or just see what other early users are building:
|
|
42
|
+
|
|
43
|
+
<p>
|
|
44
|
+
<a href="https://discord.gg/ZwFh9hZJTb"><img alt="Join the Lambda ERP Discord" src="https://img.shields.io/discord/1496911123029557359?color=7289DA&label=Join%20on%20Discord&logo=discord&logoColor=white&style=for-the-badge"></a>
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
This release is not yet production-ready. It's a complete prototype but not vetted enough to run your payroll on it yet.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Why another ERP?
|
|
52
|
+
|
|
53
|
+
Today, the bulk of an ERP rollout isn't the software license — it's everything that has to happen on top of it before the system actually fits the company. For a small or mid-sized company, the license is typically a few thousand a year, while getting it set up routinely runs **$10-50k** — many times the annual license spend before anyone has logged in. The system also keeps evolving after go-live — each custom report, country-specific tax rule, or workflow change is its own round of work. Larger systems like Oracle NetSuite and Microsoft Dynamics follow the same shape, and S/4HANA-class projects run into the millions.
|
|
54
|
+
|
|
55
|
+
ERPs cost what they cost because they're generic platforms that have to be bent into the shape of each company. The work is configuration, workflow design, custom reports, data migration, integrations — language and structure work. It's been slow and manual because software couldn't do it. Until recently.
|
|
56
|
+
|
|
57
|
+
We think that's about to flip. The bulk of an ERP implementation is text-transformation and configuration that LLMs are now genuinely good at:
|
|
58
|
+
|
|
59
|
+
- **Reading documents.** A supplier PDF becomes a Purchase Invoice. A bank statement becomes reconciled journal entries. An onboarding form becomes a Customer record.
|
|
60
|
+
- **Chart of Accounts design and mapping.** Taking a client's legacy accounts, translating to local GAAP, producing the mapping table - pure language-plus-structure work.
|
|
61
|
+
- **Master data migration.** Cleaning, deduplicating, and loading customer/supplier/item masters and opening balances from whatever mess the legacy system coughs up.
|
|
62
|
+
- **Custom reports and print formats.** The bespoke chart, the specific invoice layout, the cash-flow view nobody else has - unbounded client taste, infinite long tail.
|
|
63
|
+
- **End-user training and ongoing questions.** "How do I issue a credit note?" "Why is this balance off?" "What was last quarter's margin by product line?" - natural-language lookups that used to need a help-desk hour.
|
|
64
|
+
|
|
65
|
+
Each of those is hours of skilled work today. With a capable LLM in the loop, the same task takes seconds of compute and a review pass from someone who knows the business. The software starts tailoring itself to you, instead of the other way around. And because Lambda ERP is open source and self-hosted, the configuration doesn't stop at go-live — the system can keep evolving alongside the company. For implementation partners, the shape of the work shifts: less time spent hand-writing every change, more time spent on the judgment calls — chart-of-accounts design, compliance, change management — that actually need a human.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## How it works
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
┌────────────────────────────┐ ┌────────────────────────────┐
|
|
74
|
+
│ React + Vite frontend │◄──►│ FastAPI backend │
|
|
75
|
+
│ - Document forms │ │ - Document CRUD API │
|
|
76
|
+
│ - Reports / Analytics │ │ - Report endpoints │
|
|
77
|
+
│ - Chat (WebSocket) │ │ - Auth (JWT cookie) │
|
|
78
|
+
│ - Client-side JS runtime │ │ - WebSocket chat gateway │
|
|
79
|
+
│ (Web Worker) for charts │ └────────────┬───────────────┘
|
|
80
|
+
└────────────────────────────┘ │
|
|
81
|
+
▼
|
|
82
|
+
┌──────────────────────────────────────────┐
|
|
83
|
+
│ LLM orchestrator │
|
|
84
|
+
│ - GPT-5.4 drives the reasoning loop │
|
|
85
|
+
│ - Tool-use: document CRUD, search, │
|
|
86
|
+
│ reports, aggregations, analytics │
|
|
87
|
+
│ - Delegates JS generation to Anthropic │
|
|
88
|
+
│ code-specialist sub-agent │
|
|
89
|
+
└──────────────────┬───────────────────────┘
|
|
90
|
+
│
|
|
91
|
+
▼
|
|
92
|
+
┌────────────────────────────────────────────┐
|
|
93
|
+
│ lambda_erp/ (pure Python, no framework) │
|
|
94
|
+
│ - Document base class + lifecycle │
|
|
95
|
+
│ - General Ledger (double-entry) │
|
|
96
|
+
│ - Stock Ledger (moving average) │
|
|
97
|
+
│ - Tax calculation engine │
|
|
98
|
+
│ - Pluggable DB backend (SQLite / Postgres)│
|
|
99
|
+
└────────────────────────────────────────────┘
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Key design choices:**
|
|
103
|
+
|
|
104
|
+
- **Chat-first, not chat-bolted-on.** The chat isn't a copilot sidebar - it's the primary way to interact with the system. Every document type, every report, every master record is reachable from tool-use.
|
|
105
|
+
- **One shape for every document.** Invoices, sales orders, stock entries, payments - all share a single `Document` base class and the same three-state lifecycle (Draft → Submitted → Cancelled) with `on_submit`/`on_cancel` hooks. The LLM learns the pattern once and drives every doctype the same way. Leading open-source and commercial ERPs have per-model action verbs spread across 150+ core models; each one is a separate tool the model has to get right.
|
|
106
|
+
- **Metadata-driven UI, shared with the LLM.** A single React form component renders every doctype from `frontend/src/lib/doctypes.ts`. The schema the model reasons over and the schema the user sees are literally the same file. Adding a field is two lines - one in the Python class, one in the config - not a new module with hand-written views and inheritance overlays.
|
|
107
|
+
- **Two-model orchestration.** A planner model handles reasoning and tool-use. When it needs to generate code for a custom report, it delegates to a code-specialist sub-agent. This keeps each model doing what it's best at and keeps latency down on simple turns.
|
|
108
|
+
- **Semantic datasets, not free SQL.** The LLM can't write raw SQL; it calls whitelisted semantic datasets (`sales_invoices`, `purchase_invoices`, `ar_open_items`, `stock_balances`, etc.) with whitelisted filters and group-bys. This makes the system auditable without sacrificing flexibility.
|
|
109
|
+
- **Client-side analytics runtime.** Custom report JS executes in a sandboxed Web Worker in the user's browser. The server never runs untrusted JS. Charts are persisted as portable specs, not screenshots.
|
|
110
|
+
- **Double-entry invariant enforced.** Every submitted document that touches the GL must balance to zero. The engine adds round-off entries for rounding gaps and refuses to post imbalanced vouchers.
|
|
111
|
+
- **One deployment per customer, simple to operate.** Lambda ERP is built to be self-hosted by a single company for its own books - not as a multi-tenant SaaS. One FastAPI process, one database, one VPS is enough. If you want a hosted offering, we'll ship a dedicated instance per customer.
|
|
112
|
+
|
|
113
|
+
### Tech stack at a glance
|
|
114
|
+
|
|
115
|
+
| Layer | What |
|
|
116
|
+
|---|---|
|
|
117
|
+
| Backend business logic | Pure Python, no framework (`lambda_erp/`) |
|
|
118
|
+
| Web API | FastAPI + Pydantic |
|
|
119
|
+
| Storage | SQLite in the prototype; Postgres planned for production |
|
|
120
|
+
| Frontend | React + Vite + TypeScript + Tailwind + Recharts |
|
|
121
|
+
| Chat transport | WebSocket |
|
|
122
|
+
| LLM orchestrator | OpenAI (configurable) |
|
|
123
|
+
| Code specialist | Anthropic (configurable) |
|
|
124
|
+
| Auth | JWT httponly cookie, three roles + demo |
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## What works today
|
|
129
|
+
|
|
130
|
+
- Full sales and purchase cycles (Quotation → Sales Order → Delivery Note → Sales Invoice → Payment Entry, and the buying equivalents)
|
|
131
|
+
- Returns / credit notes / debit notes with proper GL and stock reversal
|
|
132
|
+
- Moving-average stock ledger with negative-stock protection
|
|
133
|
+
- Double-entry General Ledger with cancellation reversal
|
|
134
|
+
- Preset reports: Trial Balance, Profit & Loss, Balance Sheet, General Ledger, AR/AP Aging, Stock Balance
|
|
135
|
+
- Custom analytics drafts via chat (persisted, shareable, editable)
|
|
136
|
+
- Server-side aggregation tool for in-chat factual answers across large datasets
|
|
137
|
+
- PDF / image attachment → add invoices, create quotations, etc. all directly by adding them in the chat
|
|
138
|
+
- Auth with admin/manager/viewer roles plus a public demo mode
|
|
139
|
+
- Full test suite that exercises every cycle against an in-memory SQLite
|
|
140
|
+
|
|
141
|
+
## What's still todo (Suggestions welcome)
|
|
142
|
+
|
|
143
|
+
- MCP integration for supplier/customer communication (quotes, orders, confirmations)
|
|
144
|
+
- Multi-currency beyond the simplified current handling
|
|
145
|
+
- Workflows / approval chains
|
|
146
|
+
- Serial & batch tracking
|
|
147
|
+
- Manufacturing (BOM, work orders)
|
|
148
|
+
- HR / Payroll beyond the journal-entry workaround
|
|
149
|
+
- Regional compliance packs (GST, VAT returns, etc.) - see below
|
|
150
|
+
- PDF report creation directly inside the chat
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Why now
|
|
155
|
+
|
|
156
|
+
Four things had to be true for this to work, and they all became true in the last ~18 months:
|
|
157
|
+
|
|
158
|
+
1. **LLMs can reliably call tools.** A year ago, models would hallucinate tool calls, mangle JSON, or drift after 2–3 steps. Today's frontier models can run an 8-step reasoning loop over a real tool inventory without falling off.
|
|
159
|
+
2. **Costs collapsed.** Generating a custom report via a code-specialist sub-agent is cents of compute. Even keeping a human reviewer fully in the loop, the marginal cost of "one more report" or "one more dashboard" drops by orders of magnitude — which means companies actually ask for them, instead of living with the defaults.
|
|
160
|
+
3. **Structured output + function calling are first-class.** We can constrain the LLM's outputs to valid tool-call schemas, safe SQL parameters, and typed JSON - which is what makes an AI-native ERP even conceivable as a safe thing to run.
|
|
161
|
+
4. **Greenfield is finally cheaper than retrofit.** Twenty-year ERP codebases have hundreds of bespoke models and thousands of hand-written forms - teaching an LLM to drive that reliably means curating a custom tool layer over every quirk. Starting from scratch around one Document lifecycle and a metadata-driven UI is now cheaper than retrofitting an existing platform.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Why open source
|
|
166
|
+
|
|
167
|
+
Every company configuring an ERP runs into the same problems: local tax rules, common workflow patterns, industry-specific accounting quirks. Most of that knowledge isn't a competitive advantage — it's the same ground being re-covered separately at every implementation, by every team, in slightly different ways.
|
|
168
|
+
|
|
169
|
+
We want Lambda ERP to be where that knowledge lives in public. The base system is MIT-licensed, and we're organizing the repo so that community contributions - a German VAT pack, a U.S. sales-tax-by-state module, an industry template for professional services - can slot in under `docs/` and be picked up by the LLM as reference material. The goal is a true community where running an ERP gets cheaper, faster, and more flexible for everyone, not just the implementer.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Get it running
|
|
174
|
+
|
|
175
|
+
### Docker (one command)
|
|
176
|
+
|
|
177
|
+
You need Docker and Compose v2. The canonical installs:
|
|
178
|
+
|
|
179
|
+
- **Mac / Windows**: install [Docker Desktop](https://www.docker.com/products/docker-desktop/) — ships `docker compose` v2 built in.
|
|
180
|
+
- **Linux / WSL**: Docker's own one-liner installs both the engine and the Compose plugin:
|
|
181
|
+
```bash
|
|
182
|
+
curl -fsSL https://get.docker.com | sh
|
|
183
|
+
sudo usermod -aG docker $USER && newgrp docker
|
|
184
|
+
```
|
|
185
|
+
(Ubuntu's `docker.io` apt package and Snap's `docker` omit Compose; use the script above instead.)
|
|
186
|
+
|
|
187
|
+
Then, from the repo root:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
cp .env.example .env # add your OPENAI_API_KEY and ANTHROPIC_API_KEY (for custom analytics)
|
|
191
|
+
docker compose up --build
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
First boot takes **~2-3 minutes** — the container runs a 3-year historical simulator to populate realistic demo data. Watch the logs; you'll see monthly `[sim]` progress lines and, when it's done, a clear banner:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
======================================================
|
|
198
|
+
Lambda ERP is READY — open http://127.0.0.1:8000 in your browser
|
|
199
|
+
======================================================
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Hitting the URL before that banner will look like the page "hangs" — uvicorn isn't listening yet, so requests queue at the TCP layer until bootstrap finishes. Wait for the banner, then open the URL.
|
|
203
|
+
|
|
204
|
+
> **Use `127.0.0.1`, not `localhost`.** On WSL2 + Docker Desktop, browsers occasionally stall for minutes on WebSocket upgrades to `localhost` even when HTTP works fine. `127.0.0.1` has never been observed to misbehave.
|
|
205
|
+
|
|
206
|
+
The container serves both the UI and the API at the same origin - there's no separate frontend port in Docker mode. You'll land on the login page with **register-your-first-admin** enabled; create an account and that account becomes the admin for your instance.
|
|
207
|
+
|
|
208
|
+
If instead you want the hosted-demo experience (a shared `public_manager` account and the "Enter Live Demo" button), add `LAMBDA_ERP_ENABLE_PUBLIC_DEMO=1` to your `.env`.
|
|
209
|
+
|
|
210
|
+
State persists to a named Docker volume, so subsequent `docker compose up` starts in seconds. `docker compose down -v` wipes the volume to force a fresh re-seed.
|
|
211
|
+
|
|
212
|
+
### Local dev
|
|
213
|
+
|
|
214
|
+
Requires Python 3.10+ and Node 18+.
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Backend
|
|
218
|
+
python3 -m venv .venv
|
|
219
|
+
source .venv/bin/activate
|
|
220
|
+
pip install -e .
|
|
221
|
+
uvicorn api.main:app --reload --port 8000
|
|
222
|
+
|
|
223
|
+
# Frontend (separate terminal)
|
|
224
|
+
cd frontend
|
|
225
|
+
npm install
|
|
226
|
+
npm run dev
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Open `http://localhost:5173`. Vite proxies `/api/*` to the backend.
|
|
230
|
+
|
|
231
|
+
### Environment
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
OPENAI_API_KEY=sk-...
|
|
235
|
+
ANTHROPIC_API_KEY=sk-ant-... # optional, used for the code-specialist sub-agent
|
|
236
|
+
ANTHROPIC_CODE_MODEL=claude-opus-4-7 # optional, default shown
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Chat needs `OPENAI_API_KEY`. Custom-report code generation uses `ANTHROPIC_API_KEY` when set; otherwise it falls back and the chat will tell you it can't generate reports.
|
|
240
|
+
|
|
241
|
+
### Run the validation suite
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
source .venv/bin/activate
|
|
245
|
+
python -m tests.test_erp_validation
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Runs a full cycle in-memory: setup, sales cycle, purchase cycle, returns, submits, cancels, payments, trial balance. No credentials, no network, ~2 seconds.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Repository layout
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
lambda_erp/ # Pure Python business logic (no framework)
|
|
256
|
+
accounting/ # GL, journal entries, invoices, payments
|
|
257
|
+
selling/ # Quotations, sales orders
|
|
258
|
+
buying/ # Purchase orders
|
|
259
|
+
stock/ # Stock ledger, stock entries
|
|
260
|
+
controllers/ # Tax engine
|
|
261
|
+
api/ # FastAPI + WebSocket chat
|
|
262
|
+
routers/ # REST endpoints
|
|
263
|
+
chat.py # LLM orchestrator, tool schemas, reasoning loop
|
|
264
|
+
frontend/ # React + Vite app
|
|
265
|
+
tests/ # Validation / regression suite
|
|
266
|
+
docs/agents/ # Invariants, gotchas, design decisions (LLM-readable)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`docs/agents/` is worth a read if you're going to contribute - it captures the invariants the code assumes but doesn't always enforce, plus the landmines that have bitten us.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Building a customer deployment on top of the core
|
|
274
|
+
|
|
275
|
+
Lambda ERP is **one deployment per customer** (see the design choices above). A
|
|
276
|
+
customer that needs **core business-logic changes** should **not fork this
|
|
277
|
+
repo**. Instead, create a **separate private repo that depends on this one as
|
|
278
|
+
the core** and overrides behavior at defined seams. Core fixes then arrive via a
|
|
279
|
+
version bump, not a merge into a diverging fork. Full plan and rationale:
|
|
280
|
+
[`docs/core-extension-architecture.md`](docs/core-extension-architecture.md).
|
|
281
|
+
|
|
282
|
+
**Customer repo layout**
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
acme-erp/
|
|
286
|
+
pyproject.toml # depends on lambda-erp (git / path / PyPI)
|
|
287
|
+
acme/
|
|
288
|
+
plugin.py # register() — wires backend overrides + hooks
|
|
289
|
+
sales_invoice.py # e.g. class AcmeSalesInvoice(SalesInvoice): ...
|
|
290
|
+
frontend/
|
|
291
|
+
package.json # depends on @lambda-development/erp-core
|
|
292
|
+
tailwind.config.ts # scans @lambda-development/erp-core dist + adds its preset
|
|
293
|
+
src/
|
|
294
|
+
plugin.ts # registers frontend overrides (doctypes/routes/nav/branding)
|
|
295
|
+
main.tsx # import plugin + styles, then bootstrap()
|
|
296
|
+
config/ # branding, enabled features, base currency, OAuth
|
|
297
|
+
deploy/ # Dockerfile, env/secrets
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Override core logic (replace) — subclass + register**
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
# acme/sales_invoice.py
|
|
304
|
+
from lambda_erp.accounting.sales_invoice import SalesInvoice
|
|
305
|
+
class AcmeSalesInvoice(SalesInvoice):
|
|
306
|
+
def _get_gl_entries(self):
|
|
307
|
+
gl = super()._get_gl_entries()
|
|
308
|
+
# customer-specific posting
|
|
309
|
+
return gl
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Registering it makes every loader path (`create/load/update/submit/cancel`) **and
|
|
313
|
+
document conversions** use the subclass.
|
|
314
|
+
|
|
315
|
+
**Add behavior (don't replace) — lifecycle hooks**
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
from lambda_erp.hooks import register_hook
|
|
319
|
+
register_hook("Sales Invoice:after_submit", push_to_external_system)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Events are `"<DocType>:{before,after}_{save,submit,cancel}"`. `before_*` run
|
|
323
|
+
**inside** the document's transaction (a raise aborts and rolls back — use for
|
|
324
|
+
guards/validation); `after_*` run **post-commit** (the voucher is durable — use
|
|
325
|
+
for side-effects/integrations).
|
|
326
|
+
|
|
327
|
+
**Wire it up**
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
# acme/plugin.py
|
|
331
|
+
from api.services import register_doctype, register_converter
|
|
332
|
+
from lambda_erp.hooks import register_hook
|
|
333
|
+
from .sales_invoice import AcmeSalesInvoice
|
|
334
|
+
|
|
335
|
+
def register():
|
|
336
|
+
register_doctype("Sales Invoice", AcmeSalesInvoice)
|
|
337
|
+
register_hook("Sales Invoice:after_submit", push_to_external_system)
|
|
338
|
+
# register_converter(source, target, fn) # only to replace conversion *logic*
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Point the deployment at it with `LAMBDA_ERP_PLUGINS=acme` (comma-separated for
|
|
342
|
+
several). On startup the core imports each module and calls `register()`. Unset
|
|
343
|
+
= the core runs unchanged.
|
|
344
|
+
|
|
345
|
+
**Frontend overrides — the `@lambda-development/erp-core` library**
|
|
346
|
+
|
|
347
|
+
The frontend ships as a library. The customer app depends on it, registers its
|
|
348
|
+
overrides in a plugin module, then boots the shared app shell. The seams mirror
|
|
349
|
+
the backend: add/replace doctypes, routes, nav, whole components, branding, and
|
|
350
|
+
the API base — without editing core files.
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
// frontend/src/plugin.ts — runs before bootstrap()
|
|
354
|
+
import {
|
|
355
|
+
registerDoctype, registerRoute, registerNavGroup, registerComponent,
|
|
356
|
+
configureBranding, configureApiBase,
|
|
357
|
+
} from "@lambda-development/erp-core";
|
|
358
|
+
import AcmeDashboard from "./acme-dashboard";
|
|
359
|
+
|
|
360
|
+
configureApiBase(import.meta.env.VITE_API_BASE ?? "/api");
|
|
361
|
+
configureBranding({ appName: "Acme ERP", tokens: { "--brand": "260 80% 55%" } });
|
|
362
|
+
|
|
363
|
+
registerDoctype({ slug: "service-ticket", label: "Service Ticket", /* …schema… */ });
|
|
364
|
+
registerNavGroup({ label: "Service", icon: null, items: [{ label: "Tickets", path: "/app/service-ticket" }] });
|
|
365
|
+
registerRoute({ path: "reports/sla", element: <SlaReport /> }); // under the app shell
|
|
366
|
+
registerComponent("Dashboard", AcmeDashboard); // swap a core component
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
// frontend/src/main.tsx
|
|
371
|
+
import "./plugin"; // register overrides first
|
|
372
|
+
import "@lambda-development/erp-core/styles.css"; // base tokens + layers (your Tailwind processes it)
|
|
373
|
+
import "./acme.css"; // optional: override :root tokens, add utilities
|
|
374
|
+
import { bootstrap } from "@lambda-development/erp-core";
|
|
375
|
+
bootstrap(); // builds routes AFTER registration, then mounts
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Styling follows the **"consumer scans source"** model — your app runs Tailwind
|
|
379
|
+
and the library provides the tokens and preset:
|
|
380
|
+
|
|
381
|
+
```ts
|
|
382
|
+
// frontend/tailwind.config.ts
|
|
383
|
+
import erpPreset from "@lambda-development/erp-core/tailwind-preset";
|
|
384
|
+
export default {
|
|
385
|
+
content: ["./src/**/*.{ts,tsx}", "./node_modules/@lambda-development/erp-core/dist/**/*.js"],
|
|
386
|
+
presets: [erpPreset],
|
|
387
|
+
};
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Rebrand by overriding the `:root` CSS variables (`--brand`, `--surface`, `--text`,
|
|
391
|
+
…) — at runtime via `configureBranding({ tokens })` or statically in your own CSS.
|
|
392
|
+
|
|
393
|
+
**Wire up overrides:** registry registration at runtime (above) covers most
|
|
394
|
+
cases. For a build-time whole-module swap, point a Vite `resolve.alias` at your
|
|
395
|
+
replacement file.
|
|
396
|
+
|
|
397
|
+
**Rules**
|
|
398
|
+
|
|
399
|
+
- **Don't edit core files** — override at a seam. If what you need to change
|
|
400
|
+
isn't a seam yet, add the seam to the core (a PR here), then override from the
|
|
401
|
+
customer repo.
|
|
402
|
+
- Keep branding / feature toggles / base currency / auth config in `config`/env,
|
|
403
|
+
not code.
|
|
404
|
+
- Bump the core version to pull fixes; never copy core code in.
|
|
405
|
+
|
|
406
|
+
Both the backend seams (document classes, lifecycle hooks, converters, plugin
|
|
407
|
+
loading) and the frontend seams (doctype/route/nav/component registries,
|
|
408
|
+
branding, configurable API base, Tailwind preset) are implemented. The backend
|
|
409
|
+
also builds a clean pip wheel and the frontend a `@lambda-development/erp-core` npm library
|
|
410
|
+
— see [`docs/packaging-distribution-plan.md`](docs/packaging-distribution-plan.md)
|
|
411
|
+
for the publish path.
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Contributing
|
|
416
|
+
|
|
417
|
+
This is early. The project needs:
|
|
418
|
+
|
|
419
|
+
- Country compliance packs (tax rules, invoice formats, mandatory fields)
|
|
420
|
+
- Industry templates (services, retail, light manufacturing, SaaS)
|
|
421
|
+
- More preset reports
|
|
422
|
+
- A Postgres storage adapter (the current SQLite layer is fine for local evaluation but will need to be swapped for real multi-user write loads)
|
|
423
|
+
- Better observability around token spend per turn
|
|
424
|
+
- Native messenger integration, for WhatsApp, Telegram, etc.
|
|
425
|
+
|
|
426
|
+
PRs welcome. File an issue first if it's a big change, or drop by our [Discord](https://discord.gg/ZwFh9hZJTb) to discuss ideas.
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## License
|
|
431
|
+
|
|
432
|
+
MIT. See [LICENSE](./LICENSE) for the full text.
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Changelog
|
|
437
|
+
|
|
438
|
+
Release notes live in [CHANGELOG.md](./CHANGELOG.md). Releases are tagged
|
|
439
|
+
`vX.Y.Z` and published in lockstep to PyPI (`lambda-erp`) and npm
|
|
440
|
+
(`@lambda-development/erp-core`).
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## Status
|
|
445
|
+
|
|
446
|
+
Version 0. Working prototype that implements the vision. Fine for demos, internal tools, and hacking. Not yet ready to handle your company's actual books - run it alongside your real ERP if you want to kick the tires.
|
|
447
|
+
|
|
448
|
+
If you try it, we'd love to know what broke.
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## Trademarks and affiliations
|
|
453
|
+
|
|
454
|
+
Lambda ERP and [lambda.dev](https://lambda.dev/) are product and trade names of **TORUS INVESTMENTS AG**. It is not affiliated with, endorsed by, or sponsored by OpenAI, Anthropic, SAP, Oracle, Microsoft, or any other company named in this repository. SAP, Business One, S/4HANA, Oracle, NetSuite, Microsoft, Dynamics, OpenAI, GPT, Anthropic, and Claude are trademarks of their respective owners and are referenced here only for descriptive and comparative purposes (nominative fair use). We interoperate with OpenAI and Anthropic APIs as a customer like anyone else; you supply your own API keys.
|