lambda-erp 0.8.13__tar.gz → 0.8.14__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 (89) hide show
  1. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/PKG-INFO +1 -1
  2. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/masters.py +14 -2
  3. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/services.py +12 -0
  4. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/pyproject.toml +1 -1
  5. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/.gitignore +0 -0
  6. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/LICENSE +0 -0
  7. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/README.md +0 -0
  8. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/__init__.py +0 -0
  9. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/attachments.py +0 -0
  10. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/auth.py +0 -0
  11. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/bootstrap.py +0 -0
  12. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/chat.py +0 -0
  13. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/demo_limits.py +0 -0
  14. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/deps.py +0 -0
  15. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/errors.py +0 -0
  16. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/main.py +0 -0
  17. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/oauth.py +0 -0
  18. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/pdf.py +0 -0
  19. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/providers.py +0 -0
  20. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/remarks_md.py +0 -0
  21. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/__init__.py +0 -0
  22. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/accounting.py +0 -0
  23. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/actions.py +0 -0
  24. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/admin.py +0 -0
  25. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/analytics.py +0 -0
  26. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/availability.py +0 -0
  27. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/bank_reconciliation.py +0 -0
  28. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/chat_api.py +0 -0
  29. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/documents.py +0 -0
  30. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/mcp.py +0 -0
  31. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/proposals.py +0 -0
  32. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/reports.py +0 -0
  33. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/routers/setup.py +0 -0
  34. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/templates/document.html +0 -0
  35. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/api/templates/proposal.html +0 -0
  36. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/docs/agents/README.md +0 -0
  37. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/frontend/LICENSE +0 -0
  38. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/frontend/README.md +0 -0
  39. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/frontend/src/api/client.ts +0 -0
  40. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/__init__.py +0 -0
  41. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/__init__.py +0 -0
  42. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/bank_transaction.py +0 -0
  43. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/budget.py +0 -0
  44. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/chart_of_accounts.py +0 -0
  45. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/general_ledger.py +0 -0
  46. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/journal_entry.py +0 -0
  47. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/payment_entry.py +0 -0
  48. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/pos_invoice.py +0 -0
  49. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/purchase_invoice.py +0 -0
  50. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/revaluation.py +0 -0
  51. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/sales_invoice.py +0 -0
  52. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/__init__.py +0 -0
  53. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/engine.py +0 -0
  54. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/pack.py +0 -0
  55. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/packs/__init__.py +0 -0
  56. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/packs/ch.py +0 -0
  57. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/packs/de_common.py +0 -0
  58. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/packs/de_skr03.py +0 -0
  59. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/packs/de_skr04.py +0 -0
  60. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/packs/generic.py +0 -0
  61. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/profiles.py +0 -0
  62. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/setup/spine.py +0 -0
  63. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/accounting/subscription.py +0 -0
  64. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/assets/__init__.py +0 -0
  65. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/assets/asset.py +0 -0
  66. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/assets/reservation.py +0 -0
  67. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/buying/__init__.py +0 -0
  68. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/buying/purchase_order.py +0 -0
  69. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/controllers/__init__.py +0 -0
  70. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/controllers/currency.py +0 -0
  71. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/controllers/defaults.py +0 -0
  72. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/controllers/pricing_rule.py +0 -0
  73. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/controllers/taxes_and_totals.py +0 -0
  74. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/database.py +0 -0
  75. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/exceptions.py +0 -0
  76. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/hooks.py +0 -0
  77. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/model.py +0 -0
  78. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/selling/__init__.py +0 -0
  79. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/selling/proposal.py +0 -0
  80. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/selling/quotation.py +0 -0
  81. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/selling/sales_order.py +0 -0
  82. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/simulation.py +0 -0
  83. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/stock/__init__.py +0 -0
  84. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/stock/delivery_note.py +0 -0
  85. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/stock/purchase_receipt.py +0 -0
  86. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/stock/stock_entry.py +0 -0
  87. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/stock/stock_ledger.py +0 -0
  88. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/lambda_erp/utils.py +0 -0
  89. {lambda_erp-0.8.13 → lambda_erp-0.8.14}/terraform/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lambda-erp
3
- Version: 0.8.13
3
+ Version: 0.8.14
4
4
  Summary: Core ERP logic - accounting, sales, purchasing, inventory
5
5
  Author: TORUS INVESTMENTS AG
6
6
  License-Expression: Apache-2.0
@@ -1,6 +1,7 @@
1
1
  """Master data CRUD: Customer, Supplier, Item, Warehouse, Account, Company."""
2
2
 
3
3
  import re
4
+ import secrets
4
5
 
5
6
  from fastapi import APIRouter, Depends, HTTPException, Query, Request
6
7
  from lambda_erp.database import get_db
@@ -9,7 +10,7 @@ from lambda_erp.utils import _dict, now
9
10
  # them; imported here (not moved) so existing `from api.routers.masters import
10
11
  # MASTER_IDENTITY_ALIAS`-style imports keep working.
11
12
  from api.services import (
12
- MASTER_TABLES, MASTER_NAME_PREFIXES, MASTER_IDENTITY_ALIAS,
13
+ MASTER_TABLES, MASTER_NAME_PREFIXES, MASTER_RANDOM_NAME_TYPES, MASTER_IDENTITY_ALIAS,
13
14
  MASTER_REFERENCE_CHECKS,
14
15
  _search_clause, _where_from_filters, master_search_columns, count_query_cached,
15
16
  )
@@ -183,6 +184,14 @@ def _generate_master_name(db, doctype: str, prefix: str) -> str:
183
184
  return f"{prefix}-{number:03d}"
184
185
 
185
186
 
187
+ def _generate_random_master_name(db, doctype: str, prefix: str) -> str:
188
+ """Opaque, index-scale id for high-volume registered masters."""
189
+ while True:
190
+ name = f"{prefix}-{secrets.token_hex(8).upper()}"
191
+ if not db.exists(doctype, name):
192
+ return name
193
+
194
+
186
195
  def _normalize_master_data(data: dict) -> dict:
187
196
  normalized = _dict(data)
188
197
  for key, value in list(normalized.items()):
@@ -218,7 +227,10 @@ def create_master_record(master_type: str, data: dict) -> dict:
218
227
  if not doc.get("name"):
219
228
  prefix = MASTER_NAME_PREFIXES.get(master_type)
220
229
  if prefix:
221
- doc["name"] = _generate_master_name(db, doctype, prefix)
230
+ if master_type in MASTER_RANDOM_NAME_TYPES:
231
+ doc["name"] = _generate_random_master_name(db, doctype, prefix)
232
+ else:
233
+ doc["name"] = _generate_master_name(db, doctype, prefix)
222
234
  elif master_type == "company" and doc.get("company_name"):
223
235
  # A company's id is conventionally its name (mirrors /setup/company).
224
236
  doc["name"] = doc["company_name"]
@@ -90,6 +90,11 @@ MASTER_NAME_PREFIXES = {
90
90
  "warehouse": "WH",
91
91
  }
92
92
 
93
+ # High-volume plugin masters can opt out of sequential ids. Sequential naming
94
+ # must inspect existing suffixes after a process restart; opaque random ids keep
95
+ # inserts constant-time for index-scale reference tables.
96
+ MASTER_RANDOM_NAME_TYPES: set[str] = set()
97
+
93
98
  # An Item's code is stored in the primary-key column `name`, but every
94
99
  # transaction line and report references it as `item_code`. Accept that
95
100
  # intuitive alias on the master API so callers (LLM tools, REST clients) can
@@ -213,6 +218,7 @@ def document_columns(doctype_slug: str) -> set:
213
218
 
214
219
  def register_master(slug: str, table: str, name_field: str, *,
215
220
  name_prefix: str | None = None,
221
+ random_name: bool = False,
216
222
  identity_alias: str | None = None,
217
223
  description: str | None = None,
218
224
  fields: list[str] | None = None,
@@ -236,6 +242,12 @@ def register_master(slug: str, table: str, name_field: str, *,
236
242
  MASTER_TABLES[slug] = (table, name_field)
237
243
  if name_prefix:
238
244
  MASTER_NAME_PREFIXES[slug] = name_prefix
245
+ if random_name:
246
+ if not name_prefix:
247
+ raise ValueError("random_name requires name_prefix")
248
+ MASTER_RANDOM_NAME_TYPES.add(slug)
249
+ else:
250
+ MASTER_RANDOM_NAME_TYPES.discard(slug)
239
251
  if identity_alias:
240
252
  MASTER_IDENTITY_ALIAS[slug] = identity_alias
241
253
  if description or fields:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lambda-erp"
3
- version = "0.8.13"
3
+ version = "0.8.14"
4
4
  description = "Core ERP logic - accounting, sales, purchasing, inventory"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.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