weixin
This commit is contained in:
parent
4dee3b1036
commit
598b205052
|
@ -3,12 +3,13 @@ package main
|
|||
import (
|
||||
"enterprise/common/config"
|
||||
"enterprise/common/global"
|
||||
"enterprise/common/model"
|
||||
"enterprise/server"
|
||||
"enterprise/worker"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main1() {
|
||||
func main() {
|
||||
config.LoadServerConfig()
|
||||
global.InitGlobal()
|
||||
|
||||
|
@ -21,12 +22,12 @@ func main1() {
|
|||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
func main22() {
|
||||
config.LoadServerConfig()
|
||||
global.InitGlobal()
|
||||
//cfg := config.GetConfig()
|
||||
for i := 0; i < 10; i++ {
|
||||
new(worker.Approval).SyncCheckinMonth(time.Now().AddDate(0, 0-i, 0).Format("200601"))
|
||||
new(worker.Approval).Sync(time.Now().AddDate(0, 0-i, 0).Format("200601"), model.ApprovalTypeVacation)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"enterprise/common/model"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ApprovalVacationDao struct {
|
||||
}
|
||||
|
||||
func NewApprovalVacationDao() *ApprovalVacationDao {
|
||||
return &ApprovalVacationDao{}
|
||||
}
|
||||
|
||||
func (d *ApprovalVacationDao) TableName() string {
|
||||
return "approval_vacation"
|
||||
}
|
||||
|
||||
func (d *ApprovalVacationDao) Create(o *model.ApprovalVacation) (int64, error) {
|
||||
o.CreateTime = time.Now().Unix()
|
||||
res := GetDB().Table(d.TableName()).Create(o)
|
||||
return o.Id, res.Error
|
||||
}
|
||||
|
||||
func (d *ApprovalVacationDao) Update(o *model.ApprovalVacation) error {
|
||||
o.UpdateTime = time.Now().Unix()
|
||||
tx := GetDB().Table(d.TableName())
|
||||
res := tx.Save(o)
|
||||
return res.Error
|
||||
}
|
||||
|
||||
func (d *ApprovalVacationDao) Delete(id int64) error {
|
||||
res := GetDB().Table(d.TableName()).Delete(&model.ApprovalVacation{}, id)
|
||||
return res.Error
|
||||
}
|
||||
|
||||
func (d *ApprovalVacationDao) Get(id int64) (*model.ApprovalVacation, error) {
|
||||
var u model.ApprovalVacation
|
||||
tx := GetDB().Table(d.TableName())
|
||||
tx = tx.Where("id = ?", id)
|
||||
res := tx.First(&u)
|
||||
if res.Error == gorm.ErrRecordNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func (d *ApprovalVacationDao) GetBySpNo(spNo string) (*model.ApprovalVacation, error) {
|
||||
var u model.ApprovalVacation
|
||||
tx := GetDB().Table(d.TableName())
|
||||
tx = tx.Where("sp_no = ?", spNo)
|
||||
res := tx.First(&u)
|
||||
if res.Error == gorm.ErrRecordNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
return &u, nil
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
package model
|
||||
|
||||
var ()
|
||||
|
||||
type ApprovalCheckin struct {
|
||||
Id int64
|
||||
Username string
|
||||
Month string
|
||||
SpNo string
|
||||
CheckinType string
|
||||
CheckinDate string
|
||||
|
|
|
@ -3,11 +3,15 @@ package model
|
|||
var (
|
||||
ApprovalRefundStatusCreated = 1
|
||||
ApprovalRefundStatusPayed = 2
|
||||
ApprovalTypeRefund = "refund"
|
||||
ApprovalTypeCheckin = "checkin"
|
||||
ApprovalTypeVacation = "vacation"
|
||||
)
|
||||
|
||||
type ApprovalRefund struct {
|
||||
Id int64
|
||||
Username string
|
||||
Month string
|
||||
SpNo string
|
||||
RefundType string
|
||||
RefundDate string
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package model
|
||||
|
||||
var ()
|
||||
|
||||
type ApprovalVacation struct {
|
||||
Id int64
|
||||
Username string
|
||||
Month string
|
||||
SpNo string
|
||||
VacationType string
|
||||
VacationDate string
|
||||
VacationStartTime string
|
||||
VacationEndTime string
|
||||
VacationDuration float64
|
||||
VacationRemark string
|
||||
ApplyTime string
|
||||
CreateTime int64
|
||||
UpdateTime int64
|
||||
}
|
|
@ -3,10 +3,13 @@ package weixin
|
|||
import (
|
||||
"encoding/json"
|
||||
butil "enterprise/base/util"
|
||||
"enterprise/common/model"
|
||||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/smbrave/goutil"
|
||||
"github.com/spf13/cast"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Applyer struct {
|
||||
|
@ -104,14 +107,12 @@ func (d *ApproveDetail) GetValue(title string) string {
|
|||
value = content.Value.NewMoney
|
||||
} else if content.Control == "File" {
|
||||
value = content.Value.Files[0].FileId
|
||||
} else if content.Control == "Vacation" { //请假 : 请假类型,请假时长
|
||||
} else if content.Control == "Vacation" { //请假 : 请假类型,开始时间,结束时间,请假时长
|
||||
tp := content.Value.Vacation.Selector.Options[0].Value[0].Text
|
||||
duration := cast.ToString(content.Value.Vacation.Attendance.DateRange.NewDuration)
|
||||
value = tp + "," + duration
|
||||
value = tp + "," + cast.ToString(content.Value.Vacation.Attendance.DateRange.NewBegin) +
|
||||
"," + cast.ToString(content.Value.Vacation.Attendance.DateRange.NewEnd) +
|
||||
"," + cast.ToString(content.Value.Vacation.Attendance.DateRange.NewDuration)
|
||||
} else if content.Control == "PunchCorrection" { //补卡:日期,时间,状态
|
||||
if d.SpNo == "202312010001" {
|
||||
fmt.Println(goutil.EncodeJSON(d.ApplyData))
|
||||
}
|
||||
mp := cast.ToStringMap(content.Value.PunchCorrection)
|
||||
ddate := cast.ToString(mp["daymonthyear"])
|
||||
dtime := cast.ToString(mp["time"])
|
||||
|
@ -133,6 +134,53 @@ func (d *ApproveDetail) GetUserid() string {
|
|||
return d.Applyer.Userid
|
||||
}
|
||||
|
||||
func (d *ApproveDetail) ToVacation() *model.ApprovalVacation {
|
||||
vacation := new(model.ApprovalVacation)
|
||||
vacation.SpNo = d.SpNo
|
||||
vacation.Username = d.GetUserid()
|
||||
vacation.ApplyTime = goutil.TimeToDateTime(d.ApplyTime)
|
||||
|
||||
fields := strings.SplitN(d.GetValue("请假类型"), ",", 4)
|
||||
dTime := cast.ToInt64(fields[1])
|
||||
vacation.VacationType = fields[0]
|
||||
vacation.VacationDate = goutil.TimeToDate(dTime)
|
||||
vacation.VacationStartTime = goutil.TimeToDateTime(cast.ToInt64(fields[1]))
|
||||
vacation.VacationEndTime = goutil.TimeToDateTime(cast.ToInt64(fields[2]))
|
||||
vacation.VacationDuration = float64(cast.ToInt64(fields[3])) / float64(3600*8)
|
||||
vacation.VacationRemark = d.GetValue("请假事由")
|
||||
vacation.Month = time.Unix(dTime, 0).Format("200601")
|
||||
return vacation
|
||||
}
|
||||
|
||||
func (d *ApproveDetail) ToRefund() *model.ApprovalRefund {
|
||||
refund := new(model.ApprovalRefund)
|
||||
refund.SpNo = d.SpNo
|
||||
refund.Username = d.GetUserid()
|
||||
refund.ApplyTime = goutil.TimeToDateTime(d.ApplyTime)
|
||||
refund.Status = model.ApprovalRefundStatusCreated
|
||||
refund.RefundType = d.GetValue("报销类型")
|
||||
refundTime := cast.ToInt64(d.GetValue("发生时间"))
|
||||
refund.Month = time.Unix(refundTime, 0).Format("200601")
|
||||
refund.RefundDate = goutil.TimeToDateTime(refundTime)
|
||||
refund.RefundAmount = cast.ToFloat64(d.GetValue("报销费用"))
|
||||
refund.RefundRemark = d.GetValue("报销说明")
|
||||
return refund
|
||||
}
|
||||
|
||||
func (d *ApproveDetail) ToCheckin() *model.ApprovalCheckin {
|
||||
ac := new(model.ApprovalCheckin)
|
||||
value := strings.SplitN(d.GetValue("补卡"), ",", 3)
|
||||
ac.SpNo = d.SpNo
|
||||
ac.Username = d.GetUserid()
|
||||
ac.ApplyTime = goutil.TimeToDateTime(d.ApplyTime)
|
||||
ac.CheckinRemark = d.GetValue("补卡事由")
|
||||
ac.CheckinDate = goutil.TimeToDate(cast.ToInt64(value[0]))
|
||||
ac.Month = time.Unix(cast.ToInt64(value[0]), 0).Format("200601")
|
||||
ac.CheckinTime = goutil.TimeToDateTime(cast.ToInt64(value[1]))
|
||||
ac.CheckinType = value[2]
|
||||
return ac
|
||||
}
|
||||
|
||||
func NewQyWeixinApprove(corpId, secret, agent string) *QyWeixinApprove {
|
||||
return &QyWeixinApprove{
|
||||
QyWeixin: QyWeixin{
|
||||
|
|
|
@ -11,9 +11,7 @@ import (
|
|||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/smbrave/goutil"
|
||||
"github.com/spf13/cast"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -56,59 +54,87 @@ func (a *Approve) handle(msg *message.MixMessage) {
|
|||
func (a *Approve) handleApprovalChange(msg *message.MixMessage) {
|
||||
spStatus := msg.ApprovalInfo.SpStatus
|
||||
spNo := msg.ApprovalInfo.SpNo
|
||||
spName := msg.ApprovalInfo.SpName
|
||||
templateId := msg.ApprovalInfo.TemplateId
|
||||
if spStatus != SpStatusPassed {
|
||||
return
|
||||
}
|
||||
|
||||
if spName == "费用报销" {
|
||||
a.handleRefund(spNo)
|
||||
} else if spName == "请假" {
|
||||
a.handleHoliday(spNo)
|
||||
} else if spName == "打卡补卡" {
|
||||
a.handleCheckin(spNo)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Approve) handleHoliday(spNo string) {
|
||||
|
||||
}
|
||||
|
||||
func (a *Approve) handleCheckin(spNo string) {
|
||||
|
||||
}
|
||||
func (a *Approve) handleRefund(spNo string) {
|
||||
detail, err := a.approveClient.GetDetail(spNo)
|
||||
if err != nil {
|
||||
log.Errorf("get spn detail error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
if templateId == model.ApprovalTypeRefund {
|
||||
a.handleRefund(detail)
|
||||
} else if templateId == model.ApprovalTypeVacation {
|
||||
a.handleVacation(detail)
|
||||
} else if templateId == model.ApprovalTypeCheckin {
|
||||
a.handleCheckin(detail)
|
||||
}
|
||||
}
|
||||
|
||||
refund, err := dao.NewApprovalRefundDao().GetBySpNo(spNo)
|
||||
func (a *Approve) handleVacation(detail *weixin.ApproveDetail) {
|
||||
newData := detail.ToVacation()
|
||||
dbDao := dao.NewApprovalVacationDao()
|
||||
|
||||
old, err := dbDao.GetBySpNo(detail.SpNo)
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
isUpdate := true
|
||||
if refund == nil {
|
||||
refund = new(model.ApprovalRefund)
|
||||
refund.SpNo = detail.SpNo
|
||||
isUpdate = false
|
||||
}
|
||||
refund.Username = detail.Applyer.Userid
|
||||
refund.ApplyTime = goutil.TimeToDateTime(detail.ApplyTime)
|
||||
refund.Status = model.ApprovalRefundStatusCreated
|
||||
refund.RefundType = detail.GetValue("报销类型")
|
||||
refund.RefundDate = time.Unix(cast.ToInt64(detail.GetValue("发生时间")), 0).Format("2006-01-02")
|
||||
refund.RefundAmount = cast.ToFloat64(detail.GetValue("报销费用"))
|
||||
refund.RefundRemark = detail.GetValue("报销说明")
|
||||
|
||||
if isUpdate {
|
||||
err = dao.NewApprovalRefundDao().Update(refund)
|
||||
if old != nil {
|
||||
newData.Id = old.Id
|
||||
newData.CreateTime = old.CreateTime
|
||||
err = dbDao.Update(newData)
|
||||
} else {
|
||||
_, err = dao.NewApprovalRefundDao().Create(refund)
|
||||
_, err = dbDao.Create(newData)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Approve) handleCheckin(detail *weixin.ApproveDetail) {
|
||||
newData := detail.ToCheckin()
|
||||
dbDao := dao.NewApprovalCheckinDao()
|
||||
|
||||
old, err := dbDao.GetBySpNo(detail.SpNo)
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
if old != nil {
|
||||
newData.Id = old.Id
|
||||
newData.CreateTime = old.CreateTime
|
||||
err = dbDao.Update(newData)
|
||||
} else {
|
||||
_, err = dbDao.Create(newData)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
func (a *Approve) handleRefund(detail *weixin.ApproveDetail) {
|
||||
newData := detail.ToRefund()
|
||||
dbDao := dao.NewApprovalRefundDao()
|
||||
|
||||
old, err := dbDao.GetBySpNo(detail.SpNo)
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
if old != nil {
|
||||
newData.Id = old.Id
|
||||
newData.CreateTime = old.CreateTime
|
||||
err = dbDao.Update(newData)
|
||||
} else {
|
||||
_, err = dbDao.Create(newData)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
return
|
||||
|
@ -116,27 +142,27 @@ func (a *Approve) handleRefund(spNo string) {
|
|||
|
||||
// 支付费用
|
||||
var req weixin.RedMoneyReq
|
||||
req.BillNo = fmt.Sprintf("BX%s%s", refund.SpNo, butil.CutTail(refund.Username, 12))
|
||||
req.Title = fmt.Sprintf("【%s】报销", refund.RefundType)
|
||||
req.Userid = refund.Username
|
||||
req.TotalAmount = int64(100 * refund.RefundAmount)
|
||||
req.BillNo = fmt.Sprintf("BX%s%s", newData.SpNo, butil.CutTail(newData.Username, 12))
|
||||
req.Title = fmt.Sprintf("【%s】报销", newData.RefundType)
|
||||
req.Userid = newData.Username
|
||||
req.TotalAmount = int64(100 * newData.RefundAmount)
|
||||
if err := weixin.NewQyPay().PayRedMoney(&req); err != nil {
|
||||
log.Errorf("pay error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
message := make([]string, 0)
|
||||
|
||||
message = append(message, fmt.Sprintf("【红包发放】[%s]", refund.RefundType))
|
||||
message = append(message, fmt.Sprintf("发放金额:%s", fmt.Sprintf("%.2f", refund.RefundAmount)))
|
||||
message = append(message, fmt.Sprintf("员工名称:%s", refund.Username))
|
||||
message = append(message, fmt.Sprintf("费用说明:%s", refund.RefundRemark))
|
||||
message = append(message, fmt.Sprintf("【红包发放】[%s]", newData.RefundType))
|
||||
message = append(message, fmt.Sprintf("发放金额:%s", fmt.Sprintf("%.2f", newData.RefundAmount)))
|
||||
message = append(message, fmt.Sprintf("员工名称:%s", newData.Username))
|
||||
message = append(message, fmt.Sprintf("费用说明:%s", newData.RefundRemark))
|
||||
|
||||
if err := global.SendMessage([]string{"jiangyong"}, strings.Join(message, "\n")); err != nil {
|
||||
log.Errorf("send message error :%s", err.Error())
|
||||
}
|
||||
|
||||
refund.Status = model.ApprovalRefundStatusPayed
|
||||
if err := dao.NewApprovalRefundDao().Update(refund); err != nil {
|
||||
newData.Status = model.ApprovalRefundStatusPayed
|
||||
if err := dao.NewApprovalRefundDao().Update(newData); err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,25 @@ import (
|
|||
"enterprise/common/model"
|
||||
"enterprise/common/weixin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/smbrave/goutil"
|
||||
"github.com/spf13/cast"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Approval struct {
|
||||
}
|
||||
|
||||
func (s *Approval) SyncCheckinMonth(month string) {
|
||||
templateId := "C4UCJS891Afmu1rE1Ws6cvph7YHqebWtt7KRFqh8c"
|
||||
func (s *Approval) getTemplateId(tp string) string {
|
||||
if tp == model.ApprovalTypeCheckin {
|
||||
return "C4UCJS891Afmu1rE1Ws6cvph7YHqebWtt7KRFqh8c"
|
||||
} else if tp == model.ApprovalTypeRefund {
|
||||
return "C4UE6NT3ZE7XzER9TBk2ynHEeqA11NE2GGCuBq5yH"
|
||||
} else if tp == model.ApprovalTypeVacation {
|
||||
return "3WLJF6naF5jhnXvwisuPmE85wVMYcy1S1ZvYibkw"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Approval) Sync(month, tp string) {
|
||||
templateId := s.getTemplateId(tp)
|
||||
cfg := config.GetConfig().QyWeixin
|
||||
approve := weixin.NewQyWeixinApprove(cfg.Corpid, cfg.ApproveSecret, cfg.ApproveAgent)
|
||||
startTime, _ := time.ParseInLocation("200601", month, time.Local)
|
||||
|
@ -26,42 +34,81 @@ func (s *Approval) SyncCheckinMonth(month string) {
|
|||
log.Errorf("approve getlist error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
//result := make(map[string]float64)
|
||||
|
||||
for _, spNo := range spNos {
|
||||
detail, err := approve.GetDetail(spNo)
|
||||
if err != nil {
|
||||
log.Errorf("approve GetDetail error :%s", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
value := strings.SplitN(detail.GetValue("补卡"), ",", 3)
|
||||
|
||||
ac, err := dao.NewApprovalCheckinDao().GetBySpNo(spNo)
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
continue
|
||||
}
|
||||
isNew := false
|
||||
if ac == nil {
|
||||
ac = new(model.ApprovalCheckin)
|
||||
ac.SpNo = detail.SpNo
|
||||
isNew = true
|
||||
}
|
||||
|
||||
ac.Username = detail.GetUserid()
|
||||
ac.ApplyTime = goutil.TimeToDateTime(detail.ApplyTime)
|
||||
ac.CheckinRemark = detail.GetValue("补卡事由")
|
||||
ac.CheckinDate = goutil.TimeToDate(cast.ToInt64(value[0]))
|
||||
ac.CheckinTime = goutil.TimeToDateTime(cast.ToInt64(value[1]))
|
||||
ac.CheckinType = value[2]
|
||||
if isNew {
|
||||
_, err = dao.NewApprovalCheckinDao().Create(ac)
|
||||
} else {
|
||||
err = dao.NewApprovalCheckinDao().Update(ac)
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
if tp == model.ApprovalTypeCheckin {
|
||||
s.saveCheckin(detail)
|
||||
} else if tp == model.ApprovalTypeRefund {
|
||||
s.saveRefund(detail)
|
||||
} else if tp == model.ApprovalTypeVacation {
|
||||
s.saveVacation(detail)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Approval) saveCheckin(detail *weixin.ApproveDetail) {
|
||||
dbDao := dao.NewApprovalCheckinDao()
|
||||
newData := detail.ToCheckin()
|
||||
old, err := dbDao.GetBySpNo(detail.SpNo)
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
if old == nil {
|
||||
_, err = dbDao.Create(newData)
|
||||
} else {
|
||||
newData.Id = old.Id
|
||||
newData.CreateTime = old.CreateTime
|
||||
err = dbDao.Update(newData)
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Approval) saveRefund(detail *weixin.ApproveDetail) {
|
||||
dbDao := dao.NewApprovalRefundDao()
|
||||
newData := detail.ToRefund()
|
||||
old, err := dbDao.GetBySpNo(detail.SpNo)
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
if old == nil {
|
||||
_, err = dbDao.Create(newData)
|
||||
} else {
|
||||
newData.Id = old.Id
|
||||
newData.CreateTime = old.CreateTime
|
||||
err = dbDao.Update(newData)
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Approval) saveVacation(detail *weixin.ApproveDetail) {
|
||||
dbDao := dao.NewApprovalVacationDao()
|
||||
newData := detail.ToVacation()
|
||||
old, err := dbDao.GetBySpNo(detail.SpNo)
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
return
|
||||
}
|
||||
if old == nil {
|
||||
_, err = dbDao.Create(newData)
|
||||
} else {
|
||||
newData.Id = old.Id
|
||||
newData.CreateTime = old.CreateTime
|
||||
err = dbDao.Update(newData)
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("db error :%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue