first commit
This commit is contained in:
commit
e36c1ec5be
123
README.md
Normal file
123
README.md
Normal file
@ -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
|
||||
|
||||
#### 参与商户营销
|
||||
##### 商户获客营销
|
||||
##### 参与售卖营销
|
||||
|
||||
### 商户机构功能
|
||||
|
||||
#### 成为商户
|
||||
在客户用户界面点击【成为商户】,系统自动将此平台客户添加商户角色,成为平台商户
|
||||
并为其开通商户经营所需的记账账号。
|
||||
与平台或平台其他商户(供应商)线下协商成为其产品的分销商
|
||||
|
||||
#### 添加不同角色的用户
|
||||
|
||||
#### 发展分销商户(自己成为分销商户的供应商)
|
||||
新添一个分销协议,在协议中添加协议明细,设置分销折扣,或阶梯折扣,将自己的产品授权给分销商销售。
|
||||
##### 供应商添加分销协议
|
||||
##### 供应商添加协议明细
|
||||
##### 供应商添加协议明细步骤(只有阶梯折扣协议才需要)
|
||||
##### 供应商添加分销产品到分销协议
|
||||
##### 商户设置一个无指定客户的分销协议
|
||||
#### 自有产品管理
|
||||
##### 添加自由产品
|
||||
##### 自有产品区域管理
|
||||
##### 自有产品数量管理
|
||||
##### 自有产品计价管理
|
||||
##### 自有产品计价表管理
|
||||
|
||||
#### 设置产品销售折扣或价格
|
||||
##### 公用产品折扣设置
|
||||
为所有客户提供一个以产品计价为基础的折扣
|
||||
##### 为指定客户设置特殊折扣
|
||||
|
||||
#### 主推产品管理
|
||||
#### 大屏功能
|
||||
##### 展示位置设置
|
||||
##### 展示指标管理
|
||||
##### 大屏风格设置
|
||||
|
||||
#### 短信管理
|
||||
##### 开通短信功能
|
||||
##### 通知场景管理
|
||||
##### 短信计费
|
||||
|
||||
### 产品购买
|
||||
#### 标的
|
||||
卖房提供的产品
|
||||
#### 参与方
|
||||
* 卖房
|
||||
* 买房
|
||||
* 平台
|
||||
* 供应方
|
||||
#### 分录设置
|
||||
借:买方-
|
||||
23
json/agreedetail.json
Normal file
23
json/agreedetail.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
12
json/agreedetailstep.json
Normal file
12
json/agreedetailstep.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tblname":"agreedetailstep",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
32
json/agreement.json
Normal file
32
json/agreement.json
Normal file
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
13
json/agreeproduct.json
Normal file
13
json/agreeproduct.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"tblname":"agreeproduct",
|
||||
"params":{
|
||||
"logined_userorgid":"orgid",
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "agreedetailid"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id", "agreedetailid", "resellerpid"
|
||||
]
|
||||
}
|
||||
}
|
||||
12
json/biz_order.json
Normal file
12
json/biz_order.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tblname":"biz_order",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
3
json/build.sh
Executable file
3
json/build.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
xls2ui -m ../models -o ../wwwroot platformbiz *.json
|
||||
40
json/choose_prodtype.json
Normal file
40
json/choose_prodtype.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
12
json/coupon.json
Normal file
12
json/coupon.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tblname":"coupon",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
12
json/coupon_log.json
Normal file
12
json/coupon_log.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tblname":"coupon_log",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
12
json/coupontype.json
Normal file
12
json/coupontype.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tblname":"coupontype",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
12
json/paychannel.json
Normal file
12
json/paychannel.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tblname":"paychannel",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
20
json/prodpricing.json
Normal file
20
json/prodpricing.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"tblname":"prodpricing",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "prodid" ],
|
||||
"alters": {
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id", "prodid"
|
||||
],
|
||||
"subtables":[
|
||||
{
|
||||
"field":"prodpricingid",
|
||||
"subtable":"prodpricingtab",
|
||||
"title":"产品定价表"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
12
json/prodpricingtab.json
Normal file
12
json/prodpricingtab.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tblname":"prodpricingtab",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "prodid"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id", "prodid"
|
||||
]
|
||||
}
|
||||
}
|
||||
24
json/prodtype.json
Normal file
24
json/prodtype.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
15
json/prodtypespec.json
Normal file
15
json/prodtypespec.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"tblname":"prodtypespec",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "prodtypeid"],
|
||||
"alters": {}
|
||||
},
|
||||
"data_params":{
|
||||
"prodtypeid":"${params_kw.id}"
|
||||
},
|
||||
"editexclouded": [
|
||||
"id", "prodtypeid"
|
||||
]
|
||||
}
|
||||
}
|
||||
24
json/product.json
Normal file
24
json/product.json
Normal file
@ -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":"产品定价"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
30
json/provide_agree.json
Normal file
30
json/provide_agree.json
Normal file
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
24
json/provide_agreedetail.json
Normal file
24
json/provide_agreedetail.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
14
json/provide_agreeproduct.json
Normal file
14
json/provide_agreeproduct.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"tblname":"agreeproduct",
|
||||
"alias":"provide_agreeproduct",
|
||||
"params":{
|
||||
"logined_userorgid":"orgid",
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "agreedetailid", "resellerpid"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id", "agreedetailid", "resellerpid"
|
||||
]
|
||||
}
|
||||
}
|
||||
12
json/recgarge_log.json
Normal file
12
json/recgarge_log.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tblname":"recharge_log",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
12
json/resource.json
Normal file
12
json/resource.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tblname":"resource",
|
||||
"params":{
|
||||
"browserfields": {
|
||||
"exclouded": ["id"],
|
||||
"alters": {}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
25
json/retail_agree.json
Normal file
25
json/retail_agree.json
Normal file
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
models/agreedetail.xlsx
Normal file
BIN
models/agreedetail.xlsx
Normal file
Binary file not shown.
BIN
models/agreedetailstep.xlsx
Normal file
BIN
models/agreedetailstep.xlsx
Normal file
Binary file not shown.
BIN
models/agreement.xlsx
Normal file
BIN
models/agreement.xlsx
Normal file
Binary file not shown.
BIN
models/agreeproduct.xlsx
Normal file
BIN
models/agreeproduct.xlsx
Normal file
Binary file not shown.
BIN
models/biz_order.xlsx
Normal file
BIN
models/biz_order.xlsx
Normal file
Binary file not shown.
BIN
models/biz_orderdetail.xlsx
Normal file
BIN
models/biz_orderdetail.xlsx
Normal file
Binary file not shown.
BIN
models/coupon.xlsx
Normal file
BIN
models/coupon.xlsx
Normal file
Binary file not shown.
BIN
models/coupon_log.xlsx
Normal file
BIN
models/coupon_log.xlsx
Normal file
Binary file not shown.
BIN
models/coupontype.xlsx
Normal file
BIN
models/coupontype.xlsx
Normal file
Binary file not shown.
BIN
models/distributionagreement.xlsx
Normal file
BIN
models/distributionagreement.xlsx
Normal file
Binary file not shown.
BIN
models/paychannel.xlsx
Normal file
BIN
models/paychannel.xlsx
Normal file
Binary file not shown.
BIN
models/pr_link.xlsx
Normal file
BIN
models/pr_link.xlsx
Normal file
Binary file not shown.
BIN
models/prodpricing.xlsx
Normal file
BIN
models/prodpricing.xlsx
Normal file
Binary file not shown.
BIN
models/prodpricingtab.xlsx
Normal file
BIN
models/prodpricingtab.xlsx
Normal file
Binary file not shown.
BIN
models/prodtype.xlsx
Normal file
BIN
models/prodtype.xlsx
Normal file
Binary file not shown.
BIN
models/prodtypespec.xlsx
Normal file
BIN
models/prodtypespec.xlsx
Normal file
Binary file not shown.
BIN
models/product.xlsx
Normal file
BIN
models/product.xlsx
Normal file
Binary file not shown.
BIN
models/recharge_log.xlsx
Normal file
BIN
models/recharge_log.xlsx
Normal file
Binary file not shown.
BIN
models/reseller.xlsx
Normal file
BIN
models/reseller.xlsx
Normal file
Binary file not shown.
BIN
models/resource.xlsx
Normal file
BIN
models/resource.xlsx
Normal file
Binary file not shown.
10
platformbiz/biz.py
Normal file
10
platformbiz/biz.py
Normal file
@ -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)
|
||||
:
|
||||
121
platformbiz/biz_order.py
Normal file
121
platformbiz/biz_order.py
Normal file
@ -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)
|
||||
|
||||
2
platformbiz/const.py
Normal file
2
platformbiz/const.py
Normal file
@ -0,0 +1,2 @@
|
||||
ORDER_INITIAL = '0'
|
||||
RECHARGE_INITIAL = '0'
|
||||
8
platformbiz/getdbname.py
Normal file
8
platformbiz/getdbname.py
Normal file
@ -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')
|
||||
|
||||
20
platformbiz/init.py
Normal file
20
platformbiz/init.py
Normal file
@ -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
|
||||
|
||||
111
platformbiz/pb_acc.py
Normal file
111
platformbiz/pb_acc.py
Normal file
@ -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)
|
||||
|
||||
273
platformbiz/pricing.py
Normal file
273
platformbiz/pricing.py
Normal file
@ -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
|
||||
|
||||
59
platformbiz/product.py
Normal file
59
platformbiz/product.py
Normal file
@ -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)
|
||||
|
||||
51
platformbiz/recharge.py
Normal file
51
platformbiz/recharge.py
Normal file
@ -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 ....')
|
||||
|
||||
|
||||
1
platformbiz/version.py
Normal file
1
platformbiz/version.py
Normal file
@ -0,0 +1 @@
|
||||
__version__ = '0.0.1'
|
||||
4
pyproject.toml
Normal file
4
pyproject.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@ -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
|
||||
8
script/roleperm.sh
Executable file
8
script/roleperm.sh
Executable file
@ -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
|
||||
15
setup.cfg
Normal file
15
setup.cfg
Normal file
@ -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
|
||||
52
setup.py
Executable file
52
setup.py
Executable file
@ -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',
|
||||
],
|
||||
)
|
||||
BIN
tables.xlsx
Normal file
BIN
tables.xlsx
Normal file
Binary file not shown.
26
test/test_get_price_infos.py
Normal file
26
test/test_get_price_infos.py
Normal file
@ -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)
|
||||
|
||||
12
test/test_open_agree_account.py
Normal file
12
test/test_open_agree_account.py
Normal file
@ -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)
|
||||
|
||||
12
test/test_prodclone.py
Normal file
12
test/test_prodclone.py
Normal file
@ -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)
|
||||
|
||||
23
test/test_prodpricing.py
Normal file
23
test/test_prodpricing.py
Normal file
@ -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)
|
||||
|
||||
23
test/testenv.py
Normal file
23
test/testenv.py
Normal file
@ -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))
|
||||
|
||||
|
||||
9
wwwroot/agree_prodclone.dspy
Normal file
9
wwwroot/agree_prodclone.dspy
Normal file
@ -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')
|
||||
7
wwwroot/agree_productauth.dspy
Normal file
7
wwwroot/agree_productauth.dspy
Normal file
@ -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')
|
||||
7
wwwroot/agreeproduct_auth.dspy
Normal file
7
wwwroot/agreeproduct_auth.dspy
Normal file
@ -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')
|
||||
27
wwwroot/menu.ui
Normal file
27
wwwroot/menu.ui
Normal file
@ -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 %}
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
18
wwwroot/open_customer_accounts.dspy
Normal file
18
wwwroot/open_customer_accounts.dspy
Normal file
@ -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 "开帐失败"
|
||||
6
wwwroot/open_owner_accounts.dspy
Normal file
6
wwwroot/open_owner_accounts.dspy
Normal file
@ -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'
|
||||
33
wwwroot/owner.operator.menu.ui
Normal file
33
wwwroot/owner.operator.menu.ui
Normal file
@ -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')}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
17
wwwroot/recharge.dspy
Normal file
17
wwwroot/recharge.dspy
Normal file
@ -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}')
|
||||
|
||||
38
wwwroot/recharge.ui
Normal file
38
wwwroot/recharge.ui
Normal file
@ -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')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
30
wwwroot/reseller.operator.menu.ui
Normal file
30
wwwroot/reseller.operator.menu.ui
Normal file
@ -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')}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user