feat: 补充供销协议和协议产品明细折扣CRUD管理

- 新增 json/supply_contracts_list.json CRUD定义(供销协议管理)
- 新增 json/supply_contract_items_list.json CRUD定义(协议产品明细折扣)
- 修复 json/suppliers_list.json JSON语法错误(缺少逗号)
- 修复6个API dspy文件违规(移除import/print,改用return)
- 新增4个搜索API(供应商/合同/产品分类/产品下拉数据)
- 新增 supplychain/__init__.py 导出所有公共函数
- 重新生成CRUD UI文件并修正editable URL指向自定义API
- 更新load_path.py注册新路径
- 添加.gitignore排除自动生成文件
This commit is contained in:
Hermes Agent 2026-06-17 15:03:10 +08:00
parent d26bb4cd0c
commit bfc50f2364
19 changed files with 290 additions and 88 deletions

20
.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
# Python
__pycache__/
*.pyc
*.egg-info/
build/
dist/
# Auto-generated CRUD directories (regenerated by build.sh)
wwwroot/suppliers_list/
wwwroot/supply_contracts_list/
wwwroot/supply_contract_items_list/
wwwroot/sub_resellers_list/
wwwroot/sub_distributors_list/
wwwroot/distribution_agreements_list/
wwwroot/distribution_agreement_items_list/
wwwroot/sales_ledger_list/
wwwroot/supplychain_accounting_list/
wwwroot/platform_supply_relations_list/
wwwroot/platform_supply_products_list/
wwwroot/product_supplier_mapping_list/

View File

@ -15,8 +15,9 @@
"supplier_name": "供应商名称",
"status": "状态"
},
"editexclouded": ["id", "resellerid", "created_by", "created_at", "updated_at"],
"browserfields": {
"exclouded": ["id"],
"exclouded": ["id", "resellerid" ],
"alters": {
"status": {
"uitype": "code",

View File

@ -1,13 +1,45 @@
{
"tblname": "supply_contract_items",
"alias": "supply_contract_items_list",
"title": "供销合同产品折扣",
"title": "协议产品明细折扣管理",
"params": {
"sortby": ["prodtypeid", "productid"],
"sortby": [
"prodtypeid",
"productid"
],
"logined_userorgid": "resellerid",
"browserfields": {
"exclouded": ["id", "contract_id", "resellerid"]
"exclouded": [
"id",
"contract_id",
"resellerid"
],
"alters": {
"contract_id": {
"uitype": "code",
"dataurl": "{{entire_url('../api/get_search_contract_id.dspy')}}",
"valueField": "contract_id",
"textField": "contract_id_text"
},
"prodtypeid": {
"uitype": "code",
"dataurl": "{{entire_url('../api/get_search_prodtypeid.dspy')}}",
"valueField": "prodtypeid",
"textField": "prodtypeid_text"
},
"productid": {
"uitype": "code",
"dataurl": "{{entire_url('../api/get_search_productid.dspy')}}",
"valueField": "productid",
"textField": "productid_text"
}
}
},
"editexclouded": [
"id",
"resellerid",
"created_at"
],
"editable": {
"new_data_url": "{{entire_url('../api/supply_contract_items_create.dspy')}}",
"update_data_url": "{{entire_url('../api/supply_contract_items_update.dspy')}}",

View File

@ -1,35 +1,86 @@
{
"tblname": "supply_contracts",
"alias": "supply_contracts_list",
"title": "供销合同管理",
"title": "供销协议管理",
"params": {
"sortby": ["created_at desc"],
"sortby": [
"created_at desc"
],
"logined_userorgid": "resellerid",
"data_filter": {
"AND": [
{"field": "contract_name", "op": "LIKE", "var": "contract_name"},
{"field": "contract_code", "op": "LIKE", "var": "contract_code"},
{"field": "status", "op": "=", "var": "status"}
{
"field": "contract_name",
"op": "LIKE",
"var": "contract_name"
},
{
"field": "contract_code",
"op": "LIKE",
"var": "contract_code"
},
{
"field": "supplier_id",
"op": "=",
"var": "supplier_id"
},
{
"field": "status",
"op": "=",
"var": "status"
}
]
},
"filter_labels": {
"contract_name": "合同名称",
"contract_code": "合同编号",
"supplier_id": "供应商",
"status": "状态"
},
"browserfields": {
"exclouded": ["id"],
"exclouded": [
"id",
"resellerid"
],
"alters": {
"supplier_id": {
"uitype": "code",
"dataurl": "{{entire_url('../api/get_search_supplier_id.dspy')}}",
"valueField": "supplier_id",
"textField": "supplier_id_text"
},
"status": {
"uitype": "code",
"data": [
{"value": "1", "text": "生效中"},
{"value": "2", "text": "已到期"},
{"value": "0", "text": "已终止"}
{
"value": "1",
"text": "生效中"
},
{
"value": "2",
"text": "已到期"
},
{
"value": "0",
"text": "已终止"
}
]
}
}
},
"editexclouded": [
"id",
"resellerid",
"contract_code",
"created_by",
"created_at",
"updated_at"
],
"editable": {
"new_data_url": "{{entire_url('../api/supply_contracts_create.dspy')}}",
"update_data_url": "{{entire_url('../api/supply_contracts_update.dspy')}}",
"delete_data_url": "{{entire_url('../api/supply_contracts_delete.dspy')}}"
},
"subtables": [
{
"field": "contract_id",
@ -37,11 +88,6 @@
"url": "{{entire_url('../supply_contract_items_list')}}",
"subtable": "supply_contract_items"
}
],
"editable": {
"new_data_url": "{{entire_url('../api/supply_contracts_create.dspy')}}",
"update_data_url": "{{entire_url('../api/supply_contracts_update.dspy')}}",
"delete_data_url": "{{entire_url('../api/supply_contracts_delete.dspy')}}"
}
]
}
}

View File

@ -94,6 +94,11 @@ PATHS_LOGINED = [
"/supplychain/api/supply_contract_items_create.dspy",
"/supplychain/api/supply_contract_items_update.dspy",
"/supplychain/api/supply_contract_items_delete.dspy",
# Search API — filter dropdowns
"/supplychain/api/get_search_supplier_id.dspy",
"/supplychain/api/get_search_contract_id.dspy",
"/supplychain/api/get_search_prodtypeid.dspy",
"/supplychain/api/get_search_productid.dspy",
# CRUD API — sub_distributors
"/supplychain/api/sub_distributors_create.dspy",
"/supplychain/api/sub_distributors_update.dspy",

34
supplychain/__init__.py Normal file
View File

@ -0,0 +1,34 @@
from .init import (
create_supplier,
update_supplier,
delete_supplier,
create_supply_contract,
update_supply_contract,
delete_supply_contract,
create_supply_contract_item,
update_supply_contract_item,
delete_supply_contract_item,
create_sub_reseller,
update_sub_reseller,
delete_sub_reseller,
create_distribution_agreement,
update_distribution_agreement,
delete_distribution_agreement,
create_distribution_agreement_item,
update_distribution_agreement_item,
delete_distribution_agreement_item,
create_sales_ledger,
update_sales_ledger,
delete_sales_ledger,
calculate_sale_amounts,
create_platform_supply_relations,
update_platform_supply_relations,
delete_platform_supply_relations,
create_platform_supply_products,
update_platform_supply_products,
delete_platform_supply_products,
create_product_supplier_mapping,
update_product_supplier_mapping,
delete_product_supplier_mapping,
load_supplychain,
)

View File

@ -0,0 +1,16 @@
result = [{'contract_id': '', 'contract_id_text': '全部'}]
try:
userorgid = await get_userorgid()
if not userorgid:
return json.dumps(result, ensure_ascii=False)
db = DBPools()
dbname = get_module_dbname('supplychain')
async with db.sqlorContext(dbname) as sor:
rows = await sor.sqlExe(
"select id as contract_id, contract_name as contract_id_text from supply_contracts where resellerid = ${userorgid}$ and status = '1' order by contract_name",
{"userorgid": userorgid}
)
return json.dumps(result + list(rows), ensure_ascii=False)
except Exception as e:
debug(f'get_search_contract_id error: {e}')
return json.dumps(result, ensure_ascii=False)

View File

@ -0,0 +1,13 @@
result = [{'prodtypeid': '', 'prodtypeid_text': '全部'}]
try:
db = DBPools()
dbname = get_module_dbname('supplychain')
async with db.sqlorContext(dbname) as sor:
rows = await sor.sqlExe(
"select id as prodtypeid, type_name as prodtypeid_text from product_types order by type_name",
{}
)
return json.dumps(result + list(rows), ensure_ascii=False)
except Exception as e:
debug(f'get_search_prodtypeid error: {e}')
return json.dumps(result, ensure_ascii=False)

View File

@ -0,0 +1,13 @@
result = [{'productid': '', 'productid_text': '全部'}]
try:
db = DBPools()
dbname = get_module_dbname('supplychain')
async with db.sqlorContext(dbname) as sor:
rows = await sor.sqlExe(
"select id as productid, product_name as productid_text from products order by product_name",
{}
)
return json.dumps(result + list(rows), ensure_ascii=False)
except Exception as e:
debug(f'get_search_productid error: {e}')
return json.dumps(result, ensure_ascii=False)

View File

@ -0,0 +1,16 @@
result = [{'supplier_id': '', 'supplier_id_text': '全部'}]
try:
userorgid = await get_userorgid()
if not userorgid:
return json.dumps(result, ensure_ascii=False)
db = DBPools()
dbname = get_module_dbname('supplychain')
async with db.sqlorContext(dbname) as sor:
rows = await sor.sqlExe(
"select id as supplier_id, supplier_name as supplier_id_text from suppliers where resellerid = ${userorgid}$ and status = '1' order by supplier_name",
{"userorgid": userorgid}
)
return json.dumps(result + list(rows), ensure_ascii=False)
except Exception as e:
debug(f'get_search_supplier_id error: {e}')
return json.dumps(result, ensure_ascii=False)

View File

@ -1,9 +1,2 @@
import json
from ahserver.serverenv import ServerEnv
env = ServerEnv()
create_func = getattr(env, 'create_supply_contract_items', None)
if create_func is None:
print(json.dumps({"status": "error", "message": "create_supply_contract_items function not found"}))
else:
result = await create_func(request, params_kw)
print(result)
result = await create_supply_contract_items(request, params_kw)
return json.loads(result) if isinstance(result, str) else result

View File

@ -1,9 +1,2 @@
import json
from ahserver.serverenv import ServerEnv
env = ServerEnv()
delete_func = getattr(env, 'delete_supply_contract_items', None)
if delete_func is None:
print(json.dumps({"status": "error", "message": "delete_supply_contract_items function not found"}))
else:
result = await delete_func(request, params_kw)
print(result)
result = await delete_supply_contract_items(request, params_kw)
return json.loads(result) if isinstance(result, str) else result

View File

@ -1,9 +1,2 @@
import json
from ahserver.serverenv import ServerEnv
env = ServerEnv()
update_func = getattr(env, 'update_supply_contract_items', None)
if update_func is None:
print(json.dumps({"status": "error", "message": "update_supply_contract_items function not found"}))
else:
result = await update_func(request, params_kw)
print(result)
result = await update_supply_contract_items(request, params_kw)
return json.loads(result) if isinstance(result, str) else result

View File

@ -1,9 +1,2 @@
import json
from ahserver.serverenv import ServerEnv
env = ServerEnv()
create_func = getattr(env, 'create_supply_contracts', None)
if create_func is None:
print(json.dumps({"status": "error", "message": "create_supply_contracts function not found"}))
else:
result = await create_func(request, params_kw)
print(result)
result = await create_supply_contracts(request, params_kw)
return json.loads(result) if isinstance(result, str) else result

View File

@ -1,9 +1,2 @@
import json
from ahserver.serverenv import ServerEnv
env = ServerEnv()
delete_func = getattr(env, 'delete_supply_contracts', None)
if delete_func is None:
print(json.dumps({"status": "error", "message": "delete_supply_contracts function not found"}))
else:
result = await delete_func(request, params_kw)
print(result)
result = await delete_supply_contracts(request, params_kw)
return json.loads(result) if isinstance(result, str) else result

View File

@ -1,9 +1,2 @@
import json
from ahserver.serverenv import ServerEnv
env = ServerEnv()
update_func = getattr(env, 'update_supply_contracts', None)
if update_func is None:
print(json.dumps({"status": "error", "message": "update_supply_contracts function not found"}))
else:
result = await update_func(request, params_kw)
print(result)
result = await update_supply_contracts(request, params_kw)
return json.loads(result) if isinstance(result, str) else result

View File

@ -23,7 +23,7 @@ if not ns.get('page'):
if not ns.get('sort'):
ns['sort'] = ["prodtypeid","productid"]
ns['sort'] = ["prodtypeid", "productid"]

View File

@ -20,13 +20,13 @@
"editable":{
"new_data_url":"{{entire_url('add_supply_contract_items.dspy')}}",
"new_data_url":"{{entire_url('../api/supply_contract_items_create.dspy')}}",
"delete_data_url":"{{entire_url('delete_supply_contract_items.dspy')}}",
"delete_data_url":"{{entire_url('../api/supply_contract_items_delete.dspy')}}",
"update_data_url":"{{entire_url('update_supply_contract_items.dspy')}}"
"update_data_url":"{{entire_url('../api/supply_contract_items_update.dspy')}}"
},
@ -44,10 +44,36 @@
"id",
"contract_id",
"resellerid"
]
],
"alters": {
"contract_id": {
"uitype": "code",
"dataurl": "{{entire_url('../api/get_search_contract_id.dspy')}}",
"valueField": "contract_id",
"textField": "contract_id_text"
},
"prodtypeid": {
"uitype": "code",
"dataurl": "{{entire_url('../api/get_search_prodtypeid.dspy')}}",
"valueField": "prodtypeid",
"textField": "prodtypeid_text"
},
"productid": {
"uitype": "code",
"dataurl": "{{entire_url('../api/get_search_productid.dspy')}}",
"valueField": "productid",
"textField": "productid_text"
}
}
},
"editexclouded":[
"id",
"resellerid",
"created_at"
],
"fields":[
{
"name": "id",
@ -78,7 +104,7 @@
"valueField": "contract_id",
"textField": "contract_id_text"
},
"dataurl": "{{entire_url('/appbase/get_code.dspy')}}"
"dataurl": "{{entire_url('../api/get_search_contract_id.dspy')}}"
},
{
"name": "resellerid",
@ -108,7 +134,7 @@
"valueField": "prodtypeid",
"textField": "prodtypeid_text"
},
"dataurl": "{{entire_url('/appbase/get_code.dspy')}}"
"dataurl": "{{entire_url('../api/get_search_prodtypeid.dspy')}}"
},
{
"name": "productid",
@ -127,7 +153,7 @@
"valueField": "productid",
"textField": "productid_text"
},
"dataurl": "{{entire_url('/appbase/get_code.dspy')}}"
"dataurl": "{{entire_url('../api/get_search_productid.dspy')}}"
},
{
"name": "discount",

View File

@ -31,13 +31,13 @@
"editable":{
"new_data_url":"{{entire_url('add_supply_contracts.dspy')}}",
"new_data_url":"{{entire_url('../api/supply_contracts_create.dspy')}}",
"delete_data_url":"{{entire_url('delete_supply_contracts.dspy')}}",
"delete_data_url":"{{entire_url('../api/supply_contracts_delete.dspy')}}",
"update_data_url":"{{entire_url('update_supply_contracts.dspy')}}"
"update_data_url":"{{entire_url('../api/supply_contracts_update.dspy')}}"
},
@ -52,9 +52,16 @@
"browserfields": {
"exclouded": [
"id"
"id",
"resellerid"
],
"alters": {
"supplier_id": {
"uitype": "code",
"dataurl": "{{entire_url('../api/get_search_supplier_id.dspy')}}",
"valueField": "supplier_id",
"textField": "supplier_id_text"
},
"status": {
"uitype": "code",
"data": [
@ -76,6 +83,15 @@
},
"editexclouded":[
"id",
"resellerid",
"contract_code",
"created_by",
"created_at",
"updated_at"
],
"fields":[
{
"name": "id",
@ -126,7 +142,7 @@
"valueField": "supplier_id",
"textField": "supplier_id_text"
},
"dataurl": "{{entire_url('/appbase/get_code.dspy')}}"
"dataurl": "{{entire_url('../api/get_search_supplier_id.dspy')}}"
},
{
"name": "contract_code",
@ -271,6 +287,11 @@
"op": "LIKE",
"var": "contract_code"
},
{
"field": "supplier_id",
"op": "=",
"var": "supplier_id"
},
{
"field": "status",
"op": "=",
@ -283,6 +304,7 @@
"filter_labels":{
"contract_name": "合同名称",
"contract_code": "合同编号",
"supplier_id": "供应商",
"status": "状态"
},