feat: 供应商表增加机构ID、内外部标识、结算周期/日、付款方式字段

- models/suppliers.json: 新增 orgid, is_external, settlement_cycle, settlement_day, payment_type
- 外部供应商创建时自动在sage库开设机构
- 内部供应商通过机构名称搜索选择机构ID
- suppliers_create.dspy: 处理is_external逻辑
- get_search_orgid.dspy: 机构名称搜索API
- load_path.py: 注册新API路由
This commit is contained in:
Hermes Agent 2026-06-17 18:59:51 +08:00
parent 2dd2953757
commit 2ff2ca7685
8 changed files with 354 additions and 10 deletions

View File

@ -8,17 +8,42 @@
"data_filter": { "data_filter": {
"AND": [ "AND": [
{"field": "supplier_name", "op": "LIKE", "var": "supplier_name"}, {"field": "supplier_name", "op": "LIKE", "var": "supplier_name"},
{"field": "is_external", "op": "=", "var": "is_external"},
{"field": "status", "op": "=", "var": "status"} {"field": "status", "op": "=", "var": "status"}
] ]
}, },
"filter_labels": { "filter_labels": {
"supplier_name": "供应商名称", "supplier_name": "供应商名称",
"is_external": "供应商类型",
"status": "状态" "status": "状态"
}, },
"editexclouded": ["id", "resellerid", "created_by", "created_at", "updated_at"], "editexclouded": ["id", "resellerid", "status", "created_by", "created_at", "updated_at"],
"browserfields": { "browserfields": {
"exclouded": ["id", "resellerid" ], "exclouded": ["id", "resellerid", "created_by"],
"alters": { "alters": {
"is_external": {
"uitype": "code",
"data": [
{"value": "1", "text": "外部供应商"},
{"value": "0", "text": "内部供应商"}
]
},
"payment_type": {
"uitype": "code",
"data": [
{"value": "prepaid", "text": "预付费"},
{"value": "postpaid", "text": "后付费"}
]
},
"settlement_cycle": {
"uitype": "code",
"data": [
{"value": "monthly", "text": "月结"},
{"value": "weekly", "text": "周结"},
{"value": "biweekly", "text": "半月结"},
{"value": "quarterly", "text": "季结"}
]
},
"status": { "status": {
"uitype": "code", "uitype": "code",
"data": [ "data": [

View File

@ -78,6 +78,38 @@
"type": "str", "type": "str",
"length": 64 "length": 64
}, },
{
"name": "orgid",
"title": "机构ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "is_external",
"title": "是否外部供应商",
"type": "char",
"length": 1,
"nullable": "no",
"default": "1"
},
{
"name": "settlement_cycle",
"title": "结算周期",
"type": "str",
"length": 20
},
{
"name": "settlement_day",
"title": "结算日",
"type": "int"
},
{
"name": "payment_type",
"title": "付款方式",
"type": "str",
"length": 20
},
{ {
"name": "status", "name": "status",
"title": "状态", "title": "状态",
@ -115,6 +147,11 @@
"idxtype": "index", "idxtype": "index",
"idxfields": ["resellerid"] "idxfields": ["resellerid"]
}, },
{
"name": "idx_suppliers_orgid",
"idxtype": "index",
"idxfields": ["orgid"]
},
{ {
"name": "idx_suppliers_code", "name": "idx_suppliers_code",
"idxtype": "unique", "idxtype": "unique",
@ -127,6 +164,12 @@
"table": "organization", "table": "organization",
"valuefield": "id", "valuefield": "id",
"textfield": "orgname" "textfield": "orgname"
},
{
"field": "orgid",
"table": "organization",
"valuefield": "id",
"textfield": "orgname"
} }
] ]
} }

View File

@ -101,6 +101,7 @@ PATHS_LOGINED = [
"/supplychain/api/get_search_productid.dspy", "/supplychain/api/get_search_productid.dspy",
"/supplychain/api/get_search_sub_reseller_id.dspy", "/supplychain/api/get_search_sub_reseller_id.dspy",
"/supplychain/api/get_search_agreement_id.dspy", "/supplychain/api/get_search_agreement_id.dspy",
"/supplychain/api/get_search_orgid.dspy",
# CRUD API — sub_distributors # CRUD API — sub_distributors
"/supplychain/api/sub_distributors_create.dspy", "/supplychain/api/sub_distributors_create.dspy",
"/supplychain/api/sub_distributors_update.dspy", "/supplychain/api/sub_distributors_update.dspy",

View File

@ -95,6 +95,35 @@ async def create_supplier(request, params_kw):
resellerid = await env.get_userorgid() resellerid = await env.get_userorgid()
data = params_kw data = params_kw
db, dbname = _get_sor() db, dbname = _get_sor()
# Handle external supplier: create new org
is_external = data.get("is_external", "1")
orgid = data.get("orgid")
if is_external == "1" and not orgid:
# External supplier: create new organization
supplier_name = data.get("supplier_name")
if not supplier_name:
return json.dumps({"status": "error", "message": "供应商名称不能为空"})
# Create org in sage database
new_orgid = getID()
now_org = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
org_rec = {
"id": new_orgid,
"orgname": supplier_name,
"orgtype": "supplier",
"status": "1",
"created_by": user_id,
"created_at": now_org,
"updated_at": now_org
}
async with db.sqlorContext("sage") as sor_sage:
await sor_sage.C("organization", org_rec)
orgid = new_orgid
elif is_external == "0" and not orgid:
return json.dumps({"status": "error", "message": "内部供应商必须选择所属机构"})
async with db.sqlorContext(dbname) as sor: async with db.sqlorContext(dbname) as sor:
supplier_code = data.get("supplier_code") or _generate_supplier_code(resellerid) supplier_code = data.get("supplier_code") or _generate_supplier_code(resellerid)
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@ -110,6 +139,11 @@ async def create_supplier(request, params_kw):
"tax_number": data.get("tax_number"), "tax_number": data.get("tax_number"),
"bank_name": data.get("bank_name"), "bank_name": data.get("bank_name"),
"bank_account": data.get("bank_account"), "bank_account": data.get("bank_account"),
"orgid": orgid,
"is_external": is_external,
"settlement_cycle": data.get("settlement_cycle"),
"settlement_day": data.get("settlement_day"),
"payment_type": data.get("payment_type"),
"status": data.get("status", "1"), "status": data.get("status", "1"),
"remark": data.get("remark"), "remark": data.get("remark"),
"created_by": user_id, "created_by": user_id,
@ -128,7 +162,9 @@ async def update_supplier(request, params_kw):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
rec = {"id": data["id"], "updated_at": now} rec = {"id": data["id"], "updated_at": now}
for key in ["supplier_name", "contact_person", "contact_phone", "contact_email", for key in ["supplier_name", "contact_person", "contact_phone", "contact_email",
"address", "tax_number", "bank_name", "bank_account", "status", "remark"]: "address", "tax_number", "bank_name", "bank_account",
"orgid", "is_external", "settlement_cycle", "settlement_day",
"payment_type", "status", "remark"]:
if key in data: if key in data:
rec[key] = data[key] rec[key] = data[key]
await sor.U("suppliers", rec) await sor.U("suppliers", rec)

View File

@ -0,0 +1,15 @@
result = [{'orgid': '', 'orgid_text': '全部'}]
try:
userorgid = await get_userorgid()
if not userorgid:
return json.dumps(result, ensure_ascii=False)
db = DBPools()
async with db.sqlorContext('sage') as sor:
rows = await sor.sqlExe(
"select id as orgid, orgname as orgid_text from organization where status = '1' order by orgname",
{}
)
return json.dumps(result + list(rows), ensure_ascii=False)
except Exception as e:
debug(f'get_search_orgid error: {e}')
return json.dumps(result, ensure_ascii=False)

View File

@ -12,6 +12,34 @@ try:
data['created_at'] = timestampstr() data['created_at'] = timestampstr()
data['updated_at'] = timestampstr() data['updated_at'] = timestampstr()
# Handle external supplier: create new org
is_external = data.get('is_external', '1')
orgid = data.get('orgid')
if is_external == '1' and not orgid:
supplier_name = data.get('supplier_name')
if not supplier_name:
result['options'] = {'title': 'Error', 'message': '供应商名称不能为空', 'type': 'error'}
return json.dumps(result, ensure_ascii=False)
new_orgid = getID()
now_org = timestampstr()
org_rec = {
'id': new_orgid,
'orgname': supplier_name,
'orgtype': 'supplier',
'status': '1',
'created_by': user_id,
'created_at': now_org,
'updated_at': now_org
}
db = DBPools()
async with db.sqlorContext('sage') as sor_sage:
await sor_sage.C('organization', org_rec)
data['orgid'] = new_orgid
elif is_external == '0' and not orgid:
result['options'] = {'title': 'Error', 'message': '内部供应商必须选择所属机构', 'type': 'error'}
return json.dumps(result, ensure_ascii=False)
async with DBPools().sqlorContext(dbname) as sor: async with DBPools().sqlorContext(dbname) as sor:
await sor.C('suppliers', data) await sor.C('suppliers', data)

View File

@ -27,9 +27,10 @@ if not ns.get('sort'):
sql = '''select a.*, b.resellerid_text sql = '''select a.*, b.resellerid_text, c.orgid_text
from (select * from suppliers where 1=1 [[filterstr]]) a left join (select id as resellerid, from (select * from suppliers where 1=1 [[filterstr]]) a left join (select id as resellerid,
orgname as resellerid_text from organization where 1 = 1) b on a.resellerid = b.resellerid''' orgname as resellerid_text from organization where 1 = 1) b on a.resellerid = b.resellerid left join (select id as orgid,
orgname as orgid_text from organization where 1 = 1) c on a.orgid = c.orgid'''
filterjson = params_kw.get('data_filter') filterjson = params_kw.get('data_filter')
if filterjson and isinstance(filterjson, str): if filterjson and isinstance(filterjson, str):
@ -108,6 +109,38 @@ fields_str=r'''[
"type": "str", "type": "str",
"length": 64 "length": 64
}, },
{
"name": "orgid",
"title": "机构ID",
"type": "str",
"length": 32,
"nullable": "no"
},
{
"name": "is_external",
"title": "是否外部供应商",
"type": "char",
"length": 1,
"nullable": "no",
"default": "1"
},
{
"name": "settlement_cycle",
"title": "结算周期",
"type": "str",
"length": 20
},
{
"name": "settlement_day",
"title": "结算日",
"type": "int"
},
{
"name": "payment_type",
"title": "付款方式",
"type": "str",
"length": 20
},
{ {
"name": "status", "name": "status",
"title": "状态", "title": "状态",

View File

@ -20,13 +20,13 @@
"editable":{ "editable":{
"new_data_url":"{{entire_url('add_suppliers.dspy')}}", "new_data_url":"{{entire_url('../api/suppliers_create.dspy')}}",
"delete_data_url":"{{entire_url('delete_suppliers.dspy')}}", "delete_data_url":"{{entire_url('../api/suppliers_delete.dspy')}}",
"update_data_url":"{{entire_url('update_suppliers.dspy')}}" "update_data_url":"{{entire_url('../api/suppliers_update.dspy')}}"
}, },
@ -41,9 +41,58 @@
"browserfields": { "browserfields": {
"exclouded": [ "exclouded": [
"id" "id",
"resellerid",
"created_by"
], ],
"alters": { "alters": {
"is_external": {
"uitype": "code",
"data": [
{
"value": "1",
"text": "外部供应商"
},
{
"value": "0",
"text": "内部供应商"
}
]
},
"payment_type": {
"uitype": "code",
"data": [
{
"value": "prepaid",
"text": "预付费"
},
{
"value": "postpaid",
"text": "后付费"
}
]
},
"settlement_cycle": {
"uitype": "code",
"data": [
{
"value": "monthly",
"text": "月结"
},
{
"value": "weekly",
"text": "周结"
},
{
"value": "biweekly",
"text": "半月结"
},
{
"value": "quarterly",
"text": "季结"
}
]
},
"status": { "status": {
"uitype": "code", "uitype": "code",
"data": [ "data": [
@ -61,6 +110,15 @@
}, },
"editexclouded":[
"id",
"resellerid",
"status",
"created_by",
"created_at",
"updated_at"
],
"fields":[ "fields":[
{ {
"name": "id", "name": "id",
@ -84,7 +142,7 @@
"valueField": "resellerid", "valueField": "resellerid",
"textField": "resellerid_text", "textField": "resellerid_text",
"params": { "params": {
"dbname": "{{get_module_dbname('supplychain')}}", "dbname": "sage",
"table": "organization", "table": "organization",
"tblvalue": "id", "tblvalue": "id",
"tbltext": "orgname", "tbltext": "orgname",
@ -185,6 +243,105 @@
"datatype": "str", "datatype": "str",
"label": "银行账号" "label": "银行账号"
}, },
{
"name": "orgid",
"title": "机构ID",
"type": "str",
"length": 32,
"nullable": "no",
"label": "机构ID",
"uitype": "code",
"valueField": "orgid",
"textField": "orgid_text",
"params": {
"dbname": "sage",
"table": "organization",
"tblvalue": "id",
"tbltext": "orgname",
"valueField": "orgid",
"textField": "orgid_text"
},
"dataurl": "{{entire_url('/appbase/get_code.dspy')}}"
},
{
"name": "is_external",
"title": "是否外部供应商",
"type": "char",
"length": 1,
"nullable": "no",
"default": "1",
"cwidth": 4,
"uitype": "code",
"datatype": "char",
"label": "是否外部供应商",
"data": [
{
"value": "1",
"text": "外部供应商"
},
{
"value": "0",
"text": "内部供应商"
}
]
},
{
"name": "settlement_cycle",
"title": "结算周期",
"type": "str",
"length": 20,
"cwidth": 18,
"uitype": "code",
"datatype": "str",
"label": "结算周期",
"data": [
{
"value": "monthly",
"text": "月结"
},
{
"value": "weekly",
"text": "周结"
},
{
"value": "biweekly",
"text": "半月结"
},
{
"value": "quarterly",
"text": "季结"
}
]
},
{
"name": "settlement_day",
"title": "结算日",
"type": "int",
"length": 0,
"uitype": "int",
"datatype": "int",
"label": "结算日"
},
{
"name": "payment_type",
"title": "付款方式",
"type": "str",
"length": 20,
"cwidth": 18,
"uitype": "code",
"datatype": "str",
"label": "付款方式",
"data": [
{
"value": "prepaid",
"text": "预付费"
},
{
"value": "postpaid",
"text": "后付费"
}
]
},
{ {
"name": "status", "name": "status",
"title": "状态", "title": "状态",
@ -257,6 +414,11 @@
"op": "LIKE", "op": "LIKE",
"var": "supplier_name" "var": "supplier_name"
}, },
{
"field": "is_external",
"op": "=",
"var": "is_external"
},
{ {
"field": "status", "field": "status",
"op": "=", "op": "=",
@ -268,6 +430,7 @@
"filter_labels":{ "filter_labels":{
"supplier_name": "供应商名称", "supplier_name": "供应商名称",
"is_external": "供应商类型",
"status": "状态" "status": "状态"
}, },