lambda-erp 0.8.2__tar.gz → 0.8.4__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 (88) hide show
  1. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/PKG-INFO +1 -1
  2. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/chat.py +1 -0
  3. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/services.py +49 -0
  4. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/packs/ch.py +9 -0
  5. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/database.py +12 -0
  6. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/pyproject.toml +1 -1
  7. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/.gitignore +0 -0
  8. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/LICENSE +0 -0
  9. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/README.md +0 -0
  10. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/__init__.py +0 -0
  11. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/attachments.py +0 -0
  12. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/auth.py +0 -0
  13. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/bootstrap.py +0 -0
  14. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/demo_limits.py +0 -0
  15. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/deps.py +0 -0
  16. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/errors.py +0 -0
  17. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/main.py +0 -0
  18. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/oauth.py +0 -0
  19. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/pdf.py +0 -0
  20. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/providers.py +0 -0
  21. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/remarks_md.py +0 -0
  22. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/__init__.py +0 -0
  23. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/accounting.py +0 -0
  24. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/admin.py +0 -0
  25. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/analytics.py +0 -0
  26. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/availability.py +0 -0
  27. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/bank_reconciliation.py +0 -0
  28. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/chat_api.py +0 -0
  29. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/documents.py +0 -0
  30. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/masters.py +0 -0
  31. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/mcp.py +0 -0
  32. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/proposals.py +0 -0
  33. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/reports.py +0 -0
  34. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/routers/setup.py +0 -0
  35. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/templates/document.html +0 -0
  36. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/api/templates/proposal.html +0 -0
  37. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/docs/agents/README.md +0 -0
  38. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/frontend/LICENSE +0 -0
  39. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/frontend/README.md +0 -0
  40. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/frontend/src/api/client.ts +0 -0
  41. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/__init__.py +0 -0
  42. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/__init__.py +0 -0
  43. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/bank_transaction.py +0 -0
  44. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/budget.py +0 -0
  45. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/chart_of_accounts.py +0 -0
  46. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/general_ledger.py +0 -0
  47. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/journal_entry.py +0 -0
  48. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/payment_entry.py +0 -0
  49. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/pos_invoice.py +0 -0
  50. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/purchase_invoice.py +0 -0
  51. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/revaluation.py +0 -0
  52. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/sales_invoice.py +0 -0
  53. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/__init__.py +0 -0
  54. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/engine.py +0 -0
  55. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/pack.py +0 -0
  56. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/packs/__init__.py +0 -0
  57. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/packs/de_common.py +0 -0
  58. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/packs/de_skr03.py +0 -0
  59. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/packs/de_skr04.py +0 -0
  60. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/packs/generic.py +0 -0
  61. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/profiles.py +0 -0
  62. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/setup/spine.py +0 -0
  63. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/accounting/subscription.py +0 -0
  64. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/assets/__init__.py +0 -0
  65. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/assets/asset.py +0 -0
  66. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/assets/reservation.py +0 -0
  67. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/buying/__init__.py +0 -0
  68. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/buying/purchase_order.py +0 -0
  69. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/controllers/__init__.py +0 -0
  70. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/controllers/currency.py +0 -0
  71. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/controllers/defaults.py +0 -0
  72. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/controllers/pricing_rule.py +0 -0
  73. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/controllers/taxes_and_totals.py +0 -0
  74. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/exceptions.py +0 -0
  75. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/hooks.py +0 -0
  76. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/model.py +0 -0
  77. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/selling/__init__.py +0 -0
  78. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/selling/proposal.py +0 -0
  79. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/selling/quotation.py +0 -0
  80. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/selling/sales_order.py +0 -0
  81. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/simulation.py +0 -0
  82. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/stock/__init__.py +0 -0
  83. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/stock/delivery_note.py +0 -0
  84. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/stock/purchase_receipt.py +0 -0
  85. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/stock/stock_entry.py +0 -0
  86. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/stock/stock_ledger.py +0 -0
  87. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/lambda_erp/utils.py +0 -0
  88. {lambda_erp-0.8.2 → lambda_erp-0.8.4}/terraform/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lambda-erp
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: Core ERP logic - accounting, sales, purchasing, inventory
5
5
  Author: TORUS INVESTMENTS AG
6
6
  License-Expression: Apache-2.0
@@ -2280,6 +2280,7 @@ Use these only for items flagged `is_asset_tracked` on the Item master. Neither
2280
2280
  - **Reservation** (slug `reservation`) books a time window. Required: `from_datetime` and `to_datetime` (`YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`; a bare date means midnight). Windows are **half-open** — a hire ending on the 19th and the next starting on the 19th do NOT clash. Two ways to book:
2281
2281
  - **pooled** — set `item_code`, `warehouse` and `qty`: "*a* machine of this type from that yard". Use this at quotation/order time when the specific unit doesn't matter yet.
2282
2282
  - **unit** — set `asset`: "*that* machine". `item_code`, `warehouse` and `qty` are filled in from the Asset automatically.
2283
+ - **Who it's for (do NOT skip this):** set `party` to the hiring customer's id (e.g. `CUST-005`) with `party_type = "Customer"`. The customer is almost always given by NAME — "reserve the 17t for Hans Meisterhans" — so FIRST resolve the name to a record with `search_masters` (master_type "customer") and set `party` to the matched id; never put the raw name in `party`. No match → create the customer (or ask which existing one is meant), then book. A reservation with no `party` is only for an internal block (maintenance, transport, transfer) — if the user named a customer, a booking without `party` set is a bug, not an option.
2283
2284
  - `status` is Reserved / Out / Returned / Cancelled. **Reserved and Out block the calendar; Returned and Cancelled do not** — so a hire that comes back early frees its slot the moment you set it to Returned. Link a booking to the order that caused it with `voucher_type` + `voucher_no`.
2284
2285
  - **Double-booking is refused at save time.** If you get "already committed" (that unit is taken) or "free at" (the yard's pool is exhausted for that window), do NOT retry blindly — report the conflict, then offer the alternatives: different dates, a different yard, or a different model.
2285
2286
  - To answer "what's free between X and Y", list reservations for the item over the window and compare against the Assets that exist; a unit with no overlapping Reserved/Out row is available.
@@ -284,10 +284,59 @@ def get_document_class(doctype_slug: str):
284
284
  return doctype, DOCUMENT_CLASSES[doctype]
285
285
 
286
286
 
287
+ # Which Company default a doc's `taxes` table draws from — keyed by the taxes
288
+ # child doctype, so any doc that has a taxes table is covered without a hardcoded
289
+ # doctype list. Selling -> sales default, buying -> purchase default.
290
+ _TAX_CHILD_DEFAULT_FIELD = {
291
+ "Sales Taxes and Charges": "default_sales_tax_template",
292
+ "Purchase Taxes and Charges": "default_purchase_tax_template",
293
+ }
294
+
295
+
296
+ def _default_tax_rows(cls, data: dict) -> list | None:
297
+ """`taxes[]` rows to seed a new document from its company's default Tax
298
+ Template, or None to leave taxes untouched.
299
+
300
+ Applied ONLY when the caller omits `taxes` entirely — the exact case the chat
301
+ model kept hitting (it forgets to add MWST). An explicit `taxes: []` is left
302
+ alone, so a deliberately tax-free doc (export, reverse-charge, exempt) is
303
+ expressed by passing an empty list. The document's own tax calc then computes
304
+ `tax_amount` from the rate, so this only seeds charge_type/account/rate.
305
+ """
306
+ taxes_child = cls.CHILD_TABLES.get("taxes")
307
+ if not taxes_child or "taxes" in data:
308
+ return None
309
+ field = _TAX_CHILD_DEFAULT_FIELD.get(taxes_child[0])
310
+ company = data.get("company")
311
+ if not field or not company:
312
+ return None
313
+ db = get_db()
314
+ template = db.get_value("Company", company, field)
315
+ if not template:
316
+ return None
317
+ details = db.get_all("Tax Template Detail", filters={"parent": template},
318
+ fields=["*"], order_by="idx")
319
+ if not details:
320
+ return None
321
+ return [
322
+ {
323
+ "charge_type": d.get("charge_type") or "On Net Total",
324
+ "account_head": d.get("account_head"),
325
+ "rate": d.get("rate") or 0,
326
+ "description": d.get("description") or template,
327
+ "add_deduct_tax": "Add",
328
+ }
329
+ for d in details
330
+ ]
331
+
332
+
287
333
  def create_document(doctype_slug: str, data: dict) -> dict:
288
334
  doctype, cls = get_document_class(doctype_slug)
289
335
  if not cls:
290
336
  raise ValueError(f"Unknown document type: {doctype_slug}")
337
+ default_taxes = _default_tax_rows(cls, data)
338
+ if default_taxes is not None:
339
+ data = {**data, "taxes": default_taxes}
291
340
  doc = cls(data)
292
341
  doc.save()
293
342
  return doc.as_dict()
@@ -238,6 +238,15 @@ def ch_setup_tax(company_name, currency):
238
238
  for title, rate, leaf in _PURCHASE_TAXES:
239
239
  _template(title, "Purchase", rate, leaf)
240
240
 
241
+ # Make the 8.1% standard rate the company's default sales tax, so new
242
+ # invoices/quotations pick up MWST automatically (create_document seeds it
243
+ # when `taxes` is omitted). Purchase Vorsteuer stays manual — it varies by
244
+ # goods vs. investment, so there's no single sensible default.
245
+ default_sales = f"MWST Normalsatz 8.1% - {abbr}"
246
+ if db.exists("Tax Template", default_sales):
247
+ db.set_value("Company", company_name, {"default_sales_tax_template": default_sales})
248
+ summary.append(f"default sales tax: MWST Normalsatz 8.1%")
249
+
241
250
  return summary
242
251
 
243
252
 
@@ -2125,6 +2125,17 @@ def _m021_item_is_asset_tracked(db: "Database") -> None:
2125
2125
  db._add_column_if_missing("Item", "is_asset_tracked", "INTEGER DEFAULT 0")
2126
2126
 
2127
2127
 
2128
+ def _m022_company_default_tax_templates(db: "Database") -> None:
2129
+ """Per-company default sales/purchase Tax Templates. When a selling/buying
2130
+ document is created with NO `taxes` rows at all, create_document applies the
2131
+ matching default so VAT/MWST is not silently omitted (see api/services.py).
2132
+ Both default to NULL, so existing deployments are unaffected until a default
2133
+ is wired — e.g. the CH pack points the sales default at the 8.1% MWST
2134
+ template. Passing an explicit empty `taxes: []` still means a tax-free doc."""
2135
+ db._add_column_if_missing("Company", "default_sales_tax_template", "TEXT")
2136
+ db._add_column_if_missing("Company", "default_purchase_tax_template", "TEXT")
2137
+
2138
+
2128
2139
  Database.MIGRATIONS = [
2129
2140
  (1, "chat_message_session_id", _m001_chat_message_session_id),
2130
2141
  (2, "chat_session_user_id", _m002_chat_session_user_id),
@@ -2147,6 +2158,7 @@ Database.MIGRATIONS = [
2147
2158
  (19, "api_keys_per_user", _m019_api_keys_per_user),
2148
2159
  (20, "chat_attachment_openai_file", _m020_chat_attachment_openai_file),
2149
2160
  (21, "item_is_asset_tracked", _m021_item_is_asset_tracked),
2161
+ (22, "company_default_tax_templates", _m022_company_default_tax_templates),
2150
2162
  ]
2151
2163
 
2152
2164
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lambda-erp"
3
- version = "0.8.2"
3
+ version = "0.8.4"
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