Make dynamic column-facet values respect other active filters (Excel-style), with backend facet context helpers, list-page wiring, slip print/sidebar fixes, and refreshed graphify graphs. Co-authored-by: Cursor <cursoragent@cursor.com>
800 lines
29 KiB
Python
800 lines
29 KiB
Python
"""CRM 报价单 API 路由 — 挂接到 crm.router"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from fastapi import Depends
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.database import get_db
|
|
from app.core.menu_guard import require_admin_or_super_admin, require_menu_code
|
|
from app.core.response import error_response, success_response
|
|
from app.core.tenant_context import get_tenant_no
|
|
from app.crud.crm.data_scope_helpers import crm_user_info
|
|
from app.crud.crm.quote import (
|
|
accept_quote,
|
|
create_new_version,
|
|
create_quote,
|
|
generate_pdf,
|
|
generate_share,
|
|
get_quote_detail,
|
|
get_template,
|
|
invalid_quote,
|
|
list_quote_column_facet_values,
|
|
list_quotes,
|
|
query_versions,
|
|
reject_quote,
|
|
save_template,
|
|
soft_delete_quotes,
|
|
statistics_overview,
|
|
statistics_status,
|
|
statistics_trend,
|
|
submit_quote,
|
|
to_contract,
|
|
total_pages as quote_total_pages,
|
|
update_quote,
|
|
)
|
|
from app.crud.crm.quote_approver_setting import get_setting, save_setting
|
|
from app.crud.crm.quote_approval import (
|
|
approve_quote,
|
|
list_quote_approvals,
|
|
refresh_approver_names,
|
|
withdraw_approval,
|
|
)
|
|
from app.crud.crm.quote_product import (
|
|
insert_quote_product,
|
|
list_quote_products,
|
|
soft_delete_quote_products,
|
|
update_quote_product,
|
|
)
|
|
from app.crud.crm.quote_share_log import list_share_logs, share_log_statistics
|
|
from app.schemas.crm.quote_approver_setting import QuoteApproverSettingSaveBody
|
|
from app.schemas.crm.quote import (
|
|
QuoteAcceptBody,
|
|
QuoteApprovalApproveBody,
|
|
QuoteApprovalListBody,
|
|
QuoteApprovalRefreshNamesBody,
|
|
QuoteApprovalWithdrawBody,
|
|
QuoteColumnFacetBody,
|
|
QuoteCreateBody,
|
|
QuoteDeleteBody,
|
|
QuoteDetailBody,
|
|
QuoteInvalidBody,
|
|
QuoteListBody,
|
|
QuoteNewVersionBody,
|
|
QuotePdfBody,
|
|
QuoteProductCreateBody,
|
|
QuoteProductDeleteBody,
|
|
QuoteProductListBody,
|
|
QuoteProductUpdateBody,
|
|
QuoteRejectBody,
|
|
QuoteShareGenerateBody,
|
|
QuoteShareLogListBody,
|
|
QuoteShareLogStatisticsBody,
|
|
QuoteStatisticsOverviewBody,
|
|
QuoteStatisticsTrendBody,
|
|
QuoteSubmitBody,
|
|
QuoteTemplateGetBody,
|
|
QuoteTemplateSaveBody,
|
|
QuoteToContractBody,
|
|
QuoteUpdateBody,
|
|
QuoteVersionsBody,
|
|
)
|
|
from app.utils.crm_export_columns import QUOTE_EXPORT_COLUMNS
|
|
from app.utils.excel_export import (
|
|
EXPORT_MAX_ROWS,
|
|
ExportValidationError,
|
|
export_rows_to_xlsx_response,
|
|
filter_export_columns,
|
|
)
|
|
from app.api.crm import _filter_export_rows_by_ids
|
|
|
|
|
|
def register_quote_routes(router):
|
|
@router.post("/quote/list")
|
|
async def quote_list(
|
|
body: QuoteListBody = None,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
list_body = body or QuoteListBody()
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
rows, total = list_quotes(
|
|
db,
|
|
tenant_no,
|
|
user_info=user_info,
|
|
page=list_body.page,
|
|
page_size=list_body.pageSize,
|
|
filters=list_body.model_dump(by_alias=True, exclude={"page", "pageSize"}),
|
|
current_user_id=user_id,
|
|
)
|
|
tp = quote_total_pages(total, list_body.pageSize)
|
|
return success_response(
|
|
{
|
|
"list": rows,
|
|
"rows": rows,
|
|
"total": total,
|
|
"page": list_body.page,
|
|
"pageSize": list_body.pageSize,
|
|
"totalPages": tp,
|
|
}
|
|
)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/column-facet-values")
|
|
async def quote_column_facet_values(
|
|
body: QuoteColumnFacetBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""报价列表列头可选值(单号/客户去重;Excel 级联上下文)"""
|
|
try:
|
|
from app.services.crm.facet_context import facet_filter_kwargs_from_body
|
|
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
filter_kwargs = facet_filter_kwargs_from_body(body, body.field)
|
|
rows = list_quote_column_facet_values(
|
|
db,
|
|
tenant_no,
|
|
field=body.field,
|
|
keyword=body.keyword,
|
|
limit=body.limit if body.limit is not None else 500,
|
|
user_info=user_info,
|
|
filter_kwargs=filter_kwargs,
|
|
)
|
|
return success_response({"list": rows})
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/detail")
|
|
async def quote_detail(
|
|
body: QuoteDetailBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
detail = get_quote_detail(db, body.id, tenant_no=tenant_no, user_info=user_info)
|
|
return success_response(detail)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/add")
|
|
async def quote_add(
|
|
body: QuoteCreateBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
dept_id = _user.get("dept_id", "") or _user.get("create_dept_id", "") or ""
|
|
detail = create_quote(
|
|
db,
|
|
tenant_no=tenant_no,
|
|
data=body.model_dump(by_alias=True),
|
|
user_id=user_id,
|
|
dept_id=dept_id,
|
|
)
|
|
return success_response(detail, message="新增成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/update")
|
|
async def quote_update(
|
|
body: QuoteUpdateBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
dept_id = _user.get("dept_id", "") or _user.get("create_dept_id", "") or ""
|
|
data = body.model_dump(by_alias=True)
|
|
quote_id = data.pop("id")
|
|
detail = update_quote(
|
|
db,
|
|
quote_id,
|
|
tenant_no=tenant_no,
|
|
data=data,
|
|
user_id=user_id,
|
|
dept_id=dept_id,
|
|
user_info=user_info,
|
|
)
|
|
return success_response(detail, message="修改成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/delete")
|
|
async def quote_delete(
|
|
body: QuoteDeleteBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
soft_delete_quotes(db, body.ids, tenant_no=tenant_no, user_info=user_info)
|
|
return success_response(None, message="删除成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/submit")
|
|
async def quote_submit(
|
|
body: QuoteSubmitBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
submit_quote(
|
|
db,
|
|
body.id,
|
|
tenant_no=tenant_no,
|
|
approver_ids=body.approverIds,
|
|
remark=body.remark,
|
|
user_info=user_info,
|
|
)
|
|
return success_response(None, message="提交成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/saveTemplate")
|
|
async def quote_save_template(
|
|
body: QuoteTemplateSaveBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
save_template(db, body.id, body.templateContent, tenant_no=tenant_no)
|
|
return success_response(None, message="保存成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/getTemplate")
|
|
async def quote_get_template(
|
|
body: QuoteTemplateGetBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
content = get_template(db, body.id, tenant_no=tenant_no, user_info=user_info)
|
|
return success_response(content or "")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/generatePdf")
|
|
async def quote_generate_pdf(
|
|
body: QuotePdfBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
pdf_url = generate_pdf(db, body.id, tenant_no=tenant_no)
|
|
return success_response(pdf_url)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/generateShare")
|
|
async def quote_generate_share(
|
|
body: QuoteShareGenerateBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
link = generate_share(
|
|
db,
|
|
body.id,
|
|
tenant_no=tenant_no,
|
|
validity_days=body.validityDays,
|
|
password=body.password or "",
|
|
user_info=user_info,
|
|
)
|
|
return success_response(link)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/accept")
|
|
async def quote_accept(
|
|
body: QuoteAcceptBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
accept_quote(db, body.id, tenant_no=tenant_no, remark=body.remark)
|
|
return success_response(None, message="操作成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/reject")
|
|
async def quote_reject(
|
|
body: QuoteRejectBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
reject_quote(
|
|
db,
|
|
body.id,
|
|
tenant_no=tenant_no,
|
|
reject_reason=body.rejectReason,
|
|
remark=body.remark,
|
|
user_info=user_info,
|
|
)
|
|
return success_response(None, message="操作成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/toContract")
|
|
async def quote_to_contract(
|
|
body: QuoteToContractBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
dept_id = _user.get("dept_id", "") or _user.get("create_dept_id", "") or ""
|
|
contract_id = to_contract(
|
|
db,
|
|
body.id,
|
|
tenant_no=tenant_no,
|
|
contract_no=body.contractNo,
|
|
contract_title=body.contractTitle,
|
|
sign_date=body.signDate,
|
|
start_date=body.startDate,
|
|
end_date=body.endDate,
|
|
user_id=user_id,
|
|
dept_id=dept_id,
|
|
user_info=user_info,
|
|
)
|
|
return success_response(contract_id)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/newVersion")
|
|
async def quote_new_version(
|
|
body: QuoteNewVersionBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
dept_id = _user.get("dept_id", "") or _user.get("create_dept_id", "") or ""
|
|
new_id = create_new_version(
|
|
db,
|
|
body.id,
|
|
tenant_no=tenant_no,
|
|
version=body.version,
|
|
remark=body.remark,
|
|
user_id=user_id,
|
|
dept_id=dept_id,
|
|
user_info=user_info,
|
|
)
|
|
return success_response(new_id)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/versions")
|
|
async def quote_versions(
|
|
body: QuoteVersionsBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
rows = query_versions(db, body.quoteNo, tenant_no=tenant_no)
|
|
return success_response(rows)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/invalid")
|
|
async def quote_invalid(
|
|
body: QuoteInvalidBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
invalid_quote(
|
|
db,
|
|
body.id,
|
|
tenant_no=tenant_no,
|
|
invalid_reason=body.invalidReason or "",
|
|
remark=body.remark or "",
|
|
user_info=user_info,
|
|
)
|
|
return success_response(None, message="作废成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/export")
|
|
async def quote_export(
|
|
body: QuoteListBody = None,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
list_body = body or QuoteListBody()
|
|
rows, _ = list_quotes(
|
|
db,
|
|
tenant_no,
|
|
user_info=user_info,
|
|
page=1,
|
|
page_size=EXPORT_MAX_ROWS,
|
|
filters=list_body.model_dump(by_alias=True, exclude={"page", "pageSize"}),
|
|
)
|
|
rows = _filter_export_rows_by_ids(rows, getattr(list_body, "ids", None))
|
|
export_cols = filter_export_columns(QUOTE_EXPORT_COLUMNS, getattr(list_body, "columns", None))
|
|
return export_rows_to_xlsx_response(
|
|
db,
|
|
tenant_no=tenant_no,
|
|
sheet_title="报价单",
|
|
columns=export_cols,
|
|
rows=rows,
|
|
filename_stem=f"报价单_{datetime.now().strftime('%Y%m%d%H%M%S')}",
|
|
include_legacy_filename=False,
|
|
)
|
|
except ExportValidationError as e:
|
|
return error_response(400, str(e))
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/statistics/overview")
|
|
async def quote_statistics_overview(
|
|
body: QuoteStatisticsOverviewBody = None,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
req = body or QuoteStatisticsOverviewBody()
|
|
data = statistics_overview(
|
|
db,
|
|
tenant_no,
|
|
owner_user_id=req.ownerUserId,
|
|
begin_time=req.beginTime,
|
|
end_time=req.endTime,
|
|
user_info=user_info,
|
|
)
|
|
return success_response(data)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/statistics/status")
|
|
async def quote_statistics_status(
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
return success_response(statistics_status(db, tenant_no))
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/statistics/trend")
|
|
async def quote_statistics_trend(
|
|
body: QuoteStatisticsTrendBody = None,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
req = body or QuoteStatisticsTrendBody()
|
|
data = statistics_trend(
|
|
db,
|
|
tenant_no,
|
|
period_type=req.type or "month",
|
|
begin_time=req.beginTime,
|
|
end_time=req.endTime,
|
|
user_info=user_info,
|
|
)
|
|
return success_response(data)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/product/list")
|
|
async def quote_product_list(
|
|
body: QuoteProductListBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
rows = list_quote_products(db, body.quoteId, tenant_no=tenant_no)
|
|
return success_response(rows)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/product/add")
|
|
async def quote_product_add(
|
|
body: QuoteProductCreateBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
dept_id = _user.get("dept_id", "") or _user.get("create_dept_id", "") or ""
|
|
obj = insert_quote_product(
|
|
db,
|
|
tenant_no=tenant_no,
|
|
data=body.model_dump(by_alias=True),
|
|
user_id=user_id,
|
|
dept_id=dept_id,
|
|
)
|
|
from app.crud.crm.quote_product import product_row_to_dict
|
|
|
|
return success_response(product_row_to_dict(obj), message="新增成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/product/update")
|
|
async def quote_product_update(
|
|
body: QuoteProductUpdateBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
data = body.model_dump(by_alias=True)
|
|
line_id = data.pop("id")
|
|
obj = update_quote_product(
|
|
db,
|
|
line_id,
|
|
tenant_no=tenant_no,
|
|
data=data,
|
|
user_id=user_id,
|
|
user_info=user_info,
|
|
)
|
|
from app.crud.crm.quote_product import product_row_to_dict
|
|
|
|
return success_response(product_row_to_dict(obj), message="修改成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/product/delete")
|
|
async def quote_product_delete(
|
|
body: QuoteProductDeleteBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
soft_delete_quote_products(db, body.ids, tenant_no=tenant_no, user_info=user_info)
|
|
return success_response(None, message="删除成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/approval/list")
|
|
async def quote_approval_list(
|
|
body: QuoteApprovalListBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
rows = list_quote_approvals(db, body.quoteId, tenant_no=tenant_no)
|
|
return success_response(rows)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/approval/approve")
|
|
async def quote_approval_approve(
|
|
body: QuoteApprovalApproveBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
approve_quote(
|
|
db,
|
|
body.id,
|
|
tenant_no=tenant_no,
|
|
approval_result=body.approvalResult,
|
|
approval_opinion=body.approvalOpinion or "",
|
|
remark=body.remark or "",
|
|
user_id=user_id,
|
|
)
|
|
return success_response(None, message="审批成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/approval/withdraw")
|
|
async def quote_approval_withdraw(
|
|
body: QuoteApprovalWithdrawBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
withdraw_approval(
|
|
db,
|
|
body.quoteId,
|
|
tenant_no=tenant_no,
|
|
remark=body.remark or "",
|
|
user_id=user_id,
|
|
)
|
|
return success_response(None, message="撤回成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/approval/refreshNames")
|
|
async def quote_approval_refresh_names(
|
|
body: QuoteApprovalRefreshNamesBody = None,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
req = body or QuoteApprovalRefreshNamesBody()
|
|
count = refresh_approver_names(db, quote_id=req.quoteId)
|
|
return success_response(count)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/shareLog/list")
|
|
async def quote_share_log_list(
|
|
body: QuoteShareLogListBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
rows = list_share_logs(db, body.quoteId, tenant_no=tenant_no)
|
|
return success_response(rows)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote/shareLog/statistics")
|
|
async def quote_share_log_statistics(
|
|
body: QuoteShareLogStatisticsBody,
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_info = crm_user_info(_user)
|
|
stats = share_log_statistics(db, body.quoteId, tenant_no=tenant_no)
|
|
return success_response(stats)
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote-approver-setting/get")
|
|
async def quote_approver_setting_get(
|
|
_user: dict = Depends(require_admin_or_super_admin),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
data = get_setting(db, tenant_no=tenant_no)
|
|
# 无配置时默认「启用」,避免设置页首次打开落到「停用」导致保存后无法预填
|
|
return success_response(data or {"approverIds": [], "status": "0"})
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|
|
|
|
@router.post("/quote-approver-setting/save")
|
|
async def quote_approver_setting_save(
|
|
body: QuoteApproverSettingSaveBody,
|
|
_user: dict = Depends(require_menu_code("system:approverSetting")),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, _user)
|
|
user_id = _user.get("user_id", "") or _user.get("id", "")
|
|
dept_id = _user.get("dept_id", "") or _user.get("create_dept_id", "") or ""
|
|
data = save_setting(
|
|
db,
|
|
tenant_no=tenant_no,
|
|
approver_ids=body.approverIds or [],
|
|
status=body.status or "0",
|
|
user_id=user_id,
|
|
dept_id=dept_id,
|
|
)
|
|
return success_response(data, message="保存成功")
|
|
except ValueError as e:
|
|
return error_response(400, str(e))
|
|
except Exception as e:
|
|
return error_response(500, str(e))
|