lambda-erp 0.6.0__tar.gz → 0.6.1__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 (83) hide show
  1. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/PKG-INFO +1 -1
  2. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/chat.py +39 -2
  3. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/services.py +17 -0
  4. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/pyproject.toml +1 -1
  5. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/.gitignore +0 -0
  6. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/LICENSE +0 -0
  7. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/README.md +0 -0
  8. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/__init__.py +0 -0
  9. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/attachments.py +0 -0
  10. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/auth.py +0 -0
  11. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/bootstrap.py +0 -0
  12. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/demo_limits.py +0 -0
  13. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/deps.py +0 -0
  14. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/errors.py +0 -0
  15. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/main.py +0 -0
  16. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/oauth.py +0 -0
  17. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/pdf.py +0 -0
  18. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/providers.py +0 -0
  19. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/remarks_md.py +0 -0
  20. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/__init__.py +0 -0
  21. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/accounting.py +0 -0
  22. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/admin.py +0 -0
  23. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/analytics.py +0 -0
  24. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/bank_reconciliation.py +0 -0
  25. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/chat_api.py +0 -0
  26. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/documents.py +0 -0
  27. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/masters.py +0 -0
  28. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/proposals.py +0 -0
  29. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/reports.py +0 -0
  30. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/routers/setup.py +0 -0
  31. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/templates/document.html +0 -0
  32. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/api/templates/proposal.html +0 -0
  33. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/docs/agents/README.md +0 -0
  34. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/frontend/LICENSE +0 -0
  35. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/frontend/README.md +0 -0
  36. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/frontend/src/api/client.ts +0 -0
  37. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/__init__.py +0 -0
  38. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/__init__.py +0 -0
  39. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/bank_transaction.py +0 -0
  40. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/budget.py +0 -0
  41. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/chart_of_accounts.py +0 -0
  42. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/general_ledger.py +0 -0
  43. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/journal_entry.py +0 -0
  44. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/payment_entry.py +0 -0
  45. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/pos_invoice.py +0 -0
  46. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/purchase_invoice.py +0 -0
  47. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/revaluation.py +0 -0
  48. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/sales_invoice.py +0 -0
  49. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/__init__.py +0 -0
  50. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/engine.py +0 -0
  51. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/pack.py +0 -0
  52. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/packs/__init__.py +0 -0
  53. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/packs/ch.py +0 -0
  54. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/packs/de_common.py +0 -0
  55. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/packs/de_skr03.py +0 -0
  56. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/packs/de_skr04.py +0 -0
  57. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/packs/generic.py +0 -0
  58. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/profiles.py +0 -0
  59. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/setup/spine.py +0 -0
  60. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/accounting/subscription.py +0 -0
  61. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/buying/__init__.py +0 -0
  62. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/buying/purchase_order.py +0 -0
  63. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/controllers/__init__.py +0 -0
  64. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/controllers/currency.py +0 -0
  65. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/controllers/defaults.py +0 -0
  66. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/controllers/pricing_rule.py +0 -0
  67. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/controllers/taxes_and_totals.py +0 -0
  68. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/database.py +0 -0
  69. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/exceptions.py +0 -0
  70. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/hooks.py +0 -0
  71. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/model.py +0 -0
  72. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/selling/__init__.py +0 -0
  73. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/selling/proposal.py +0 -0
  74. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/selling/quotation.py +0 -0
  75. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/selling/sales_order.py +0 -0
  76. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/simulation.py +0 -0
  77. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/stock/__init__.py +0 -0
  78. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/stock/delivery_note.py +0 -0
  79. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/stock/purchase_receipt.py +0 -0
  80. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/stock/stock_entry.py +0 -0
  81. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/stock/stock_ledger.py +0 -0
  82. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/lambda_erp/utils.py +0 -0
  83. {lambda_erp-0.6.0 → lambda_erp-0.6.1}/terraform/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lambda-erp
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: Core ERP logic - accounting, sales, purchasing, inventory
5
5
  Author: TORUS INVESTMENTS AG
6
6
  License-Expression: Apache-2.0
@@ -463,7 +463,9 @@ TOOLS = [
463
463
  "type": "object",
464
464
  "properties": {
465
465
  "doctype": {"type": "string", "enum": DOCUMENT_SLUGS, "description": "Document type slug"},
466
- "filters": {"type": "object", "description": "Optional filters like {\"status\": \"Draft\", \"customer\": \"CUST-001\"}", "default": {}},
466
+ "filters": {"type": "object", "description": "Optional equality filters on any column, e.g. {\"status\": \"Draft\", \"customer\": \"CUST-001\", \"lead_id\": \"LEAD-3316\"}", "default": {}},
467
+ "order_by": {"type": "string", "description": "Optional column to sort by (e.g. \"occurred_at\" for a timeline). Defaults to creation."},
468
+ "order": {"type": "string", "enum": ["asc", "desc"], "description": "Sort direction (default desc)", "default": "desc"},
467
469
  "limit": {"type": "integer", "description": "Max results (default 20, max 500)", "default": 20},
468
470
  },
469
471
  "required": ["doctype"],
@@ -1175,6 +1177,38 @@ def _extra_master_types():
1175
1177
  return sorted(m for m in services.MASTER_TABLES if m not in MASTER_TYPES)
1176
1178
 
1177
1179
 
1180
+ def _chat_doctype_section() -> str:
1181
+ """A '## Custom record types' block for doctypes registered via
1182
+ register_chat_doctype: each type's description, key fields, and its
1183
+ LINK_FIELDS relationships (read live from the Document class). Tells the
1184
+ model these are driven with the DOCUMENT tools and how they attach to a
1185
+ parent (e.g. a contact links to a lead via `lead_id`). Empty when none."""
1186
+ slugs = [s for s in services.CHAT_DOCTYPES if s in services.SLUG_TO_DOCTYPE]
1187
+ if not slugs:
1188
+ return ""
1189
+ lines = []
1190
+ for slug in slugs:
1191
+ meta = services.CHAT_DOCTYPES[slug]
1192
+ doctype = services.SLUG_TO_DOCTYPE[slug]
1193
+ cls = services.DOCUMENT_CLASSES.get(doctype)
1194
+ links = getattr(cls, "LINK_FIELDS", None) or {}
1195
+ line = f"- **{doctype}** (slug `{slug}`): {meta['description']}"
1196
+ if meta.get("fields"):
1197
+ line += f" Key fields: {', '.join('`' + f + '`' for f in meta['fields'])}."
1198
+ if links:
1199
+ link_str = ", ".join(f"`{f}` → the {t}'s name" for f, t in links.items())
1200
+ line += f" Links: {link_str}."
1201
+ lines.append(line)
1202
+ return (
1203
+ "\n\n## Custom record types (deployment-specific)\n"
1204
+ "Manage these with the DOCUMENT tools (`create_document`, `update_document`, "
1205
+ "`list_documents`, `get_document`) — NOT the master tools — so their `validate()` "
1206
+ "runs. To attach one to a parent, set its link field to the parent record's `name` "
1207
+ "(find the parent first, e.g. with `search_masters` or `list_documents`). Do not put "
1208
+ "child data on the parent's own fields.\n" + "\n".join(lines)
1209
+ )
1210
+
1211
+
1178
1212
  def build_tools():
1179
1213
  """TOOLS with the doctype/master enums widened from the live registries.
1180
1214
 
@@ -1217,6 +1251,8 @@ def _handle_list_documents(args):
1217
1251
  args["doctype"],
1218
1252
  filters=args.get("filters"),
1219
1253
  limit=args.get("limit", 20),
1254
+ order_by=args.get("order_by"),
1255
+ order=args.get("order", "desc"),
1220
1256
  )
1221
1257
  for row in rows:
1222
1258
  for key in child_keys:
@@ -1898,6 +1934,7 @@ def build_system_prompt(user_info: dict | None = None, channel: str = "web"):
1898
1934
  f"display = `{services.MASTER_TABLES[m][1]}`"
1899
1935
  for m in extra_masters
1900
1936
  )
1937
+ chat_doctype_section = _chat_doctype_section()
1901
1938
 
1902
1939
  # Channel-aware link guidance. On the "web" channel the reader is a browser
1903
1940
  # inside the ERP, so web-relative links are clickable. On the "api" channel the
@@ -2006,7 +2043,7 @@ When a user asks you to do something they don't have permission for, explain wha
2006
2043
  - **Buying:** purchase-order, purchase-invoice
2007
2044
  - **Accounting:** payment-entry, journal-entry, budget, subscription, bank-transaction
2008
2045
  - **Stock:** stock-entry, delivery-note, purchase-receipt
2009
- - **Settings:** pricing-rule{extension_doctypes_line}
2046
+ - **Settings:** pricing-rule{extension_doctypes_line}{chat_doctype_section}
2010
2047
 
2011
2048
  ## Document Workflow & What Each Document Does
2012
2049
 
@@ -216,6 +216,23 @@ def register_master(slug: str, table: str, name_field: str, *,
216
216
  MASTER_IDENTITY_ALIAS[slug] = identity_alias
217
217
 
218
218
 
219
+ # Chat guidance for registered doctypes: slug -> {"description", "fields"}. The
220
+ # document tools (create/update/list/get_document) already accept any registered
221
+ # doctype and run its validate(); this registry only teaches the AI chat WHAT a
222
+ # custom doctype is and HOW it links, so it uses the validated document path
223
+ # instead of, say, scribbling on a parent's fields. Relationships are read
224
+ # automatically from the Document class's LINK_FIELDS — not declared here.
225
+ CHAT_DOCTYPES: dict = {}
226
+
227
+
228
+ def register_chat_doctype(slug: str, *, description: str, fields: list | None = None) -> None:
229
+ """Give the AI chat a description (and optional key-field hints) for a doctype
230
+ already registered via `register_doctype`, so build_system_prompt can tell the
231
+ model what it is and how it links (LINK_FIELDS are surfaced automatically).
232
+ No new tools — the document tools already cover every registered doctype."""
233
+ CHAT_DOCTYPES[slug] = {"description": description, "fields": fields or []}
234
+
235
+
219
236
  def get_document_class(doctype_slug: str):
220
237
  """Get document class from URL slug."""
221
238
  doctype = SLUG_TO_DOCTYPE.get(doctype_slug)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lambda-erp"
3
- version = "0.6.0"
3
+ version = "0.6.1"
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