fix: replace uuid.uuid4() with getID() from appPublic.uniqueID for all table ID generation
This commit is contained in:
parent
2e8630c5ab
commit
797ac1d935
@ -1,38 +1,79 @@
|
||||
{
|
||||
"tblname": "opportunities",
|
||||
"alias": "opportunities_list",
|
||||
"title": "商机列表",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "customer_id", "owner_id", "org_id"],
|
||||
"alters": {
|
||||
"sales_stage": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{"value": "初步接洽", "text": "初步接洽"},
|
||||
{"value": "需求确认", "text": "需求确认"},
|
||||
{"value": "方案报价", "text": "方案报价"},
|
||||
{"value": "合同谈判", "text": "合同谈判"},
|
||||
{"value": "成交", "text": "成交"}
|
||||
]
|
||||
"tblname": "opportunities",
|
||||
"alias": "opportunities_list",
|
||||
"title": "商机列表",
|
||||
"params": {
|
||||
"sortby": [
|
||||
"created_at desc"
|
||||
],
|
||||
"browserfields": {
|
||||
"exclouded": [
|
||||
"id",
|
||||
"customer_id",
|
||||
"owner_id"
|
||||
],
|
||||
"alters": {
|
||||
"current_stage": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "初步接洽",
|
||||
"text": "初步接洽"
|
||||
},
|
||||
{
|
||||
"value": "需求确认",
|
||||
"text": "需求确认"
|
||||
},
|
||||
{
|
||||
"value": "方案报价",
|
||||
"text": "方案报价"
|
||||
},
|
||||
{
|
||||
"value": "合同谈判",
|
||||
"text": "合同谈判"
|
||||
},
|
||||
{
|
||||
"value": "成交",
|
||||
"text": "成交"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "active",
|
||||
"text": "活跃"
|
||||
},
|
||||
{
|
||||
"value": "won",
|
||||
"text": "已成交"
|
||||
},
|
||||
{
|
||||
"value": "lost",
|
||||
"text": "已丢失"
|
||||
}
|
||||
]
|
||||
},
|
||||
"source_type": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "manual",
|
||||
"text": "手动录入"
|
||||
},
|
||||
{
|
||||
"value": "lead_conversion",
|
||||
"text": "线索转化"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{"value": "active", "text": "活跃"},
|
||||
{"value": "won", "text": "已成交"},
|
||||
{"value": "lost", "text": "已丢失"}
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{"value": "manual", "text": "手动录入"},
|
||||
{"value": "lead_conversion", "text": "线索转化"}
|
||||
]
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/opportunities_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/opportunities_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/opportunities_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,9 +3,19 @@
|
||||
"alias": "predictions_list",
|
||||
"title": "商机预测记录",
|
||||
"params": {
|
||||
"sortby": ["prediction_date desc"],
|
||||
"sortby": [
|
||||
"prediction_date desc"
|
||||
],
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "opportunity_id"]
|
||||
"exclouded": [
|
||||
"id",
|
||||
"opportunity_id"
|
||||
]
|
||||
},
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/opportunity_predictions_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/opportunity_predictions_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/opportunity_predictions_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,24 +3,47 @@
|
||||
"alias": "sales_stages_list",
|
||||
"title": "销售阶段管理",
|
||||
"params": {
|
||||
"sortby": ["stage_order"],
|
||||
"sortby": [
|
||||
"stage_order"
|
||||
],
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "updated_at"],
|
||||
"exclouded": [
|
||||
"id",
|
||||
"updated_at"
|
||||
],
|
||||
"alters": {
|
||||
"is_active": {
|
||||
"is_won_stage": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "1",
|
||||
"text": "启用"
|
||||
"value": "yes",
|
||||
"text": "是"
|
||||
},
|
||||
{
|
||||
"value": "0",
|
||||
"text": "禁用"
|
||||
"value": "no",
|
||||
"text": "否"
|
||||
}
|
||||
]
|
||||
},
|
||||
"is_lost_stage": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "yes",
|
||||
"text": "是"
|
||||
},
|
||||
{
|
||||
"value": "no",
|
||||
"text": "否"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sales_stages_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sales_stages_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sales_stages_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,9 +3,21 @@
|
||||
"alias": "stage_history_list",
|
||||
"title": "阶段变更历史",
|
||||
"params": {
|
||||
"sortby": ["changed_at desc"],
|
||||
"sortby": [
|
||||
"changed_at desc"
|
||||
],
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "opportunity_id", "changed_by"]
|
||||
"exclouded": [
|
||||
"id",
|
||||
"opportunity_id",
|
||||
"changed_by_id",
|
||||
"changed_by_name"
|
||||
]
|
||||
},
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/opportunity_stage_history_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/opportunity_stage_history_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/opportunity_stage_history_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ from datetime import datetime, date
|
||||
from decimal import Decimal
|
||||
from typing import List, Dict, Optional
|
||||
import uuid
|
||||
from appPublic.uniqueID import getID
|
||||
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from appPublic.worker import awaitify
|
||||
@ -30,7 +31,7 @@ async def create_opportunity(
|
||||
db.databases = config.databases
|
||||
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
opportunity_id = str(uuid.uuid4()).replace('-', '')
|
||||
opportunity_id = getID()
|
||||
|
||||
# 验证客户是否存在
|
||||
customer_records = await sor.R("customers", {"filters": [{"field": "customer_name", "op": "=", "value": customer_name}]})
|
||||
@ -100,7 +101,7 @@ async def update_opportunity_stage(
|
||||
raise ValueError("只能更新活跃状态的商机")
|
||||
|
||||
# 记录阶段变更历史
|
||||
history_id = str(uuid.uuid4()).replace('-', '')
|
||||
history_id = getID()
|
||||
history_data = {
|
||||
"id": history_id,
|
||||
"opportunity_id": opportunity_id,
|
||||
@ -160,7 +161,7 @@ async def assign_opportunity(
|
||||
old_owner_id = opportunity["owner_id"]
|
||||
|
||||
# 记录分配历史
|
||||
assignment_id = str(uuid.uuid4()).replace('-', '')
|
||||
assignment_id = getID()
|
||||
assignment_data = {
|
||||
"id": assignment_id,
|
||||
"opportunity_id": opportunity_id,
|
||||
|
||||
@ -6,6 +6,7 @@ from datetime import datetime, date
|
||||
from decimal import Decimal
|
||||
from typing import List, Dict, Optional
|
||||
import uuid
|
||||
from appPublic.uniqueID import getID
|
||||
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from appPublic.worker import awaitify
|
||||
@ -34,7 +35,7 @@ async def create_opportunity(
|
||||
db.databases = config.databases
|
||||
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
opportunity_id = str(uuid.uuid4()).replace('-', '')
|
||||
opportunity_id = getID()
|
||||
|
||||
# 验证客户是否存在
|
||||
customer_records = await sor.R("customers", {"filters": [{"field": "customer_name", "op": "=", "value": customer_name}]})
|
||||
@ -104,7 +105,7 @@ async def update_opportunity_stage(
|
||||
raise ValueError("只能更新活跃状态的商机")
|
||||
|
||||
# 记录阶段变更历史
|
||||
history_id = str(uuid.uuid4()).replace('-', '')
|
||||
history_id = getID()
|
||||
history_data = {
|
||||
"id": history_id,
|
||||
"opportunity_id": opportunity_id,
|
||||
@ -164,7 +165,7 @@ async def assign_opportunity(
|
||||
old_owner_id = opportunity["owner_id"]
|
||||
|
||||
# 记录分配历史
|
||||
assignment_id = str(uuid.uuid4()).replace('-', '')
|
||||
assignment_id = getID()
|
||||
assignment_data = {
|
||||
"id": assignment_id,
|
||||
"opportunity_id": opportunity_id,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Opportunity create API"""
|
||||
import json, uuid, time
|
||||
import json, time
|
||||
from appPublic.uniqueID import getID
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}}
|
||||
|
||||
@ -19,7 +20,7 @@ try:
|
||||
result['options'] = {'title': 'Error', 'message': '请填写必填字段', 'type': 'error'}
|
||||
else:
|
||||
dbname = get_module_dbname('opportunity_management')
|
||||
new_id = str(uuid.uuid4()).replace('-', '')
|
||||
new_id = getID()
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
|
||||
39
wwwroot/api/opportunity_predictions_create.dspy
Normal file
39
wwwroot/api/opportunity_predictions_create.dspy
Normal file
@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Opportunity predictions create API"""
|
||||
import json, time
|
||||
from appPublic.uniqueID import getID
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
opportunity_id = params_kw.get('opportunity_id', '').strip()
|
||||
predicted_amount = params_kw.get('predicted_amount', '0').strip()
|
||||
confidence_level = params_kw.get('confidence_level', '0').strip()
|
||||
prediction_date = params_kw.get('prediction_date', '').strip()
|
||||
|
||||
if not opportunity_id:
|
||||
result['options'] = {'title': 'Error', 'message': '请填写必填字段', 'type': 'error'}
|
||||
else:
|
||||
dbname = get_module_dbname('opportunity_management')
|
||||
new_id = getID()
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
if not prediction_date:
|
||||
prediction_date = time.strftime('%Y-%m-%d')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.sqlExe("""INSERT INTO opportunity_predictions (id, opportunity_id, predicted_amount, confidence_level, prediction_date, created_at)
|
||||
VALUES (${id}$, ${opportunity_id}$, ${predicted_amount}$, ${confidence_level}$, ${prediction_date}$, ${created_at}$)""", {
|
||||
'id': new_id,
|
||||
'opportunity_id': opportunity_id,
|
||||
'predicted_amount': float(predicted_amount) if predicted_amount else 0.0,
|
||||
'confidence_level': float(confidence_level) if confidence_level else 0.0,
|
||||
'prediction_date': prediction_date,
|
||||
'created_at': now
|
||||
})
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '预测记录创建成功', 'type': 'success'}}
|
||||
|
||||
except Exception as e:
|
||||
result['options'] = {'title': 'Error', 'message': f'创建失败: {str(e)}', 'type': 'error'}
|
||||
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
23
wwwroot/api/opportunity_predictions_delete.dspy
Normal file
23
wwwroot/api/opportunity_predictions_delete.dspy
Normal file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Opportunity predictions delete API"""
|
||||
import json
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
row_id = params_kw.get('id', '').strip()
|
||||
|
||||
if not row_id:
|
||||
result['options'] = {'title': 'Error', 'message': '缺少记录ID', 'type': 'error'}
|
||||
else:
|
||||
dbname = get_module_dbname('opportunity_management')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.sqlExe("DELETE FROM opportunity_predictions WHERE id=${id}$", {'id': row_id})
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '预测记录删除成功', 'type': 'success'}}
|
||||
|
||||
except Exception as e:
|
||||
result['options'] = {'title': 'Error', 'message': f'删除失败: {str(e)}', 'type': 'error'}
|
||||
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
37
wwwroot/api/opportunity_predictions_update.dspy
Normal file
37
wwwroot/api/opportunity_predictions_update.dspy
Normal file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Opportunity predictions update API"""
|
||||
import json, time
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
row_id = params_kw.get('id', '').strip()
|
||||
predicted_amount = params_kw.get('predicted_amount', '0').strip()
|
||||
confidence_level = params_kw.get('confidence_level', '0').strip()
|
||||
actual_amount = params_kw.get('actual_amount', '').strip()
|
||||
deviation_rate = params_kw.get('deviation_rate', '').strip()
|
||||
|
||||
if not row_id:
|
||||
result['options'] = {'title': 'Error', 'message': '缺少记录ID', 'type': 'error'}
|
||||
else:
|
||||
dbname = get_module_dbname('opportunity_management')
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.sqlExe("""UPDATE opportunity_predictions SET
|
||||
predicted_amount=${predicted_amount}$, confidence_level=${confidence_level}$,
|
||||
actual_amount=${actual_amount}$, deviation_rate=${deviation_rate}$
|
||||
WHERE id=${id}$""", {
|
||||
'id': row_id,
|
||||
'predicted_amount': float(predicted_amount) if predicted_amount else 0.0,
|
||||
'confidence_level': float(confidence_level) if confidence_level else 0.0,
|
||||
'actual_amount': float(actual_amount) if actual_amount else None,
|
||||
'deviation_rate': float(deviation_rate) if deviation_rate else None,
|
||||
})
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '预测记录更新成功', 'type': 'success'}}
|
||||
|
||||
except Exception as e:
|
||||
result['options'] = {'title': 'Error', 'message': f'更新失败: {str(e)}', 'type': 'error'}
|
||||
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Opportunity stage history create API"""
|
||||
import json, uuid, time
|
||||
import json, time
|
||||
from appPublic.uniqueID import getID
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}}
|
||||
|
||||
@ -16,7 +17,7 @@ try:
|
||||
result['options'] = {'title': 'Error', 'message': '请填写必填字段', 'type': 'error'}
|
||||
else:
|
||||
dbname = get_module_dbname('opportunity_management')
|
||||
new_id = str(uuid.uuid4()).replace('-', '')
|
||||
new_id = getID()
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Sales stages create API"""
|
||||
import json, uuid, time
|
||||
import json, time
|
||||
from appPublic.uniqueID import getID
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}}
|
||||
|
||||
@ -16,7 +17,7 @@ try:
|
||||
result['options'] = {'title': 'Error', 'message': '请填写必填字段', 'type': 'error'}
|
||||
else:
|
||||
dbname = get_module_dbname('opportunity_management')
|
||||
new_id = str(uuid.uuid4()).replace('-', '')
|
||||
new_id = getID()
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user