fix: Bugs created after the overhaul, performance and layout fixes
This commit is contained in:
@@ -19,7 +19,7 @@ from crm.quotation_models import (
|
||||
QuotationUpdate,
|
||||
)
|
||||
from crm.service import get_customer
|
||||
from mqtt import database as mqtt_db
|
||||
import database as mqtt_db
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -153,10 +153,42 @@ async def get_next_number() -> str:
|
||||
return await _generate_quotation_number(db)
|
||||
|
||||
|
||||
async def list_all_quotations() -> list[dict]:
|
||||
"""Return all quotations across all customers, with customer_name injected."""
|
||||
from shared.firebase import get_db as get_firestore
|
||||
db = await mqtt_db.get_db()
|
||||
rows = await db.execute_fetchall(
|
||||
"SELECT id, quotation_number, title, customer_id, status, final_total, created_at, updated_at, "
|
||||
"nextcloud_pdf_url, is_legacy, legacy_date, legacy_pdf_path "
|
||||
"FROM crm_quotations ORDER BY created_at DESC",
|
||||
(),
|
||||
)
|
||||
items = [dict(r) for r in rows]
|
||||
# Fetch unique customer names from Firestore in one pass
|
||||
customer_ids = {i["customer_id"] for i in items if i.get("customer_id")}
|
||||
customer_names: dict[str, str] = {}
|
||||
if customer_ids:
|
||||
fstore = get_firestore()
|
||||
for cid in customer_ids:
|
||||
try:
|
||||
doc = fstore.collection("crm_customers").document(cid).get()
|
||||
if doc.exists:
|
||||
d = doc.to_dict()
|
||||
parts = [d.get("name", ""), d.get("surname", ""), d.get("organization", "")]
|
||||
label = " ".join(p for p in parts if p).strip()
|
||||
customer_names[cid] = label or cid
|
||||
except Exception:
|
||||
customer_names[cid] = cid
|
||||
for item in items:
|
||||
item["customer_name"] = customer_names.get(item["customer_id"], "")
|
||||
return items
|
||||
|
||||
|
||||
async def list_quotations(customer_id: str) -> list[QuotationListItem]:
|
||||
db = await mqtt_db.get_db()
|
||||
rows = await db.execute_fetchall(
|
||||
"SELECT id, quotation_number, title, customer_id, status, final_total, created_at, updated_at, nextcloud_pdf_url "
|
||||
"SELECT id, quotation_number, title, customer_id, status, final_total, created_at, updated_at, "
|
||||
"nextcloud_pdf_url, is_legacy, legacy_date, legacy_pdf_path "
|
||||
"FROM crm_quotations WHERE customer_id = ? ORDER BY created_at DESC",
|
||||
(customer_id,),
|
||||
)
|
||||
@@ -210,6 +242,7 @@ async def create_quotation(data: QuotationCreate, generate_pdf: bool = False) ->
|
||||
subtotal_before_discount, global_discount_amount, new_subtotal, vat_amount, final_total,
|
||||
nextcloud_pdf_path, nextcloud_pdf_url,
|
||||
client_org, client_name, client_location, client_phone, client_email,
|
||||
is_legacy, legacy_date, legacy_pdf_path,
|
||||
created_at, updated_at
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?,
|
||||
@@ -220,6 +253,7 @@ async def create_quotation(data: QuotationCreate, generate_pdf: bool = False) ->
|
||||
?, ?, ?, ?, ?,
|
||||
NULL, NULL,
|
||||
?, ?, ?, ?, ?,
|
||||
?, ?, ?,
|
||||
?, ?
|
||||
)""",
|
||||
(
|
||||
@@ -231,6 +265,7 @@ async def create_quotation(data: QuotationCreate, generate_pdf: bool = False) ->
|
||||
totals["subtotal_before_discount"], totals["global_discount_amount"],
|
||||
totals["new_subtotal"], totals["vat_amount"], totals["final_total"],
|
||||
data.client_org, data.client_name, data.client_location, data.client_phone, data.client_email,
|
||||
1 if data.is_legacy else 0, data.legacy_date, data.legacy_pdf_path,
|
||||
now, now,
|
||||
),
|
||||
)
|
||||
@@ -240,11 +275,12 @@ async def create_quotation(data: QuotationCreate, generate_pdf: bool = False) ->
|
||||
item_id = str(uuid.uuid4())
|
||||
await db.execute(
|
||||
"""INSERT INTO crm_quotation_items
|
||||
(id, quotation_id, product_id, description, unit_type, unit_cost,
|
||||
discount_percent, quantity, vat_percent, line_total, sort_order)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
(id, quotation_id, product_id, description, description_en, description_gr,
|
||||
unit_type, unit_cost, discount_percent, quantity, vat_percent, line_total, sort_order)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
(
|
||||
item_id, qid, item.get("product_id"), item.get("description"),
|
||||
item.get("description_en"), item.get("description_gr"),
|
||||
item.get("unit_type", "pcs"), item.get("unit_cost", 0),
|
||||
item.get("discount_percent", 0), item.get("quantity", 1),
|
||||
item.get("vat_percent", 24), item["line_total"], item.get("sort_order", i),
|
||||
@@ -255,7 +291,7 @@ async def create_quotation(data: QuotationCreate, generate_pdf: bool = False) ->
|
||||
|
||||
quotation = await get_quotation(qid)
|
||||
|
||||
if generate_pdf:
|
||||
if generate_pdf and not data.is_legacy:
|
||||
quotation = await _do_generate_and_upload_pdf(quotation)
|
||||
|
||||
return quotation
|
||||
@@ -285,6 +321,7 @@ async def update_quotation(quotation_id: str, data: QuotationUpdate, generate_pd
|
||||
"shipping_cost", "shipping_cost_discount", "install_cost",
|
||||
"install_cost_discount", "extras_label", "extras_cost",
|
||||
"client_org", "client_name", "client_location", "client_phone", "client_email",
|
||||
"legacy_date", "legacy_pdf_path",
|
||||
]
|
||||
|
||||
for field in scalar_fields:
|
||||
@@ -343,11 +380,12 @@ async def update_quotation(quotation_id: str, data: QuotationUpdate, generate_pd
|
||||
item_id = str(uuid.uuid4())
|
||||
await db.execute(
|
||||
"""INSERT INTO crm_quotation_items
|
||||
(id, quotation_id, product_id, description, unit_type, unit_cost,
|
||||
discount_percent, quantity, vat_percent, line_total, sort_order)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
(id, quotation_id, product_id, description, description_en, description_gr,
|
||||
unit_type, unit_cost, discount_percent, quantity, vat_percent, line_total, sort_order)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
(
|
||||
item_id, quotation_id, item.get("product_id"), item.get("description"),
|
||||
item.get("description_en"), item.get("description_gr"),
|
||||
item.get("unit_type", "pcs"), item.get("unit_cost", 0),
|
||||
item.get("discount_percent", 0), item.get("quantity", 1),
|
||||
item.get("vat_percent", 24), item["line_total"], item.get("sort_order", i),
|
||||
@@ -488,7 +526,33 @@ async def get_quotation_pdf_bytes(quotation_id: str) -> bytes:
|
||||
"""Download the PDF for a quotation from Nextcloud and return raw bytes."""
|
||||
from fastapi import HTTPException
|
||||
quotation = await get_quotation(quotation_id)
|
||||
if not quotation.nextcloud_pdf_path:
|
||||
raise HTTPException(status_code=404, detail="No PDF generated for this quotation")
|
||||
pdf_bytes, _ = await nextcloud.download_file(quotation.nextcloud_pdf_path)
|
||||
# For legacy quotations, the PDF is at legacy_pdf_path
|
||||
path = quotation.legacy_pdf_path if quotation.is_legacy else quotation.nextcloud_pdf_path
|
||||
if not path:
|
||||
raise HTTPException(status_code=404, detail="No PDF available for this quotation")
|
||||
pdf_bytes, _ = await nextcloud.download_file(path)
|
||||
return pdf_bytes
|
||||
|
||||
|
||||
async def upload_legacy_pdf(quotation_id: str, pdf_bytes: bytes, filename: str) -> QuotationInDB:
|
||||
"""Upload a legacy PDF to Nextcloud and store its path in the quotation record."""
|
||||
quotation = await get_quotation(quotation_id)
|
||||
if not quotation.is_legacy:
|
||||
raise HTTPException(status_code=400, detail="This quotation is not a legacy quotation")
|
||||
|
||||
from crm.service import get_customer, get_customer_nc_path
|
||||
customer = get_customer(quotation.customer_id)
|
||||
nc_folder = get_customer_nc_path(customer)
|
||||
|
||||
await nextcloud.ensure_folder(f"customers/{nc_folder}/quotations")
|
||||
rel_path = f"customers/{nc_folder}/quotations/{filename}"
|
||||
await nextcloud.upload_file(rel_path, pdf_bytes, "application/pdf")
|
||||
|
||||
db = await mqtt_db.get_db()
|
||||
now = datetime.utcnow().isoformat()
|
||||
await db.execute(
|
||||
"UPDATE crm_quotations SET legacy_pdf_path = ?, updated_at = ? WHERE id = ?",
|
||||
(rel_path, now, quotation_id),
|
||||
)
|
||||
await db.commit()
|
||||
return await get_quotation(quotation_id)
|
||||
|
||||
Reference in New Issue
Block a user