lambda-erp 0.1.10__tar.gz → 0.1.12__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 (65) hide show
  1. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/PKG-INFO +1 -1
  2. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/pdf.py +35 -3
  3. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/routers/masters.py +9 -0
  4. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/pyproject.toml +1 -1
  5. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/.gitignore +0 -0
  6. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/LICENSE +0 -0
  7. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/README.md +0 -0
  8. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/__init__.py +0 -0
  9. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/attachments.py +0 -0
  10. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/auth.py +0 -0
  11. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/bootstrap.py +0 -0
  12. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/chat.py +0 -0
  13. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/demo_limits.py +0 -0
  14. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/deps.py +0 -0
  15. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/errors.py +0 -0
  16. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/main.py +0 -0
  17. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/providers.py +0 -0
  18. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/routers/__init__.py +0 -0
  19. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/routers/accounting.py +0 -0
  20. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/routers/admin.py +0 -0
  21. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/routers/analytics.py +0 -0
  22. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/routers/bank_reconciliation.py +0 -0
  23. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/routers/documents.py +0 -0
  24. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/routers/reports.py +0 -0
  25. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/routers/setup.py +0 -0
  26. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/services.py +0 -0
  27. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/api/templates/document.html +0 -0
  28. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/docs/agents/README.md +0 -0
  29. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/frontend/README.md +0 -0
  30. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/frontend/src/api/client.ts +0 -0
  31. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/__init__.py +0 -0
  32. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/__init__.py +0 -0
  33. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/bank_transaction.py +0 -0
  34. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/budget.py +0 -0
  35. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/chart_of_accounts.py +0 -0
  36. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/general_ledger.py +0 -0
  37. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/journal_entry.py +0 -0
  38. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/payment_entry.py +0 -0
  39. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/pos_invoice.py +0 -0
  40. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/purchase_invoice.py +0 -0
  41. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/revaluation.py +0 -0
  42. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/sales_invoice.py +0 -0
  43. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/accounting/subscription.py +0 -0
  44. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/buying/__init__.py +0 -0
  45. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/buying/purchase_order.py +0 -0
  46. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/controllers/__init__.py +0 -0
  47. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/controllers/currency.py +0 -0
  48. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/controllers/defaults.py +0 -0
  49. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/controllers/pricing_rule.py +0 -0
  50. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/controllers/taxes_and_totals.py +0 -0
  51. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/database.py +0 -0
  52. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/exceptions.py +0 -0
  53. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/hooks.py +0 -0
  54. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/model.py +0 -0
  55. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/selling/__init__.py +0 -0
  56. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/selling/quotation.py +0 -0
  57. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/selling/sales_order.py +0 -0
  58. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/simulation.py +0 -0
  59. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/stock/__init__.py +0 -0
  60. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/stock/delivery_note.py +0 -0
  61. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/stock/purchase_receipt.py +0 -0
  62. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/stock/stock_entry.py +0 -0
  63. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/stock/stock_ledger.py +0 -0
  64. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/lambda_erp/utils.py +0 -0
  65. {lambda_erp-0.1.10 → lambda_erp-0.1.12}/terraform/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lambda-erp
3
- Version: 0.1.10
3
+ Version: 0.1.12
4
4
  Summary: Core ERP logic - accounting, sales, purchasing, inventory
5
5
  Author: TORUS INVESTMENTS AG
6
6
  License-Expression: MIT
@@ -1,13 +1,41 @@
1
1
  """PDF generation for ERP documents."""
2
2
 
3
3
  import os
4
- from jinja2 import Environment, FileSystemLoader
4
+ from jinja2 import Environment, FileSystemLoader, ChoiceLoader
5
5
  from weasyprint import HTML
6
6
  from lambda_erp.database import get_db
7
7
  from api.services import load_document
8
8
 
9
9
  TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "templates")
10
- _jinja_env = Environment(loader=FileSystemLoader(TEMPLATE_DIR), autoescape=True)
10
+
11
+ # Extension seam: deployment plugins can register their own template
12
+ # directories (searched BEFORE the built-in one) to override document.html with
13
+ # custom branding/styling. The render context in generate_pdf is unchanged, so
14
+ # a custom template just re-styles the same data.
15
+ _plugin_template_dirs: list[str] = []
16
+
17
+
18
+ def _build_loader():
19
+ return ChoiceLoader(
20
+ [FileSystemLoader(d) for d in _plugin_template_dirs] + [FileSystemLoader(TEMPLATE_DIR)]
21
+ )
22
+
23
+
24
+ _jinja_env = Environment(loader=_build_loader(), autoescape=True)
25
+
26
+
27
+ def register_pdf_template_dir(path: str) -> None:
28
+ """Register a directory of PDF templates searched before the built-in ones.
29
+
30
+ Call from a plugin's register(). Drop a `document.html` (and/or other
31
+ templates) in `path` to override the built-in PDF layout with your own
32
+ styling — the template receives the same context generate_pdf() builds
33
+ (doc, title, company_info, party_info, currency, meta_fields, items, taxes,
34
+ page_size, ...). Later registrations win.
35
+ """
36
+ if path and path not in _plugin_template_dirs:
37
+ _plugin_template_dirs.insert(0, path)
38
+ _jinja_env.loader = _build_loader()
11
39
 
12
40
  # Document type → (title, party_field, party_name_field, party_doctype, party_label)
13
41
  DOC_CONFIG = {
@@ -132,6 +160,10 @@ def generate_pdf(doctype_slug: str, name: str) -> bytes:
132
160
 
133
161
  # Render
134
162
  template = _jinja_env.get_template("document.html")
163
+ # base_url = the resolved template's own directory, so a template (e.g. a
164
+ # plugin's branded override) can reference sibling assets — logo.png, fonts,
165
+ # CSS — by relative path. Falls back to the built-in templates dir.
166
+ base_url = template.filename or os.path.join(TEMPLATE_DIR, "document.html")
135
167
  html_str = template.render(
136
168
  doc=doc,
137
169
  title=title,
@@ -148,4 +180,4 @@ def generate_pdf(doctype_slug: str, name: str) -> bytes:
148
180
  page_size=page_size,
149
181
  )
150
182
 
151
- return HTML(string=html_str).write_pdf()
183
+ return HTML(string=html_str, base_url=base_url).write_pdf()
@@ -222,6 +222,15 @@ def create_master_record(master_type: str, data: dict) -> dict:
222
222
  if db.exists(doctype, doc["name"]):
223
223
  raise HTTPException(status_code=409, detail=f"{doctype} {doc['name']} already exists")
224
224
 
225
+ # Party masters inherit the company's base currency when none is specified.
226
+ # Otherwise the Customer/Supplier default_currency column default ('USD')
227
+ # wins, so a non-USD company (e.g. CHF) gets USD customers that then force
228
+ # every sales/purchase document to USD (and fail with no USD->base rate).
229
+ if master_type in ("customer", "supplier") and not doc.get("default_currency"):
230
+ companies = db.get_all("Company", fields=["name", "default_currency"], limit=1)
231
+ if companies:
232
+ doc["default_currency"] = companies[0].get("default_currency") or "USD"
233
+
225
234
  db.insert(doctype, doc)
226
235
  row = db.get_all(doctype, filters={"name": doc["name"]}, fields=["*"])[0]
227
236
  return _echo_identity_alias(master_type, row)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lambda-erp"
3
- version = "0.1.10"
3
+ version = "0.1.12"
4
4
  description = "Core ERP logic - accounting, sales, purchasing, inventory"
5
5
  readme = "README.md"
6
6
  license = "MIT"
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