lambda-erp 0.8.2__tar.gz → 0.8.3__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-0.8.2 → lambda_erp-0.8.3}/PKG-INFO +1 -1
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/services.py +49 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/packs/ch.py +9 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/database.py +12 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/pyproject.toml +1 -1
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/.gitignore +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/LICENSE +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/README.md +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/attachments.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/auth.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/bootstrap.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/chat.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/demo_limits.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/deps.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/errors.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/main.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/oauth.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/pdf.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/providers.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/remarks_md.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/accounting.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/admin.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/analytics.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/availability.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/bank_reconciliation.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/chat_api.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/documents.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/masters.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/mcp.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/proposals.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/reports.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/routers/setup.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/templates/document.html +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/api/templates/proposal.html +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/docs/agents/README.md +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/frontend/LICENSE +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/frontend/README.md +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/frontend/src/api/client.ts +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/bank_transaction.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/budget.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/chart_of_accounts.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/general_ledger.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/journal_entry.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/payment_entry.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/pos_invoice.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/purchase_invoice.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/revaluation.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/sales_invoice.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/engine.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/pack.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/packs/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/packs/de_common.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/packs/de_skr03.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/packs/de_skr04.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/packs/generic.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/profiles.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/setup/spine.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/accounting/subscription.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/assets/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/assets/asset.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/assets/reservation.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/buying/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/buying/purchase_order.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/controllers/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/controllers/currency.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/controllers/defaults.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/controllers/pricing_rule.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/controllers/taxes_and_totals.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/exceptions.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/hooks.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/model.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/selling/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/selling/proposal.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/selling/quotation.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/selling/sales_order.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/simulation.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/stock/__init__.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/stock/delivery_note.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/stock/purchase_receipt.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/stock/stock_entry.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/stock/stock_ledger.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/lambda_erp/utils.py +0 -0
- {lambda_erp-0.8.2 → lambda_erp-0.8.3}/terraform/README.md +0 -0
|
@@ -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
|
|
|
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
|