commit e36c1ec5be22025d288abbe8e8792dc44b2cfa4d Author: yumoqing Date: Sat Aug 2 00:27:44 2025 +0800 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..6202ff4 --- /dev/null +++ b/README.md @@ -0,0 +1,123 @@ +# platformbiz +平台业务模块,包括业务功能和对应的数据表设计 + +## 依赖 + +* 记账模块[accounting](https://git.kaiyuancloud.cn/yumoqing/accounting) +* 鉴权模块[rbac](https://git.kaiyuancloud.cn/yumoqing/rbac) +* 支付模块[payment](https://git.kaiyuancloud.cn/yumoqing/payment) + +## 功能 + +### 平台机构功能 +#### 供应商管理 +##### 供应商协议 +##### 协议明细 +##### 阶梯折扣管理 +##### 分销产品管理 +##### 供应商接口测试 + +#### 机构用户管理 + +### 客户机构功能 +#### 账户管理 +##### 充值 +##### 提现 +##### 账户明细查询 +#### 订单管理 +##### 订单查询 +##### 取消订单 +##### 订单支付 +##### 重复购买 + +#### 账单管理 +##### 账单查询 +#### 资源管理 +##### 资源续费 +##### 资源退订 +##### 重复购买 +##### 资源入口 + +#### 产品浏览 +##### 产品比价 +##### 产品种类浏览 +##### 产品热卖 +##### 商户推荐 + +#### 购买 +##### 产品配置 +##### 产品询价 +##### 加入购物车 +##### 立即购买 +##### 支付 +##### 记账 +###### 记账角色 +* 客户:产品购买方 +* 商户:产品售卖方 +* 分销方:产品分销协议分销方 +* 供应方:产品分销协议供应方 +* 平台:算力平台 +###### 分录设置 +* 借: 客户 客户资金账户 交易金额 +* 贷: 商户 商户交易费支出 交易金额 * 交易费率 +* 借: 平台 平台交易费收入 交易金额 * 交易费率 +* 借: 商户 商户交易费支出 交易金额 * 交易费率 +* 借: 分销方-供应方 商户采购成本 采购金额 +* 贷: 分销方 客户资金账户 交易金额 - 采购金额 - (交易金额 * 交易费率) +* 借*: 商户-分销方 供应商分销收入 交易金额1 +* 贷*: 商户-供应方 商户采购成本 采购成本1 +* 贷*: 商户 客户资金账户 交易金额1 - 采购成本1 + +#### 参与商户营销 +##### 商户获客营销 +##### 参与售卖营销 + +### 商户机构功能 + +#### 成为商户 +在客户用户界面点击【成为商户】,系统自动将此平台客户添加商户角色,成为平台商户 +并为其开通商户经营所需的记账账号。 +与平台或平台其他商户(供应商)线下协商成为其产品的分销商 + +#### 添加不同角色的用户 + +#### 发展分销商户(自己成为分销商户的供应商) +新添一个分销协议,在协议中添加协议明细,设置分销折扣,或阶梯折扣,将自己的产品授权给分销商销售。 +##### 供应商添加分销协议 +##### 供应商添加协议明细 +##### 供应商添加协议明细步骤(只有阶梯折扣协议才需要) +##### 供应商添加分销产品到分销协议 +##### 商户设置一个无指定客户的分销协议 +#### 自有产品管理 +##### 添加自由产品 +##### 自有产品区域管理 +##### 自有产品数量管理 +##### 自有产品计价管理 +##### 自有产品计价表管理 + +#### 设置产品销售折扣或价格 +##### 公用产品折扣设置 +为所有客户提供一个以产品计价为基础的折扣 +##### 为指定客户设置特殊折扣 + +#### 主推产品管理 +#### 大屏功能 +##### 展示位置设置 +##### 展示指标管理 +##### 大屏风格设置 + +#### 短信管理 +##### 开通短信功能 +##### 通知场景管理 +##### 短信计费 + +### 产品购买 +#### 标的 +卖房提供的产品 +#### 参与方 +* 卖房 +* 买房 +* 平台 +* 供应方 +#### 分录设置 +借:买方- \ No newline at end of file diff --git a/json/agreedetail.json b/json/agreedetail.json new file mode 100644 index 0000000..66a1641 --- /dev/null +++ b/json/agreedetail.json @@ -0,0 +1,23 @@ +{ + "tblname":"agreedetail", + "params":{ + "browserfields": { + "exclouded": ["id", "agreeid"], + "alters": {} + }, + "editexclouded": [ + "id", "agreeid" + ], + "subtables":[ + { + "field":"agreedetailid", + "title":"协议产品", + "params":{ + "agreeid":"${agreeid}", + "prodtypeid":"${prodtypeid}" + }, + "subtable":"agreeproduct" + } + ] + } +} diff --git a/json/agreedetailstep.json b/json/agreedetailstep.json new file mode 100644 index 0000000..bd63014 --- /dev/null +++ b/json/agreedetailstep.json @@ -0,0 +1,12 @@ +{ + "tblname":"agreedetailstep", + "params":{ + "browserfields": { + "exclouded": ["id"], + "alters": {} + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/json/agreement.json b/json/agreement.json new file mode 100644 index 0000000..9f4bf39 --- /dev/null +++ b/json/agreement.json @@ -0,0 +1,32 @@ +{ + "tblname":"agreement", + "params":{ + "toolbar":{ + "tools":[ + { + "name":"prodauth", + "selected_row":true, + "label":"授权" + } + ] + }, + "binds":[ + { + "wid":"self", + "event":"prodauth", + "actiontype":"urlwidget", + "target":"PopupWindow", + "options":{ + "url":"{{entire_url('../agree_prodclone.dspy')}}" + } + } + ], + "browserfields": { + "exclouded": ["id"], + "alters": {} + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/json/agreeproduct.json b/json/agreeproduct.json new file mode 100644 index 0000000..5866643 --- /dev/null +++ b/json/agreeproduct.json @@ -0,0 +1,13 @@ +{ + "tblname":"agreeproduct", + "params":{ + "logined_userorgid":"orgid", + "browserfields": { + "exclouded": ["id", "agreedetailid"], + "alters": {} + }, + "editexclouded": [ + "id", "agreedetailid", "resellerpid" + ] + } +} diff --git a/json/biz_order.json b/json/biz_order.json new file mode 100644 index 0000000..9f55b9d --- /dev/null +++ b/json/biz_order.json @@ -0,0 +1,12 @@ +{ + "tblname":"biz_order", + "params":{ + "browserfields": { + "exclouded": ["id"], + "alters": {} + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/json/build.sh b/json/build.sh new file mode 100755 index 0000000..e596dff --- /dev/null +++ b/json/build.sh @@ -0,0 +1,3 @@ +#!/usr/bin/bash + +xls2ui -m ../models -o ../wwwroot platformbiz *.json diff --git a/json/choose_prodtype.json b/json/choose_prodtype.json new file mode 100644 index 0000000..8baf780 --- /dev/null +++ b/json/choose_prodtype.json @@ -0,0 +1,40 @@ +{ + "tblname":"prodtype", + "uitype":"tree", + "alias":"choose_prodtype", + "params":{ + "toolbar":{ + "bar_cwidth":2, + "tools":[ + { + "name":"test", + "selected_data":true, + "label":"TEST" + } + ] + }, + "binds":[ + { + "wid":"self", + "event":"prodauth", + "actiontype":"urlwidget", + "target":"PopupWindow", + "options":{ + "url":"{{entire_url('platformbiz/ag.dspy')}}", + "params":{ + "agreedetailid":"{{params_kw.agreedetailid}}" + } + } + } + ], + "idField":"id", + "textField":"name", + "editable":true, + "browserfields":{ + "alters":{} + }, + "edit_exclouded_fields":["del_flg", "create_at"], + "parentField":"parentid" + } +} + diff --git a/json/coupon.json b/json/coupon.json new file mode 100644 index 0000000..74ad971 --- /dev/null +++ b/json/coupon.json @@ -0,0 +1,12 @@ +{ + "tblname":"coupon", + "params":{ + "browserfields": { + "exclouded": ["id"], + "alters": {} + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/json/coupon_log.json b/json/coupon_log.json new file mode 100644 index 0000000..87b4fc0 --- /dev/null +++ b/json/coupon_log.json @@ -0,0 +1,12 @@ +{ + "tblname":"coupon_log", + "params":{ + "browserfields": { + "exclouded": ["id"], + "alters": {} + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/json/coupontype.json b/json/coupontype.json new file mode 100644 index 0000000..b6b3518 --- /dev/null +++ b/json/coupontype.json @@ -0,0 +1,12 @@ +{ + "tblname":"coupontype", + "params":{ + "browserfields": { + "exclouded": ["id"], + "alters": {} + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/json/paychannel.json b/json/paychannel.json new file mode 100644 index 0000000..ffc3471 --- /dev/null +++ b/json/paychannel.json @@ -0,0 +1,12 @@ +{ + "tblname":"paychannel", + "params":{ + "browserfields": { + "exclouded": ["id"], + "alters": {} + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/json/prodpricing.json b/json/prodpricing.json new file mode 100644 index 0000000..194d663 --- /dev/null +++ b/json/prodpricing.json @@ -0,0 +1,20 @@ +{ + "tblname":"prodpricing", + "params":{ + "browserfields": { + "exclouded": ["id", "prodid" ], + "alters": { + } + }, + "editexclouded": [ + "id", "prodid" + ], + "subtables":[ + { + "field":"prodpricingid", + "subtable":"prodpricingtab", + "title":"产品定价表" + } + ] + } +} diff --git a/json/prodpricingtab.json b/json/prodpricingtab.json new file mode 100644 index 0000000..0ac556c --- /dev/null +++ b/json/prodpricingtab.json @@ -0,0 +1,12 @@ +{ + "tblname":"prodpricingtab", + "params":{ + "browserfields": { + "exclouded": ["id", "prodid"], + "alters": {} + }, + "editexclouded": [ + "id", "prodid" + ] + } +} diff --git a/json/prodtype.json b/json/prodtype.json new file mode 100644 index 0000000..08179a7 --- /dev/null +++ b/json/prodtype.json @@ -0,0 +1,24 @@ +{ + "tblname":"prodtype", + "uitype":"tree", + "params":{ + "idField":"id", + "textField":"name", + "parentField":"parentid", + "editable":true, + "browserfields": { + "exclouded": ["id", "parentid" ], + "alters": {} + }, + "editexclouded": [ + "id", "parentid" + ], + "subtables":[ + { + "field":"prodtypeid", + "title":"产品类型", + "subtable":"prodtypespec" + } + ] + } +} diff --git a/json/prodtypespec.json b/json/prodtypespec.json new file mode 100644 index 0000000..6f538c4 --- /dev/null +++ b/json/prodtypespec.json @@ -0,0 +1,15 @@ +{ + "tblname":"prodtypespec", + "params":{ + "browserfields": { + "exclouded": ["id", "prodtypeid"], + "alters": {} + }, + "data_params":{ + "prodtypeid":"${params_kw.id}" + }, + "editexclouded": [ + "id", "prodtypeid" + ] + } +} diff --git a/json/product.json b/json/product.json new file mode 100644 index 0000000..ce0cd3a --- /dev/null +++ b/json/product.json @@ -0,0 +1,24 @@ +{ + "tblname":"product", + "params":{ + "logined_userorgid":"orgid", + "browserfields": { + "exclouded": ["id", "orgid", "providerid", "agreeid", "providerpid" ], + "alters": { + } + }, + "editexclouded": [ + "id", "orgid", "providerid", "agreeid", "providerpid" + ], + "subtables":[ + { + "field":"prodid", + "params":{ + "prodtypeid":"${prodtypeid}" + }, + "subtable":"prodpricing", + "title":"产品定价" + } + ] + } +} diff --git a/json/provide_agree.json b/json/provide_agree.json new file mode 100644 index 0000000..f163166 --- /dev/null +++ b/json/provide_agree.json @@ -0,0 +1,30 @@ +{ + "tblname":"agreement", + "alias":"provide_agree", + "params":{ + "title":"供应协议", + "logined_userorgid":"resellerid", + "browserfields": { + "exclouded": ["id", "resellerid" ], + "alters": { + "providerid":{ + "uitype":"search", + "search_event":"row_selected", + "search_url":"{{entire_url('../select_org')}}", + "valueField":"id", + "textField":"orgname" + } + } + }, + "subtables":[ + { + "title":"协议明细", + "subtable":"provide_agreedetail", + "field":"agreeid" + } + ], + "editexclouded": [ + "id", "resellerid" + ] + } +} diff --git a/json/provide_agreedetail.json b/json/provide_agreedetail.json new file mode 100644 index 0000000..df6691f --- /dev/null +++ b/json/provide_agreedetail.json @@ -0,0 +1,24 @@ +{ + "tblname":"agreedetail", + "alias":"provide_agreedetail", + "params":{ + "browserfields": { + "exclouded": ["id", "agreeid"], + "alters": {} + }, + "editexclouded": [ + "id", "agreeid" + ], + "subtables":[ + { + "field":"agreedetailid", + "title":"供应协议产品", + "params":{ + "agreeid":"${agreeid}", + "prodtypeid":"${prodtypeid}" + }, + "subtable":"provide_agreeproduct" + } + ] + } +} diff --git a/json/provide_agreeproduct.json b/json/provide_agreeproduct.json new file mode 100644 index 0000000..ba691ee --- /dev/null +++ b/json/provide_agreeproduct.json @@ -0,0 +1,14 @@ +{ + "tblname":"agreeproduct", + "alias":"provide_agreeproduct", + "params":{ + "logined_userorgid":"orgid", + "browserfields": { + "exclouded": ["id", "agreedetailid", "resellerpid"], + "alters": {} + }, + "editexclouded": [ + "id", "agreedetailid", "resellerpid" + ] + } +} diff --git a/json/recgarge_log.json b/json/recgarge_log.json new file mode 100644 index 0000000..8af16b5 --- /dev/null +++ b/json/recgarge_log.json @@ -0,0 +1,12 @@ +{ + "tblname":"recharge_log", + "params":{ + "browserfields": { + "exclouded": ["id"], + "alters": {} + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/json/resource.json b/json/resource.json new file mode 100644 index 0000000..9cfdcd1 --- /dev/null +++ b/json/resource.json @@ -0,0 +1,12 @@ +{ + "tblname":"resource", + "params":{ + "browserfields": { + "exclouded": ["id"], + "alters": {} + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/json/retail_agree.json b/json/retail_agree.json new file mode 100644 index 0000000..d21e86e --- /dev/null +++ b/json/retail_agree.json @@ -0,0 +1,25 @@ +{ + "tblname":"agreement", + "alias":"retail_agree", + "params":{ + "title":"分销协议", + "sortby":"name", + "logined_userorgid":"providerid", + "logined_userid":"provideruid", + "browserfields": { + "exclouded": ["id", "providerid", "provideruid" ], + "alters": { + "resellerid":{ + "uitype":"search", + "search_event":"row_selected", + "search_url":"{{entire_url('../select_org')}}", + "valueField":"id", + "textField":"orgname" + } + } + }, + "editexclouded": [ + "id", "providerid", "provideruid" + ] + } +} diff --git a/models/agreedetail.xlsx b/models/agreedetail.xlsx new file mode 100644 index 0000000..3c1f45a Binary files /dev/null and b/models/agreedetail.xlsx differ diff --git a/models/agreedetailstep.xlsx b/models/agreedetailstep.xlsx new file mode 100644 index 0000000..be4ea04 Binary files /dev/null and b/models/agreedetailstep.xlsx differ diff --git a/models/agreement.xlsx b/models/agreement.xlsx new file mode 100644 index 0000000..2cf4c6a Binary files /dev/null and b/models/agreement.xlsx differ diff --git a/models/agreeproduct.xlsx b/models/agreeproduct.xlsx new file mode 100644 index 0000000..503a84c Binary files /dev/null and b/models/agreeproduct.xlsx differ diff --git a/models/biz_order.xlsx b/models/biz_order.xlsx new file mode 100644 index 0000000..af31e66 Binary files /dev/null and b/models/biz_order.xlsx differ diff --git a/models/biz_orderdetail.xlsx b/models/biz_orderdetail.xlsx new file mode 100644 index 0000000..ce66b8b Binary files /dev/null and b/models/biz_orderdetail.xlsx differ diff --git a/models/coupon.xlsx b/models/coupon.xlsx new file mode 100644 index 0000000..7b7e2e3 Binary files /dev/null and b/models/coupon.xlsx differ diff --git a/models/coupon_log.xlsx b/models/coupon_log.xlsx new file mode 100644 index 0000000..b9c36a2 Binary files /dev/null and b/models/coupon_log.xlsx differ diff --git a/models/coupontype.xlsx b/models/coupontype.xlsx new file mode 100644 index 0000000..11b4c3f Binary files /dev/null and b/models/coupontype.xlsx differ diff --git a/models/distributionagreement.xlsx b/models/distributionagreement.xlsx new file mode 100644 index 0000000..b46d5f2 Binary files /dev/null and b/models/distributionagreement.xlsx differ diff --git a/models/paychannel.xlsx b/models/paychannel.xlsx new file mode 100644 index 0000000..73da683 Binary files /dev/null and b/models/paychannel.xlsx differ diff --git a/models/pr_link.xlsx b/models/pr_link.xlsx new file mode 100644 index 0000000..9195b0d Binary files /dev/null and b/models/pr_link.xlsx differ diff --git a/models/prodpricing.xlsx b/models/prodpricing.xlsx new file mode 100644 index 0000000..a74c85d Binary files /dev/null and b/models/prodpricing.xlsx differ diff --git a/models/prodpricingtab.xlsx b/models/prodpricingtab.xlsx new file mode 100644 index 0000000..77030ed Binary files /dev/null and b/models/prodpricingtab.xlsx differ diff --git a/models/prodtype.xlsx b/models/prodtype.xlsx new file mode 100644 index 0000000..0a1ec77 Binary files /dev/null and b/models/prodtype.xlsx differ diff --git a/models/prodtypespec.xlsx b/models/prodtypespec.xlsx new file mode 100644 index 0000000..6376502 Binary files /dev/null and b/models/prodtypespec.xlsx differ diff --git a/models/product.xlsx b/models/product.xlsx new file mode 100644 index 0000000..eb2b8e4 Binary files /dev/null and b/models/product.xlsx differ diff --git a/models/recharge_log.xlsx b/models/recharge_log.xlsx new file mode 100644 index 0000000..c65398c Binary files /dev/null and b/models/recharge_log.xlsx differ diff --git a/models/reseller.xlsx b/models/reseller.xlsx new file mode 100644 index 0000000..f6730d8 Binary files /dev/null and b/models/reseller.xlsx differ diff --git a/models/resource.xlsx b/models/resource.xlsx new file mode 100644 index 0000000..a89ed9c Binary files /dev/null and b/models/resource.xlsx differ diff --git a/platformbiz/biz.py b/platformbiz/biz.py new file mode 100644 index 0000000..7fb349c --- /dev/null +++ b/platformbiz/biz.py @@ -0,0 +1,10 @@ +from platformbiz.pricing import get_price_infos +from biz_order import add_pay_order + +async customerpay(sor, sellerid, customerid, userid, order_details): + await add_pay_order(sor, sellerid, customerid, userid, order_details) + price_infos = await get_price_infos(sor, sellerid, + customerid, + priductid, + prod_config) + : diff --git a/platformbiz/biz_order.py b/platformbiz/biz_order.py new file mode 100644 index 0000000..2d06af6 --- /dev/null +++ b/platformbiz/biz_order.py @@ -0,0 +1,121 @@ +from time import time +from ahserver.serverenv import get_serverenv +from sqlor.dbpools import DBPools +from appPublic.dictObject import DictObject +from appPublic.log import debug +from appPublic.uniqueID import getID +from platformbiz.const import ORDER_INITIAL, RECHARGE_INITIAL +from platformbiz.pricing import get_biz_date + +async def add_recharge_order(sor, customerid, userid, action, recharge_amt): + """ + arguments: + customerid: organization who recharge + userid: user who do the recharge action + recharge_amt: recharge amount + action: business action name + return: + order record + """ + rec = DictObject() + rec.id = getID() + rec.customerid = customerid + rec.userid = userid + get_business_date = get_serverenv('get_business_date') + rec.order_date = await get_business_date() + rec.business_op = action + rec.amount = recharge_amt + rec.order_status = ORDER_INITIAL + await sor.C('biz_order', rec.copy()) + return rec + +async def get_paychannel_by_name(sor, name): + sql = "select * from paychannel where name=${name}$" + recs = await sor.sqlExe(sql, {'name':name}) + if len(recs) > 0: + return recs[0] + debug(f'get paychannel error({name})') + return None + +async def add_recharge_log(sor, customerid, userid, action, orderid, transdate, recharge_amt, name): + rec = DictObject() + rec.id = getID() + rec.customerid = customerid + rec.userid = userid + rec.action = action + rec.recharge_amt = recharge_amt + pc = await get_paychannel_by_name(sor, name) + debug(f'{pc=}, {recharge_amt=}') + if pc is None: + raise Exception(f'paychannel({name}) pay channel not found') + rec.fee_amt = recharge_amt * pc.fee_rate + rec.fee_rate = pc.fee_rate + rec.pcid = pc.id + rec.biz_orderid = orderid + rec.recharge_status = RECHARGE_INITIAL + rec.transdate = transdate + await sor.C('recharge_log', rec.copy()) + return rec + +async def change_recharge_status(sor, rlid, status, tid): + recs = await sor.R('recharge_log', {'id':rlid}) + if len(recs) < 1: + return None + recs[0].recharge_status = status + recs[0].channel_tid = tid + await sor.U('recharge_log', recs[0].copy()) + return recs[0] + + +async def add_pay_order(sor, sellerid, + customerid, + userid, + action, + order_details, + origin_orderid=None): + env = globals() + customerid1 = await env.get_userorgid() + userid1 = await env.get_user() + if customerid != customerid1: + e = Exception(f'{custmerid} is not logined orgid') + exception(f'{e}') + raise e + if userid != userid1: + e = Exception(f'{userid} is not logined userid') + exception(f'{e}') + raise e + id = getID() + amount = 0.0 + for order_detail in order_details: + ns1 = { + "id": id, + "orderid":ns['id'], + "productid":order_detail.productid, + "product_cnt":order_detail.product_cnt, + "prod_config":order_detail.prod_config, + "list_amount":order_detail.list_amount, + "trans_amount":order_detail.trans_amount + } + await sor.C('biz_orderdetail', ns1) + amount += order_detail.trans_amount + price_infos = await get_price_infos(sor, sellerid, + customerid, + order_detail.productid, + order_detail.prod_config) + + + ns = { + "id": id, + "customerid":customerid, + "userid":userid, + "resellerid":sellerid, + "order_date":await get_biz_date(sor), + "order_status":"0", + "business_op":action, + "amount":pay_amount, + "ordertype":None, + "pay_date":None, + "origin_orderid":origin_orderid + } + await sor.C('biz_order', ns) + diff --git a/platformbiz/const.py b/platformbiz/const.py new file mode 100644 index 0000000..ad7bb01 --- /dev/null +++ b/platformbiz/const.py @@ -0,0 +1,2 @@ +ORDER_INITIAL = '0' +RECHARGE_INITIAL = '0' diff --git a/platformbiz/getdbname.py b/platformbiz/getdbname.py new file mode 100644 index 0000000..e7af9f9 --- /dev/null +++ b/platformbiz/getdbname.py @@ -0,0 +1,8 @@ +from ahserver.serverenv import get_serverenv + +def get_dbname(): + f = get_serverenv('get_module_dbname') + if f is None: + raise Exception('get_module_dbname() not found') + return f('platformbiz') + diff --git a/platformbiz/init.py b/platformbiz/init.py new file mode 100644 index 0000000..e88b1e4 --- /dev/null +++ b/platformbiz/init.py @@ -0,0 +1,20 @@ +from ahserver.serverenv import ServerEnv, get_serverenv +from platformbiz.recharge import Recharge +from platformbiz.pb_acc import PlatformBizAccRecharge, get_owner_orgid, get_balance +from platformbiz.biz_order import change_recharge_status +from platformbiz.pricing import calc_prod_price, get_sell_price, get_price_infos +from platformbiz.product import agree_products_clone, open_agree_account + +def load_platformbiz(): + g = ServerEnv() + g.Recharge = Recharge + g.PlatformBizAccRecharge = PlatformBizAccRecharge + g.change_recharge_status = change_recharge_status + g.get_owner_orgid = get_owner_orgid + g.get_balance = get_balance + g.calc_prod_price = calc_prod_price + g.get_sell_price = get_sell_price + g.get_price_infos = get_price_infos + g.agree_products_clone = agree_products_clone + g.open_agree_account = open_agree_account + diff --git a/platformbiz/pb_acc.py b/platformbiz/pb_acc.py new file mode 100644 index 0000000..8d5e13d --- /dev/null +++ b/platformbiz/pb_acc.py @@ -0,0 +1,111 @@ +import time +import json +from appPublic.timeUtils import timestampstr +from appPublic.registerfunction import rfexe +from sqlor.dbpools import DBPools +from ahserver.serverenv import get_serverenv +from accounting.accounting_config import Accounting +from accounting.bizaccount import BizAccounting +from accounting.bill import write_bill +from platformbiz.getdbname import get_dbname + +async def get_owner_orgid(sor, orgid): + return '0' + +async def get_balance(orgid): + db = DBPools() + dbname = get_dbname() + async with db.sqlorContext(dbname) as sor: + f = get_serverenv('getCustomerBalance') + if f: + return await f(sor, orgid) + return None + +class PlatformBizAcc: + """ + """ + async def build_accountset(self, sor, biz_order, biz_orderdetails): + acconuntset = DictObject() + accountset['action'] = biz_order.business_op + accountset['owner'] = get_owner_orgid(sor, '0') + accountset['reseller'] = biz_order.resellerid + accountset['customer'] = biz_order.customerid + accountset['交易金额'] = biz_order.amount + transfee = await get_transfee(sor, self.resellerid, biz_order.amount, self.curdate) + accountset['交易费用'] = transfee + accountset.subsets = [] + for od in biz_orderdetails: + price_infos = await get_price_infos(sor, self.resellerid, + self.customerid, + detail.productid, + detail.prod_config) + if len(price_infos) > 1: + for pi in price_infos[:-1]: + actions = biz_order.business_op.split('_') + actions[0] = actions[0] + '*' + aset = DictObject() + aset['action'] = '_'.join(actions) + aset['owner'] = get_owner_orgid(sor, '0') + aset['reseller'] = pi['buyerid'] + aset['provider'] = pi['resellerid'] + aset['采购成本'] = pi['sell_price'] + accountset.subsets.append(aset) + return accountset + + async def accounting(self, sor, biz_orderid): + biz_order = await sor.R('biz_order', {'id':biz_orderid}) + details = await sor.R('biz_orderdetail',{'orderid':biz_orderid}) + accountset = await self.build_accountset(biz_order, details) + self.curdate = await get_business_date(sor) + transfee = await get_transfee(sor, self.resellerid, biz_order.amount, self.curdate) + a = BizAccounting(self.curdate, biz_order, accountset) + r = await a.do_accounting(sor) + + async def get_orgid_by_trans_role(self, sor, orgtype): + if orgtype== 'customer': + return self.customerid + if orgtype== 'reseller': + return self.resellerid + if orgtype== 'provider': + return self.providerid + if orgtype == 'owner': + return '0' + return None + +class PlatformBizAccRecharge(PlatformBizAcc): + def __init__(self, rlid): + self.rlid = rlid + + async def accounting(self, sor): + sql = """select * from recharge_log where id=${rlid}$""" + recs = await sor.sqlExe(sql, {'rlid':self.rlid}) + if len(recs) < 1: + e = Exception(f'get recharge log err by {rlid=}') + exception(f'{e=}') + raise e + self.recharge = recs[0] + self.customerid = self.recharge.customerid + self.orderid = self.recharge.biz_orderid + self.userid = self.recharge.userid + get_business_date = get_serverenv('get_business_date') + self.curdate = await get_business_date(sor) + self.variable = { + "交易金额":self.recharge.recharge_amt, + "充值费率":self.recharge.fee_rate, + "充值费用":self.recharge.feemat + } + bill = await write_bill(sor, self.customerid, self.userid, + self.recharge.orderid, + self.recharge.action, + self.recharge.recharge_amt) + + self.billid = bill.id + self.bill = bill + self.providerid = None + self.resellerid = None + self.action = self.recharge.action + self.productid = None + self.timestamp = timestampstr() + a = Accounting(self) + r = await a.do_accounting(sor) + diff --git a/platformbiz/pricing.py b/platformbiz/pricing.py new file mode 100644 index 0000000..83abf4c --- /dev/null +++ b/platformbiz/pricing.py @@ -0,0 +1,273 @@ + +from appPublic.log import debug, exception +from ahserver.serverenv import get_serverenv +from traceback import format_exc + +def get_step_dates(use_date, step_type): + if step_type == '0': + # 总金额 + return '1000-01-01', '9999-12-31' + y = int(use_date[:4]) + m = int(use_date[6:7]) + if step_type == '1': + y = int(use_date[:4]) + return '%04d-01-01' % y, '%04d-01-01' % (y+1) + if step_type == '2': + ss = [1,4,7,10, 13] + for i, s in enumerate(ss): + if m >=s and m < ss[i+1]: + if i < 3: + return '%04d-%02d-01' % (y, s), '%04d-%02d-01' % (y, ss[i+1]) + else: + return '%04d-%02d-01' % (y, s), '%04d-01-01' % (y+1) + if step_type == '3': + if m < 12: + return '%04d-%02d-01' % (y, m), '%04d-%02d-01' % (y, m+1) + else: + return '%04d-%02d-01' % (y, m), '%04d-01-01' % (yi+1) + e = Exception(f'unknown {step_type=}') + exception(f'{e=}') + raise e + +async def get_biz_date(sor): + biz_date = '' + f = get_serverenv('get_business_date') + if f: + biz_date = await f(sor) + return biz_date + e = Exception(f'get_serverenv("get_business_date") return None') + exception(f'{e=}') + raise e + +async def get_step_discount(sor, agreedetailid, step_type, sellerid, buyerid): + amount = await get_sale_amount(sor, step_type, sellerid, buyerid) + sql = """select * from agreedetailstep +where adid = ${adid} + and minamt <= ${amount}$ + and maxamt > ${amount}$ +""" + recs = await sor.sqlExe(sql, {'adid':agreedetailid, + 'amount':amount + }) + if len(recs) < 1: + e = Exception(f'{sql=} not data, {agreedetailid=}, {amount=}') + exception(f'{e=}') + raise e + return rec.discount + +async def get_sale_amount(sor, step_type, sellerid, buyerid): + """ + 统计销售收入,根据step_type分别统计总销售额,本年销售累计 + 本季度销售累计,本月销售累计和本周销售累计等 + """ + subjectname = '供应商分销收入' + f = get_serverenv('getAccountByName') + if f is None: + e = Exception(f'get_serverenv("getAccountByName") return None') + exception(f'{e=}') + raise e + + acc = await f(sor, accounting_orgid, sellerid, subjectname, buyerid) + biz_date = await get_biz_date(sor) + from_date, to_date = get_date_between(biz_date, step_type) + f = get_serverenv('get_account_total_amount') + if f is None: + e = Exception(f'get_serverenv("get_account_total_amount") return None') + exception(f'{e=}') + raise e + amount = await f(sor, acc.id, acc.blance_at, from_date, to_date) + return amount + +async def get_product_agreement(sor, sellerid, buyerid, productid, biz_date): + sql = """select a.*, +c.id as agreedetailid, +c.prodtypeid as agree_prodtypeid, +c.discount, +c.step_type +from product as a, agreement as b, agreedetail as c +where c.agreeid = b.id + and ( a.prodtypeid = c.prodtypeid or c.prodtypeid is NULL) + and a.orgid = b.providerid + and b.enable_date <= ${biz_date}$ + and b.expire_date > ${biz_date}$ + and a.orgid = ${sellerid}$ + and b.resellerid = ${buyerid}$ + and a.id = ${productid}$ +""" + recs = await sor.sqlExe(sql, {'sellerid':sellerid, + 'buyerid':buyerid, + 'biz_date':biz_date, + 'productid':productid + }) + if len(recs) == 0: + e = f'there is no agreement for {sellerid=} and {buyerid=}, {productid=} at {biz_date=} {sql=}' + debug(f'{e=}') + return None + rec = None + for r in recs: + if r.prodtypeid == r.agree_prodtypeid: + rec = r + break + if rec is None: + for r in recs: + if r.agree_prodtypeid is None: + rec = r + break + if rec is None: + e = Exception(f'Not agreements for {sellerid=} and {buyerid=}, {productid=} at {biz_date=} ') + exception(f'{e=}') + raise e + return rec + +def get_unit_value_price(sc, pricingtab): + for pt in pricingtab: + if pt.specvalue == '': + pt.specvalue = None + if sc.spec_name == pt.specname and \ + sc.spec_value == pt.specvalue: + # print(f'found {sc.spec_name=},{sc.spec_value=}') + return pt.unit_value, pt.unit_amt + # print(f'{sc.spec_name=},{sc.spec_value=}:{pt.specname=},{pt.specvalue=}') + return None, None + +async def calc_prod_price(sor, productid, spec_config): + """fact_config: + [ + { + spec_name, spec_value, count + } + ] + """ + biz_date = await get_biz_date(sor) + sql = """select c.*, +e.name as specname +from product a, prodpricing b, + prodpricingtab c, prodtype d, + prodtypespec e +where a.id=${productid}$ + and a.prodtypeid = d.id + and e.prodtypeid = d.id + and a.id = b.prodid + and b.enable_date <= ${biz_date}$ + and b.expire_date > ${biz_date}$ + and c.prodpricingid = b.id + and c.ptspecid = e.id +""" + recs = await sor.sqlExe(sql, {'productid':productid, + 'biz_date':biz_date}) + + if len(recs) < 1: + e = Exception(f'{sql=}, {productid=} {biz_date=} return not data') + exception(f'{e=}') + raise e + price = 0.0 + for sc in spec_config: + # print(f'{sc=}, {recs=}') + uv, up = get_unit_value_price(sc, recs) + if uv is None: + continue + cnt = sc.count / uv + price += up * cnt + return price + +async def get_sell_price(sor, sellerid, buyerid, productid, list_price): + """ + 获得商品售价 + """ + + biz_date = await get_biz_date(sor) + agree = await get_product_agreement(sor, sellerid, buyerid, productid, biz_date) + if agree is None: + return list_price + + debug(f'{agree=}') + if agree.discount: + return list_price * agree.discount + discount = await get_step_discount(sor, agree.agreedetailid, agree.step_type, selllerid, buyerid) + debug(f'step {discount=}') + return discount * list_price + +async def get_price_infos(sor, sellerid, buyerid, productid, prod_config): + """ + 获得商品价格 + resellerid:商户id + productid:商品id + prod_config:商品配置 + 返回 + [ + {ownerid, list_price, price}, ... + ] + """ + cost_pricing_infos = [] + sql1 = """select a.*, +b.pricing_method, +b.apiid +from product a left join prodpricing b on a.id=b.prodid +where a.id = ${productid}$ + and a.orgid = ${sellerid}$""" + prods = await sor.sqlExe(sql1, {'productid':productid, 'sellerid':sellerid}) + if len(prods) < 1: + e = Exception(f'{resellerid=} {productid=} product not found') + exception(f'{e=}') + raise e + prod = prods[0] + if prod.agreeid is not None: + cost_pricing_infos = await get_price_infos(sor, prod.providerid, sellerid, prod.providerpid, prod_config) + + if prod.pricing_method == '0': + # 按资源因子计费 + list_price = await calc_prod_price(sor, productid, prod_config) + sell_price = await get_sell_price(sor, sellerid, buyerid, productid, list_price) + pricing_info = { + 'sellerid': sellerid, + 'buyerid': buyerid, + 'productid': productid, + 'list_price': list_price, + 'sell_price': sell_price + } + elif prod.pricing_method == '1': + # 外部计费 + f = get_serverenv('pricing_api') + list_price = await f(sor, prod.apiid, providerpid, prod_config) + sell_price = await get_sell_price(sor, sellerid, buyerid, productid, list_price) + pricing_info = { + 'sellerid': sellerid, + 'buyerid': buyerid, + 'productid': productid, + 'list_price': list_price, + 'sell_price': sell_price + } + else: + # 供应商计费 + if len(cost_pricing_infos) < 1: + e = Exception(f'{sellerid=}, {buyerid=}, {productid=} has not resell agreement') + exception(e) + raise e + list_price = cost_pricing_infos[-1]['list_price'] + sell_price = await get_sell_price(sor, sellerid, buyerid, productid, list_price) + pricing_info = { + 'sellerid': sellerid, + 'buyerid': buyerid, + 'productid': productid, + 'list_price': list_price, + 'sell_price': sell_price + } + if len(cost_pricing_infos) > 0: + pricing_info['cost_price'] = cost_pricing_infos[-1]['sell_price'] + cost_pricing_infos.append(pricing_info) + return cost_pricing_infos + +async def get_transfee(sor, resellerid, transamt, biz_date): + sql = """select * from reseller +where orgid=${resellerid}$ + and enable_date<=${biz_date}$ + and expire_date>${biz_date}$ +""" + recs = sor.sqlExe(sql, {'resellerid':resellerid, 'biz_date':biz_date}) + if len(recs) < 1: + e = Exception(f'reseller(id={resellerid}) not available at {biz_date=}') + exception(f'{e=}') + raise e + rate = recs[0].transrate + return transamt * rate + diff --git a/platformbiz/product.py b/platformbiz/product.py new file mode 100644 index 0000000..7c7b4ea --- /dev/null +++ b/platformbiz/product.py @@ -0,0 +1,59 @@ +from appPublic.uniqueID import getID +from appPublic.log import debug +from accounting.openaccount import openRetailRelationshipAccounts + +async def get_accounting_orgid(sor): + return '0' + +async def agree_products_clone(sor, orgid, agreeid): + """ + adid:agreedetailid + sor: + return None + """ + sql = """select a.id, +a.name, +a.prodtypeid, +a.description, +a.prod_state, +a.product_code, +a.spec_note, +b.id as apid, +d.id as agreeid, +d.providerid, +d.resellerid +from product a, agreeproduct b, agreedetail c, agreement d +where a.id = b.providerpid + and b.resellerpid is null + and c.id = b.agreedetailid + and d.id = c.agreeid + and d.id = ${agreeid}$ + and a.orgid = ${orgid}$ +""" + recs = await sor.sqlExe(sql, {'agreeid':agreeid, 'orgid':orgid}) + if len(recs) < 1: + debug(f'{orgid=}, {agreeid=}, {sql=}: found no data') + return + + for rec in recs: + pid = getID() + d = { + "id":pid, + "name":rec.name, + "prodtypeid":rec.prodtypeid, + "orgid":rec.resellerid, + "providerid":rec.providerid, + "agreeid":rec.agreeid, + "providerpid":rec.id, + "description":rec.description, + "prod_state":rec.prod_state, + "product_code":rec.product_code, + "spec_note":rec.spec_note + } + await sor.C('product', d) + await sor.U('agreeproduct', {'id':rec.apid, 'resellerpid':pid}) + +async def open_agree_account(sor, providerid, sellerid): + accounting_orgid = await get_accounting_orgid(sor) + await openRetailRelationshipAccounts(sor, accounting_orgid, providerid, sellerid) + diff --git a/platformbiz/recharge.py b/platformbiz/recharge.py new file mode 100644 index 0000000..df3a39c --- /dev/null +++ b/platformbiz/recharge.py @@ -0,0 +1,51 @@ +from accounting.accounting_config import Accounting +from appPublic.registerfunction import rfexe +from appPublic.log import exception, debug +from sqlor.dbpools import DBPools +from pf_pay.ali_pay import Zhifubao_Pay +from platformbiz.getdbname import get_dbname +from platformbiz.biz_order import add_recharge_log, add_recharge_order + +class Recharge: + def __init__(self, customerid, userid, recharge_amt, pc_name): + self.customerid = customerid + self.userid = userid + self.recharge_amt = recharge_amt + self.pc_name = pc_name + if pc_name not in ['alipay']: + raise Exception(f'{pc_name} pay channel not implemented') + + async def start_recharge(self): + return await self.start_recharge_action('RECHARGE') + + + async def start_recharge_reverse(self): + return await self.start_recharge_action('RECHARGE_REVERSE') + + async def start_recharge_action(self, action): + db = DBPools() + dbname = get_dbname() + async with db.sqlorContext(dbname) as sor: + order = await add_recharge_order(sor, self.customerid, + self.userid, + action, + self.recharge_amt) + if order is None: + return None + rl = await add_recharge_log(sor, self.customerid, + self.userid, + action, + order.id, + order.order_date, + self.recharge_amt, + self.pc_name) + if self.pc_name == 'alipay': + z = Zhifubao_Pay() + url = await z.alipay_payment(rl.id, rl.recharge_amt, action) + return url + + exception(f'exception ...........{self.pc_name}') + raise Exception(f'{self.pc_name} pay channel not implemented') + exception('Exception happend ....') + + diff --git a/platformbiz/version.py b/platformbiz/version.py new file mode 100644 index 0000000..b8023d8 --- /dev/null +++ b/platformbiz/version.py @@ -0,0 +1 @@ +__version__ = '0.0.1' diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..59514a1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ +[build-system] +requires = ["setuptools>=61", "wheel"] +build-backend = "setuptools.build_meta" + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a745bff --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +# git+https://git.kaiyuancloud.cn/yumoqing/rbac +# git+https://git.kaiyuancloud.cn/yumoqing/accounting +# git+https://git.kaiyuancloud.cn/yumoqing/pf_pay diff --git a/script/roleperm.sh b/script/roleperm.sh new file mode 100755 index 0000000..c320114 --- /dev/null +++ b/script/roleperm.sh @@ -0,0 +1,8 @@ +#!/usr/bin/bash + +python ~/py/rbac/script/roleperm.py sage platformbiz provider sale retail_agree agreedetail agreedetailstep agreeproduct organization +python ~/py/rbac/script/roleperm.py sage platformbiz provider sale product prodpricing prodpricingtab +python ~/py/rbac/script/roleperm.py sage platformbiz reseller sale reseller retail_agree agreedetail agreedetailstep agreeproduct organization +python ~/py/rbac/script/roleperm.py sage platformbiz reseller operator provide_agree coupontype agreedetail agreedetailstep agreeproduct organization product prodpricing prodpricingtab coupontype coupon coupon_log recharge_log resource biz_order +python ~/py/rbac/script/roleperm.py sage platformbiz owner operator paychannel prodtype +python ~/py/rbac/script/roleperm.py sage platformbiz customer customer coupon coupon_log biz_order recharge_log resource diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..960f5e0 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,15 @@ +[metadata] +name=platformbiz +version = 0.0.1 +description = a platform business module +author = "yu moqing" +author_email = "yumoqing@gmail.com" +readme = "README.md" +license = "MIT" +[options] +packages = find: +requires_python = ">=3.8" +install_requires = + apppublic + sqlor + ahserver diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..9a4d22c --- /dev/null +++ b/setup.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +from platformbiz.version import __version__ +try: + from setuptools import setup +except ImportError: + from distutils.core import setup +required = [] +with open('requirements.txt', 'r') as f: + ls = f.read() + required = ls.split('\n') + +with open('platformbiz/version.py', 'r') as f: + x = f.read() + y = x[x.index("'")+1:] + z = y[:y.index("'")] + version = z +with open("README.md", "r") as fh: + long_description = fh.read() + +name = "platformbiz" +description = "platformbiz" +author = "yumoqing" +email = "yumoqing@gmail.com" + +package_data = {} + +setup( + name="platformbiz", + version=version, + + # uncomment the following lines if you fill them out in release.py + description=description, + author=author, + author_email=email, + platforms='any', + install_requires=required , + packages=[ + "platformbiz" + ], + package_data=package_data, + keywords = [ + ], + url="https://github.com/yumoqing/platformbiz", + long_description=long_description, + long_description_content_type="text/markdown", + classifiers = [ + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 3', + 'License :: OSI Approved :: MIT License', + ], +) diff --git a/tables.xlsx b/tables.xlsx new file mode 100644 index 0000000..ca35904 Binary files /dev/null and b/tables.xlsx differ diff --git a/test/test_get_price_infos.py b/test/test_get_price_infos.py new file mode 100644 index 0000000..8ee713c --- /dev/null +++ b/test/test_get_price_infos.py @@ -0,0 +1,26 @@ + +from testenv import runtest +from appPublic.dictObject import DictObject +from platformbiz.pricing import get_price_infos + +async def test(sor): + # prodid = 'a--akNeu1Ia-NOZAJAadf' + prodid = '1wleQ7o-RkrSbR-n368bO' + buyerid = 'yyoJwfBDsRiwN8-WFl2q1' + sellerid = '0' + spec_config = [ + DictObject(**{ + "spec_name":"input_tokens", + "count":12832 + }), + DictObject(**{ + "spec_name":"output_tokens", + "count":786323 + }) + ] + d = await get_price_infos(sor, sellerid, buyerid, prodid, spec_config) + print(f'{d=}') + +if __name__ == '__main__': + runtest(test) + diff --git a/test/test_open_agree_account.py b/test/test_open_agree_account.py new file mode 100644 index 0000000..b1a84a3 --- /dev/null +++ b/test/test_open_agree_account.py @@ -0,0 +1,12 @@ +from testenv import runtest +from appPublic.dictObject import DictObject +from platformbiz.product import open_agree_account + +async def test(sor): + orgid = 'ac4HnQ0txGqmbAFWpI3wp' + agreeid = 'NUvV1sP9TtAdnlxCISzjo' + await open_agree_account(sor, orgid, '0') + +if __name__ == '__main__': + runtest(test) + diff --git a/test/test_prodclone.py b/test/test_prodclone.py new file mode 100644 index 0000000..2b42274 --- /dev/null +++ b/test/test_prodclone.py @@ -0,0 +1,12 @@ +from testenv import runtest +from appPublic.dictObject import DictObject +from platformbiz.product import agree_products_clone + +async def test(sor): + orgid = 'ac4HnQ0txGqmbAFWpI3wp' + agreeid = 'NUvV1sP9TtAdnlxCISzjo' + await agree_products_clone(sor, orgid, agreeid) + +if __name__ == '__main__': + runtest(test) + diff --git a/test/test_prodpricing.py b/test/test_prodpricing.py new file mode 100644 index 0000000..d86b399 --- /dev/null +++ b/test/test_prodpricing.py @@ -0,0 +1,23 @@ +from testenv import runtest +from appPublic.dictObject import DictObject +from platformbiz.pricing import calc_prod_price + +async def test(sor): + # prodid = 'a--akNeu1Ia-NOZAJAadf' + prodid = 'MT-k34zPUAuWqgov4QCmG' + spec_config = [ + DictObject(**{ + "spec_name":"input_tokens", + "count":12832 + })#, + #DictObject(**{ + # "spec_name":"output_tokens", + # "count":786323 + #}) + ] + price = await calc_prod_price(sor, prodid, spec_config) + print(f'{prodid=}, {spec_config=}, {price=}') + +if __name__ == '__main__': + runtest(test) + diff --git a/test/testenv.py b/test/testenv.py new file mode 100644 index 0000000..b5aa057 --- /dev/null +++ b/test/testenv.py @@ -0,0 +1,23 @@ +import os +import asyncio +from appPublic.jsonConfig import getConfig +from sqlor.dbpools import DBPools +from appbase.init import load_appbase +from accounting.init import load_accounting +from platformbiz.init import load_platformbiz + +async def main(asyncfunc): + home = os.environ['HOME'] + p = f'{home}/py/sage' + config = getConfig(p, {'workdir':p}) + db = DBPools(config.databases) + load_appbase() + load_accounting() + load_platformbiz() + async with db.sqlorContext('sage') as sor: + await asyncfunc(sor) + +def runtest(asyncfunc): + asyncio.new_event_loop().run_until_complete(main(asyncfunc)) + + diff --git a/wwwroot/agree_prodclone.dspy b/wwwroot/agree_prodclone.dspy new file mode 100644 index 0000000..8020c2b --- /dev/null +++ b/wwwroot/agree_prodclone.dspy @@ -0,0 +1,9 @@ +debug(f'{params_kw=}') +orgid = await get_userorgid() +db = DBPools() +dbname = get_module_dbname('platformbiz') +async with db.sqlorContext(dbname) as sor: + await agree_products_clone(sor, orgid, params_kw.id) + return UiMessage(title='product clone', message='product clone finished') +debug('product clone error') +return UiError(title='product clone', message='product clone error') diff --git a/wwwroot/agree_productauth.dspy b/wwwroot/agree_productauth.dspy new file mode 100644 index 0000000..49b3ebf --- /dev/null +++ b/wwwroot/agree_productauth.dspy @@ -0,0 +1,7 @@ +debug(f'{params_kw=}') +db = DBPools() +dbname = await get_module_dbname('platformbiz') +async with db.sqlorContext(dbname) as sor: + await agreedetail_products_clone(params_kw.agreedetailid) + return UiMessage(title='clone product', message='OK') +return UiError(title='Product clone', message='Product clone error') diff --git a/wwwroot/agreeproduct_auth.dspy b/wwwroot/agreeproduct_auth.dspy new file mode 100644 index 0000000..4895ab1 --- /dev/null +++ b/wwwroot/agreeproduct_auth.dspy @@ -0,0 +1,7 @@ +debug(f'{params_kw=}') +db = DBPools() +dbname = await get_module_dbname('platformbiz') +async with db.sqlorContext(dbname) as sor: + await agreedetail_products_clone(sor, params_kw.agreedetailid) + return UiMessage(title='clone product', message='OK') +return UiError(title='Product clone', message='Product clone error') diff --git a/wwwroot/menu.ui b/wwwroot/menu.ui new file mode 100644 index 0000000..31a5224 --- /dev/null +++ b/wwwroot/menu.ui @@ -0,0 +1,27 @@ +{% set roles = get_user_roles(get_user()) %} +{ + "widgettype":"Menu", + "options":{ + "target":"page_center", + "cwidth":10, + "items":[ +{% if 'reseller.operator' in roles %} + { + "name":"provider", + "label":"供应商管理" + "url":"{{entire_url('/platformbiz/provider')}}" + } +{% endif %} +{% if 'reseller.sale' in roles %} + { + "name":"reseller", + "label":"分销商管理", + "url":"{{entire_url('/platformbiz/reseller'}}" + } +{% endif %} +{% if 'reseller.accountant' in roles %} +{% endif %} + {} + ] + } +} diff --git a/wwwroot/open_customer_accounts.dspy b/wwwroot/open_customer_accounts.dspy new file mode 100644 index 0000000..68a938b --- /dev/null +++ b/wwwroot/open_customer_accounts.dspy @@ -0,0 +1,18 @@ +username = params_kw.username +db = DBPools() +dbname = await rfexe('get_module_dbname', 'platformbiz') +async with db.sqlorContext(dbname) as sor: + if username: + sql = "select * from users where username = ${username}$" + recs = await sor.sqlExe(sql, {'username':username}) + if len(recs) > 0: + userorgid = recs[0].orgid + else: + e = Exception(f'{user}:user not found') + exception(f'Eeception:{e}') + raise e + else: + userorgid = await get_userorgid() + await openCustomerAccounts(sor, '0', userorgid) + return "开帐成功" +return "开帐失败" diff --git a/wwwroot/open_owner_accounts.dspy b/wwwroot/open_owner_accounts.dspy new file mode 100644 index 0000000..15b892b --- /dev/null +++ b/wwwroot/open_owner_accounts.dspy @@ -0,0 +1,6 @@ +db = DBPools() +dbname = await rfexe('get_module_dbname', 'accounting') +async with db.sqlorContext(dbname) as sor: + await openOwnerAccounts(sor, '0') + return 'OK' +return 'error' diff --git a/wwwroot/owner.operator.menu.ui b/wwwroot/owner.operator.menu.ui new file mode 100644 index 0000000..1529b87 --- /dev/null +++ b/wwwroot/owner.operator.menu.ui @@ -0,0 +1,33 @@ +[ + { + "name":"agree", + "label":"供应商管理", + "url":"{{entire_url('/platformbiz/provider')}}" + }, + { + "name":"paychannel", + "label":"支付渠道", + "url":"{{entire_url('paychannel')}}" + }, + { + "name":"prodtype", + "label":"产品类型", + "url":"{{entire_url('/platformbiz/prodtype')}}" + }, + { + "name":"recharge", + "label":"充值", + "items":[ + { + "name":"recharge_log", + "label":"充值日志" + "url":"{{entire_url('/platformbiz/recharge_log')}}" + }, + { + "name":"recharge_4u", + "label":"充值错账处理", + "url":"{{entire_url('/platformbiz/rechange_accounting.ui')}}" + } + ] + } +] diff --git a/wwwroot/recharge.dspy b/wwwroot/recharge.dspy new file mode 100644 index 0000000..e50ad3f --- /dev/null +++ b/wwwroot/recharge.dspy @@ -0,0 +1,17 @@ +debug(f'{params_kw=}') +try: + userid = await get_user() + userorgid = await get_userorgid() + r = Recharge(userorgid, userid, float(params_kw.recharge_amt), params_kw.channel) + url = await r.start_recharge() + return { + "widgettype":"NewWindow", + "options":{ + "url":url + } + } +except Exception as e: + es = format_exc() + exception(f'{e=}, {es}') + return UiError(title='Error', message=f'Error:{e},trackback={es}') + diff --git a/wwwroot/recharge.ui b/wwwroot/recharge.ui new file mode 100644 index 0000000..0623ea3 --- /dev/null +++ b/wwwroot/recharge.ui @@ -0,0 +1,38 @@ +{ + "widgettype":"Form", + "options":{ + "fields":[ + { + "name":"recharge_amt", + "uitype":"float", + "required":true, + "label":"充值金额" + }, + { + "name":"channel", + "label":"选择充值方式", + "uitype":"checkbox", + "multicheck":false, + "required":true, + "value":"alipay", + "data":[ + { + "value":"alipay", + "text":"支付宝" + } + ] + } + ] + }, + "binds":[ + { + "wid":"self", + "event":"submit", + "actiontype":"urlwidget", + "target":"self", + "options":{ + "url":"{{entire_url('recharge.dspy')}}" + } + } + ] +} diff --git a/wwwroot/reseller.operator.menu.ui b/wwwroot/reseller.operator.menu.ui new file mode 100644 index 0000000..50998f2 --- /dev/null +++ b/wwwroot/reseller.operator.menu.ui @@ -0,0 +1,30 @@ +[ + { + "name":"product", + "label":"产品管理", + "url":"{{entire_url('/platformbiz/product')}}" + }, + { + "name":"coupon", + "label":"代金券", + "items":[ + { + "name":"coupontype", + "label":"代金券设置" + "url":"{{entire_url('/platformbiz/coupontype')}}" + }, + { + "name":"coupon_issue", + "label":"代金券发放", + "url":"{{entire_url('/platformbiz/coupon_issue.ui')}}" + }, + { + "name":"coupon", + "label":"代金券管理", + "url":"{{entire_url('/platformbiz/coupon')}}" + } + ] + } +] + +