lambda-erp 1.1.9__tar.gz → 1.1.10__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-1.1.9 → lambda_erp-1.1.10}/PKG-INFO +1 -1
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/chat.py +12 -6
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/pdf.py +11 -11
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/pdf_contract.py +3 -2
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/pyproject.toml +1 -1
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/.gitignore +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/LICENSE +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/README.md +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/attachments.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/auth.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/bootstrap.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/chat_links.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/demo_limits.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/deps.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/erp_context.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/errors.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/list_values.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/main.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/mcp_oauth.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/oauth.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/pdf_exports.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/pdf_profiles.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/providers.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/remarks_md.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/accounting.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/actions.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/admin.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/analytics.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/availability.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/bank_reconciliation.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/bank_statements.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/chat_api.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/documents.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/masters.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/mcp.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/proposals.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/reports.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/setup.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/services.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/_proposal_totals.html +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/_record_base.html +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/document.html +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/proposal.html +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/record.html +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/time_filters.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/tool_permissions.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/docs/agents/README.md +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/frontend/LICENSE +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/frontend/README.md +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/frontend/src/api/client.ts +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/bank_account.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/bank_reconciliation.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/bank_statement_import.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/bank_transaction.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/budget.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/camt.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/chart_of_accounts.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/general_ledger.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/journal_entry.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/payment_entry.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/pos_invoice.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/purchase_invoice.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/revaluation.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/sales_invoice.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/settlement.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/engine.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/pack.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/ch.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/de_common.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/de_skr03.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/de_skr04.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/generic.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/profiles.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/spine.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/subscription.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/assets/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/assets/asset.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/assets/lifecycle.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/assets/reservation.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/buying/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/buying/purchase_order.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/currency.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/defaults.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/item_price.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/item_prices.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/pricing_rule.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/taxes_and_totals.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/database.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/exceptions.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/hooks.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/master_integrity.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/model.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/selling/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/selling/proposal.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/selling/quotation.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/selling/sales_order.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/simulation.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/__init__.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/delivery_note.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/purchase_receipt.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/stock_entry.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/stock_ledger.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/timestamps.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/utils.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/validation.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/workflow.py +0 -0
- {lambda_erp-1.1.9 → lambda_erp-1.1.10}/terraform/README.md +0 -0
|
@@ -473,6 +473,11 @@ DOCUMENT_SLUGS = [
|
|
|
473
473
|
|
|
474
474
|
MASTER_TYPES = ["customer", "supplier", "item", "warehouse", "account", "company", "cost-center"]
|
|
475
475
|
|
|
476
|
+
# Discovery for direct (MCP) clients, which don't get the chat system prompt.
|
|
477
|
+
# The native chat already carries WORKFLOW_GUIDANCE in its prompt; offering the
|
|
478
|
+
# tool there made the model re-fetch ~4k tokens of the same context most turns.
|
|
479
|
+
DIRECT_CLIENT_ONLY_TOOLS = frozenset({"get_erp_context"})
|
|
480
|
+
|
|
476
481
|
TOOLS = [
|
|
477
482
|
{"type": "function", "function": {
|
|
478
483
|
"name": "get_erp_context",
|
|
@@ -1400,7 +1405,7 @@ def _chat_doctype_section() -> str:
|
|
|
1400
1405
|
)
|
|
1401
1406
|
|
|
1402
1407
|
|
|
1403
|
-
def build_tools(user_info: dict | None = None):
|
|
1408
|
+
def build_tools(user_info: dict | None = None, *, native_chat: bool = False):
|
|
1404
1409
|
"""TOOLS widened with live plugin types and registered business actions.
|
|
1405
1410
|
|
|
1406
1411
|
Plugins register doctypes and masters at startup — after this module is
|
|
@@ -1410,19 +1415,20 @@ def build_tools(user_info: dict | None = None):
|
|
|
1410
1415
|
per request (cheap next to the LLM call), which also makes plugin load
|
|
1411
1416
|
order irrelevant. Only enums that exactly match the core lists are
|
|
1412
1417
|
widened, so any differently-constrained doctype/master param (e.g. a
|
|
1413
|
-
curated subset) is left alone.
|
|
1418
|
+
curated subset) is left alone. `native_chat` drops the direct-client-only
|
|
1419
|
+
discovery tools the chat prompt already covers.
|
|
1414
1420
|
"""
|
|
1415
1421
|
extra_docs = _extra_document_slugs()
|
|
1416
1422
|
extra_masters = _extra_master_types()
|
|
1417
1423
|
action_tools = services.registered_action_tools()
|
|
1418
|
-
hidden_tools: set[str] = set()
|
|
1424
|
+
hidden_tools: set[str] = set(DIRECT_CLIENT_ONLY_TOOLS) if native_chat else set()
|
|
1419
1425
|
if user_info is not None:
|
|
1420
1426
|
role = user_info.get("role")
|
|
1421
1427
|
action_tools = [
|
|
1422
1428
|
tool for tool in action_tools
|
|
1423
1429
|
if tool_allowed(tool["function"]["name"], role)
|
|
1424
1430
|
]
|
|
1425
|
-
hidden_tools
|
|
1431
|
+
hidden_tools |= {
|
|
1426
1432
|
tool["function"]["name"] for tool in TOOLS
|
|
1427
1433
|
if not tool_allowed(tool["function"]["name"], role)
|
|
1428
1434
|
}
|
|
@@ -3645,7 +3651,7 @@ async def run_thinking_loop(
|
|
|
3645
3651
|
# or the legacy API for compatible models. Returns Chat-shaped
|
|
3646
3652
|
# (message, usage) either way, so everything below is unchanged.
|
|
3647
3653
|
message, usage = await asyncio.to_thread(
|
|
3648
|
-
_orchestrator_turn, openai_client, messages, build_tools(user_info), max_completion,
|
|
3654
|
+
_orchestrator_turn, openai_client, messages, build_tools(user_info, native_chat=True), max_completion,
|
|
3649
3655
|
)
|
|
3650
3656
|
except Exception as e:
|
|
3651
3657
|
await on_event({"type": "error", "content": f"Error calling LLM: {e}"})
|
|
@@ -3817,7 +3823,7 @@ async def run_thinking_loop(
|
|
|
3817
3823
|
"stands alone: do NOT refer to 'the results above' or to earlier messages."
|
|
3818
3824
|
),
|
|
3819
3825
|
}],
|
|
3820
|
-
build_tools(user_info),
|
|
3826
|
+
build_tools(user_info, native_chat=True),
|
|
3821
3827
|
2048,
|
|
3822
3828
|
tool_choice="none",
|
|
3823
3829
|
)
|
|
@@ -193,17 +193,17 @@ def _render_commercial_pdf(doctype_slug, name, doc, contract):
|
|
|
193
193
|
if doc.get("status"):
|
|
194
194
|
meta_fields.append({"label": "Status", "value": doc["status"]})
|
|
195
195
|
|
|
196
|
-
# Items —
|
|
197
|
-
#
|
|
198
|
-
#
|
|
199
|
-
#
|
|
196
|
+
# Items — print the name written on each line. A line may carry its own
|
|
197
|
+
# wording under a generic article (e.g. one project service with a
|
|
198
|
+
# "Phase 1: …" line per step), so the Item master's name is only a
|
|
199
|
+
# fallback for a line that has none.
|
|
200
200
|
items = doc.get("items", [])
|
|
201
201
|
for item in items:
|
|
202
202
|
code = item.get("item_code")
|
|
203
|
-
if code:
|
|
204
|
-
|
|
205
|
-
if
|
|
206
|
-
item["item_name"] =
|
|
203
|
+
if code and not item.get("item_name"):
|
|
204
|
+
master_item_name = db.get_value("Item", code, "item_name")
|
|
205
|
+
if master_item_name:
|
|
206
|
+
item["item_name"] = master_item_name
|
|
207
207
|
|
|
208
208
|
taxes = doc.get("taxes", [])
|
|
209
209
|
|
|
@@ -340,11 +340,11 @@ def _render_proposal_pdf(name, proposal, contract):
|
|
|
340
340
|
contract['tokens'].extend(quote_contract['tokens'])
|
|
341
341
|
contract['warnings'].extend(quote_contract['warnings'])
|
|
342
342
|
items = quote.get("items", []) or []
|
|
343
|
-
#
|
|
344
|
-
# single-document path.
|
|
343
|
+
# The line's own name wins; the master's only fills an empty one,
|
|
344
|
+
# consistent with the single-document path.
|
|
345
345
|
for item in items:
|
|
346
346
|
code = item.get("item_code")
|
|
347
|
-
if code:
|
|
347
|
+
if code and not item.get("item_name"):
|
|
348
348
|
live = db.get_value("Item", code, "item_name")
|
|
349
349
|
if live:
|
|
350
350
|
item["item_name"] = live
|
|
@@ -254,8 +254,9 @@ def _item(row, tokens, index):
|
|
|
254
254
|
if not any(not missing(row.get(k)) for k in ('item_code', 'item_name', 'description')):
|
|
255
255
|
raise PDFError(f'PDF requires item identity: items[{index}]')
|
|
256
256
|
if row.get('item_code'):
|
|
257
|
-
|
|
258
|
-
row
|
|
257
|
+
master_name = resolve('Item', row['item_code']) # the article must still exist
|
|
258
|
+
if missing(row.get('item_name')):
|
|
259
|
+
row['item_name'] = master_name # only the isolated output snapshot is changed
|
|
259
260
|
tokens.append(str(row.get('item_name') or row.get('item_code') or row['description']))
|
|
260
261
|
|
|
261
262
|
|
|
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
|
|
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
|