feat(crm): 联系人、产品、报价与合同域接入数据权限过滤
补全 PR3 Phase 2.4–2.7,列表 apply_data_scope、详情/CUD assert_row_in_data_scope,合同/报价主表支持 owner_user_id。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
ee531e9cfe
commit
bf73b6b2d8
15 files changed
+598
-82
No files matched your search
+78
-16
@@ -17,6 +17,8 @@ from app.core.tenant_context import get_tenant_no
|
||||
from app.crud.crm.data_scope_helpers import (
|
||||
crm_user_info,
|
||||
ensure_crm_create_by_row_in_scope,
|
||||
ensure_crm_contact_row_in_scope,
|
||||
ensure_crm_customer_linked_row_in_scope,
|
||||
ensure_crm_owner_row_in_scope,
|
||||
)
|
||||
from app.utils.crm_export_columns import (
|
||||
@@ -329,6 +331,8 @@ async def category_tree(
|
||||
"""获取产品分类树"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_info = crm_user_info(_user)
|
||||
payload = body or {}
|
||||
category_name = payload.get("categoryName") or payload.get("category_name")
|
||||
status = payload.get("status")
|
||||
@@ -337,6 +341,7 @@ async def category_tree(
|
||||
tenant_no,
|
||||
category_name=category_name,
|
||||
status=status,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(rows)
|
||||
except ValueError as e:
|
||||
@@ -516,6 +521,7 @@ async def category_add(
|
||||
"""新增产品分类"""
|
||||
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(exclude_unset=True)
|
||||
obj = create_category(db, tenant_no=tenant_no, user_id=user_id, **data)
|
||||
@@ -537,7 +543,7 @@ async def category_update(
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
data = body.model_dump(exclude_unset=True)
|
||||
category_id = data.pop("id")
|
||||
obj = update_category(db, category_id, user_id=user_id, **data)
|
||||
obj = update_category(db, category_id, user_id=user_id, user_info=user_info, **data)
|
||||
return success_response(_category_row_to_dict(obj), message="更新成功")
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -553,7 +559,7 @@ async def category_delete(
|
||||
):
|
||||
"""删除产品分类"""
|
||||
try:
|
||||
soft_delete_category(db, body.id)
|
||||
soft_delete_category(db, body.id, user_info=user_info)
|
||||
return success_response(None, message="删除成功")
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -573,6 +579,7 @@ async def product_list(
|
||||
"""分页查询产品列表"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
rows, total = list_products(
|
||||
db,
|
||||
tenant_no,
|
||||
@@ -581,6 +588,7 @@ async def product_list(
|
||||
keyword=body.keyword,
|
||||
category_id=body.category_id,
|
||||
status=body.status,
|
||||
user_info=user_info,
|
||||
)
|
||||
tp = total_pages(total, body.pageSize)
|
||||
return success_response(
|
||||
@@ -606,9 +614,12 @@ async def product_detail(
|
||||
):
|
||||
"""获取产品详情"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
obj = get_product(db, body.id)
|
||||
if not obj:
|
||||
return error_response(404, "产品不存在")
|
||||
ensure_crm_create_by_row_in_scope(db, obj, user_info, tenant_no, message="无权访问该产品")
|
||||
return success_response(_product_row_to_dict(obj))
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -625,6 +636,7 @@ async def product_add(
|
||||
"""新增产品"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
obj = create_product(
|
||||
db,
|
||||
@@ -650,7 +662,9 @@ async def product_update(
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
data = body.model_dump(exclude_unset=True)
|
||||
product_id = data.pop("id")
|
||||
obj = update_product(db, product_id, user_id=user_id, **data)
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
obj = update_product(db, product_id, user_id=user_id, user_info=user_info, **data)
|
||||
return success_response(_product_row_to_dict(obj), message="更新成功")
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -666,6 +680,8 @@ async def product_delete(
|
||||
):
|
||||
"""删除产品(支持单条 body.id 和批量 body.ids)"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
ids_to_delete: List[str] = []
|
||||
if body.ids:
|
||||
ids_to_delete = body.ids
|
||||
@@ -675,7 +691,7 @@ async def product_delete(
|
||||
return error_response(400, "请传入 id 或 ids")
|
||||
|
||||
for pid in ids_to_delete:
|
||||
soft_delete_product(db, pid)
|
||||
soft_delete_product(db, pid, user_info=user_info)
|
||||
|
||||
return success_response(None, message="删除成功")
|
||||
except ValueError as e:
|
||||
@@ -692,10 +708,12 @@ async def product_update_status(
|
||||
):
|
||||
"""批量上架/下架"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
status_label = "上架" if body.status == "on" else "下架"
|
||||
for pid in body.ids:
|
||||
update_product(db, pid, user_id=user_id, status=body.status)
|
||||
update_product(db, pid, user_id=user_id, user_info=user_info, status=body.status)
|
||||
return success_response(None, message=f"批量{status_label}成功")
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -712,11 +730,13 @@ async def product_options(
|
||||
"""获取产品下拉选项"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
options = list_product_options(
|
||||
db,
|
||||
tenant_no,
|
||||
keyword=body.keyword,
|
||||
category_id=body.category_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(options)
|
||||
except ValueError as e:
|
||||
@@ -739,6 +759,7 @@ async def product_import(
|
||||
from openpyxl import load_workbook
|
||||
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
|
||||
content = await file.read()
|
||||
@@ -833,6 +854,7 @@ async def product_export(
|
||||
"""Excel 导出产品列表"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
rows, _ = list_products(
|
||||
db,
|
||||
tenant_no,
|
||||
@@ -1424,10 +1446,11 @@ async def contact_list(
|
||||
"""联系人分页列表"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
page = body.page if body else 1
|
||||
page_size = body.pageSize if body else 10
|
||||
rows, total = list_contacts(
|
||||
db, tenant_no, page=page, page_size=page_size,
|
||||
db, tenant_no, page=page, page_size=page_size, user_info=user_info,
|
||||
keyword=body.keyword if body else None,
|
||||
contact_name=body.contact_name if body else None,
|
||||
mobile=body.mobile if body else None,
|
||||
@@ -1461,9 +1484,12 @@ async def contact_detail(
|
||||
contact_id = body.get("id")
|
||||
if not contact_id:
|
||||
return error_response(400, "缺少联系人ID")
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
row = get_contact(db, contact_id)
|
||||
if not row:
|
||||
return error_response(404, "联系人不存在")
|
||||
ensure_crm_contact_row_in_scope(db, row, user_info, tenant_no, message="无权访问该联系人")
|
||||
return success_response(contact_row_to_dict(row))
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -1480,10 +1506,11 @@ async def contact_by_customer(
|
||||
"""按客户ID查询联系人"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
customer_id = body.get("customerId", "")
|
||||
if not customer_id:
|
||||
return error_response(400, "缺少客户ID")
|
||||
rows = list_contacts_by_customer(db, tenant_no, customer_id)
|
||||
rows = list_contacts_by_customer(db, tenant_no, customer_id, user_info=user_info)
|
||||
return success_response(rows)
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -1500,9 +1527,11 @@ async def contact_add(
|
||||
"""新增联系人"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
user_info = crm_user_info(_user)
|
||||
obj = create_contact(
|
||||
db, tenant_no=tenant_no, user_id=user_id,
|
||||
db, tenant_no=tenant_no, user_id=user_id, user_info=user_info,
|
||||
**body.model_dump(exclude_unset=True),
|
||||
)
|
||||
return success_response(contact_row_to_dict(obj), message="创建成功")
|
||||
@@ -1523,7 +1552,9 @@ async def contact_update(
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
data = body.model_dump(exclude_unset=True)
|
||||
contact_id = data.pop("id")
|
||||
obj = update_contact(db, contact_id, user_id=user_id, **data)
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
obj = update_contact(db, contact_id, user_id=user_id, tenant_no=tenant_no, user_info=user_info, **data)
|
||||
return success_response(contact_row_to_dict(obj), message="更新成功")
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -1539,8 +1570,11 @@ async def contact_delete(
|
||||
):
|
||||
"""删除联系人"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
for cid in body.ids:
|
||||
soft_delete_contact(db, cid)
|
||||
soft_delete_contact(db, cid, user_id=user_id, tenant_no=tenant_no, user_info=user_info)
|
||||
return success_response(None, message="删除成功")
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -1560,7 +1594,9 @@ async def contact_set_primary(
|
||||
if not contact_id:
|
||||
return error_response(400, "缺少联系人ID")
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
obj = set_primary_contact(db, contact_id, user_id=user_id)
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
obj = set_primary_contact(db, contact_id, user_id=user_id, tenant_no=tenant_no, user_info=user_info)
|
||||
return success_response(contact_row_to_dict(obj), message="设置成功")
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -1577,8 +1613,9 @@ async def contact_export(
|
||||
"""导出联系人列表"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
rows, _ = list_contacts(
|
||||
db, tenant_no, page=1, page_size=10000,
|
||||
db, tenant_no, page=1, page_size=10000, user_info=user_info,
|
||||
keyword=body.keyword if body else None,
|
||||
customer_id=body.customer_id if body else None,
|
||||
)
|
||||
@@ -1606,10 +1643,11 @@ async def contact_decision(
|
||||
"""决策人联系人列表"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
page = body.page if body else 1
|
||||
page_size = body.pageSize if body else 10
|
||||
rows, total = list_contacts(
|
||||
db, tenant_no, page=page, page_size=page_size,
|
||||
db, tenant_no, page=page, page_size=page_size, user_info=user_info,
|
||||
is_decision=True,
|
||||
keyword=body.keyword if body else None,
|
||||
)
|
||||
@@ -1636,10 +1674,11 @@ async def contact_primary(
|
||||
"""主联系人列表"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
page = body.page if body else 1
|
||||
page_size = body.pageSize if body else 10
|
||||
rows, total = list_contacts(
|
||||
db, tenant_no, page=page, page_size=page_size,
|
||||
db, tenant_no, page=page, page_size=page_size, user_info=user_info,
|
||||
is_primary=True,
|
||||
keyword=body.keyword if body else None,
|
||||
)
|
||||
@@ -1666,10 +1705,11 @@ async def contact_birthday(
|
||||
"""本月生日联系人列表"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
page = body.page if body else 1
|
||||
page_size = body.pageSize if body else 10
|
||||
rows, total = list_contacts(
|
||||
db, tenant_no, page=page, page_size=page_size,
|
||||
db, tenant_no, page=page, page_size=page_size, user_info=user_info,
|
||||
birthday_month=datetime.now().month,
|
||||
keyword=body.keyword if body else None,
|
||||
)
|
||||
@@ -3044,10 +3084,12 @@ async def supplier_info_list(
|
||||
"""分页查询供方列表 — Java: CrmSupplierInfoController.list"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
list_body = body or SupplierInfoListBody()
|
||||
rows, total = list_supplier_info(
|
||||
db,
|
||||
tenant_no,
|
||||
user_info=user_info,
|
||||
page=list_body.page,
|
||||
page_size=list_body.pageSize,
|
||||
supplier_name=list_body.supplier_name,
|
||||
@@ -3077,10 +3119,12 @@ async def supplier_info_list_all(
|
||||
"""供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
list_body = body or SupplierInfoListAllBody()
|
||||
rows = list_all_supplier_info(
|
||||
db,
|
||||
tenant_no,
|
||||
user_info=user_info,
|
||||
supplier_name=list_body.supplier_name,
|
||||
status=list_body.status,
|
||||
)
|
||||
@@ -3100,9 +3144,13 @@ async def supplier_info_detail(
|
||||
"""供方详情 — Java: CrmSupplierInfoController.getInfo"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
obj = get_supplier_info(db, body.id, tenant_no=tenant_no)
|
||||
if not obj:
|
||||
return error_response(404, "供方信息不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, obj, user_info, tenant_no, message="无权访问该供方信息"
|
||||
)
|
||||
return success_response(supplier_info_row_to_dict(obj))
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -3119,6 +3167,7 @@ async def supplier_info_add(
|
||||
"""新增供方 — Java: CrmSupplierInfoController.add"""
|
||||
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 = create_supplier_info(
|
||||
@@ -3144,6 +3193,7 @@ async def supplier_info_update(
|
||||
"""修改供方 — Java: CrmSupplierInfoController.edit"""
|
||||
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(exclude_unset=True)
|
||||
supplier_id = data.pop("id")
|
||||
@@ -3152,6 +3202,7 @@ async def supplier_info_update(
|
||||
supplier_id,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
**data,
|
||||
)
|
||||
return success_response(supplier_info_row_to_dict(obj), message="修改成功")
|
||||
@@ -3170,6 +3221,7 @@ async def supplier_info_delete(
|
||||
"""删除供方 — Java: CrmSupplierInfoController.remove"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
ids_to_delete: List[str] = []
|
||||
if body.ids:
|
||||
@@ -3198,10 +3250,12 @@ async def supplier_info_export(
|
||||
"""导出供方列表 — Java: CrmSupplierInfoController.export"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
list_body = body or SupplierInfoListBody()
|
||||
rows, _ = list_supplier_info(
|
||||
db,
|
||||
tenant_no,
|
||||
user_info=user_info,
|
||||
page=1,
|
||||
page_size=10000,
|
||||
supplier_name=list_body.supplier_name,
|
||||
@@ -3231,7 +3285,10 @@ async def _invoice_info_by_customer_impl(
|
||||
db: Session,
|
||||
):
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
rows = list_invoice_info_by_customer(db, tenant_no, body.customer_id)
|
||||
user_info = crm_user_info(_user)
|
||||
rows = list_invoice_info_by_customer(
|
||||
db, tenant_no, body.customer_id, user_info=user_info
|
||||
)
|
||||
return success_response(rows)
|
||||
|
||||
|
||||
@@ -3274,11 +3331,13 @@ async def invoice_info_add(
|
||||
"""新增开票信息"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
obj = create_invoice_info(
|
||||
db,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
**body.model_dump(exclude_unset=True),
|
||||
)
|
||||
return success_response(invoice_info_row_to_dict(obj), message="创建成功")
|
||||
@@ -3297,6 +3356,7 @@ async def invoice_info_update(
|
||||
"""更新开票信息"""
|
||||
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(exclude_unset=True)
|
||||
invoice_id = data.pop("id")
|
||||
@@ -3305,6 +3365,7 @@ async def invoice_info_update(
|
||||
invoice_id,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
**data,
|
||||
)
|
||||
return success_response(invoice_info_row_to_dict(obj), message="更新成功")
|
||||
@@ -3346,6 +3407,7 @@ async def invoice_info_set_default(
|
||||
"""设置默认开票信息"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
obj = set_default_invoice_info(
|
||||
db,
|
||||
|
||||
@@ -11,6 +11,7 @@ from app.core.database import get_db
|
||||
from app.core.menu_guard import require_admin_or_super_admin
|
||||
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.contract import (
|
||||
batch_assign_owner,
|
||||
batch_submit_contract_approval,
|
||||
@@ -99,6 +100,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
query = ContractListQuery(
|
||||
pageNum=pageNum,
|
||||
@@ -131,6 +133,7 @@ def register_contract_routes(router):
|
||||
rows, total = list_contracts(
|
||||
db,
|
||||
tenant_no,
|
||||
user_info=user_info,
|
||||
page=query.pageNum,
|
||||
page_size=query.pageSize,
|
||||
filters=filters,
|
||||
@@ -160,6 +163,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
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_contract(
|
||||
@@ -183,6 +187,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
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)
|
||||
@@ -194,6 +199,7 @@ def register_contract_routes(router):
|
||||
data=data,
|
||||
user_id=user_id,
|
||||
dept_id=dept_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(detail, message="修改成功")
|
||||
except ValueError as e:
|
||||
@@ -209,6 +215,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
id_list = [x.strip() for x in ids.split(",") if x.strip()]
|
||||
soft_delete_contracts(db, id_list, tenant_no=tenant_no)
|
||||
return success_response(None, message="删除成功")
|
||||
@@ -225,6 +232,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
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 ""
|
||||
submit_contract_approval(
|
||||
@@ -235,6 +243,7 @@ def register_contract_routes(router):
|
||||
remark=body.remark,
|
||||
user_id=user_id,
|
||||
dept_id=dept_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(None, message="提交成功")
|
||||
except ValueError as e:
|
||||
@@ -250,6 +259,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
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 ""
|
||||
count = batch_submit_contract_approval(
|
||||
@@ -260,6 +270,7 @@ def register_contract_routes(router):
|
||||
remark=body.remark,
|
||||
user_id=user_id,
|
||||
dept_id=dept_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(count, message="批量提交完成")
|
||||
except ValueError as e:
|
||||
@@ -275,6 +286,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
withdraw_contract_approval(
|
||||
db,
|
||||
@@ -282,6 +294,7 @@ def register_contract_routes(router):
|
||||
tenant_no=tenant_no,
|
||||
remark=body.remark,
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(None, message="撤回成功")
|
||||
except ValueError as e:
|
||||
@@ -297,6 +310,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
start_execute(db, body.id, tenant_no=tenant_no, remark=body.remark, user_id=user_id)
|
||||
return success_response(None, message="操作成功")
|
||||
@@ -313,6 +327,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
finish_contract(db, body.id, tenant_no=tenant_no, remark=body.remark, user_id=user_id)
|
||||
return success_response(None, message="操作成功")
|
||||
@@ -329,6 +344,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
invalid_contract(
|
||||
db,
|
||||
@@ -337,6 +353,7 @@ def register_contract_routes(router):
|
||||
invalid_reason=body.invalidReason,
|
||||
remark=body.remark,
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(None, message="作废成功")
|
||||
except ValueError as e:
|
||||
@@ -352,12 +369,14 @@ def register_contract_routes(router):
|
||||
):
|
||||
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,
|
||||
settlement_clause_drafts=body.settlementClauseDrafts,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(None, message="保存成功")
|
||||
except ValueError as e:
|
||||
@@ -373,6 +392,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
content = get_template(db, contract_id, tenant_no=tenant_no)
|
||||
return success_response(content or "", message="获取模板成功")
|
||||
except ValueError as e:
|
||||
@@ -388,6 +408,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
upload_file(db, body.id, body.fileUrl, tenant_no=tenant_no)
|
||||
return success_response(None, message="上传成功")
|
||||
except ValueError as e:
|
||||
@@ -403,9 +424,11 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
if body.attachments:
|
||||
upload_attachments_with_category(
|
||||
db, body.id, body.attachments, tenant_no=tenant_no
|
||||
db, body.id, body.attachments, tenant_no=tenant_no,
|
||||
user_info=user_info,
|
||||
)
|
||||
elif body.ossIds:
|
||||
upload_attachments(db, body.id, body.ossIds, tenant_no=tenant_no)
|
||||
@@ -425,6 +448,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
list_body = body or ContractExportBody()
|
||||
filters = list_body.model_dump(
|
||||
by_alias=True,
|
||||
@@ -456,6 +480,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
req = ContractStatisticsOverviewQuery(
|
||||
ownerUserId=ownerUserId,
|
||||
beginTime=beginTime,
|
||||
@@ -467,6 +492,7 @@ def register_contract_routes(router):
|
||||
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:
|
||||
@@ -481,6 +507,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
return success_response(statistics_by_status(db, tenant_no))
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -494,6 +521,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
return success_response(statistics_by_type(db, tenant_no))
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -510,6 +538,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
req = ContractStatisticsTrendQuery(type=type, beginTime=beginTime, endTime=endTime)
|
||||
data = statistics_trend(
|
||||
db,
|
||||
@@ -517,6 +546,7 @@ def register_contract_routes(router):
|
||||
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:
|
||||
@@ -532,6 +562,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
rows = query_expiring(db, tenant_no, days=days)
|
||||
return success_response(rows)
|
||||
except ValueError as e:
|
||||
@@ -548,6 +579,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
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 = renew_contract(
|
||||
@@ -573,6 +605,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
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 = create_from_quote(
|
||||
@@ -598,6 +631,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
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 = create_from_opportunity(
|
||||
@@ -622,6 +656,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
sign_contract(
|
||||
db,
|
||||
@@ -631,6 +666,7 @@ def register_contract_routes(router):
|
||||
contract_file=body.contractFile,
|
||||
remark=body.remark or "",
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(None, message="签订成功")
|
||||
except ValueError as e:
|
||||
@@ -646,6 +682,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
count = batch_assign_owner(
|
||||
db,
|
||||
@@ -654,6 +691,7 @@ def register_contract_routes(router):
|
||||
owner_user_id=body.ownerUserId,
|
||||
remark=body.remark or "",
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(count, message="分配成功")
|
||||
except ValueError as e:
|
||||
@@ -669,6 +707,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
file_path = download_contract_pdf(db, contract_id, tenant_no=tenant_no)
|
||||
return success_response(file_path)
|
||||
except ValueError as e:
|
||||
@@ -684,6 +723,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
approve_contract_by_contract_id(
|
||||
db,
|
||||
@@ -693,6 +733,7 @@ def register_contract_routes(router):
|
||||
attachments=body.attachments or "",
|
||||
remark=body.remark or "",
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
db.commit()
|
||||
return success_response(None, message="审批成功")
|
||||
@@ -709,6 +750,7 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
rows = list_contract_approvals(db, contract_id, tenant_no=tenant_no)
|
||||
return success_response(rows)
|
||||
except ValueError as e:
|
||||
@@ -724,12 +766,14 @@ def register_contract_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
detail = get_contract_detail(
|
||||
db,
|
||||
contract_id,
|
||||
tenant_no=tenant_no,
|
||||
current_user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(detail)
|
||||
except ValueError as e:
|
||||
|
||||
@@ -11,6 +11,7 @@ from app.core.database import get_db
|
||||
from app.core.menu_guard import require_admin_or_super_admin
|
||||
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.contract import (
|
||||
create_contract_change,
|
||||
create_contract_template,
|
||||
@@ -82,6 +83,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
query = ContractChangeListQuery(
|
||||
pageNum=pageNum,
|
||||
pageSize=pageSize,
|
||||
@@ -96,6 +98,7 @@ def register_contract_sub_routes(router):
|
||||
rows, total = list_contract_changes(
|
||||
db,
|
||||
tenant_no,
|
||||
user_info=user_info,
|
||||
page=query.pageNum,
|
||||
page_size=query.pageSize,
|
||||
filters=query.model_dump(by_alias=True, exclude={"pageNum", "pageSize"}, exclude_none=True),
|
||||
@@ -123,7 +126,8 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
detail = get_contract_change_detail(db, change_id, tenant_no=tenant_no)
|
||||
user_info = crm_user_info(_user)
|
||||
detail = get_contract_change_detail(db, change_id, tenant_no=tenant_no, user_info=user_info)
|
||||
return success_response(detail)
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -138,6 +142,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
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_contract_change(
|
||||
@@ -161,6 +166,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
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)
|
||||
change_id = data.pop("id")
|
||||
@@ -170,6 +176,7 @@ def register_contract_sub_routes(router):
|
||||
tenant_no=tenant_no,
|
||||
data=data,
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(detail, message="修改成功")
|
||||
except ValueError as e:
|
||||
@@ -185,8 +192,9 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
id_list = [x.strip() for x in ids.split(",") if x.strip()]
|
||||
soft_delete_contract_changes(db, id_list, tenant_no=tenant_no)
|
||||
soft_delete_contract_changes(db, id_list, tenant_no=tenant_no, user_info=user_info)
|
||||
return success_response(None, message="删除成功")
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -201,6 +209,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
submit_change_approval(
|
||||
db,
|
||||
@@ -224,6 +233,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
withdraw_change(db, change_id, tenant_no=tenant_no, user_id=user_id)
|
||||
return success_response(None, message="撤回成功")
|
||||
@@ -240,6 +250,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
execute_change(db, change_id, tenant_no=tenant_no, user_id=user_id)
|
||||
return success_response(None, message="执行成功")
|
||||
@@ -256,6 +267,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
list_body = body or ContractChangeExportBody()
|
||||
filters = list_body.model_dump(
|
||||
by_alias=True,
|
||||
@@ -293,6 +305,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
query = ContractTemplateListQuery(
|
||||
pageNum=pageNum,
|
||||
pageSize=pageSize,
|
||||
@@ -306,6 +319,7 @@ def register_contract_sub_routes(router):
|
||||
rows, total = list_contract_templates(
|
||||
db,
|
||||
tenant_no,
|
||||
user_info=user_info,
|
||||
page=query.pageNum,
|
||||
page_size=query.pageSize,
|
||||
filters=query.model_dump(by_alias=True, exclude={"pageNum", "pageSize"}, exclude_none=True),
|
||||
@@ -333,6 +347,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
detail = get_contract_template_detail(db, template_id, tenant_no=tenant_no)
|
||||
return success_response(detail)
|
||||
except ValueError as e:
|
||||
@@ -348,6 +363,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
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_contract_template(
|
||||
@@ -371,6 +387,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
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)
|
||||
template_id = data.pop("id")
|
||||
@@ -380,6 +397,7 @@ def register_contract_sub_routes(router):
|
||||
tenant_no=tenant_no,
|
||||
data=data,
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(detail, message="修改成功")
|
||||
except ValueError as e:
|
||||
@@ -395,8 +413,9 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
id_list = [x.strip() for x in ids.split(",") if x.strip()]
|
||||
soft_delete_contract_templates(db, id_list, tenant_no=tenant_no)
|
||||
soft_delete_contract_templates(db, id_list, tenant_no=tenant_no, user_info=user_info)
|
||||
return success_response(None, message="删除成功")
|
||||
except ValueError as e:
|
||||
return error_response(400, str(e))
|
||||
@@ -411,6 +430,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
toggle_contract_template_active(
|
||||
db, template_id, tenant_no=tenant_no, is_active=1, user_id=user_id
|
||||
@@ -429,6 +449,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
toggle_contract_template_active(
|
||||
db, template_id, tenant_no=tenant_no, is_active=0, user_id=user_id
|
||||
@@ -447,6 +468,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
list_body = body or ContractTemplateExportBody()
|
||||
filters = list_body.model_dump(
|
||||
by_alias=True,
|
||||
@@ -485,6 +507,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
query = ContractApprovalListQuery(
|
||||
pageNum=pageNum,
|
||||
pageSize=pageSize,
|
||||
@@ -526,6 +549,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
detail = get_contract_approval_detail(db, approval_id, tenant_no=tenant_no)
|
||||
return success_response(detail)
|
||||
except ValueError as e:
|
||||
@@ -595,6 +619,7 @@ def register_contract_sub_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
list_body = body or ContractApprovalExportBody()
|
||||
filters = list_body.model_dump(
|
||||
by_alias=True,
|
||||
|
||||
@@ -10,6 +10,7 @@ from app.core.database import get_db
|
||||
from app.core.menu_guard import require_admin_or_super_admin
|
||||
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,
|
||||
@@ -88,11 +89,13 @@ def register_quote_routes(router):
|
||||
):
|
||||
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"}),
|
||||
@@ -122,7 +125,8 @@ def register_quote_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
detail = get_quote_detail(db, body.id, tenant_no=tenant_no)
|
||||
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))
|
||||
@@ -137,6 +141,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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(
|
||||
@@ -160,6 +165,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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)
|
||||
@@ -171,6 +177,7 @@ def register_quote_routes(router):
|
||||
data=data,
|
||||
user_id=user_id,
|
||||
dept_id=dept_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(detail, message="修改成功")
|
||||
except ValueError as e:
|
||||
@@ -186,6 +193,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
soft_delete_quotes(db, body.ids, tenant_no=tenant_no)
|
||||
return success_response(None, message="删除成功")
|
||||
except ValueError as e:
|
||||
@@ -201,12 +209,14 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -222,6 +232,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -237,6 +248,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
content = get_template(db, body.id, tenant_no=tenant_no)
|
||||
return success_response(content or "")
|
||||
except ValueError as e:
|
||||
@@ -252,6 +264,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -267,12 +280,14 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -288,6 +303,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -303,12 +319,14 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -324,6 +342,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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(
|
||||
@@ -337,6 +356,7 @@ def register_quote_routes(router):
|
||||
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:
|
||||
@@ -352,6 +372,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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(
|
||||
@@ -362,6 +383,7 @@ def register_quote_routes(router):
|
||||
remark=body.remark,
|
||||
user_id=user_id,
|
||||
dept_id=dept_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(new_id)
|
||||
except ValueError as e:
|
||||
@@ -377,6 +399,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -392,6 +415,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
invalid_quote(db, body.id, tenant_no=tenant_no, remark=body.remark)
|
||||
return success_response(None, message="作废成功")
|
||||
except ValueError as e:
|
||||
@@ -407,10 +431,12 @@ def register_quote_routes(router):
|
||||
):
|
||||
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=10000,
|
||||
filters=list_body.model_dump(by_alias=True, exclude={"page", "pageSize"}),
|
||||
@@ -437,6 +463,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
req = body or QuoteStatisticsOverviewBody()
|
||||
data = statistics_overview(
|
||||
db,
|
||||
@@ -444,6 +471,7 @@ def register_quote_routes(router):
|
||||
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:
|
||||
@@ -458,6 +486,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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))
|
||||
@@ -472,6 +501,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
req = body or QuoteStatisticsTrendBody()
|
||||
data = statistics_trend(
|
||||
db,
|
||||
@@ -479,6 +509,7 @@ def register_quote_routes(router):
|
||||
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:
|
||||
@@ -494,6 +525,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -509,6 +541,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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(
|
||||
@@ -534,6 +567,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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")
|
||||
@@ -543,6 +577,7 @@ def register_quote_routes(router):
|
||||
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
|
||||
|
||||
@@ -560,6 +595,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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)
|
||||
return success_response(None, message="删除成功")
|
||||
except ValueError as e:
|
||||
@@ -575,6 +611,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -590,6 +627,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
approve_quote(
|
||||
db,
|
||||
@@ -599,6 +637,7 @@ def register_quote_routes(router):
|
||||
approval_opinion=body.approvalOpinion or "",
|
||||
remark=body.remark or "",
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(None, message="审批成功")
|
||||
except ValueError as e:
|
||||
@@ -614,6 +653,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, _user)
|
||||
user_info = crm_user_info(_user)
|
||||
user_id = _user.get("user_id", "") or _user.get("id", "")
|
||||
withdraw_approval(
|
||||
db,
|
||||
@@ -621,6 +661,7 @@ def register_quote_routes(router):
|
||||
tenant_no=tenant_no,
|
||||
remark=body.remark or "",
|
||||
user_id=user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
return success_response(None, message="撤回成功")
|
||||
except ValueError as e:
|
||||
@@ -651,6 +692,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
@@ -666,6 +708,7 @@ def register_quote_routes(router):
|
||||
):
|
||||
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:
|
||||
|
||||
@@ -9,6 +9,11 @@ from sqlalchemy import func, or_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
from app.crud.crm.data_scope_helpers import (
|
||||
apply_crm_create_by_scope,
|
||||
ensure_crm_contact_row_in_scope,
|
||||
ensure_crm_owner_row_in_scope,
|
||||
)
|
||||
from app.models.crm_models import CrmContact
|
||||
|
||||
|
||||
@@ -86,11 +91,14 @@ def list_contacts(
|
||||
is_decision: bool = None,
|
||||
birthday_month: int = None,
|
||||
role: str = None,
|
||||
user_info=None,
|
||||
) -> Tuple[List[Dict[str, Any]], int]:
|
||||
q = db.query(CrmContact).filter(
|
||||
CrmContact.tenant_no == tenant_no,
|
||||
CrmContact.del_flag == 0,
|
||||
)
|
||||
if user_info:
|
||||
q = apply_crm_create_by_scope(q, CrmContact, user_info, db, tenant_no)
|
||||
|
||||
kw = (keyword or "").strip()
|
||||
if kw:
|
||||
@@ -146,7 +154,18 @@ def list_contacts_by_customer(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
customer_id: str,
|
||||
*,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
if user_info:
|
||||
from app.crud.crm.customer import get_customer
|
||||
|
||||
customer = get_customer(db, customer_id)
|
||||
if not customer or customer.tenant_no != tenant_no:
|
||||
raise ValueError("客户不存在")
|
||||
ensure_crm_owner_row_in_scope(
|
||||
db, customer, user_info, tenant_no, message="无权访问该客户"
|
||||
)
|
||||
rows = (
|
||||
db.query(CrmContact)
|
||||
.filter(
|
||||
@@ -177,14 +196,22 @@ def create_contact(
|
||||
tenant_no: str,
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
**kwargs,
|
||||
) -> CrmContact:
|
||||
customer_id = kwargs.get("customer_id")
|
||||
customer_name = kwargs.get("customer_name")
|
||||
if customer_id and not customer_name:
|
||||
if customer_id:
|
||||
from app.crud.crm.customer import get_customer
|
||||
|
||||
cust = get_customer(db, customer_id)
|
||||
if cust:
|
||||
if user_info:
|
||||
if not cust or cust.tenant_no != tenant_no:
|
||||
raise ValueError("客户不存在")
|
||||
ensure_crm_owner_row_in_scope(
|
||||
db, cust, user_info, tenant_no, message="无权访问该客户"
|
||||
)
|
||||
if cust and not customer_name:
|
||||
customer_name = cust.customer_name
|
||||
|
||||
obj = CrmContact(
|
||||
@@ -229,11 +256,16 @@ def update_contact(
|
||||
contact_id: str,
|
||||
*,
|
||||
user_id: str = None,
|
||||
tenant_no: str = None,
|
||||
user_info=None,
|
||||
**kwargs,
|
||||
) -> CrmContact:
|
||||
obj = get_contact(db, contact_id)
|
||||
if not obj:
|
||||
raise ValueError("联系人不存在")
|
||||
if tenant_no and obj.tenant_no != tenant_no:
|
||||
raise ValueError("联系人不存在")
|
||||
ensure_crm_contact_row_in_scope(db, obj, user_info, obj.tenant_no)
|
||||
|
||||
_updatable = (
|
||||
"contact_name", "gender", "mobile", "telephone", "email",
|
||||
@@ -260,10 +292,20 @@ def update_contact(
|
||||
return obj
|
||||
|
||||
|
||||
def set_primary_contact(db: Session, contact_id: str, user_id: str = None) -> CrmContact:
|
||||
def set_primary_contact(
|
||||
db: Session,
|
||||
contact_id: str,
|
||||
user_id: str = None,
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_info=None,
|
||||
) -> CrmContact:
|
||||
obj = get_contact(db, contact_id)
|
||||
if not obj:
|
||||
raise ValueError("联系人不存在")
|
||||
if tenant_no and obj.tenant_no != tenant_no:
|
||||
raise ValueError("联系人不存在")
|
||||
ensure_crm_contact_row_in_scope(db, obj, user_info, obj.tenant_no)
|
||||
|
||||
db.query(CrmContact).filter(
|
||||
CrmContact.customer_id == obj.customer_id,
|
||||
@@ -279,10 +321,20 @@ def set_primary_contact(db: Session, contact_id: str, user_id: str = None) -> Cr
|
||||
return obj
|
||||
|
||||
|
||||
def soft_delete_contact(db: Session, contact_id: str, user_id: str = None) -> bool:
|
||||
def soft_delete_contact(
|
||||
db: Session,
|
||||
contact_id: str,
|
||||
user_id: str = None,
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
obj = get_contact(db, contact_id)
|
||||
if not obj:
|
||||
raise ValueError("联系人不存在")
|
||||
if tenant_no and obj.tenant_no != tenant_no:
|
||||
raise ValueError("联系人不存在")
|
||||
ensure_crm_contact_row_in_scope(db, obj, user_info, obj.tenant_no)
|
||||
customer_id = obj.customer_id
|
||||
obj.del_flag = 1
|
||||
obj.update_by = user_id
|
||||
|
||||
@@ -13,6 +13,7 @@ from sqlalchemy import func, or_
|
||||
from sqlalchemy.orm import Query, Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
from app.crud.crm.data_scope_helpers import apply_crm_owner_scope, ensure_crm_owner_row_in_scope, apply_crm_create_by_scope, ensure_crm_create_by_row_in_scope
|
||||
from app.crud.crm.contact import get_contact
|
||||
from app.crud.crm.contract_approval import (
|
||||
can_user_approve,
|
||||
@@ -42,6 +43,25 @@ from app.models.crm_models import (
|
||||
from app.models.models import SysUser
|
||||
|
||||
_FMT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
def _ensure_contract_in_scope(db: Session, row: CrmContract, user_info, tenant_no: str) -> None:
|
||||
ensure_crm_owner_row_in_scope(db, row, user_info, tenant_no, message="无权访问该合同")
|
||||
|
||||
|
||||
def _get_contract_row_in_scope(
|
||||
db: Session,
|
||||
contract_id: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> CrmContract:
|
||||
row = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
if not row:
|
||||
raise ValueError("合同不存在")
|
||||
_ensure_contract_in_scope(db, row, user_info, tenant_no)
|
||||
return row
|
||||
|
||||
|
||||
_DATE_FMT = "%Y-%m-%d"
|
||||
|
||||
|
||||
@@ -374,10 +394,13 @@ def list_contracts(
|
||||
page_size: int = 10,
|
||||
filters: dict = None,
|
||||
current_user_id: str = None,
|
||||
user_info=None,
|
||||
) -> Tuple[List[Dict[str, Any]], int]:
|
||||
"""Java: queryPageList"""
|
||||
q = db.query(CrmContract)
|
||||
q = _build_contract_filters(db, q, filters or {}, tenant_no=tenant_no)
|
||||
if user_info:
|
||||
q = apply_crm_owner_scope(q, CrmContract, user_info, db, tenant_no)
|
||||
total = q.order_by(None).with_entities(func.count(CrmContract.id)).scalar() or 0
|
||||
page = max(1, page)
|
||||
page_size = min(max(1, page_size), 100)
|
||||
@@ -403,6 +426,7 @@ def get_contract(
|
||||
*,
|
||||
tenant_no: str,
|
||||
current_user_id: str = None,
|
||||
user_info=None,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Java: queryById"""
|
||||
row = (
|
||||
@@ -416,6 +440,7 @@ def get_contract(
|
||||
)
|
||||
if not row:
|
||||
return None
|
||||
_ensure_contract_in_scope(db, row, user_info, tenant_no)
|
||||
|
||||
detail = _row_to_dict(row)
|
||||
detail["createByName"] = _resolve_user_display_name(db, detail.get("createBy"))
|
||||
@@ -506,12 +531,14 @@ def get_contract_detail(
|
||||
*,
|
||||
tenant_no: str,
|
||||
current_user_id: str = None,
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
detail = get_contract(
|
||||
db,
|
||||
contract_id,
|
||||
tenant_no=tenant_no,
|
||||
current_user_id=current_user_id,
|
||||
user_info=user_info,
|
||||
)
|
||||
if not detail:
|
||||
raise ValueError("合同不存在")
|
||||
@@ -957,9 +984,10 @@ def update_contract(
|
||||
data: dict,
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Java: updateByBo"""
|
||||
old = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
old = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not old:
|
||||
raise ValueError("合同不存在")
|
||||
if old.status != "draft":
|
||||
@@ -1026,11 +1054,12 @@ def soft_delete_contracts(
|
||||
*,
|
||||
tenant_no: str,
|
||||
validate: bool = True,
|
||||
user_info=None,
|
||||
) -> int:
|
||||
"""Java: deleteWithValidByIds — 软删除"""
|
||||
count = 0
|
||||
for cid in contract_ids:
|
||||
entity = get_contract_row(db, cid, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, cid, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
continue
|
||||
if validate and entity.status != "draft":
|
||||
@@ -1116,9 +1145,10 @@ def submit_contract_approval(
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: submitApproval"""
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
if entity.status != "draft":
|
||||
@@ -1158,6 +1188,7 @@ def batch_submit_contract_approval(
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> int:
|
||||
"""Java: batchSubmitApproval"""
|
||||
success = 0
|
||||
@@ -1185,9 +1216,10 @@ def withdraw_contract_approval(
|
||||
tenant_no: str,
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: withdrawApproval"""
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
if entity.status != "approving":
|
||||
@@ -1211,8 +1243,9 @@ def start_execute(
|
||||
tenant_no: str,
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
if entity.status != "approved":
|
||||
@@ -1233,8 +1266,9 @@ def finish_contract(
|
||||
tenant_no: str,
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
if entity.status != "executing":
|
||||
@@ -1256,10 +1290,11 @@ def invalid_contract(
|
||||
invalid_reason: str,
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
if not (invalid_reason or "").strip():
|
||||
raise ValueError("作废原因不能为空")
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
if entity.status == "finished":
|
||||
@@ -1282,8 +1317,9 @@ def save_template(
|
||||
*,
|
||||
tenant_no: str,
|
||||
settlement_clause_drafts: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
|
||||
@@ -1305,7 +1341,7 @@ def save_template(
|
||||
|
||||
|
||||
def get_template(db: Session, contract_id: str, *, tenant_no: str) -> str:
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
return entity.template_content or ""
|
||||
@@ -1317,8 +1353,9 @@ def upload_file(
|
||||
file_url: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
entity.contract_file = file_url or ""
|
||||
@@ -1333,10 +1370,11 @@ def upload_attachments(
|
||||
oss_ids: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
if not (oss_ids or "").strip():
|
||||
raise ValueError("附件ossId不能为空")
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
|
||||
@@ -1357,10 +1395,11 @@ def upload_attachments_with_category(
|
||||
attachments: List[dict],
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
if not attachments:
|
||||
raise ValueError("附件列表不能为空")
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
|
||||
@@ -1404,6 +1443,7 @@ def statistics_overview(
|
||||
owner_user_id: str = None,
|
||||
begin_time: str = None,
|
||||
end_time: str = None,
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
q = db.query(CrmContract).filter(CrmContract.tenant_no == tenant_no, CrmContract.del_flag == 0)
|
||||
if owner_user_id:
|
||||
@@ -1416,6 +1456,8 @@ def statistics_overview(
|
||||
q = q.filter(CrmContract.create_time >= begin_dt)
|
||||
elif end_dt:
|
||||
q = q.filter(CrmContract.create_time <= end_dt)
|
||||
if user_info:
|
||||
q = apply_crm_owner_scope(q, CrmContract, user_info, db, tenant_no)
|
||||
|
||||
contracts = q.all()
|
||||
total_count = len(contracts)
|
||||
@@ -1457,12 +1499,11 @@ def statistics_overview(
|
||||
}
|
||||
|
||||
|
||||
def statistics_by_status(db: Session, tenant_no: str) -> List[Dict[str, Any]]:
|
||||
contracts = (
|
||||
db.query(CrmContract)
|
||||
.filter(CrmContract.tenant_no == tenant_no, CrmContract.del_flag == 0)
|
||||
.all()
|
||||
)
|
||||
def statistics_by_status(db: Session, tenant_no: str, *, user_info=None) -> List[Dict[str, Any]]:
|
||||
q = db.query(CrmContract).filter(CrmContract.tenant_no == tenant_no, CrmContract.del_flag == 0)
|
||||
if user_info:
|
||||
q = apply_crm_owner_scope(q, CrmContract, user_info, db, tenant_no)
|
||||
contracts = q.all()
|
||||
total_count = len(contracts)
|
||||
total_amount = sum(_decimal_value(c.amount) for c in contracts)
|
||||
|
||||
@@ -1492,12 +1533,11 @@ def statistics_by_status(db: Session, tenant_no: str) -> List[Dict[str, Any]]:
|
||||
return result
|
||||
|
||||
|
||||
def statistics_by_type(db: Session, tenant_no: str) -> List[Dict[str, Any]]:
|
||||
contracts = (
|
||||
db.query(CrmContract)
|
||||
.filter(CrmContract.tenant_no == tenant_no, CrmContract.del_flag == 0)
|
||||
.all()
|
||||
)
|
||||
def statistics_by_type(db: Session, tenant_no: str, *, user_info=None) -> List[Dict[str, Any]]:
|
||||
q = db.query(CrmContract).filter(CrmContract.tenant_no == tenant_no, CrmContract.del_flag == 0)
|
||||
if user_info:
|
||||
q = apply_crm_owner_scope(q, CrmContract, user_info, db, tenant_no)
|
||||
contracts = q.all()
|
||||
total_count = len(contracts)
|
||||
type_map = (
|
||||
("sale", "销售合同"),
|
||||
@@ -1544,12 +1584,15 @@ def statistics_trend(
|
||||
period_type: str = "month",
|
||||
begin_time: str = None,
|
||||
end_time: str = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
q = db.query(CrmContract).filter(CrmContract.tenant_no == tenant_no, CrmContract.del_flag == 0)
|
||||
begin_dt = _parse_optional_datetime(begin_time)
|
||||
end_dt = _parse_optional_datetime(end_time)
|
||||
if begin_dt and end_dt:
|
||||
q = q.filter(CrmContract.create_time >= begin_dt, CrmContract.create_time <= end_dt)
|
||||
if user_info:
|
||||
q = apply_crm_owner_scope(q, CrmContract, user_info, db, tenant_no)
|
||||
contracts = q.all()
|
||||
if not contracts:
|
||||
return []
|
||||
@@ -1585,6 +1628,7 @@ def query_expiring(
|
||||
tenant_no: str,
|
||||
*,
|
||||
days: int = 30,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
if not days or days <= 0:
|
||||
days = 30
|
||||
@@ -1642,6 +1686,7 @@ def create_from_quote(
|
||||
data: dict,
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> str:
|
||||
"""Java: createFromQuote — 返回合同 id"""
|
||||
quote = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
@@ -1700,6 +1745,7 @@ def renew_contract(
|
||||
data: dict,
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> str:
|
||||
"""Java: renewContract — 基础续签"""
|
||||
parent = get_contract_row(db, parent_contract_id, tenant_no=tenant_no)
|
||||
@@ -1750,6 +1796,7 @@ def export_contracts(
|
||||
tenant_no: str,
|
||||
*,
|
||||
filters: dict = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Java: queryList for export"""
|
||||
q = db.query(CrmContract)
|
||||
@@ -1790,11 +1837,14 @@ def list_contract_changes(
|
||||
page: int = 1,
|
||||
page_size: int = 10,
|
||||
filters: dict = None,
|
||||
user_info=None,
|
||||
) -> Tuple[List[Dict[str, Any]], int]:
|
||||
q = db.query(CrmContractChange).filter(
|
||||
CrmContractChange.tenant_no == tenant_no,
|
||||
CrmContractChange.del_flag == 0,
|
||||
)
|
||||
if user_info:
|
||||
q = apply_crm_create_by_scope(q, CrmContractChange, user_info, db, tenant_no)
|
||||
filters = filters or {}
|
||||
contract_id = (filters.get("contractId") or filters.get("contract_id") or "").strip()
|
||||
if contract_id:
|
||||
@@ -1814,6 +1864,7 @@ def get_contract_change_detail(
|
||||
change_id: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
row = (
|
||||
db.query(CrmContractChange)
|
||||
@@ -1826,6 +1877,9 @@ def get_contract_change_detail(
|
||||
)
|
||||
if not row:
|
||||
raise ValueError("合同变更记录不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, row, user_info, tenant_no, message="无权访问该合同变更"
|
||||
)
|
||||
return _change_row_to_dict(row)
|
||||
|
||||
|
||||
@@ -1861,11 +1915,14 @@ def list_contract_templates(
|
||||
page: int = 1,
|
||||
page_size: int = 10,
|
||||
filters: dict = None,
|
||||
user_info=None,
|
||||
) -> Tuple[List[Dict[str, Any]], int]:
|
||||
q = db.query(CrmContractTemplate).filter(
|
||||
CrmContractTemplate.tenant_no == tenant_no,
|
||||
CrmContractTemplate.del_flag == 0,
|
||||
)
|
||||
if user_info:
|
||||
q = apply_crm_create_by_scope(q, CrmContractTemplate, user_info, db, tenant_no)
|
||||
filters = filters or {}
|
||||
template_no = (filters.get("templateNo") or filters.get("template_no") or "").strip()
|
||||
if template_no:
|
||||
@@ -1937,6 +1994,7 @@ def create_from_opportunity(
|
||||
data: dict,
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> str:
|
||||
"""Java: createFromOpportunity — 返回合同 id"""
|
||||
opp_row = (
|
||||
@@ -1990,9 +2048,10 @@ def sign_contract(
|
||||
contract_file: str,
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: signContract"""
|
||||
entity = get_contract_row(db, contract_id, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("合同不存在")
|
||||
if entity.status != "approved":
|
||||
@@ -2019,6 +2078,7 @@ def batch_assign_owner(
|
||||
owner_user_id: str,
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> int:
|
||||
"""Java: batchAssignOwner"""
|
||||
if not contract_ids:
|
||||
@@ -2030,7 +2090,7 @@ def batch_assign_owner(
|
||||
success = 0
|
||||
owner_name = _resolve_user_display_name(db, owner_id)
|
||||
for cid in contract_ids:
|
||||
entity = get_contract_row(db, cid, tenant_no=tenant_no)
|
||||
entity = _get_contract_row_in_scope(db, cid, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
continue
|
||||
entity.owner_user_id = owner_id
|
||||
@@ -2049,6 +2109,7 @@ def download_contract_pdf(
|
||||
contract_id: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> str:
|
||||
"""Java: downloadContractPdf — 返回合同文件 URL/路径"""
|
||||
detail = get_contract_detail(db, contract_id, tenant_no=tenant_no)
|
||||
@@ -2177,10 +2238,14 @@ def update_contract_change(
|
||||
tenant_no: str,
|
||||
data: dict,
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
entity = _get_change_row(db, change_id, tenant_no=tenant_no)
|
||||
if not entity:
|
||||
raise ValueError("合同变更记录不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, entity, user_info, tenant_no, message="无权访问该合同变更"
|
||||
)
|
||||
if entity.approval_status != "pending":
|
||||
raise ValueError("只能修改待审批状态的变更记录")
|
||||
|
||||
@@ -2210,12 +2275,16 @@ def soft_delete_contract_changes(
|
||||
*,
|
||||
tenant_no: str,
|
||||
validate: bool = True,
|
||||
user_info=None,
|
||||
) -> int:
|
||||
count = 0
|
||||
for cid in change_ids:
|
||||
entity = _get_change_row(db, cid, tenant_no=tenant_no)
|
||||
if not entity:
|
||||
continue
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, entity, user_info, tenant_no, message="无权访问该合同变更"
|
||||
)
|
||||
if validate and entity.approval_status != "pending":
|
||||
raise ValueError("只能删除待审批状态的变更记录")
|
||||
entity.del_flag = 1
|
||||
@@ -2346,6 +2415,7 @@ def update_contract_template(
|
||||
tenant_no: str,
|
||||
data: dict,
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
entity = (
|
||||
db.query(CrmContractTemplate)
|
||||
@@ -2358,6 +2428,9 @@ def update_contract_template(
|
||||
)
|
||||
if not entity:
|
||||
raise ValueError("合同模板不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, entity, user_info, tenant_no, message="无权访问该合同模板"
|
||||
)
|
||||
|
||||
field_map = (
|
||||
(("template_no", "templateNo"), "template_no"),
|
||||
@@ -2398,6 +2471,7 @@ def soft_delete_contract_templates(
|
||||
*,
|
||||
tenant_no: str,
|
||||
validate: bool = True,
|
||||
user_info=None,
|
||||
) -> int:
|
||||
count = 0
|
||||
for tid in template_ids:
|
||||
@@ -2412,6 +2486,9 @@ def soft_delete_contract_templates(
|
||||
)
|
||||
if not entity:
|
||||
continue
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, entity, user_info, tenant_no, message="无权访问该合同模板"
|
||||
)
|
||||
if validate and (entity.usage_count or 0) > 0:
|
||||
raise ValueError(f"模板【{entity.template_name}】已被使用,不能删除")
|
||||
entity.del_flag = 1
|
||||
|
||||
@@ -76,7 +76,12 @@ def list_contract_products(
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
include_price_tiers: bool = False,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
if tenant_no and user_info:
|
||||
from app.crud.crm.contract import _get_contract_row_in_scope
|
||||
|
||||
_get_contract_row_in_scope(db, contract_id, tenant_no=tenant_no, user_info=user_info)
|
||||
q = db.query(CrmContractProduct).filter(
|
||||
CrmContractProduct.contract_id == contract_id,
|
||||
CrmContractProduct.del_flag == 0,
|
||||
|
||||
@@ -52,3 +52,51 @@ def ensure_crm_create_by_row_in_scope(
|
||||
return
|
||||
if not row_in_data_scope(db, row, user_info, tenant_no):
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def ensure_crm_contact_row_in_scope(
|
||||
db: Session, row, user_info, tenant_no: str, *, message: str = "无权访问该联系人"
|
||||
) -> None:
|
||||
"""联系人:create_by 在范围内,或关联客户在 owner 范围内。"""
|
||||
if not user_info or row is None:
|
||||
return
|
||||
if row_in_data_scope(db, row, user_info, tenant_no):
|
||||
return
|
||||
customer_id = getattr(row, "customer_id", None) or ""
|
||||
if customer_id:
|
||||
from app.crud.crm.customer import get_customer
|
||||
|
||||
customer = get_customer(db, customer_id)
|
||||
if customer and customer.tenant_no == tenant_no:
|
||||
if row_in_data_scope(
|
||||
db, customer, user_info, tenant_no, owner_field=CRM_OWNER_FIELD
|
||||
):
|
||||
return
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def ensure_crm_customer_linked_row_in_scope(
|
||||
db: Session,
|
||||
row,
|
||||
user_info,
|
||||
tenant_no: str,
|
||||
*,
|
||||
customer_id: str = None,
|
||||
message: str = "无权访问该数据",
|
||||
) -> None:
|
||||
"""客户子表(开票信息等):先校验关联客户 owner scope,否则校验 create_by。"""
|
||||
if not user_info or row is None:
|
||||
return
|
||||
cid = customer_id or getattr(row, "customer_id", None) or ""
|
||||
if cid:
|
||||
from app.crud.crm.customer import get_customer
|
||||
|
||||
customer = get_customer(db, cid)
|
||||
if customer and customer.tenant_no == tenant_no:
|
||||
if row_in_data_scope(
|
||||
db, customer, user_info, tenant_no, owner_field=CRM_OWNER_FIELD
|
||||
):
|
||||
return
|
||||
if row_in_data_scope(db, row, user_info, tenant_no):
|
||||
return
|
||||
raise ValueError(message)
|
||||
@@ -7,6 +7,10 @@ from typing import Any, Dict, List, Optional
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
from app.crud.crm.data_scope_helpers import (
|
||||
ensure_crm_customer_linked_row_in_scope,
|
||||
ensure_crm_owner_row_in_scope,
|
||||
)
|
||||
from app.models.crm_models import CrmInvoiceInfo
|
||||
|
||||
|
||||
@@ -39,7 +43,18 @@ def list_invoice_info_by_customer(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
customer_id: str,
|
||||
*,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
if user_info:
|
||||
from app.crud.crm.customer import get_customer
|
||||
|
||||
customer = get_customer(db, customer_id)
|
||||
if not customer or customer.tenant_no != tenant_no:
|
||||
raise ValueError("客户不存在")
|
||||
ensure_crm_owner_row_in_scope(
|
||||
db, customer, user_info, tenant_no, message="无权访问该客户"
|
||||
)
|
||||
rows = (
|
||||
db.query(CrmInvoiceInfo)
|
||||
.filter(
|
||||
@@ -87,11 +102,21 @@ def create_invoice_info(
|
||||
tenant_no: str,
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
**kwargs,
|
||||
) -> CrmInvoiceInfo:
|
||||
customer_id = kwargs.get("customer_id")
|
||||
if not customer_id:
|
||||
raise ValueError("缺少客户ID")
|
||||
if user_info:
|
||||
from app.crud.crm.customer import get_customer
|
||||
|
||||
customer = get_customer(db, customer_id)
|
||||
if not customer or customer.tenant_no != tenant_no:
|
||||
raise ValueError("客户不存在")
|
||||
ensure_crm_owner_row_in_scope(
|
||||
db, customer, user_info, tenant_no, message="无权访问该客户"
|
||||
)
|
||||
|
||||
company_name = (kwargs.get("company_name") or "").strip()
|
||||
if not company_name:
|
||||
@@ -132,6 +157,7 @@ def update_invoice_info(
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
**kwargs,
|
||||
) -> CrmInvoiceInfo:
|
||||
obj = get_invoice_info(db, invoice_id)
|
||||
@@ -140,6 +166,9 @@ def update_invoice_info(
|
||||
|
||||
if tenant_no and obj.tenant_no != tenant_no:
|
||||
raise ValueError("开票信息不存在")
|
||||
ensure_crm_customer_linked_row_in_scope(
|
||||
db, obj, user_info, obj.tenant_no, message="无权访问该开票信息"
|
||||
)
|
||||
|
||||
_updatable = (
|
||||
"company_name",
|
||||
@@ -181,12 +210,16 @@ def set_default_invoice_info(
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> CrmInvoiceInfo:
|
||||
obj = get_invoice_info(db, invoice_id)
|
||||
if not obj or obj.tenant_no != tenant_no:
|
||||
raise ValueError("开票信息不存在")
|
||||
if obj.customer_id != customer_id:
|
||||
raise ValueError("开票信息与客户不匹配")
|
||||
ensure_crm_customer_linked_row_in_scope(
|
||||
db, obj, user_info, tenant_no, message="无权访问该开票信息"
|
||||
)
|
||||
|
||||
_clear_default_for_customer(db, tenant_no, customer_id, exclude_id=invoice_id)
|
||||
obj.is_default = "1"
|
||||
@@ -197,10 +230,22 @@ def set_default_invoice_info(
|
||||
return obj
|
||||
|
||||
|
||||
def soft_delete_invoice_info(db: Session, invoice_id: str, user_id: str = None) -> bool:
|
||||
def soft_delete_invoice_info(
|
||||
db: Session,
|
||||
invoice_id: str,
|
||||
user_id: str = None,
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
obj = get_invoice_info(db, invoice_id)
|
||||
if not obj:
|
||||
raise ValueError("开票信息不存在")
|
||||
if tenant_no and obj.tenant_no != tenant_no:
|
||||
raise ValueError("开票信息不存在")
|
||||
ensure_crm_customer_linked_row_in_scope(
|
||||
db, obj, user_info, obj.tenant_no, message="无权访问该开票信息"
|
||||
)
|
||||
obj.del_flag = 1
|
||||
obj.update_by = user_id
|
||||
obj.update_time = datetime.now()
|
||||
|
||||
@@ -10,6 +10,10 @@ from sqlalchemy import and_, func, or_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
from app.crud.crm.data_scope_helpers import (
|
||||
apply_crm_create_by_scope,
|
||||
ensure_crm_create_by_row_in_scope,
|
||||
)
|
||||
from app.models.crm_models import CrmProduct, CrmProductCategory, SysOss
|
||||
|
||||
|
||||
@@ -48,12 +52,15 @@ def list_categories(
|
||||
*,
|
||||
category_name: Optional[str] = None,
|
||||
status: Optional[str] = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""获取分类(扁平列表,支持名称模糊与状态筛选)"""
|
||||
q = db.query(CrmProductCategory).filter(
|
||||
CrmProductCategory.tenant_no == tenant_no,
|
||||
CrmProductCategory.del_flag == 0,
|
||||
)
|
||||
if user_info:
|
||||
q = apply_crm_create_by_scope(q, CrmProductCategory, user_info, db, tenant_no)
|
||||
name_kw = (category_name or "").strip()
|
||||
if name_kw:
|
||||
q = q.filter(CrmProductCategory.category_name.like(f"%{name_kw}%"))
|
||||
@@ -99,6 +106,7 @@ def list_category_tree(
|
||||
*,
|
||||
category_name: Optional[str] = None,
|
||||
status: Optional[str] = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""获取产品分类树(嵌套 children,供树形表格/树选择器使用)"""
|
||||
name_kw = (category_name or "").strip()
|
||||
@@ -106,7 +114,7 @@ def list_category_tree(
|
||||
has_filter = bool(name_kw) or bool(status_val)
|
||||
|
||||
if not has_filter:
|
||||
flat = list_categories(db, tenant_no)
|
||||
flat = list_categories(db, tenant_no, user_info=user_info)
|
||||
return _build_category_tree(flat)
|
||||
|
||||
matched = list_categories(
|
||||
@@ -114,6 +122,7 @@ def list_category_tree(
|
||||
tenant_no,
|
||||
category_name=category_name,
|
||||
status=status,
|
||||
user_info=user_info,
|
||||
)
|
||||
if not matched:
|
||||
return []
|
||||
@@ -178,12 +187,16 @@ def update_category(
|
||||
category_id: str,
|
||||
*,
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
**kwargs,
|
||||
) -> CrmProductCategory:
|
||||
"""更新分类"""
|
||||
obj = get_category(db, category_id)
|
||||
if not obj:
|
||||
raise ValueError("分类不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, obj, user_info, obj.tenant_no, message="无权访问该分类"
|
||||
)
|
||||
|
||||
# 逐字段更新
|
||||
for field in ("category_name", "category_code", "status", "order_num", "remark"):
|
||||
@@ -208,11 +221,14 @@ def update_category(
|
||||
return obj
|
||||
|
||||
|
||||
def soft_delete_category(db: Session, category_id: str) -> None:
|
||||
def soft_delete_category(db: Session, category_id: str, *, user_info=None) -> None:
|
||||
"""软删除分类"""
|
||||
obj = get_category(db, category_id)
|
||||
if not obj:
|
||||
raise ValueError("分类不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, obj, user_info, obj.tenant_no, message="无权访问该分类"
|
||||
)
|
||||
|
||||
# 检查是否有子分类
|
||||
has_children = (
|
||||
@@ -303,12 +319,15 @@ def list_products(
|
||||
keyword: str = None,
|
||||
category_id: str = None,
|
||||
status: str = None,
|
||||
user_info=None,
|
||||
) -> Tuple[List[Dict[str, Any]], int]:
|
||||
"""分页查询产品列表"""
|
||||
q = db.query(CrmProduct).filter(
|
||||
CrmProduct.tenant_no == tenant_no,
|
||||
CrmProduct.del_flag == 0,
|
||||
)
|
||||
if user_info:
|
||||
q = apply_crm_create_by_scope(q, CrmProduct, user_info, db, tenant_no)
|
||||
|
||||
# keyword 筛选
|
||||
kw = (keyword or "").strip()
|
||||
@@ -482,12 +501,16 @@ def update_product(
|
||||
product_id: str,
|
||||
*,
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
**kwargs,
|
||||
) -> CrmProduct:
|
||||
"""更新产品"""
|
||||
obj = get_product(db, product_id)
|
||||
if not obj:
|
||||
raise ValueError("产品不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, obj, user_info, obj.tenant_no, message="无权访问该产品"
|
||||
)
|
||||
|
||||
# 逐字段更新
|
||||
for field in (
|
||||
@@ -535,11 +558,14 @@ def update_product(
|
||||
return obj
|
||||
|
||||
|
||||
def soft_delete_product(db: Session, product_id: str) -> None:
|
||||
def soft_delete_product(db: Session, product_id: str, *, user_info=None) -> None:
|
||||
"""软删除产品"""
|
||||
obj = get_product(db, product_id)
|
||||
if not obj:
|
||||
raise ValueError("产品不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, obj, user_info, obj.tenant_no, message="无权访问该产品"
|
||||
)
|
||||
obj.del_flag = 1
|
||||
db.commit()
|
||||
|
||||
@@ -584,6 +610,7 @@ def list_product_options(
|
||||
*,
|
||||
keyword: str = None,
|
||||
category_id: str = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""获取产品下拉选项(不分页,返回全量)"""
|
||||
q = db.query(CrmProduct).filter(
|
||||
@@ -591,6 +618,8 @@ def list_product_options(
|
||||
CrmProduct.del_flag == 0,
|
||||
CrmProduct.status == "on",
|
||||
)
|
||||
if user_info:
|
||||
q = apply_crm_create_by_scope(q, CrmProduct, user_info, db, tenant_no)
|
||||
if keyword:
|
||||
like = f"%{keyword}%"
|
||||
q = q.filter(or_(CrmProduct.product_name.like(like), CrmProduct.product_no.like(like)))
|
||||
|
||||
@@ -31,10 +31,28 @@ from app.crud.crm.supplier_info import get_supplier_info
|
||||
from app.models.crm_models import CrmContract, CrmQuote, CrmQuoteApproval
|
||||
from app.models.models import SysUser
|
||||
from app.services.crm_quote_pdf import generate_quote_pdf
|
||||
from app.crud.crm.data_scope_helpers import apply_crm_owner_scope, ensure_crm_owner_row_in_scope
|
||||
|
||||
_FMT = "%Y-%m-%d %H:%M:%S"
|
||||
_DATE_FMT = "%Y-%m-%d"
|
||||
|
||||
def _ensure_quote_in_scope(db: Session, row: CrmQuote, user_info, tenant_no: str) -> None:
|
||||
ensure_crm_owner_row_in_scope(db, row, user_info, tenant_no, message="无权访问该报价单")
|
||||
|
||||
|
||||
def _get_quote_in_scope(
|
||||
db: Session,
|
||||
quote_id: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> CrmQuote:
|
||||
obj = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
if not obj:
|
||||
raise ValueError("报价单不存在")
|
||||
_ensure_quote_in_scope(db, obj, user_info, tenant_no)
|
||||
return obj
|
||||
|
||||
|
||||
def total_pages(total: int, page_size: int) -> int:
|
||||
if page_size <= 0:
|
||||
@@ -467,11 +485,13 @@ def get_quote_detail(
|
||||
quote_id: str,
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Java: queryById"""
|
||||
obj = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
if not obj:
|
||||
raise ValueError("报价单不存在")
|
||||
_ensure_quote_in_scope(db, obj, user_info, tenant_no or obj.tenant_no)
|
||||
|
||||
detail = _row_to_dict(obj)
|
||||
detail["productList"] = list_quote_products(db, quote_id, tenant_no=tenant_no or obj.tenant_no)
|
||||
@@ -513,10 +533,13 @@ def list_quotes(
|
||||
page_size: int = 10,
|
||||
filters: dict = None,
|
||||
current_user_id: str = None,
|
||||
user_info=None,
|
||||
) -> Tuple[List[Dict[str, Any]], int]:
|
||||
"""Java: queryPageList"""
|
||||
q = db.query(CrmQuote)
|
||||
q = _build_quote_filters(db, q, filters or {}, tenant_no=tenant_no)
|
||||
if user_info:
|
||||
q = apply_crm_owner_scope(q, CrmQuote, user_info, db, tenant_no)
|
||||
total = q.order_by(None).with_entities(func.count(CrmQuote.id)).scalar() or 0
|
||||
page = max(1, page)
|
||||
page_size = min(max(1, page_size), 100)
|
||||
@@ -645,9 +668,10 @@ def update_quote(
|
||||
data: dict,
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Java: updateByBo"""
|
||||
old = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
old = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not old:
|
||||
raise ValueError("报价单不存在")
|
||||
if old.status != "draft":
|
||||
@@ -769,11 +793,12 @@ def soft_delete_quotes(
|
||||
*,
|
||||
tenant_no: str,
|
||||
validate: bool = True,
|
||||
user_info=None,
|
||||
) -> int:
|
||||
"""Java: deleteWithValidByIds"""
|
||||
count = 0
|
||||
for qid in quote_ids:
|
||||
entity = get_quote(db, qid, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, qid, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
continue
|
||||
if validate:
|
||||
@@ -797,9 +822,10 @@ def submit_quote(
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: submitQuote"""
|
||||
entity = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("报价单不存在")
|
||||
if entity.status != "draft":
|
||||
@@ -837,9 +863,10 @@ def save_template(
|
||||
template_content: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: saveTemplate"""
|
||||
entity = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("报价单不存在")
|
||||
entity.template_content = template_content
|
||||
@@ -853,9 +880,10 @@ def get_template(
|
||||
quote_id: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> str:
|
||||
"""Java: getTemplate"""
|
||||
entity = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("报价单不存在")
|
||||
return entity.template_content or ""
|
||||
@@ -866,9 +894,10 @@ def generate_pdf(
|
||||
quote_id: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> str:
|
||||
"""Java: generatePdf"""
|
||||
entity = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("报价单不存在")
|
||||
detail = get_quote_detail(db, quote_id, tenant_no=tenant_no)
|
||||
@@ -887,9 +916,10 @@ def generate_share(
|
||||
tenant_no: str,
|
||||
validity_days: int = None,
|
||||
password: str = "",
|
||||
user_info=None,
|
||||
) -> str:
|
||||
"""Java: generateShare"""
|
||||
entity = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("报价单不存在")
|
||||
|
||||
@@ -966,9 +996,10 @@ def accept_quote(
|
||||
*,
|
||||
tenant_no: str,
|
||||
remark: str = "",
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: acceptQuote"""
|
||||
entity = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("报价单不存在")
|
||||
entity.status = "accepted"
|
||||
@@ -986,9 +1017,10 @@ def reject_quote(
|
||||
tenant_no: str,
|
||||
reject_reason: str = "",
|
||||
remark: str = "",
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: rejectQuote"""
|
||||
entity = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("报价单不存在")
|
||||
entity.status = "rejected"
|
||||
@@ -1012,6 +1044,7 @@ def to_contract(
|
||||
end_date: date = None,
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> str:
|
||||
"""Java: toContract — 返回 contractId"""
|
||||
sign_date = _parse_optional_date(sign_date)
|
||||
@@ -1075,9 +1108,10 @@ def create_new_version(
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> str:
|
||||
"""Java: createNewVersion — 返回新报价单 id"""
|
||||
old = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
old = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not old:
|
||||
raise ValueError("报价单不存在")
|
||||
|
||||
@@ -1150,6 +1184,7 @@ def query_versions(
|
||||
quote_no: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Java: queryVersions"""
|
||||
rows = (
|
||||
@@ -1172,9 +1207,10 @@ def withdraw_quote_approval(
|
||||
tenant_no: str,
|
||||
remark: str = "",
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人)"""
|
||||
entity = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("报价单不存在")
|
||||
if entity.status != "submitted":
|
||||
@@ -1197,9 +1233,10 @@ def invalid_quote(
|
||||
*,
|
||||
tenant_no: str,
|
||||
remark: str = "",
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: invalidQuote"""
|
||||
entity = get_quote(db, quote_id, tenant_no=tenant_no)
|
||||
entity = _get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
if not entity:
|
||||
raise ValueError("报价单不存在")
|
||||
if entity.status == "invalid":
|
||||
@@ -1221,6 +1258,7 @@ def statistics_overview(
|
||||
owner_user_id: str = None,
|
||||
begin_time: str = None,
|
||||
end_time: str = None,
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Java: statisticsOverview"""
|
||||
q = db.query(CrmQuote).filter(CrmQuote.tenant_no == tenant_no, CrmQuote.del_flag == 0)
|
||||
@@ -1232,6 +1270,8 @@ def statistics_overview(
|
||||
q = q.filter(CrmQuote.create_time >= begin_dt)
|
||||
if end_dt:
|
||||
q = q.filter(CrmQuote.create_time <= end_dt)
|
||||
if user_info:
|
||||
q = apply_crm_owner_scope(q, CrmQuote, user_info, db, tenant_no)
|
||||
|
||||
all_quotes = q.all()
|
||||
total_count = len(all_quotes)
|
||||
@@ -1273,28 +1313,21 @@ def statistics_overview(
|
||||
def statistics_status(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Java: statisticsStatus"""
|
||||
status_array = ("draft", "submitted", "accepted", "rejected", "expired", "contract")
|
||||
status_names = ("草稿", "已提交", "已接受", "已拒绝", "已过期", "已转合同")
|
||||
|
||||
total = (
|
||||
db.query(CrmQuote)
|
||||
.filter(CrmQuote.tenant_no == tenant_no, CrmQuote.del_flag == 0)
|
||||
.count()
|
||||
)
|
||||
base_q = db.query(CrmQuote).filter(CrmQuote.tenant_no == tenant_no, CrmQuote.del_flag == 0)
|
||||
if user_info:
|
||||
base_q = apply_crm_owner_scope(base_q, CrmQuote, user_info, db, tenant_no)
|
||||
total = base_q.count()
|
||||
|
||||
result = []
|
||||
for status, status_name in zip(status_array, status_names):
|
||||
rows = (
|
||||
db.query(CrmQuote)
|
||||
.filter(
|
||||
CrmQuote.tenant_no == tenant_no,
|
||||
CrmQuote.del_flag == 0,
|
||||
CrmQuote.status == status,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
q = base_q.filter(CrmQuote.status == status)
|
||||
rows = q.all()
|
||||
count = len(rows)
|
||||
amount = sum(_decimal_value(r.final_amount) for r in rows)
|
||||
percent = (count * 100.0 / total) if total else 0.0
|
||||
@@ -1326,6 +1359,7 @@ def statistics_trend(
|
||||
period_type: str = "month",
|
||||
begin_time: str = None,
|
||||
end_time: str = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Java: statisticsTrend"""
|
||||
q = db.query(CrmQuote).filter(CrmQuote.tenant_no == tenant_no, CrmQuote.del_flag == 0)
|
||||
@@ -1335,6 +1369,8 @@ def statistics_trend(
|
||||
q = q.filter(CrmQuote.create_time >= begin_dt)
|
||||
if end_dt:
|
||||
q = q.filter(CrmQuote.create_time <= end_dt)
|
||||
if user_info:
|
||||
q = apply_crm_owner_scope(q, CrmQuote, user_info, db, tenant_no)
|
||||
|
||||
all_quotes = q.all()
|
||||
if not all_quotes:
|
||||
|
||||
@@ -44,8 +44,13 @@ def list_quote_approvals(
|
||||
quote_id: str,
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Java: queryListByQuoteId(含审批人昵称)"""
|
||||
if tenant_no and user_info:
|
||||
from app.crud.crm.quote import _get_quote_in_scope
|
||||
|
||||
_get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
q = db.query(CrmQuoteApproval).filter(
|
||||
CrmQuoteApproval.quote_id == quote_id,
|
||||
CrmQuoteApproval.del_flag == 0,
|
||||
|
||||
@@ -13,6 +13,13 @@ from app.models.crm_models import CrmQuoteProduct
|
||||
|
||||
_FMT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
def _ensure_quote_parent_scope(db: Session, quote_id: str, tenant_no: str, user_info) -> None:
|
||||
if user_info:
|
||||
from app.crud.crm.quote import _get_quote_in_scope
|
||||
|
||||
_get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
|
||||
|
||||
|
||||
def _decimal_value(value: Any, default: Decimal = Decimal("0")) -> Decimal:
|
||||
if value is None:
|
||||
@@ -76,8 +83,11 @@ def list_quote_products(
|
||||
quote_id: str,
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Java: queryListByQuoteId"""
|
||||
if tenant_no and user_info:
|
||||
_ensure_quote_parent_scope(db, quote_id, tenant_no, user_info)
|
||||
q = db.query(CrmQuoteProduct).filter(
|
||||
CrmQuoteProduct.quote_id == quote_id,
|
||||
CrmQuoteProduct.del_flag == 0,
|
||||
@@ -104,8 +114,10 @@ def insert_quote_product(
|
||||
data: dict,
|
||||
user_id: str = None,
|
||||
dept_id: str = "",
|
||||
user_info=None,
|
||||
) -> CrmQuoteProduct:
|
||||
"""Java: insertByBo"""
|
||||
_ensure_quote_parent_scope(db, quote_id, tenant_no, user_info)
|
||||
price = _decimal_value(_get_field(data, "price"), Decimal("0"))
|
||||
quantity = _decimal_value(_get_field(data, "quantity"), Decimal("0"))
|
||||
discount = _decimal_value(_get_field(data, "discount"), Decimal("0"))
|
||||
@@ -169,11 +181,14 @@ def update_quote_product(
|
||||
*,
|
||||
data: dict,
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> CrmQuoteProduct:
|
||||
"""Java: updateByBo"""
|
||||
entity = get_quote_product(db, product_line_id)
|
||||
if not entity:
|
||||
raise ValueError("报价产品明细不存在")
|
||||
if user_info:
|
||||
_ensure_quote_parent_scope(db, entity.quote_id, entity.tenant_no, user_info)
|
||||
|
||||
if "product_id" in data or "productId" in data:
|
||||
entity.product_id = (_get_field(data, "product_id", "productId") or entity.product_id or "").strip()
|
||||
@@ -217,6 +232,7 @@ def soft_delete_quote_products(
|
||||
ids: List[str],
|
||||
*,
|
||||
validate_min_one: bool = True,
|
||||
user_info=None,
|
||||
) -> int:
|
||||
"""Java: deleteWithValidByIds"""
|
||||
count = 0
|
||||
@@ -224,6 +240,8 @@ def soft_delete_quote_products(
|
||||
entity = get_quote_product(db, pid)
|
||||
if not entity:
|
||||
continue
|
||||
if user_info:
|
||||
_ensure_quote_parent_scope(db, entity.quote_id, entity.tenant_no, user_info)
|
||||
if validate_min_one:
|
||||
sibling_count = (
|
||||
db.query(CrmQuoteProduct)
|
||||
|
||||
@@ -7,7 +7,6 @@ from typing import Any, Dict, List, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
from app.models.crm_models import CrmQuoteShareLog
|
||||
|
||||
_FMT = "%Y-%m-%d %H:%M:%S"
|
||||
@@ -78,8 +77,13 @@ def list_share_logs(
|
||||
quote_id: str,
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Java: queryLogsByQuoteId"""
|
||||
if tenant_no and user_info:
|
||||
from app.crud.crm.quote import _get_quote_in_scope
|
||||
|
||||
_get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
q = db.query(CrmQuoteShareLog).filter(
|
||||
CrmQuoteShareLog.quote_id == quote_id,
|
||||
CrmQuoteShareLog.del_flag == 0,
|
||||
@@ -95,8 +99,13 @@ def share_log_statistics(
|
||||
quote_id: str,
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Java: statisticsVisits"""
|
||||
if tenant_no and user_info:
|
||||
from app.crud.crm.quote import _get_quote_in_scope
|
||||
|
||||
_get_quote_in_scope(db, quote_id, tenant_no=tenant_no, user_info=user_info)
|
||||
q = db.query(CrmQuoteShareLog).filter(
|
||||
CrmQuoteShareLog.quote_id == quote_id,
|
||||
CrmQuoteShareLog.del_flag == 0,
|
||||
|
||||
@@ -8,6 +8,10 @@ from typing import Any, Dict, List, Optional, Tuple
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
from app.crud.crm.data_scope_helpers import (
|
||||
apply_crm_create_by_scope,
|
||||
ensure_crm_create_by_row_in_scope,
|
||||
)
|
||||
from app.models.crm_models import CrmSupplierInfo
|
||||
|
||||
_FMT = "%Y-%m-%d %H:%M:%S"
|
||||
@@ -74,12 +78,15 @@ def list_supplier_info(
|
||||
page_size: int = 10,
|
||||
supplier_name: str = None,
|
||||
status: str = None,
|
||||
user_info=None,
|
||||
) -> Tuple[List[Dict[str, Any]], int]:
|
||||
"""Java: queryPageList"""
|
||||
q = db.query(CrmSupplierInfo).filter(
|
||||
CrmSupplierInfo.tenant_no == tenant_no,
|
||||
CrmSupplierInfo.del_flag == 0,
|
||||
)
|
||||
if user_info:
|
||||
q = apply_crm_create_by_scope(q, CrmSupplierInfo, user_info, db, tenant_no)
|
||||
q = _apply_list_filters(q, supplier_name=supplier_name, status=status)
|
||||
total = q.count()
|
||||
rows = (
|
||||
@@ -97,12 +104,15 @@ def list_all_supplier_info(
|
||||
*,
|
||||
supplier_name: str = None,
|
||||
status: str = None,
|
||||
user_info=None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Java: queryList(不分页,报价单下拉)"""
|
||||
q = db.query(CrmSupplierInfo).filter(
|
||||
CrmSupplierInfo.tenant_no == tenant_no,
|
||||
CrmSupplierInfo.del_flag == 0,
|
||||
)
|
||||
if user_info:
|
||||
q = apply_crm_create_by_scope(q, CrmSupplierInfo, user_info, db, tenant_no)
|
||||
q = _apply_list_filters(q, supplier_name=supplier_name, status=status)
|
||||
rows = q.order_by(CrmSupplierInfo.create_time.desc()).all()
|
||||
return [_row_to_dict(r) for r in rows]
|
||||
@@ -168,12 +178,16 @@ def update_supplier_info(
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
**kwargs,
|
||||
) -> CrmSupplierInfo:
|
||||
"""Java: updateByBo"""
|
||||
obj = get_supplier_info(db, supplier_id, tenant_no=tenant_no)
|
||||
if not obj:
|
||||
raise ValueError("供方信息不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, obj, user_info, obj.tenant_no, message="无权访问该供方信息"
|
||||
)
|
||||
|
||||
if "supplier_name" in kwargs and kwargs["supplier_name"] is not None:
|
||||
name = (kwargs["supplier_name"] or "").strip()
|
||||
@@ -200,11 +214,15 @@ def soft_delete_supplier_info(
|
||||
*,
|
||||
tenant_no: str = None,
|
||||
user_id: str = None,
|
||||
user_info=None,
|
||||
) -> bool:
|
||||
"""Java: deleteWithValidByIds(门户软删)"""
|
||||
obj = get_supplier_info(db, supplier_id, tenant_no=tenant_no)
|
||||
if not obj:
|
||||
raise ValueError("供方信息不存在")
|
||||
ensure_crm_create_by_row_in_scope(
|
||||
db, obj, user_info, obj.tenant_no, message="无权访问该供方信息"
|
||||
)
|
||||
obj.del_flag = 1
|
||||
obj.update_by = user_id
|
||||
obj.update_time = datetime.now()
|
||||
|
||||
Reference in new issue
Block a user