lambda-erp 0.1.48__tar.gz → 0.1.49__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.1.48 → lambda_erp-0.1.49}/PKG-INFO +1 -1
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/auth.py +12 -12
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/database.py +3 -3
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/pyproject.toml +1 -1
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/.gitignore +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/LICENSE +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/README.md +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/__init__.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/attachments.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/bootstrap.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/chat.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/demo_limits.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/deps.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/errors.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/main.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/oauth.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/pdf.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/providers.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/remarks_md.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/__init__.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/accounting.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/admin.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/analytics.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/bank_reconciliation.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/chat_api.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/documents.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/masters.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/proposals.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/reports.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/routers/setup.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/services.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/templates/document.html +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/api/templates/proposal.html +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/docs/agents/README.md +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/frontend/README.md +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/frontend/src/api/client.ts +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/__init__.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/__init__.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/bank_transaction.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/budget.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/chart_of_accounts.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/general_ledger.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/journal_entry.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/payment_entry.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/pos_invoice.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/purchase_invoice.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/revaluation.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/sales_invoice.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/accounting/subscription.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/buying/__init__.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/buying/purchase_order.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/controllers/__init__.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/controllers/currency.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/controllers/defaults.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/controllers/pricing_rule.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/controllers/taxes_and_totals.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/exceptions.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/hooks.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/model.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/selling/__init__.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/selling/proposal.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/selling/quotation.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/selling/sales_order.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/simulation.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/stock/__init__.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/stock/delivery_note.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/stock/purchase_receipt.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/stock/stock_entry.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/stock/stock_ledger.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/lambda_erp/utils.py +0 -0
- {lambda_erp-0.1.48 → lambda_erp-0.1.49}/terraform/README.md +0 -0
|
@@ -680,14 +680,14 @@ def get_api_caller(request: Request) -> dict:
|
|
|
680
680
|
raise HTTPException(status_code=401, detail="Empty bearer token")
|
|
681
681
|
|
|
682
682
|
rows = db.sql(
|
|
683
|
-
'SELECT id, name,
|
|
683
|
+
'SELECT id, name, owner, role, session_owner FROM "Api Key" WHERE key_hash = ? AND revoked = 0',
|
|
684
684
|
[hash_api_key(token)],
|
|
685
685
|
)
|
|
686
686
|
if not rows:
|
|
687
687
|
raise HTTPException(status_code=401, detail="Invalid API key")
|
|
688
688
|
key = dict(rows[0])
|
|
689
689
|
|
|
690
|
-
owner = db.get_value("User", key["
|
|
690
|
+
owner = db.get_value("User", key["owner"], ["name", "full_name", "role", "enabled"])
|
|
691
691
|
if not owner or not owner.get("enabled"):
|
|
692
692
|
raise HTTPException(status_code=401, detail="Invalid API key")
|
|
693
693
|
effective_role = (
|
|
@@ -699,10 +699,10 @@ def get_api_caller(request: Request) -> dict:
|
|
|
699
699
|
|
|
700
700
|
return {
|
|
701
701
|
"name": key["session_owner"],
|
|
702
|
-
"full_name": f"{owner.get('full_name') or key['
|
|
702
|
+
"full_name": f"{owner.get('full_name') or key['owner']} (API)",
|
|
703
703
|
"role": effective_role,
|
|
704
704
|
"api_key_id": key["id"],
|
|
705
|
-
"api_key_user": key["
|
|
705
|
+
"api_key_user": key["owner"],
|
|
706
706
|
}
|
|
707
707
|
|
|
708
708
|
|
|
@@ -717,7 +717,7 @@ def _serialize_api_key(row: dict) -> dict:
|
|
|
717
717
|
return {
|
|
718
718
|
"id": row["id"],
|
|
719
719
|
"name": row["name"],
|
|
720
|
-
"user": row.get("
|
|
720
|
+
"user": row.get("owner"),
|
|
721
721
|
"role": row["role"],
|
|
722
722
|
"key_prefix": row["key_prefix"],
|
|
723
723
|
"created_at": row.get("created_at"),
|
|
@@ -727,7 +727,7 @@ def _serialize_api_key(row: dict) -> dict:
|
|
|
727
727
|
|
|
728
728
|
|
|
729
729
|
def _get_key_or_404(db, key_id: str) -> dict:
|
|
730
|
-
rows = db.sql('SELECT id,
|
|
730
|
+
rows = db.sql('SELECT id, owner, revoked FROM "Api Key" WHERE id = ?', [key_id])
|
|
731
731
|
if not rows:
|
|
732
732
|
raise HTTPException(status_code=404, detail="API key not found")
|
|
733
733
|
return dict(rows[0])
|
|
@@ -735,7 +735,7 @@ def _get_key_or_404(db, key_id: str) -> dict:
|
|
|
735
735
|
|
|
736
736
|
def _require_key_access(key: dict, user: dict) -> None:
|
|
737
737
|
"""A key is managed by its owner; admins can manage every key."""
|
|
738
|
-
if key["
|
|
738
|
+
if key["owner"] != user["name"] and user["role"] != "admin":
|
|
739
739
|
raise HTTPException(status_code=403, detail="Not your API key")
|
|
740
740
|
|
|
741
741
|
|
|
@@ -748,13 +748,13 @@ def list_api_keys(user: dict = Depends(require_viewer)):
|
|
|
748
748
|
db = get_db()
|
|
749
749
|
if user["role"] == "admin":
|
|
750
750
|
rows = db.sql(
|
|
751
|
-
'SELECT id, name,
|
|
751
|
+
'SELECT id, name, owner, role, key_prefix, created_at, last_used_at, revoked '
|
|
752
752
|
'FROM "Api Key" ORDER BY created_at DESC'
|
|
753
753
|
)
|
|
754
754
|
else:
|
|
755
755
|
rows = db.sql(
|
|
756
|
-
'SELECT id, name,
|
|
757
|
-
'FROM "Api Key" WHERE
|
|
756
|
+
'SELECT id, name, owner, role, key_prefix, created_at, last_used_at, revoked '
|
|
757
|
+
'FROM "Api Key" WHERE owner = ? ORDER BY created_at DESC',
|
|
758
758
|
[user["name"]],
|
|
759
759
|
)
|
|
760
760
|
return [_serialize_api_key(dict(r)) for r in rows]
|
|
@@ -789,13 +789,13 @@ def create_api_key(data: ApiKeyCreate, user: dict = Depends(require_viewer)):
|
|
|
789
789
|
created_at = now()
|
|
790
790
|
db.sql(
|
|
791
791
|
'INSERT INTO "Api Key" '
|
|
792
|
-
'(id, name,
|
|
792
|
+
'(id, name, owner, key_hash, key_prefix, role, session_owner, created_at, last_used_at, revoked) '
|
|
793
793
|
'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0)',
|
|
794
794
|
[key_id, name, user["name"], key_hash, key_prefix, role, f"api:{user['name']}", created_at, None],
|
|
795
795
|
)
|
|
796
796
|
db.conn.commit()
|
|
797
797
|
row = {
|
|
798
|
-
"id": key_id, "name": name, "
|
|
798
|
+
"id": key_id, "name": name, "owner": user["name"], "role": role, "key_prefix": key_prefix,
|
|
799
799
|
"created_at": created_at, "last_used_at": None, "revoked": 0,
|
|
800
800
|
}
|
|
801
801
|
result = _serialize_api_key(row)
|
|
@@ -1321,7 +1321,7 @@ class Database:
|
|
|
1321
1321
|
"""CREATE TABLE IF NOT EXISTS "Api Key" (
|
|
1322
1322
|
id TEXT PRIMARY KEY,
|
|
1323
1323
|
name TEXT,
|
|
1324
|
-
|
|
1324
|
+
owner TEXT NOT NULL,
|
|
1325
1325
|
key_hash TEXT UNIQUE,
|
|
1326
1326
|
key_prefix TEXT,
|
|
1327
1327
|
role TEXT DEFAULT 'manager',
|
|
@@ -1907,14 +1907,14 @@ def _m019_api_keys_per_user(db: "Database") -> None:
|
|
|
1907
1907
|
owner's live role). Existing v1 rows are DROPPED, not migrated — decided
|
|
1908
1908
|
2026-07-14: no legacy rules; holders simply re-issue their keys.
|
|
1909
1909
|
"""
|
|
1910
|
-
if "
|
|
1910
|
+
if "owner" in db._get_table_columns("Api Key"):
|
|
1911
1911
|
return # fresh install — table already has the v2 shape
|
|
1912
1912
|
db.conn.execute('DROP TABLE "Api Key"')
|
|
1913
1913
|
db.conn.execute(
|
|
1914
1914
|
"""CREATE TABLE "Api Key" (
|
|
1915
1915
|
id TEXT PRIMARY KEY,
|
|
1916
1916
|
name TEXT,
|
|
1917
|
-
|
|
1917
|
+
owner TEXT NOT NULL,
|
|
1918
1918
|
key_hash TEXT UNIQUE,
|
|
1919
1919
|
key_prefix TEXT,
|
|
1920
1920
|
role TEXT DEFAULT 'manager',
|
|
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
|