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.
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/PKG-INFO +1 -1
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/masters.py +8 -5
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/services.py +11 -2
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/pyproject.toml +1 -1
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/.gitignore +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/LICENSE +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/README.md +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/attachments.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/auth.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/bootstrap.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/chat.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/demo_limits.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/deps.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/errors.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/main.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/oauth.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/pdf.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/providers.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/remarks_md.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/accounting.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/actions.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/admin.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/analytics.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/availability.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/bank_reconciliation.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/chat_api.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/documents.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/mcp.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/proposals.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/reports.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/routers/setup.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/templates/document.html +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/api/templates/proposal.html +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/docs/agents/README.md +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/frontend/LICENSE +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/frontend/README.md +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/frontend/src/api/client.ts +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/bank_transaction.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/budget.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/chart_of_accounts.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/general_ledger.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/journal_entry.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/payment_entry.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/pos_invoice.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/purchase_invoice.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/revaluation.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/sales_invoice.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/engine.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/pack.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/ch.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/de_common.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/de_skr03.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/de_skr04.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/packs/generic.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/profiles.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/setup/spine.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/accounting/subscription.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/assets/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/assets/asset.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/assets/reservation.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/buying/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/buying/purchase_order.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/currency.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/defaults.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/pricing_rule.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/controllers/taxes_and_totals.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/database.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/exceptions.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/hooks.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/model.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/selling/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/selling/proposal.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/selling/quotation.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/selling/sales_order.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/simulation.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/__init__.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/delivery_note.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/purchase_receipt.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/stock_entry.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/stock/stock_ledger.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/lambda_erp/utils.py +0 -0
- {lambda_erp-0.8.14 → lambda_erp-0.8.16}/terraform/README.md +0 -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,
|
|
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:
|
|
183
|
+
while db.exists(doctype, f"{prefix}-{number:0{digits}d}"):
|
|
183
184
|
number += 1
|
|
184
|
-
return f"{prefix}-{number:
|
|
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(
|
|
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`. `
|
|
240
|
-
|
|
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")
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|