Commit 847f9da6 by wuyang.zou

1、补录异常订单--功能按钮

2、同步服务端全部订单--功能按钮
3、走心跳接口回传插件版本号+版本备注
parent f324f0d6
......@@ -696,25 +696,22 @@ void FlowControl::_PullOrderData()
m_PullOrderDataMutex.lock();
PullOrderInfo* info = m_orderPullList.takeFirst();
if(info->orderId.isEmpty())
{
if ( _PullOrderList(info->pageSize,info->pageNumber) ){
if(info->orderId.isEmpty()) {
if ( _PullOrderList(info->pageSize,info->pageNumber) ) {
QLOG_INFO()<<"[<<<<----FlowControl::_PullOrderData _PullOrderList successful---->>>>>]";
m_orderPullList.removeOne(info);
delete info;
}else {
} else {
QLOG_INFO()<<"[<<<<----FlowControl::_PullOrderData _PullOrderList failed---->>>>>]";
m_orderPullList.removeOne(info);
m_orderPullList.append(info);
}
}
else
{
if (_PullOrderDetail(info->orderId) ){
} else {
if (_PullOrderDetail(info->orderId) ) {
QLOG_INFO()<<"[<<<<----FlowControl::_PullOrderData _PullOrderDetail successful---->>>>>]";
m_orderPullList.removeOne(info);
delete info;
}else{
} else {
QLOG_INFO()<<"[<<<<----FlowControl::_PullOrderData _PullOrderDetail fail---->>>>>]";
m_orderPullList.removeOne(info);
m_orderPullList.append(info);
......@@ -733,12 +730,10 @@ bool FlowControl::_PullOrderDetail(const QString& orderId)
bool result;
QJsonObject recvJson;
if(ConfigManger::GetInstance().GetOrderSslConfig())
{
if(ConfigManger::GetInstance().GetOrderSslConfig()) {
m_pullOrderSocket->SetSslConfig();
QLOG_INFO()<<"FlowControl::_PullOrderDetail load ssl";
}
else{
} else {
QLOG_INFO()<<"FlowControl::_PullOrderDetail load not ssl";
}
QString url = ConfigManger::GetInstance().GetOrderServerUrl()+
......@@ -751,11 +746,9 @@ bool FlowControl::_PullOrderDetail(const QString& orderId)
result = m_pullOrderSocket->GetRequest(recvJson, error);
QLOG_INFO() << QString("[<<<<---Pull Order Detail Finsh--->>>>][result:%1][msg:%2]")
.arg(result).arg(error)<<recvJson;
if(!result)
{
if(!result) {
emit setNetStatus(QString::fromLocal8Bit("<font color='#ff0000'>网络不稳定,正在重试</font>"));
}else
{
} else {
qDebug()<<recvJson;
emit setNetStatus(QString::fromLocal8Bit("正常"));
if(JSON_STATUSCODE_OK != recvJson[JSON_ERRCODE].toInt())
......@@ -764,8 +757,7 @@ bool FlowControl::_PullOrderDetail(const QString& orderId)
QLOG_ERROR() << QString("[<<<<---Pull Order Detail Error--->>>>][msg->%1]").arg(error);
emit showAlert(AlertForm::MSGERROR, QString::fromLocal8Bit("获取订单失败![%1]").arg(error));
result=false;
}else
{
} else {
//获取门店营业状态
//QString strOpeStatus=QString::fromLocal8Bit("开店");
//emit setOpeStatus(strOpeStatus);
......@@ -774,11 +766,10 @@ bool FlowControl::_PullOrderDetail(const QString& orderId)
}
}
/*解析json数据失败则20秒后重新拉单*/
if(!result)
{
if(!result) {
m_pullTimer->start(1000*10);
// 如果某次拉订单列表出现失败;此函数的外层函数 _PullOrderData:会将本次拉单对象存起来,等待下次拉取;
}else{
} else {
m_pullTimer->start(1000*2);
}
qDebug()<<"[<<<<---Pull Order Detail Result--->>>>]"<<result;
......@@ -787,8 +778,7 @@ bool FlowControl::_PullOrderDetail(const QString& orderId)
bool FlowControl::_PullOrderList(const int &pageSize, const int &pageNo)
{
if(m_pullOrderListSocket==NULL)
{
if(m_pullOrderListSocket==NULL) {
m_pullOrderListSocket =new BillSocket(this);
}
QString error;
......@@ -799,13 +789,11 @@ bool FlowControl::_PullOrderList(const int &pageSize, const int &pageNo)
sendJson = DataManger::GetInstance().GetPullOrderListData(pageNo,pageSize);
QLOG_INFO() << QString("[<<<<---FlowControl::_PullOrderList: Pull Order List--->>>>][requestData:]")<<sendJson;
if(ConfigManger::GetInstance().GetOrderSslConfig())
{
if(ConfigManger::GetInstance().GetOrderSslConfig()) {
// m_pullOrderSocket->SetSslConfig();
m_pullOrderListSocket->SetSslConfig();
QLOG_INFO()<<"FlowControl::_PullOrderList load ssl";
}
else{
} else {
QLOG_INFO()<<"FlowControl::_PullOrderList load not ssl";
}
m_pullOrderListSocket->SetUrl(QUrl(ConfigManger::GetInstance().GetOrderServerUrl()+
......@@ -815,33 +803,27 @@ bool FlowControl::_PullOrderList(const int &pageSize, const int &pageNo)
result = m_pullOrderListSocket->PostRequest(sendJson, recvJson, error);
QLOG_INFO() << QString("[<<<<---FlowControl::_PullOrderList:Pull Order List Finish.--->>>>][result:%1][msg:%2]")
.arg(result).arg(error)<<recvJson;
if(!result)
{
if(!result) {
emit setNetStatus(QString::fromLocal8Bit("<font color='#ff0000'>网络不稳定,正在重试</font>"));
}else
{
} else {
qDebug()<<recvJson;
emit setNetStatus(QString::fromLocal8Bit("正常"));
if(JSON_STATUSCODE_OK != recvJson[JSON_ERRCODE].toInt())
{
if(JSON_STATUSCODE_OK != recvJson[JSON_ERRCODE].toInt()) {
QString error = recvJson[JSON_ERRMSG].toString();
QLOG_ERROR() << QString("[<<<<---FlowControl::_PullOrderList:Pull Order List Error--->>>>][msg->%1]").arg(error);
emit showAlert(AlertForm::MSGERROR, QString::fromLocal8Bit("获取订单失败![%1]").arg(error));
result=false;
}else
{
} else {
//获取门店营业状态
//QString strOpeStatus=QString::fromLocal8Bit("开店");
//emit setOpeStatus(strOpeStatus);
QJsonArray orders = recvJson[JSON_DATA].toObject()[JSON_ORDERS].toArray();
foreach(QJsonValue order, orders)
{
foreach(QJsonValue order, orders) {
_OrderAnalysis(order.toObject());
}
ordertotalSum=recvJson[JSON_DATA].toObject()[JSON_COUNT].toInt();
m_orderCount+=50;
if(m_orderCount<ordertotalSum)
{
if(m_orderCount<ordertotalSum) {
//避免出现存在多页数据,导致出现死锁: 先调用定时器的timeout 触发槽函数:_PullOrderData,
//_PullOrderData:内部会有加锁并且会调用分页获取订单函数_AddOrderPull(QString(),QString(),pageNo+1,pageSize): 内部也有加锁,所以导致死锁;
//_AddOrderPull(QString(),QString(),pageNo+1,pageSize);
......@@ -858,8 +840,7 @@ bool FlowControl::_PullOrderList(const int &pageSize, const int &pageNo)
}
}
// 如果出现拉取订单列表失败,触发下一次拉门店有效订单的时间间隔变更为20(如果一开始就获取订单列表成功:时间间隔为5s)
if(!result)
{
if(!result) {
m_pullTimer->start(1000*10);
// 如果某次拉订单列表出现失败;此函数的外层函数 _PullOrderData: 还会将本次拉单列表存起来等待下次拉取;
}
......@@ -873,8 +854,9 @@ bool FlowControl::_SendHeart()
bool result;
QJsonObject sendJson;
QJsonObject recvJson;
m_puginVersionComments = ConfigManger::GetInstance().GetOrderServerUrl();
sendJson = DataManger::GetInstance().GetHeartData(m_password,m_posId,_GetIpAddress());
sendJson = DataManger::GetInstance().GetHeartData(m_password,m_posId,_GetIpAddress(),m_puginVersionComments);
QLOG_INFO() << QString("[<<<<---Send Heart--->>>>][requestData:%1]")<<sendJson;
m_loginSocket->SetUrl(QUrl(ConfigManger::GetInstance().GetLoginServerUrl()+
ConfigManger::GetInstance().GetInterfaceName(INI_INTERFACE_HEART)));
......@@ -2185,6 +2167,7 @@ bool FlowControl::_ResponseHM21Request(const QJsonObject &content, QJsonObject &
{
QLOG_INFO()<<__FUNCTION__;
//判断当前POS插件是否登录成功: 如果没有登录成功,尝试在次登录;
Q_UNUSED(data);
bool result=true;
if(!m_bLoginResult&&content.contains("storeId")) {
result=_ResponseSimReqTryLogin(content,error);
......@@ -2569,6 +2552,25 @@ void FlowControl::onGetBusinessStatus()
}
}
void FlowControl::onGetOMSAllOrders()
{
QLOG_INFO()<<__FUNCTION__;
if(!m_bLoginResult) {
emit showAlert(AlertForm::MSGERROR, QString::fromLocal8Bit("门店还未登录"));
return ;
}
// 类登陆后的获取全部订单动作;
QLOG_INFO() << QString(" FlowControl::onGetOMSAllOrders only _AddOrderPull begin.");
_AddOrderPull(QString(),QString());
emit showAlert(AlertForm::SYNCDATA, QString::fromLocal8Bit("正在获取平台全部订单,稍后刷新......"));
QLOG_INFO() << QString(" FlowControl::onGetOMSAllOrders only _AddOrderPull end.");
}
void FlowControl::onProcessOrder(const QString &operation, const QString &orderId)
{
if(!operation.compare(OPERATION_GETDELIVERS))
......@@ -2604,32 +2606,26 @@ void FlowControl::onSerachOrder(const QString &text)
{
QLOG_INFO() << QString::fromLocal8Bit("[<<<<---FlowControl::onSerachOrder:输入的数字串:%1 --->>>>]").arg(text);
QMap<QString,QString> orderMap;
if(!text.isEmpty())
{
if(!text.isEmpty()) {
QMap<QString, OrderObject*>::iterator order;
for(order = m_FmOrdersMap.begin(); order!=m_FmOrdersMap.end(); order++)
{
for(order = m_FmOrdersMap.begin(); order!=m_FmOrdersMap.end(); order++) {
QString orderId = order.key();
QString phoneId = order.value()->consigneePhone;
QString thirdPartyOrderId = order.value()->thirdPartyOrderId;
while (!orderId.at(0).isDigit())
{
while (!orderId.at(0).isDigit()) {
orderId = orderId.mid(1);
}
if(orderId.startsWith(text))
{
if(orderId.startsWith(text)) {
orderMap.insert(QString::fromLocal8Bit("订单号:")+orderId,QString::fromLocal8Bit("三方订单号:")+thirdPartyOrderId);
}
//移除手机号检索
/*
if(phoneId.startsWith(text))
{
if(phoneId.startsWith(text)) {
orderMap.insert(QString::fromLocal8Bit("订单号:")+orderId,QString::fromLocal8Bit("手机号:")+phoneId);
}
*/
//新增第三方订单号检索;
if(thirdPartyOrderId.startsWith(text))
{
if(thirdPartyOrderId.startsWith(text)) {
orderMap.insert(QString::fromLocal8Bit("订单号:")+orderId,QString::fromLocal8Bit("三方订单号:")+thirdPartyOrderId);
}
}
......@@ -2637,13 +2633,43 @@ void FlowControl::onSerachOrder(const QString &text)
emit showSearchOrderResult(orderMap);
}
void FlowControl::onProcessRejectOrder(const QString &orderId, const int &reasonCode,const QString &reason)
{
_RefuseOrder(orderId, reasonCode,reason);
}
void FlowControl::onProcessRepealOrder(const QString &orderId, const int &reasonCode,const QString &reason,const QString& dishesListString)
{
QLOG_INFO() << QString("[<<<<---FlowControl::onProcessRepealOrder: --->>>>]");
_RefundOrder(orderId,reasonCode,reason,dishesListString);
}
void FlowControl::onProcessReTryInputOrder(const QString &orderId)
{
QLOG_INFO() << QString("[<<<<---FlowControl::onProcessReTryInputOrder: OrderId: %1 --->>>>]").arg(orderId);
//先判断非码订单容器不为空;
if ( !m_FmOrdersMap.isEmpty() ) {
QMap<QString, OrderObject*>::iterator TempOrder;
//遍历订单容器列表,找到指定订单;
for( TempOrder = m_FmOrdersMap.begin(); TempOrder != m_FmOrdersMap.end(); TempOrder++ ) {
if( TempOrder.key() == orderId) {
OrderObject* TempOrderObject = TempOrder.value();
m_PullOrderDataMutex.lock();
/***组装从FM外卖service端的拉单请求参数***/
PullOrderInfo* orderPull = new PullOrderInfo();
orderPull->orderId=orderId;
orderPull->channel=TempOrderObject->channel;
orderPull->pageNumber=0;
orderPull->pageSize=0;
m_orderPullList.append(orderPull);
m_PullOrderDataMutex.unlock();
}
}
} else {
QLOG_INFO() << QString("[<<<<---FlowControl::onProcessReTryInputOrder: m_FmOrdersMap Is Empty --->>>>]");
}
}
......@@ -86,6 +86,7 @@ private:
// 门店信息
QString m_storeId;
QString m_password;
QString m_puginVersionComments;
QString m_posId;
QString m_cashierId;
QString m_cashierName;
......@@ -155,6 +156,7 @@ signals:
* 返回:NULL
* */
void hideAlert();
/* 功能:显示通知窗口
* 参数:[1]窗口类型[2]显示信息
* 返回:NULL
......@@ -381,19 +383,43 @@ public slots:
* 返回:NULL
* */
void onSerachOrder(const QString& text);
/* 功能:拒绝订单;
* 参数:[1]订单号 [] [] [] []
* 返回:NULL
* */
void onProcessRejectOrder(const QString& orderId, const int& reasonCode,const QString &reason);
/* 功能:作废订单;
* 参数:[1]订单号 [] [] [] []
* 返回:NULL
* */
void onProcessRepealOrder(const QString& orderId, const int& reasonCode,const QString &reason,const QString& dishesListString);
/* 功能:重新向POS录入外卖订单;
* 参数:[1]订单号
* 返回:NULL
* */
void onProcessReTryInputOrder(const QString& orderId);
/* 功能:获取门店营业状态
* 参数:NULL
* 返回:NULL
* */
void onGetBusinessStatus();
/* 功能:获取OMS服务端全部订单
* 参数:NULL
* 返回:NULL
* */
void onGetOMSAllOrders();
/* 功能:获取菜品信息
* 参数:NULL
* 返回:NULL
* */
void onPullDishes();
/* 功能:获取菜品信息
* 参数:NULL
* 返回:NULL
......@@ -405,11 +431,13 @@ public slots:
* 返回:NULL
* */
void onUpdDishesForRef(QString channelCode, QMap<QString, int> dishes);
/* 功能:设置门店营业状态
* 参数:NULL
* 返回:NULL
* */
void onSetStoreOperatingStatus(const QString& channelCode, const QString& channelName,int business_status);
/* 功能:向服务端获取日结数据
* 参数:NULL
* 返回:NULL
......
......@@ -57,7 +57,7 @@ QJsonObject DataManger::GetLoginData(const QString &partnerId, const QString &st
}
QJsonObject DataManger::GetHeartData(const QString &password,
const QString &stationId, const QString &ipAddress)
const QString &stationId, const QString &ipAddress, const QString &versionComments)
{
QJsonObject rObj;
rObj.insert(JSON_IPADDRESS, ipAddress);
......@@ -65,7 +65,8 @@ QJsonObject DataManger::GetHeartData(const QString &password,
rObj.insert(JSON_ORGCODE, m_storeId);
rObj.insert(JSON_PASSWORD, password);
rObj.insert(JSON_MACHINECODE, stationId);
//rObj.insert(JSON_USERID, cashierId);
rObj.insert(JSON_PLUGINVERSION, APP_VERSION);
rObj.insert(JSON_PLUGINVERSIONCOMMENTS, versionComments);
return rObj;
}
......
......@@ -43,7 +43,7 @@ public:
* 返回:登录数据
* */
QJsonObject GetHeartData(const QString& password,
const QString& stationId, const QString& ipAddress);
const QString& stationId, const QString& ipAddress, const QString& versionComments);
/* 功能:获取拉取订单数据
* 参数:[1]时间戳
* 返回:登录数据
......
......@@ -95,7 +95,7 @@ QString OrderObject::getOrderStatusDec()
case 4:return QString::fromLocal8Bit("配送中");
case 5:return QString::fromLocal8Bit("完成");
case 6:return QString::fromLocal8Bit("取消");
deafult: return QString::fromLocal8Bit("未知");
default: return QString::fromLocal8Bit("未知");
}
return QString::fromLocal8Bit("未知");
}
......@@ -39,6 +39,14 @@ void AlertForm::SetContent(AlertForm::Type type, const QString &msg)
ui->alertLabIng->show();
ui->alertLabError->hide();
break;
case SYNCDATA:
ui->alertBtnOk->hide();
ui->alertLabOk->hide();
ui->alertLabIng->show();
ui->alertLabError->hide();
m_timer->stop();
m_timer->start(20000);
break;
}
ui->alertLabMsg->setText(msg);
if(!m_timer->isActive())
......
......@@ -22,7 +22,8 @@ public:
{
SUCCESS=0,
MSGERROR,
LOADING
LOADING,
SYNCDATA
}Type;
/* 功能:设置显示内容
......
......@@ -14,6 +14,8 @@ DetailForm::DetailForm(QWidget *parent) :
connect(this, &DetailForm::processOrder, &FlowControl::GetInstance(), &FlowControl::onProcessOrder);
connect(this, &DetailForm::processRejectOrder, &FlowControl::GetInstance(), &FlowControl::onProcessRejectOrder);
connect(this, &DetailForm::processRepealOrder, &FlowControl::GetInstance(), &FlowControl::onProcessRepealOrder);
connect(this, &DetailForm::processReTryInputOrder, &FlowControl::GetInstance(), &FlowControl::onProcessReTryInputOrder);
m_rejectForm=NULL;
m_refuseForm=NULL;
_Init();
......@@ -207,6 +209,11 @@ void DetailForm::InitData(OrderObject *orderObject)
ui->detailBtn2->hide();
}
//订单入机失败 [销售单 / 退货单] 需要显示 重新录单 按钮 方便店员再次触发录单;
if(OrderObject::SimExceptSale == orderObject->orderStatus || OrderObject::SimExceptRefund == orderObject->orderStatus) {
ui->detailBtn0->show();
}
ui->detailBtn3->setText(GetOperNameByStatus(orderObject->orderStatus));
ui->detailBtn3->setProperty("operation", GetOperByStatus(orderObject->orderStatus));
ui->detailBtn3->setProperty("orderId", orderObject->id);
......@@ -289,3 +296,15 @@ void DetailForm::onOperaBtnClicked()
void DetailForm::on_detailBtn1_clicked()
{
}
void DetailForm::on_detailBtn0_clicked()
{
QLOG_INFO()<<QString("[<<<<---DetailForm::on_detailBtn0_clicked: begin:--->>>>]")<<QString("重新录单")<< m_orderObject->id;
if(m_orderObject) {
emit processReTryInputOrder(m_orderObject->id);
this->close();
//this->destroy();
} else {
QLOG_ERROR()<<QString("[<<<<---DetailForm::on_detailBtn0_clicked: m_orderObject point is NULL--->>>>]");
}
}
......@@ -47,19 +47,40 @@ signals:
* */
void processOrder(const QString& operation, const QString& orderId);
void processRejectOrder(const QString& orderId,const int& reasonCode,const QString& reason);
/* 功能:作废订单
* 参数:[1]订单编号 [2]作废原因码 [3]作废原因 [4]商品列表
* 返回:NULL
* */
void processRepealOrder(const QString& orderId,const int& reasonCode,const QString& reason,const QString& dishesList);
/* 功能:重新尝试录入订单
* 参数:[1]订单编号
* 返回:NULL
* */
void processReTryInputOrder(const QString& orderId);
private slots:
/* 功能:处理订单按钮点击
* 参数:NULL
* 返回:NULL
* */
void onOperaBtnClicked();
/* 功能:处理重新打印按钮点击
* 参数:NULL
* 返回:NULL
* */
void on_detailBtn1_clicked();
/* 功能:处理点击 重新录单按钮
* 参数:NULL
* 返回:NULL
* */
void on_detailBtn0_clicked();
};
#endif // DETAILFORM_H
......@@ -44,6 +44,7 @@ MainForm::MainForm(QWidget *parent) :
connect(this, &MainForm::getOrderDetails, &FlowControl::GetInstance(), &FlowControl::onGetOrderDetails);
connect(this, &MainForm::PullDishes, &FlowControl::GetInstance(), &FlowControl::onPullDishes);
connect(this, &MainForm::GetBusinessStatus, &FlowControl::GetInstance(), &FlowControl::onGetBusinessStatus);
connect(this, &MainForm::GetOMSAllOrders, &FlowControl::GetInstance(), &FlowControl::onGetOMSAllOrders);
connect(this, &MainForm::getDayReport, &FlowControl::GetInstance(), &FlowControl::onDailyReport);
connect(&FlowControl::GetInstance(), &FlowControl::showDailyReportData, this, &MainForm::onSetDailyReportData);
connect(&FlowControl::GetInstance(), &FlowControl::hideAlert, this, &MainForm::onHideAlert);
......@@ -109,7 +110,9 @@ void MainForm::MyShow()
m_dailyReportForm =new DailyReportForm(this);
ui->mainBtnDayReport->hide();
ui->mainBtnStoreManager->hide();
//ui->mainBtnStoreManager->hide();
//变更此按钮为 同步订单;
ui->mainBtnStoreManager->show();
#ifdef TODO
ui->mainBtnDishManager->hide();
#endif
......@@ -659,7 +662,8 @@ void MainForm::on_mainBtnDishManager_clicked()
void MainForm::on_mainBtnStoreManager_clicked()
{
emit GetBusinessStatus();
//emit GetBusinessStatus();
emit GetOMSAllOrders();
}
void MainForm::on_mainBtnDayReport_clicked()
......
......@@ -95,6 +95,7 @@ signals:
* 返回:NULL
* */
void processOrder(const QString& operation, const QString& orderId);
/* 功能:通知悬浮窗显示
* 参数:NULL
* 返回:NULL
......@@ -106,21 +107,25 @@ signals:
* 返回:NULL
* */
void startRemind(int type);
/* 功能:通知悬浮窗停止提示
* 参数:NULL
* 返回:NULL
* */
void stopRemind();
/* 功能:获取订单详情
* 参数:NULL
* 返回:NULL
* */
void getOrderDetails(const QString& orderId);
/* 功能:开启流程
* 参数:NULL
* 返回:NULL
* */
void flowStart();
/* 功能:获取菜品信息
* 参数:NULL
* 返回:NULL
......@@ -132,12 +137,18 @@ signals:
* 返回:NULL
* */
void GetBusinessStatus();
/* 功能:获取日结数据
* 参数:NULL
* 返回:NULL
* */
void getDayReport();
/* 功能:同步服务端全部订单;
* 参数:NULL
* 返回:NULL
* */
void GetOMSAllOrders();
private slots:
......
......@@ -500,13 +500,13 @@
<widget class="QPushButton" name="mainBtnStoreManager">
<property name="minimumSize">
<size>
<width>50</width>
<width>60</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<width>60</width>
<height>30</height>
</size>
</property>
......@@ -514,7 +514,7 @@
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>门店管理</string>
<string>同步订单</string>
</property>
</widget>
</item>
......
......@@ -64,6 +64,7 @@
#define JSON_PASSWORD "password"
#define JSON_IPADDRESS "iPAddress"
#define JSON_MACHINECODE "machineCode"
#define JSON_PARTNERID "partnerId"
#define JSON_ORGCODE "orgCode"
#define JSON_POSNO "posNo"
......@@ -86,6 +87,8 @@
#define JSON_TOKEN "PosToken"
#define JSON_DATA "data"
#define JSON_POSVERSION "pos_version"
#define JSON_PLUGINVERSION "version"
#define JSON_PLUGINVERSIONCOMMENTS "versionComments"
#define JSON_ORDERS "orders"
#define JSON_COUNT "count"
#define JSON_PRODUCTS "products"
......
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