Commit 6be63e92 by wuyang.zou

fix refund order analiyseRefundOrderTask

Version: 2.2022.8.1
parent a76fe5b1
Pipeline #41355 failed with stage
in 0 seconds
......@@ -800,13 +800,12 @@ void FlowControl::_ScanDelayGetRefundDetailList() {
void FlowControl::_RemindCasherBlinkFloatForm()
{
QLOG_INFO()<<QString::fromLocal8Bit("[<<<<---_RemindCasherBlinkFloatForm: m_simValidOrdersList.isEmpty()==%1--->>>>>]")
.arg(m_simValidOrdersList.isEmpty());
if(!m_simValidOrdersList.isEmpty())
{
m_remindCasherBlinkFloatTimer->start(1000*8);
emit startRemind(REMIND_SIM_ORDERLIST_NOEMPTY);
}else{
QLOG_INFO()<<QString::fromLocal8Bit("[<<<<---_RemindCasherBlinkFloatForm: m_simValidOrdersList.size()==%1--->>>>>]").arg( m_simValidOrdersList.size() );
} else {
m_remindCasherBlinkFloatTimer->start(1000*20);
}
}
......@@ -1027,46 +1026,56 @@ void FlowControl::_OrderAnalysis(const QJsonObject& jsonObject)
break;
case OrderObject::AgreeRefund:
break;
case OrderObject::Refunded: {
//退单目前本佳那边不区分 OrderObject::Refunded=6 && OrderObject::Cancled =6
//case:同意退单、退单、取消订单: 此退单还没push到POS[POS没有确认退单或没有返回确认退单的小票号]
case OrderObject::Refunded:
_analysisRefundOrderTask( orderObject );
break;
default:
break;
}
}
void FlowControl::_analysisRefundOrderTask(OrderObject* orderObject) {
if ( !orderObject ) {
QLOG_ERROR() << QString("[<<<<--- _analysisRefundOrderTask:: orderObject Pointer Is NULL --->>>>]");
return;
}
bool bRetStorageOrderMutex = false;
//内存逻辑BUG:重新获取数据库中订单数据标记来判断;
//插件程序内存中没有此订单时需要从数据库中获取该订单标志数据;同时在接受到退单情况时,也需要从数据库中重新获取(防止内存中标志数据未更新[入机后未更新]);
bool retRefundedGetDbOrderAllFlag = SimProcOrderDB::getInstance().getDbOrderAllFlag(orderObject->id,orderObject->dataBaseIsOrderExistInt,orderObject->dataBaseIsPushInt,
orderObject->dataBaseIsCancleInt,orderObject->dataBaseIsORSPushInt,orderObject->dataBaseIsORSCancleInt,orderObject->dataBasePosOrderCheckNo);
if(!retRefundedGetDbOrderAllFlag ){
QLOG_ERROR() << QString("[<<<<---_OrderAnalysis: Refund Order SimProcOrderDB::getInstance().getDbOrderAllFlag Failed--->>>>]");
QLOG_ERROR() << QString("[<<<<---_analysisRefundOrderTask: Refund Order SimProcOrderDB::getInstance().getDbOrderAllFlag Failed--->>>>]");
return;
}
QLOG_INFO() << QString("[<<<<---_OrderAnalysis: Refund Order orderObject->id:%1 ,dataBaseIsOrderExistInt:%2 ,dataBaseIsPushInt:%3 ,dataBaseIsCancleInt:%4 ,"
QLOG_INFO() << QString("[<<<<---_analysisRefundOrderTask: Refund Order orderObject->id:%1 ,dataBaseIsOrderExistInt:%2 ,dataBaseIsPushInt:%3 ,dataBaseIsCancleInt:%4 ,"
"dataBaseIsORSPushInt:%5 ,dataBaseIsORSCancleInt:%6 ,dataBasePosOrderCheckNo:%7,negativePosStatus:%8--->>>>]")
.arg(orderObject->id).arg(orderObject->dataBaseIsOrderExistInt).arg(orderObject->dataBaseIsPushInt).arg(orderObject->dataBaseIsCancleInt)
.arg(orderObject->dataBaseIsORSPushInt).arg(orderObject->dataBaseIsORSCancleInt).arg(orderObject->dataBasePosOrderCheckNo).arg(orderObject->negativePosStatus);
bool bIsRefundOrder = orderObject->dataBaseIsCancleInt;
//bool bIsORSCancleOrder = orderObject->dataBaseIsORSCancleInt;
//存在两种Case:正常退货单没有入机成功 && 非预约日期(录配送费判断)/默认值是初始化0; || 预约实物退货单没有入机 && 预约实物送达日期
//if( (!bIsRefundOrder && !orderObject->appointmentDayFlag) || (!bIsORSCancleOrder && orderObject->appointmentDayFlag) )
//为避免换系统盘导致订单重复入机,顾需要额外使用 OMS提供的订单是否入机状态进行过滤;
if( (!bIsRefundOrder) && orderObject->negativePosStatus!=1 )
if( !orderObject->dataBaseIsCancleInt && orderObject->negativePosStatus!=1 )
{
//case:校验此订单是否已经push到POS:订单还没push到POS[POS没有模拟点单或没有返回小票号]
//内存中处理存在BUG:现在改用 直接读数据库中的标志;
//存在两种Case:正常销售单没有入机成功 && 非预约日期(录配送费判断)/默认值是初始化0; || 预约实物销售单没有入机 && 预约实物送达日期
//if( (!orderObject->dataBaseIsPushInt && !orderObject->appointmentDayFlag) || (!dataBaseIsORSPushInt && orderObject->appointmentDayFlag) )
if(!orderObject->dataBaseIsPushInt) {
orderObject->pushOrderType = 0; //标记通常订单-冲正销售单待入机状态;
orderObject->pushOrderTimes = 0;
// 仅针对::①解耦2.0订单 且 ② negativePosStatus == 3 情况订单, 需要跳过解耦2.0订单的本地持久化操作【POS机重新安装】;
if ( 3 == orderObject->negativePosStatus && orderObject->decouplePosVersion >= 20 ) {
QLOG_INFO() << QString("[<<<<--- _OrderAnalysis:: orderId: %1 Had Storaged Order To LocalHost Successed, Not Need Storage Again --->>>>]").arg(orderObject->id);
QLOG_INFO() << QString("[<<<<--- _analysisRefundOrderTask:: orderId: %1 Had Storaged Order To LocalHost Successed, Not Need Storage Again --->>>>]").arg(orderObject->id);
return;
}
bRetStorageOrderMutex = m_storageOrderMutex.tryLock(VALUE_TRYLOCKMEMTIMEOUT);
if ( !bRetStorageOrderMutex ) {
QLOG_INFO() << QString("[<<<<---_OrderAnalysis:orderId: %1 m_storageOrderMutex.tryLock Failed --->>>>]").arg(orderObject->id);
QLOG_INFO() << QString("[<<<<---_analysisRefundOrderTask:: orderId: %1 m_storageOrderMutex.tryLock Failed --->>>>]").arg(orderObject->id);
return;
}
m_storagePosOrdersList.removeOne(orderObject->id);
......@@ -1074,8 +1083,7 @@ void FlowControl::_OrderAnalysis(const QJsonObject& jsonObject)
if ( m_simValidOrdersList.contains( orderObject->id ) ) {
// 订单取消, 且销售单在等待录单,此时剔除 不是正在录单队列中的此笔订单;
if ( orderObject->id != m_simValidOrdersList.first() ||
( orderObject->id == m_simValidOrdersList.first() && !m_bFloatFromLockSt ) ) {
if ( orderObject->id != m_simValidOrdersList.first() || ( orderObject->id == m_simValidOrdersList.first() && !m_bFloatFromLockSt ) ) {
m_OrderEntryMutex.lock();
m_simValidOrdersList.removeOne( orderObject->id );
m_OrderEntryMutex.unlock();
......@@ -1087,8 +1095,7 @@ void FlowControl::_OrderAnalysis(const QJsonObject& jsonObject)
}
}
}
//case:校验此订单是否已经push到POS:订单已经push到POS
} //case:校验此订单是否已经push到POS:订单已经push到POS
else {
// case:校验此订单push到POS后的小票号是否空:[POS已模拟点单且返回小票号]
// 为了兼容解耦Pos版本的过度: 不入机类型的订单也会存储订单号后5位+销售退货标识 【详情界面显示时排除销售退货标识】
......@@ -1103,7 +1110,7 @@ void FlowControl::_OrderAnalysis(const QJsonObject& jsonObject)
if ( !m_storagePosOrdersList.contains(orderObject->id) ) {
bRetStorageOrderMutex = m_storageOrderMutex.tryLock(VALUE_TRYLOCKMEMTIMEOUT);
if ( !bRetStorageOrderMutex ) {
QLOG_INFO() << QString("[<<<<---_OrderAnalysis:orderId:%1 m_storageOrderMutex.tryLock Failed --->>>>]").arg(orderObject->id);
QLOG_INFO() << QString("[<<<<---_analysisRefundOrderTask::orderId:%1 m_storageOrderMutex.tryLock Failed --->>>>]").arg(orderObject->id);
return;
}
// 退货单: 存在待入机队列则不添加存储队列,不存在则添加到存储队列;
......@@ -1112,22 +1119,16 @@ void FlowControl::_OrderAnalysis(const QJsonObject& jsonObject)
}
m_storageOrderMutex.unlock();
}
QLOG_INFO() << QString("[<<<<---_OrderAnalysis::RefundOrder m_storagePosOrdersList.first: %1 m_storagePosOrdersList.count: %2 "
QLOG_INFO() << QString("[<<<<---_analysisRefundOrderTask:: m_storagePosOrdersList.first: %1 m_storagePosOrdersList.count: %2 "
" m_simValidOrdersList.first:%3 m_simValidOrdersList.count: %4 --->>>>]")
.arg( m_storagePosOrdersList.isEmpty()?"":m_storagePosOrdersList.first() ).arg( m_storagePosOrdersList.count() )
.arg( m_simValidOrdersList.isEmpty()?"":m_simValidOrdersList.first() ).arg( m_simValidOrdersList.count() );
// 先排队把订单数据存储POS本地数据库, 然后在排队模拟录单;
} /***case:校验此订单push到POS后的小票号是否空:[POS已模拟点单但未返回小票号]***/
}
//else{未处理}
}
}
}
break;
default:
break;
}
}
void FlowControl::onGetNewStoreInfo()
......@@ -1336,27 +1337,29 @@ bool FlowControl::_Login()
void FlowControl::_PullOrderData()
{
QLOG_INFO()<<"[<<<<----FlowControl::_PullOrderData ---->>>>>]";
if(m_orderPullList.isEmpty())
return;
emit hideAlert();
QLOG_INFO()<<QString("[<<<<----_PullOrderData ListSize=%1, FirstOne: %2 ---->>>>>]").arg( m_orderPullList.size() ).arg( m_orderPullList.size()?m_orderPullList.first()->orderId:"" );
bool btryLock = m_PullOrderDataMutex.tryLock(VALUE_TRYLOCKNETTIMEOUT);
if (!btryLock) {
QLOG_ERROR()<<"[<<<<----FlowControl::_PullOrderData Try Lock Failed ---->>>>>]";
QLOG_ERROR()<<"[<<<<----_PullOrderData Try Lock Failed ---->>>>>]";
return;
}
PullOrderInfo* info = m_orderPullList.takeFirst();
if ( info->pullErrorCount > 2 ) {
QLOG_INFO()<<QString( "[<<<<----FlowControl::_PullOrderData orderId:%1,Channel:%2,PullErrorCount:%3, CallBackUrl:%4, Pull Faild More Than 3 Times, Abandon This Item ---->>>>>]" )
QLOG_INFO()<<QString( "[<<<<----_PullOrderData orderId:%1,Channel:%2,PullErrorCount:%3, CallBackUrl:%4, Pull Faild More Than 3 Times, Abandon This Item ---->>>>>]" )
.arg(info->orderId).arg(info->channel).arg(info->pullErrorCount).arg(info->callBackUrl);
m_orderPullList.removeOne(info);
// 获取 o2ofulfillment 订单列表数据失败3次,之后丢弃 此拉单列表请求 item, 不切换域名重试获取;
if ( info->orderId.isEmpty() && info->callBackUrl.contains("o2ofulfillment") ) {
QLOG_ERROR() << QString("[<<<<---FlowControl::_PullOrderData <o2ofulfillment> Error, And Don't Display Error--->>>>]");
QLOG_ERROR() << QString("[<<<<---_PullOrderData <o2ofulfillment> Error, And Don't Display Error--->>>>]");
delete info;
m_PullOrderDataMutex.unlock();
return;
......@@ -1377,22 +1380,22 @@ void FlowControl::_PullOrderData()
if(info->orderId.isEmpty()) {
if ( _PullOrderList( info->pageSize, info->pageNumber, info->callBackUrl ) ) {
QLOG_INFO()<<"[<<<<----FlowControl::_PullOrderData _PullOrderList successful---->>>>>]";
QLOG_INFO()<<"[<<<<----_PullOrderData _PullOrderList successful---->>>>>]";
m_orderPullList.removeOne(info);
delete info;
} else {
QLOG_INFO()<<"[<<<<----FlowControl::_PullOrderData _PullOrderList failed---->>>>>]";
QLOG_INFO()<<"[<<<<----_PullOrderData _PullOrderList failed---->>>>>]";
m_orderPullList.removeOne(info);
info->pullErrorCount++;
m_orderPullList.append(info);
}
} else {
if ( _PullOrderDetail( info->orderId, info->channel, info->callBackUrl, info->skipPrint ) ) {
QLOG_INFO()<<"[<<<<----FlowControl::_PullOrderData _PullOrderDetail successful---->>>>>]";
QLOG_INFO()<<"[<<<<----_PullOrderData _PullOrderDetail successful---->>>>>]";
m_orderPullList.removeOne(info);
delete info;
} else {
QLOG_INFO()<<"[<<<<----FlowControl::_PullOrderData _PullOrderDetail fail---->>>>>]";
QLOG_INFO()<<"[<<<<----_PullOrderData _PullOrderDetail fail---->>>>>]";
m_orderPullList.removeOne(info);
info->pullErrorCount++;
m_orderPullList.append(info);
......@@ -1403,6 +1406,7 @@ void FlowControl::_PullOrderData()
return ;
}
bool FlowControl::_PullOrderDetail(const QString& orderId, const QString& channel, const QString &callBackUrl, const bool& skipPrint, const bool &skipErrorDisplay)
{
if(m_pullOrderSocket==NULL)
......@@ -1414,9 +1418,6 @@ bool FlowControl::_PullOrderDetail(const QString& orderId, const QString& channe
if(ConfigManger::GetInstance().GetOrderSslConfig()) {
m_pullOrderSocket->SetSslConfig();
QLOG_INFO()<<"FlowControl::_PullOrderDetail load ssl";
} else {
QLOG_INFO()<<"FlowControl::_PullOrderDetail load not ssl";
}
if ( callBackUrl.length() ) {
......@@ -1446,7 +1447,7 @@ bool FlowControl::_PullOrderDetail(const QString& orderId, const QString& channe
emit setNetStatus(QString::fromLocal8Bit("正常"));
if(JSON_STATUSCODE_OK != recvJson[JSON_ERRCODE].toInt())
{
QString error = recvJson[JSON_ERRMSG].toString();
error = recvJson[JSON_ERRMSG].toString();
QLOG_ERROR() << QString("[<<<<---Pull Order Detail Error--->>>>][msg->%1]").arg(error);
if ( !skipErrorDisplay ) {
emit showAlert(AlertForm::MSGERROR, QString::fromLocal8Bit("获取订单失败![%1]").arg(error));
......@@ -1457,6 +1458,11 @@ bool FlowControl::_PullOrderDetail(const QString& orderId, const QString& channe
// 服务端返回 code=100 , 但订单体中无订单详情数据;
if ( !order.contains( JSON_ID ) ) {
QLOG_ERROR()<<QString("[<<<<---Pull Order Detail Failed, OrderId= %1, Channel= %2 Code= 100, But 'data' Is Empty --->>>>]").arg( orderId ).arg( channel );
error = QString(" OrderId=%1, Code=100, But 'data' Is Empty ");
result = false;
if ( !skipErrorDisplay ) {
emit showAlert(AlertForm::MSGERROR, QString::fromLocal8Bit("获取订单失败![%1]").arg( error ) );
}
// 此种情况: 肯定服务端服务异常, 即使 result = false,下次拉单, 依旧还会失败, 顾再次放弃此订单详情的继续拉取;
} else {
if ( !order.contains( JSON_CALL_BACK_URL ) ) {
......@@ -1482,6 +1488,7 @@ bool FlowControl::_PullOrderDetail(const QString& orderId, const QString& channe
return result;
}
bool FlowControl::_PullOrderList( const int &pageSize, const int &pageNo, const QString &callBackUrl )
{
if(m_pullOrderListSocket==NULL) {
......@@ -1702,7 +1709,7 @@ bool FlowControl::_RefundOrder(const QString &orderId,int reasonCode,const QStri
QString error;
QString url;
QString callBackUrl = "";
bool result;
bool result = false;
QJsonObject sendJson;
QJsonObject recvJson;
sendJson = DataManger::GetInstance().GetRefundOrderData(reasonCode,reason,orderId,m_FmOrdersMap.value(orderId)->channel, dishesListString, m_cashierId, m_cashierName);
......@@ -1731,25 +1738,24 @@ bool FlowControl::_RefundOrder(const QString &orderId,int reasonCode,const QStri
if ( !result ) {
emit showAlert(AlertForm::MSGERROR, QString::fromLocal8Bit("退单失败![网络错误]"));
return false;
} else {
if(JSON_STATUSCODE_OK != recvJson[JSON_ERRCODE].toInt())
if ( JSON_STATUSCODE_OK != recvJson[JSON_ERRCODE].toInt() )
{
result = false;
emit showAlert(AlertForm::MSGERROR, QString::fromLocal8Bit("退单失败![%1]").arg(recvJson[JSON_MESSAGE].toString()));
return false;
} else {
QJsonObject data = recvJson[JSON_DATA].toObject();
int status = data[JSON_RECEIVINGSTATUS].toInt();
if(1==status) {
if ( 1==status ) {
emit showAlert(AlertForm::SUCCESS, QString::fromLocal8Bit("退单成功,稍后入机"));
//m_OrderEntryMutex.lock(); //必须提前加锁,否则后期订单ID会变更;
//获取POS退单成功,OMS服务端返回的RefundFmId ( 此字段只有星巴克APP SVC卡支付的case,才会使用到 )
QString refundFmIdTemp = data[JSON_REFUNDFMID].toString();
int orderStatus = data[JSON_ORDERSTATUS].toInt();
int tempGlobalOrderType = data[JSON_GLOBALORDERTYPE].toInt();
int tempGlobalServiceType = data[JSON_GLOBALSERVICETYPE].toInt();
int tempRefundMethod = data[JSON_GLOBALREFUNDMETHOD].toInt(); // 1: srkit-全退 2: srkit-部分退
int tempRefundMethod = 0;
if ( data.contains(JSON_GLOBALREFUNDMETHOD) )
tempRefundMethod = data[JSON_GLOBALREFUNDMETHOD].toInt(); // 1: srkit-全退 2: srkit-部分退
OrderObject* orderObject = m_FmOrdersMap.value(orderId);
int oldOrderStatus = orderObject->orderStatus;
......@@ -1757,25 +1763,20 @@ bool FlowControl::_RefundOrder(const QString &orderId,int reasonCode,const QStri
orderObject->refundFmId = refundFmIdTemp;
orderObject->globalOrderType = tempGlobalOrderType;
orderObject->globalServiceType = tempGlobalServiceType;
// 避免订单正在尝试录入之前的销售单; 清理 待入机队列 和 待存储订单数据队列;
// 避免订单正在尝试录入之前的销售单;
orderObject->pushOrderType = 0;
orderObject->pushOrderTimes = 0;
orderObject->orsPushOrderType = 0;
orderObject->orsPushOrderTimes = 0;
if (m_simValidOrdersList.contains(orderObject->id) ) {
m_simValidOrdersList.removeOne(orderObject->id);
}
if (m_storagePosOrdersList.contains(orderObject->id) ) {
m_storagePosOrdersList.removeOne(orderObject->id);
}
emit changeOrderStatus(orderObject, oldOrderStatus);
// 部分退场景, 不能提前进入入机队列, 需等待上游更新订单数据; 同步退插件存在小概率崩溃可能, 顾需异步入机; // refundMethod: [2: srkit-部分退];
// 不立刻入机等待拉取订单详情后再如入机;
// 不立刻入机等待拉取订单详情后再如入机(插件上退单后,OMS 不推送 fulfillment 服务更新订单数据 );
QLOG_INFO() << QString("[<<<<---_RefundOrder: orderId: %1, orderStatus: %2, refundFmId: %3, refundMethod: %4, globalServiceType: %5, globalOrderType: %6, isCancle:%7--->>>>]")
.arg( orderObject->id ).arg(orderObject->orderStatus).arg(refundFmIdTemp).arg( tempRefundMethod ).arg(tempGlobalServiceType).arg(tempGlobalOrderType).arg(orderObject->isCancle);
// 查看待拉取订单详情列表;
if ( 0 != tempRefundMethod && 1 != tempRefundMethod ) {
// 添加到待拉取订单详情列表;
PullOrderInfo* orderPull = new PullOrderInfo();
if ( orderPull ) {
orderPull->orderId=orderObject->id;
......@@ -1787,18 +1788,24 @@ bool FlowControl::_RefundOrder(const QString &orderId,int reasonCode,const QStri
orderPull->skipPrint = orderObject->skipPrint;
m_orderPullList.append( orderPull );
}
QLOG_INFO() << QString("[<<<<---_RefundOrder: m_orderPullList.size: %1 , m_orderPullList.last: %2, m_simValidOrdersList.size: %3 , m_simValidOrdersList.first: %4 --->>>>]")
QLOG_INFO() << QString("[<<<<---_RefundOrder:: m_orderPullList.size: %1 , m_orderPullList.last: %2, m_simValidOrdersList.size: %3 , m_simValidOrdersList.first: %4 --->>>>]")
.arg( m_orderPullList.size() ).arg( m_orderPullList.last() ? m_orderPullList.last()->orderId : "" )
.arg( m_simValidOrdersList.size() ).arg( m_simValidOrdersList.size() ? m_simValidOrdersList.first() : "" );
emit changeOrderStatus(orderObject, oldOrderStatus);
return true;
} else {
// 存储订单 销售退货状态的内存标识;
orderObject->orderSaleRefundStatus = orderObject->orderStatus == 6 ? 6 : 3 ;
_analysisRefundOrderTask( orderObject );
}
result = true;
} else {
result = false;
emit showAlert(AlertForm::MSGERROR, QString::fromLocal8Bit("退单失败![%1]").arg(recvJson[JSON_ERRMSG].toString()));
return false;
}
}
}
return result;
}
......
......@@ -471,6 +471,7 @@ private slots:
void _RemindCasherBlinkFloatForm();
void _OrderAnalysis(const QJsonObject &jsonObject);
void _analysisRefundOrderTask(OrderObject* orderObject);
//减轻压测插件时: 插件崩溃(STACK_OVERFLOW_Qt5Gui!std::_Mutex::_Mutex+266) + POS收银系统奔溃(POS机屏幕点击频率过高 + 总次数过高 导致崩溃)
void _SmartTriggerClickPosBtn(const bool bTimerTrigger = false);
......
......@@ -7,8 +7,8 @@ IDI_ICON ICON DISCARDABLE "logo.ico"
#endif
VS_VERSION_INFO VERSIONINFO
//***每次修改后编译发版必须变更版本号(preDefine.h中APP_VERSION 需要一致)***//
FILEVERSION 2,2022,7,28
PRODUCTVERSION 2,2022,7,28
FILEVERSION 2,2022,8,1
PRODUCTVERSION 2,2022,8,1
//*************************************************************************//
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
......@@ -31,8 +31,8 @@ VS_VERSION_INFO VERSIONINFO
VALUE "OriginalFilename", "fmTakeout.exe"
VALUE "ProductName", "Delivery Order Plugin"
//***每次修改后编译发版必须变更版本号(preDefine.h中APP_VERSION 需要一致)***//
VALUE "ProductVersion", "2.2022.7.28"
VALUE "FileVersion", "2.2022.7.28"
VALUE "ProductVersion", "2.2022.8.1"
VALUE "FileVersion", "2.2022.8.1"
//*************************************************************************//
END
END
......
......@@ -42,7 +42,7 @@
//#define APP_VERSION "2.2021.9.24"
//#define APP_VERSION "2.2021.12.20"
//#define APP_VERSION "2.2022.1.18"
#define APP_VERSION "2.2022.7.28"
#define APP_VERSION "2.2022.8.1"
//修正版本号时,切记修正 FmTakeout.rc 中的版本号
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment