lambda-erp 0.8.14__tar.gz → 0.8.16__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.14 → lambda_erp-0.8.16}/PKG-INFO +1 -1
  2. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/masters.py +8 -5
  3. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/services.py +11 -2
  4. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/pyproject.toml +1 -1
  5. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/.gitignore +0 -0
  6. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/LICENSE +0 -0
  7. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/README.md +0 -0
  8. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/__init__.py +0 -0
  9. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/attachments.py +0 -0
  10. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/auth.py +0 -0
  11. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/bootstrap.py +0 -0
  12. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/chat.py +0 -0
  13. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/demo_limits.py +0 -0
  14. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/deps.py +0 -0
  15. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/errors.py +0 -0
  16. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/main.py +0 -0
  17. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/oauth.py +0 -0
  18. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/pdf.py +0 -0
  19. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/providers.py +0 -0
  20. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/remarks_md.py +0 -0
  21. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/__init__.py +0 -0
  22. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/accounting.py +0 -0
  23. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/actions.py +0 -0
  24. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/admin.py +0 -0
  25. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/analytics.py +0 -0
  26. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/availability.py +0 -0
  27. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/bank_reconciliation.py +0 -0
  28. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/chat_api.py +0 -0
  29. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/documents.py +0 -0
  30. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/mcp.py +0 -0
  31. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/proposals.py +0 -0
  32. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/reports.py +0 -0
  33. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/setup.py +0 -0
  34. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/templates/document.html +0 -0
  35. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/templates/proposal.html +0 -0
  36. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/docs/agents/README.md +0 -0
  37. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/frontend/LICENSE +0 -0
  38. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/frontend/README.md +0 -0
  39. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/frontend/src/api/client.ts +0 -0
  40. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/__init__.py +0 -0
  41. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/__init__.py +0 -0
  42. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/bank_transaction.py +0 -0
  43. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/budget.py +0 -0
  44. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/chart_of_accounts.py +0 -0
  45. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/general_ledger.py +0 -0
  46. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/journal_entry.py +0 -0
  47. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/payment_entry.py +0 -0
  48. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/pos_invoice.py +0 -0
  49. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/purchase_invoice.py +0 -0
  50. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/revaluation.py +0 -0
  51. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/sales_invoice.py +0 -0
  52. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/__init__.py +0 -0
  53. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/engine.py +0 -0
  54. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/pack.py +0 -0
  55. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/__init__.py +0 -0
  56. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/ch.py +0 -0
  57. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/de_common.py +0 -0
  58. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/de_skr03.py +0 -0
  59. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/de_skr04.py +0 -0
  60. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/generic.py +0 -0
  61. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/profiles.py +0 -0
  62. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/spine.py +0 -0
  63. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/subscription.py +0 -0
  64. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/assets/__init__.py +0 -0
  65. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/assets/asset.py +0 -0
  66. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/assets/reservation.py +0 -0
  67. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/buying/__init__.py +0 -0
  68. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/buying/purchase_order.py +0 -0
  69. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/__init__.py +0 -0
  70. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/currency.py +0 -0
  71. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/defaults.py +0 -0
  72. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/pricing_rule.py +0 -0
  73. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/taxes_and_totals.py +0 -0
  74. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/database.py +0 -0
  75. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/exceptions.py +0 -0
  76. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/hooks.py +0 -0
  77. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/model.py +0 -0
  78. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/selling/__init__.py +0 -0
  79. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/selling/proposal.py +0 -0
  80. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/selling/quotation.py +0 -0
  81. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/selling/sales_order.py +0 -0
  82. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/simulation.py +0 -0
  83. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/__init__.py +0 -0
  84. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/delivery_note.py +0 -0
  85. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/purchase_receipt.py +0 -0
  86. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/stock_entry.py +0 -0
  87. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/stock_ledger.py +0 -0
  88. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/utils.py +0 -0
  89. {lambda_erp-0.8.14 → lambda_erp-0.8.16}/terraform/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lambda-erp
3
- Version: 0.8.14
3
+ Version: 0.8.16
4
4
  Summary: Core ERP logic - accounting, sales, purchasing, inventory
5
5
  Author: TORUS INVESTMENTS AG
6
6
  License-Expression: Apache-2.0
@@ -10,7 +10,8 @@ from lambda_erp.utils import _dict, now
10
10
  # them; imported here (not moved) so existing `from api.routers.masters import
11
11
  # MASTER_IDENTITY_ALIAS`-style imports keep working.
12
12
  from api.services import (
13
- MASTER_TABLES, MASTER_NAME_PREFIXES, MASTER_RANDOM_NAME_TYPES, MASTER_IDENTITY_ALIAS,
13
+ MASTER_TABLES, MASTER_NAME_PREFIXES, MASTER_NAME_DIGITS, MASTER_RANDOM_NAME_TYPES,
14
+ MASTER_IDENTITY_ALIAS,
14
15
  MASTER_REFERENCE_CHECKS,
15
16
  _search_clause, _where_from_filters, master_search_columns, count_query_cached,
16
17
  )
@@ -161,7 +162,7 @@ def _find_reference(master_type: str, name: str) -> str | None:
161
162
  return None
162
163
 
163
164
 
164
- def _generate_master_name(db, doctype: str, prefix: str) -> str:
165
+ def _generate_master_name(db, doctype: str, prefix: str, digits: int = 3) -> str:
165
166
  # Take the highest numeric suffix among strict "PREFIX-<digits>" names.
166
167
  # Names with a non-numeric tail (e.g. a custom "ITEM-COST-TEST") are ignored
167
168
  # rather than parsed — string-sorting them to the top used to reset the
@@ -179,9 +180,9 @@ def _generate_master_name(db, doctype: str, prefix: str) -> str:
179
180
 
180
181
  # Skip any number already taken by a non-standard name so we never collide.
181
182
  number = max_number + 1
182
- while db.exists(doctype, f"{prefix}-{number:03d}"):
183
+ while db.exists(doctype, f"{prefix}-{number:0{digits}d}"):
183
184
  number += 1
184
- return f"{prefix}-{number:03d}"
185
+ return f"{prefix}-{number:0{digits}d}"
185
186
 
186
187
 
187
188
  def _generate_random_master_name(db, doctype: str, prefix: str) -> str:
@@ -230,7 +231,9 @@ def create_master_record(master_type: str, data: dict) -> dict:
230
231
  if master_type in MASTER_RANDOM_NAME_TYPES:
231
232
  doc["name"] = _generate_random_master_name(db, doctype, prefix)
232
233
  else:
233
- doc["name"] = _generate_master_name(db, doctype, prefix)
234
+ doc["name"] = _generate_master_name(
235
+ db, doctype, prefix, MASTER_NAME_DIGITS.get(master_type, 3)
236
+ )
234
237
  elif master_type == "company" and doc.get("company_name"):
235
238
  # A company's id is conventionally its name (mirrors /setup/company).
236
239
  doc["name"] = doc["company_name"]
@@ -90,6 +90,10 @@ MASTER_NAME_PREFIXES = {
90
90
  "warehouse": "WH",
91
91
  }
92
92
 
93
+ # Minimum numeric suffix width for sequential plugin master ids. Core masters
94
+ # retain the historical three-digit form; plugins can opt into e.g. FIDX-0001.
95
+ MASTER_NAME_DIGITS: dict[str, int] = {}
96
+
93
97
  # High-volume plugin masters can opt out of sequential ids. Sequential naming
94
98
  # must inspect existing suffixes after a process restart; opaque random ids keep
95
99
  # inserts constant-time for index-scale reference tables.
@@ -218,6 +222,7 @@ def document_columns(doctype_slug: str) -> set:
218
222
 
219
223
  def register_master(slug: str, table: str, name_field: str, *,
220
224
  name_prefix: str | None = None,
225
+ name_digits: int = 3,
221
226
  random_name: bool = False,
222
227
  identity_alias: str | None = None,
223
228
  description: str | None = None,
@@ -236,12 +241,16 @@ def register_master(slug: str, table: str, name_field: str, *,
236
241
 
237
242
  `name_field` is the human display column (e.g. "company_name").
238
243
  `name_prefix` enables auto-generated ids (prefix "LEAD" -> LEAD-001) when a
239
- record is created without an explicit `name`. `identity_alias` lets callers
240
- address the `name` PK under a friendlier key, like item's `item_code`.
244
+ record is created without an explicit `name`. `name_digits` controls the
245
+ minimum sequential suffix width. `identity_alias` lets callers address the
246
+ `name` PK under a friendlier key, like item's `item_code`.
241
247
  """
242
248
  MASTER_TABLES[slug] = (table, name_field)
243
249
  if name_prefix:
250
+ if name_digits < 1:
251
+ raise ValueError("name_digits must be at least 1")
244
252
  MASTER_NAME_PREFIXES[slug] = name_prefix
253
+ MASTER_NAME_DIGITS[slug] = name_digits
245
254
  if random_name:
246
255
  if not name_prefix:
247
256
  raise ValueError("random_name requires name_prefix")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lambda-erp"
3
- version = "0.8.14"
3
+ version = "0.8.16"
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