lambda-erp 0.4.0__tar.gz → 0.5.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.
Files changed (84) hide show
  1. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/PKG-INFO +1 -1
  2. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/chat.py +58 -4
  3. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/main.py +3 -2
  4. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/masters.py +4 -17
  5. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/services.py +45 -0
  6. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/frontend/src/api/client.ts +1 -0
  7. lambda_erp-0.5.0/lambda_erp/__init__.py +33 -0
  8. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/pyproject.toml +1 -1
  9. lambda_erp-0.4.0/lambda_erp/__init__.py +0 -3
  10. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/.gitignore +0 -0
  11. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/LICENSE +0 -0
  12. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/README.md +0 -0
  13. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/__init__.py +0 -0
  14. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/attachments.py +0 -0
  15. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/auth.py +0 -0
  16. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/bootstrap.py +0 -0
  17. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/demo_limits.py +0 -0
  18. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/deps.py +0 -0
  19. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/errors.py +0 -0
  20. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/oauth.py +0 -0
  21. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/pdf.py +0 -0
  22. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/providers.py +0 -0
  23. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/remarks_md.py +0 -0
  24. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/__init__.py +0 -0
  25. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/accounting.py +0 -0
  26. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/admin.py +0 -0
  27. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/analytics.py +0 -0
  28. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/bank_reconciliation.py +0 -0
  29. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/chat_api.py +0 -0
  30. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/documents.py +0 -0
  31. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/proposals.py +0 -0
  32. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/reports.py +0 -0
  33. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/routers/setup.py +0 -0
  34. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/templates/document.html +0 -0
  35. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/api/templates/proposal.html +0 -0
  36. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/docs/agents/README.md +0 -0
  37. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/frontend/LICENSE +0 -0
  38. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/frontend/README.md +0 -0
  39. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/__init__.py +0 -0
  40. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/bank_transaction.py +0 -0
  41. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/budget.py +0 -0
  42. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/chart_of_accounts.py +0 -0
  43. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/general_ledger.py +0 -0
  44. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/journal_entry.py +0 -0
  45. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/payment_entry.py +0 -0
  46. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/pos_invoice.py +0 -0
  47. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/purchase_invoice.py +0 -0
  48. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/revaluation.py +0 -0
  49. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/sales_invoice.py +0 -0
  50. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/__init__.py +0 -0
  51. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/engine.py +0 -0
  52. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/pack.py +0 -0
  53. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/packs/__init__.py +0 -0
  54. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/packs/ch.py +0 -0
  55. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/packs/de_common.py +0 -0
  56. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/packs/de_skr03.py +0 -0
  57. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/packs/de_skr04.py +0 -0
  58. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/packs/generic.py +0 -0
  59. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/profiles.py +0 -0
  60. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/setup/spine.py +0 -0
  61. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/accounting/subscription.py +0 -0
  62. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/buying/__init__.py +0 -0
  63. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/buying/purchase_order.py +0 -0
  64. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/controllers/__init__.py +0 -0
  65. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/controllers/currency.py +0 -0
  66. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/controllers/defaults.py +0 -0
  67. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/controllers/pricing_rule.py +0 -0
  68. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/controllers/taxes_and_totals.py +0 -0
  69. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/database.py +0 -0
  70. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/exceptions.py +0 -0
  71. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/hooks.py +0 -0
  72. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/model.py +0 -0
  73. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/selling/__init__.py +0 -0
  74. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/selling/proposal.py +0 -0
  75. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/selling/quotation.py +0 -0
  76. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/selling/sales_order.py +0 -0
  77. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/simulation.py +0 -0
  78. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/stock/__init__.py +0 -0
  79. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/stock/delivery_note.py +0 -0
  80. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/stock/purchase_receipt.py +0 -0
  81. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/stock/stock_entry.py +0 -0
  82. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/stock/stock_ledger.py +0 -0
  83. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/lambda_erp/utils.py +0 -0
  84. {lambda_erp-0.4.0 → lambda_erp-0.5.0}/terraform/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lambda-erp
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Core ERP logic - accounting, sales, purchasing, inventory
5
5
  Author: TORUS INVESTMENTS AG
6
6
  License-Expression: Apache-2.0
@@ -10,6 +10,7 @@ Supports multiple chat sessions, each with their own history and auto-generated
10
10
 
11
11
  import asyncio
12
12
  import base64
13
+ import copy
13
14
  import difflib
14
15
  import io
15
16
  import json
@@ -1163,6 +1164,44 @@ TOOLS = [
1163
1164
  },
1164
1165
  ]
1165
1166
 
1167
+
1168
+ def _extra_document_slugs():
1169
+ """Doctype slugs registered beyond the core list (via register_doctype)."""
1170
+ return sorted(s for s in services.SLUG_TO_DOCTYPE if s not in DOCUMENT_SLUGS)
1171
+
1172
+
1173
+ def _extra_master_types():
1174
+ """Master types registered beyond the core list (via register_master)."""
1175
+ return sorted(m for m in services.MASTER_TABLES if m not in MASTER_TYPES)
1176
+
1177
+
1178
+ def build_tools():
1179
+ """TOOLS with the doctype/master enums widened from the live registries.
1180
+
1181
+ Plugins register doctypes and masters at startup — after this module is
1182
+ imported — so the static TOOLS constant can't know about them. The tool
1183
+ handlers already resolve through the live registries; the enums in the
1184
+ schemas were the only thing shutting registered types out of chat. Built
1185
+ per request (cheap next to the LLM call), which also makes plugin load
1186
+ order irrelevant. Only enums that exactly match the core lists are
1187
+ widened, so any differently-constrained doctype/master param (e.g. a
1188
+ curated subset) is left alone.
1189
+ """
1190
+ extra_docs = _extra_document_slugs()
1191
+ extra_masters = _extra_master_types()
1192
+ if not extra_docs and not extra_masters:
1193
+ return TOOLS
1194
+ tools = copy.deepcopy(TOOLS)
1195
+ for tool in tools:
1196
+ props = tool["function"].get("parameters", {}).get("properties", {})
1197
+ for schema in props.values():
1198
+ if schema.get("enum") == DOCUMENT_SLUGS:
1199
+ schema["enum"] = DOCUMENT_SLUGS + extra_docs
1200
+ elif schema.get("enum") == MASTER_TYPES:
1201
+ schema["enum"] = MASTER_TYPES + extra_masters
1202
+ return tools
1203
+
1204
+
1166
1205
  # ---------------------------------------------------------------------------
1167
1206
  # Tool handlers — dispatch to existing service functions
1168
1207
  # ---------------------------------------------------------------------------
@@ -1845,6 +1884,21 @@ def build_system_prompt(user_info: dict | None = None, channel: str = "web"):
1845
1884
  analytics_context = _prompt_analytics_context()
1846
1885
  uom_context = _prompt_uom_context()
1847
1886
 
1887
+ # Deployment-registered types (register_doctype / register_master). Folded
1888
+ # into the type lists below so the assistant knows they exist and can act
1889
+ # on them from day one — the tool schemas widen the same way (build_tools).
1890
+ extra_docs = _extra_document_slugs()
1891
+ extra_masters = _extra_master_types()
1892
+ extension_doctypes_line = (
1893
+ f"\n- **Extensions (deployment-specific):** {', '.join(extra_docs)}" if extra_docs else ""
1894
+ )
1895
+ master_types_line = ", ".join(MASTER_TYPES + extra_masters)
1896
+ extension_master_keys = "".join(
1897
+ f"\n- **{services.MASTER_TABLES[m][0]}** (master_type `{m}`): key = `name`, "
1898
+ f"display = `{services.MASTER_TABLES[m][1]}`"
1899
+ for m in extra_masters
1900
+ )
1901
+
1848
1902
  # Channel-aware link guidance. On the "web" channel the reader is a browser
1849
1903
  # inside the ERP, so web-relative links are clickable. On the "api" channel the
1850
1904
  # reply is relayed to an EXTERNAL app (lambda-web / the Lambda app) that is not
@@ -1952,7 +2006,7 @@ When a user asks you to do something they don't have permission for, explain wha
1952
2006
  - **Buying:** purchase-order, purchase-invoice
1953
2007
  - **Accounting:** payment-entry, journal-entry, budget, subscription, bank-transaction
1954
2008
  - **Stock:** stock-entry, delivery-note, purchase-receipt
1955
- - **Settings:** pricing-rule
2009
+ - **Settings:** pricing-rule{extension_doctypes_line}
1956
2010
 
1957
2011
  ## Document Workflow & What Each Document Does
1958
2012
 
@@ -2076,7 +2130,7 @@ For purchased goods, prefer Purchase Receipt when receiving separately from bill
2076
2130
  - Cancelled documents cannot be re-submitted or modified.
2077
2131
 
2078
2132
  ## Master Data Types
2079
- customer, supplier, item, warehouse, account, company, cost-center
2133
+ {master_types_line}
2080
2134
 
2081
2135
  ## Master Data Deletion
2082
2136
  Master records CAN be deleted — use the `delete_master` tool (ADMIN role only). It is reference-protected: an unreferenced record (e.g. a mistakenly created or duplicate one) is permanently deleted; a record referenced by any document, GL entry, or stock data is NOT deleted but automatically disabled/archived instead, and the result names the blocking reference — relay that reason to the user. When the user merely wants a record out of the way (not destroyed), prefer `update_master` with `{{"disabled": 1}}`. Non-admin users: explain that deletion needs an admin and offer to disable instead.
@@ -2153,7 +2207,7 @@ Every master record has a primary key (the `name` column) and a human-readable d
2153
2207
  - **Item:** key = `item_code` (e.g. `SVC-005`), display = `item_name` (e.g. "Project Management")
2154
2208
  - **Customer:** key = `name` (e.g. `CUST-007`), display = `customer_name` (e.g. "Redstone Automotive")
2155
2209
  - **Supplier:** key = `name` (e.g. `SUPP-003`), display = `supplier_name`
2156
- - **Warehouse / Company / Account / Cost Center:** key = `name`
2210
+ - **Warehouse / Company / Account / Cost Center:** key = `name`{extension_master_keys}
2157
2211
 
2158
2212
  When you fill in `item_code`, `customer`, `supplier`, `warehouse`, `company`, etc. in a document or child-table row, you MUST use the **primary key**, never the display name. `"item_code": "Project Management"` is ALWAYS wrong — it's a name, not a code.
2159
2213
 
@@ -2646,7 +2700,7 @@ async def run_thinking_loop(
2646
2700
  openai_client.chat.completions.create,
2647
2701
  model=model_name,
2648
2702
  messages=messages,
2649
- tools=TOOLS,
2703
+ tools=build_tools(),
2650
2704
  tool_choice="auto",
2651
2705
  max_completion_tokens=max_completion,
2652
2706
  # gpt-5.6-terra rejects function tools on /v1/chat/completions
@@ -11,6 +11,7 @@ from fastapi.middleware.cors import CORSMiddleware
11
11
  from fastapi.responses import FileResponse
12
12
  from fastapi.staticfiles import StaticFiles
13
13
 
14
+ from lambda_erp import get_app_version
14
15
  from lambda_erp.database import setup
15
16
 
16
17
  from api.errors import register_exception_handlers
@@ -70,7 +71,7 @@ async def lifespan(app: FastAPI):
70
71
 
71
72
  app = FastAPI(
72
73
  title="Lambda ERP",
73
- version="0.1.0",
74
+ version=get_app_version(),
74
75
  lifespan=lifespan,
75
76
  )
76
77
 
@@ -105,7 +106,7 @@ app.include_router(chat_api.router, prefix="/api")
105
106
 
106
107
  @app.get("/api/health")
107
108
  def health():
108
- return {"status": "ok"}
109
+ return {"status": "ok", "version": get_app_version()}
109
110
 
110
111
 
111
112
  @app.websocket("/ws/chat")
@@ -5,7 +5,10 @@ import re
5
5
  from fastapi import APIRouter, Depends, HTTPException, Query
6
6
  from lambda_erp.database import get_db
7
7
  from lambda_erp.utils import _dict
8
- from api.services import MASTER_TABLES
8
+ # The per-type registries live in api.services so `register_master` can extend
9
+ # them; imported here (not moved) so existing `from api.routers.masters import
10
+ # MASTER_IDENTITY_ALIAS`-style imports keep working.
11
+ from api.services import MASTER_TABLES, MASTER_NAME_PREFIXES, MASTER_IDENTITY_ALIAS
9
12
  from api.auth import require_role, require_non_public_manager
10
13
 
11
14
  router = APIRouter(prefix="/masters", tags=["masters"])
@@ -14,22 +17,6 @@ _viewer = Depends(require_role("viewer"))
14
17
  _manager = Depends(require_non_public_manager)
15
18
  _admin = Depends(require_role("admin"))
16
19
 
17
- MASTER_NAME_PREFIXES = {
18
- "customer": "CUST",
19
- "supplier": "SUPP",
20
- "item": "ITEM",
21
- "warehouse": "WH",
22
- }
23
-
24
- # An Item's code is stored in the primary-key column `name`, but every
25
- # transaction line and report references it as
26
- # `item_code`. Accept that intuitive alias on the master API so callers
27
- # (LLM tools, REST clients) can set and read the code under the name they
28
- # already use everywhere else, instead of silently falling back to ITEM-NNN.
29
- MASTER_IDENTITY_ALIAS = {
30
- "item": "item_code",
31
- }
32
-
33
20
 
34
21
  def _echo_identity_alias(master_type: str, row):
35
22
  """Mirror the master's `name` back under its intuitive alias (item_code)."""
@@ -70,6 +70,25 @@ MASTER_TABLES = {
70
70
  "cost-center": ("Cost Center", "cost_center_name"),
71
71
  }
72
72
 
73
+ # Auto-naming prefixes: masters created without an explicit `name` get a
74
+ # generated PREFIX-NNN id (see masters.create_master_record). Types without an
75
+ # entry require an explicit name (or derive it, like company).
76
+ MASTER_NAME_PREFIXES = {
77
+ "customer": "CUST",
78
+ "supplier": "SUPP",
79
+ "item": "ITEM",
80
+ "warehouse": "WH",
81
+ }
82
+
83
+ # An Item's code is stored in the primary-key column `name`, but every
84
+ # transaction line and report references it as `item_code`. Accept that
85
+ # intuitive alias on the master API so callers (LLM tools, REST clients) can
86
+ # set and read the code under the name they already use everywhere else,
87
+ # instead of silently falling back to ITEM-NNN.
88
+ MASTER_IDENTITY_ALIAS = {
89
+ "item": "item_code",
90
+ }
91
+
73
92
  # Slug <-> doctype name mapping
74
93
  SLUG_TO_DOCTYPE = {}
75
94
  DOCTYPE_TO_SLUG = {}
@@ -95,6 +114,32 @@ def register_doctype(doctype: str, cls, slug: str | None = None) -> None:
95
114
  DOCTYPE_TO_SLUG[doctype] = slug
96
115
 
97
116
 
117
+ def register_master(slug: str, table: str, name_field: str, *,
118
+ name_prefix: str | None = None,
119
+ identity_alias: str | None = None) -> None:
120
+ """Register (or override) a master type — the master-side counterpart of
121
+ `register_doctype`.
122
+
123
+ A plugin calls this at startup and the master becomes a first-class type
124
+ everywhere `MASTER_TABLES` is consulted: the REST CRUD surface
125
+ (`/api/masters/{slug}`) and the chat tools (search_masters,
126
+ get_master_fields, create/update/delete_master — their schemas and the
127
+ system prompt are built from the live registry per request). Fields are
128
+ never declared: they're introspected from the table at call time, so every
129
+ text column of `table` is immediately searchable.
130
+
131
+ `name_field` is the human display column (e.g. "company_name").
132
+ `name_prefix` enables auto-generated ids (prefix "LEAD" -> LEAD-001) when a
133
+ record is created without an explicit `name`. `identity_alias` lets callers
134
+ address the `name` PK under a friendlier key, like item's `item_code`.
135
+ """
136
+ MASTER_TABLES[slug] = (table, name_field)
137
+ if name_prefix:
138
+ MASTER_NAME_PREFIXES[slug] = name_prefix
139
+ if identity_alias:
140
+ MASTER_IDENTITY_ALIAS[slug] = identity_alias
141
+
142
+
98
143
  def get_document_class(doctype_slug: str):
99
144
  """Get document class from URL slug."""
100
145
  doctype = SLUG_TO_DOCTYPE.get(doctype_slug)
@@ -447,6 +447,7 @@ export const api = {
447
447
  getPublicManagerStatus: () => request<{ active: boolean; user?: any }>("/auth/public-manager"),
448
448
  createPublicManager: () => request<any>("/auth/public-manager", { method: "POST" }),
449
449
  removePublicManager: () => request<any>("/auth/public-manager", { method: "DELETE" }),
450
+ getHealth: () => request<{ status: string; version: string }>("/health"),
450
451
  getSettings: () => request<Record<string, string>>("/auth/settings"),
451
452
  updateSettings: (data: Record<string, string>) =>
452
453
  request<Record<string, string>>("/auth/settings", { method: "PUT", body: JSON.stringify(data) }),
@@ -0,0 +1,33 @@
1
+ """Lambda ERP - Core ERP logic packaged as a standalone library."""
2
+
3
+
4
+ def get_app_version() -> str:
5
+ """Runtime version of the ERP core.
6
+
7
+ The single human-edited source is the packaging version in `pyproject.toml`,
8
+ which the release process bumps. A wheel/pip install doesn't ship
9
+ `pyproject.toml`, so there we read the installed package metadata (populated
10
+ from that same version at build time). A source checkout (incl. editable
11
+ dev) reads `pyproject.toml` directly, so it's correct without a reinstall.
12
+ Falls back to "dev" when neither is available.
13
+ """
14
+ import re
15
+ from pathlib import Path
16
+
17
+ pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
18
+ if pyproject.is_file():
19
+ match = re.search(
20
+ r'^version\s*=\s*"([^"]+)"', pyproject.read_text(encoding="utf-8"), re.MULTILINE
21
+ )
22
+ if match:
23
+ return match.group(1)
24
+
25
+ try:
26
+ from importlib.metadata import version
27
+
28
+ return version("lambda-erp")
29
+ except Exception:
30
+ return "dev"
31
+
32
+
33
+ __version__ = get_app_version()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lambda-erp"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Core ERP logic - accounting, sales, purchasing, inventory"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -1,3 +0,0 @@
1
- """Lambda ERP - Core ERP logic packaged as a standalone library."""
2
-
3
- __version__ = "0.1.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes