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.
Files changed (114) hide show
  1. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/PKG-INFO +1 -1
  2. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/chat.py +12 -6
  3. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/pdf.py +11 -11
  4. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/pdf_contract.py +3 -2
  5. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/pyproject.toml +1 -1
  6. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/.gitignore +0 -0
  7. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/LICENSE +0 -0
  8. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/README.md +0 -0
  9. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/__init__.py +0 -0
  10. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/attachments.py +0 -0
  11. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/auth.py +0 -0
  12. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/bootstrap.py +0 -0
  13. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/chat_links.py +0 -0
  14. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/demo_limits.py +0 -0
  15. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/deps.py +0 -0
  16. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/erp_context.py +0 -0
  17. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/errors.py +0 -0
  18. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/list_values.py +0 -0
  19. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/main.py +0 -0
  20. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/mcp_oauth.py +0 -0
  21. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/oauth.py +0 -0
  22. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/pdf_exports.py +0 -0
  23. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/pdf_profiles.py +0 -0
  24. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/providers.py +0 -0
  25. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/remarks_md.py +0 -0
  26. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/__init__.py +0 -0
  27. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/accounting.py +0 -0
  28. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/actions.py +0 -0
  29. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/admin.py +0 -0
  30. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/analytics.py +0 -0
  31. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/availability.py +0 -0
  32. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/bank_reconciliation.py +0 -0
  33. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/bank_statements.py +0 -0
  34. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/chat_api.py +0 -0
  35. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/documents.py +0 -0
  36. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/masters.py +0 -0
  37. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/mcp.py +0 -0
  38. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/proposals.py +0 -0
  39. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/reports.py +0 -0
  40. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/routers/setup.py +0 -0
  41. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/services.py +0 -0
  42. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/_proposal_totals.html +0 -0
  43. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/_record_base.html +0 -0
  44. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/document.html +0 -0
  45. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/proposal.html +0 -0
  46. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/templates/record.html +0 -0
  47. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/time_filters.py +0 -0
  48. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/api/tool_permissions.py +0 -0
  49. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/docs/agents/README.md +0 -0
  50. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/frontend/LICENSE +0 -0
  51. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/frontend/README.md +0 -0
  52. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/frontend/src/api/client.ts +0 -0
  53. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/__init__.py +0 -0
  54. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/__init__.py +0 -0
  55. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/bank_account.py +0 -0
  56. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/bank_reconciliation.py +0 -0
  57. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/bank_statement_import.py +0 -0
  58. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/bank_transaction.py +0 -0
  59. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/budget.py +0 -0
  60. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/camt.py +0 -0
  61. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/chart_of_accounts.py +0 -0
  62. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/general_ledger.py +0 -0
  63. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/journal_entry.py +0 -0
  64. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/payment_entry.py +0 -0
  65. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/pos_invoice.py +0 -0
  66. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/purchase_invoice.py +0 -0
  67. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/revaluation.py +0 -0
  68. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/sales_invoice.py +0 -0
  69. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/settlement.py +0 -0
  70. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/__init__.py +0 -0
  71. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/engine.py +0 -0
  72. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/pack.py +0 -0
  73. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/__init__.py +0 -0
  74. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/ch.py +0 -0
  75. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/de_common.py +0 -0
  76. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/de_skr03.py +0 -0
  77. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/de_skr04.py +0 -0
  78. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/packs/generic.py +0 -0
  79. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/profiles.py +0 -0
  80. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/setup/spine.py +0 -0
  81. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/accounting/subscription.py +0 -0
  82. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/assets/__init__.py +0 -0
  83. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/assets/asset.py +0 -0
  84. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/assets/lifecycle.py +0 -0
  85. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/assets/reservation.py +0 -0
  86. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/buying/__init__.py +0 -0
  87. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/buying/purchase_order.py +0 -0
  88. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/__init__.py +0 -0
  89. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/currency.py +0 -0
  90. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/defaults.py +0 -0
  91. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/item_price.py +0 -0
  92. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/item_prices.py +0 -0
  93. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/pricing_rule.py +0 -0
  94. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/controllers/taxes_and_totals.py +0 -0
  95. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/database.py +0 -0
  96. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/exceptions.py +0 -0
  97. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/hooks.py +0 -0
  98. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/master_integrity.py +0 -0
  99. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/model.py +0 -0
  100. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/selling/__init__.py +0 -0
  101. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/selling/proposal.py +0 -0
  102. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/selling/quotation.py +0 -0
  103. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/selling/sales_order.py +0 -0
  104. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/simulation.py +0 -0
  105. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/__init__.py +0 -0
  106. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/delivery_note.py +0 -0
  107. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/purchase_receipt.py +0 -0
  108. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/stock_entry.py +0 -0
  109. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/stock/stock_ledger.py +0 -0
  110. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/timestamps.py +0 -0
  111. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/utils.py +0 -0
  112. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/validation.py +0 -0
  113. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/lambda_erp/workflow.py +0 -0
  114. {lambda_erp-1.1.9 → lambda_erp-1.1.10}/terraform/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lambda-erp
3
- Version: 1.1.9
3
+ Version: 1.1.10
4
4
  Summary: Core ERP logic - accounting, sales, purchasing, inventory
5
5
  Author: TORUS INVESTMENTS AG
6
6
  License-Expression: Apache-2.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 — refresh each line's item_name from the Item master so a later
197
- # correction to an item's name appears on the PDF, consistent with the
198
- # party/company names. Quantities, rates, and amounts stay as transacted.
199
- # Falls back to the name stored on the line if the master is gone.
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
- live_item_name = db.get_value("Item", code, "item_name")
205
- if live_item_name:
206
- item["item_name"] = live_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
- # Refresh each line's item_name from the master, consistent with the
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
- live = resolve('Item', row['item_code'])
258
- row['item_name'] = live # only the isolated output snapshot is changed
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
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lambda-erp"
3
- version = "1.1.9"
3
+ version = "1.1.10"
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
File without changes
File without changes
File without changes