Skip to content

Commit

Permalink
feat: add support to wpp message order details
Browse files Browse the repository at this point in the history
  • Loading branch information
paulobernardoaf committed Oct 4, 2024
1 parent 8277187 commit 760e7af
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 40 deletions.
133 changes: 117 additions & 16 deletions flows/actions/send_wpp_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package actions

import (
"encoding/json"
"strconv"

"github.com/nyaruka/gocommon/urns"
"github.com/nyaruka/goflow/assets"
Expand All @@ -26,20 +27,21 @@ type SendWppMsgAction struct {
}

type createWppMsgAction struct {
HeaderType string `json:"header_type,omitempty"`
HeaderText string `json:"header_text,omitempty"`
Attachment string `json:"attachment,omitempty"`
Text string `json:"text,omitempty"`
Footer string `json:"footer,omitempty"`
ListItems []flows.ListItems `json:"list_items,omitempty"`
ButtonText string `json:"button_text,omitempty"`
QuickReplies []string `json:"quick_replies,omitempty"`
InteractionType string `json:"interaction_type,omitempty"`
ActionURL string `json:"action_url,omitempty"`
FlowID string `json:"flow_id,omitempty"`
FlowData flows.FlowData `json:"flow_data,omitempty"`
FlowScreen string `json:"flow_screen,omitempty"`
FlowMode string `json:"flow_mode,omitempty"`
HeaderType string `json:"header_type,omitempty"`
HeaderText string `json:"header_text,omitempty"`
Attachment string `json:"attachment,omitempty"`
Text string `json:"text,omitempty"`
Footer string `json:"footer,omitempty"`
ListItems []flows.ListItems `json:"list_items,omitempty"`
ButtonText string `json:"button_text,omitempty"`
QuickReplies []string `json:"quick_replies,omitempty"`
InteractionType string `json:"interaction_type,omitempty"`
ActionURL string `json:"action_url,omitempty"`
FlowID string `json:"flow_id,omitempty"`
FlowData flows.FlowData `json:"flow_data,omitempty"`
FlowScreen string `json:"flow_screen,omitempty"`
FlowMode string `json:"flow_mode,omitempty"`
OrderDetails flows.OrderDetails `json:"order_details,omitempty"`
}

type Header struct {
Expand All @@ -65,6 +67,7 @@ func NewSendWppMsg(
flowData flows.FlowData,
flowScreen string,
flowMode string,
orderDetails flows.OrderDetails,
allURNs bool) *SendWppMsgAction {
return &SendWppMsgAction{
baseAction: newBaseAction(TypeSendWppMsg, uuid),
Expand All @@ -83,6 +86,7 @@ func NewSendWppMsg(
FlowData: flowData,
FlowScreen: flowScreen,
FlowMode: flowMode,
OrderDetails: orderDetails,
},
AllURNs: allURNs,
}
Expand Down Expand Up @@ -142,6 +146,103 @@ func (a *SendWppMsgAction) Execute(run flows.FlowRun, step flows.Step, logModifi
}
}

orderDetailsMessage := flows.OrderDetailsMessage{}
if a.InteractionType == "order_details" {
evaluatedReferenceID, _ := run.EvaluateTemplate(a.OrderDetails.ReferenceID)
evaluatedOrderItems, _ := run.EvaluateTemplate(a.OrderDetails.Items)

orderItems := []flows.MessageOrderItem{}
tempOrderItems := []map[string]interface{}{}
err := json.Unmarshal([]byte(evaluatedOrderItems), &tempOrderItems)
if err != nil {
logEvent(events.NewErrorf("error unmarshalling order items: %v", err))
return nil
}

evaluatedOrderTax, _ := run.EvaluateTemplate(a.OrderDetails.Tax.Value)
evaluatedOrderTaxDescription, _ := run.EvaluateTemplate(a.OrderDetails.Tax.Description)

evaluatedOrderShipping, _ := run.EvaluateTemplate(a.OrderDetails.Shipping.Value)
evaluatedOrderShippingDescription, _ := run.EvaluateTemplate(a.OrderDetails.Shipping.Description)

evaluatedOrderDiscount, _ := run.EvaluateTemplate(a.OrderDetails.Discount.Value)
evaluatedOrderDiscountDescription, _ := run.EvaluateTemplate(a.OrderDetails.Discount.Description)
evaluatedOrderDiscountProgramName, _ := run.EvaluateTemplate(a.OrderDetails.Discount.ProgramName)

evaluatedOrderPaymentType, _ := run.EvaluateTemplate(a.OrderDetails.PaymentSettings.Type)
evaluatedOrderPaymentLink, _ := run.EvaluateTemplate(a.OrderDetails.PaymentSettings.PaymentLink)

evaluatedOrderPixKey, _ := run.EvaluateTemplate(a.OrderDetails.PaymentSettings.PixConfig.Key)
evaluatedOrderPixKeyType, _ := run.EvaluateTemplate(a.OrderDetails.PaymentSettings.PixConfig.KeyType)
evaluatedOrderPixMerchantName, _ := run.EvaluateTemplate(a.OrderDetails.PaymentSettings.PixConfig.MerchantName)
evaluatedOrderPixCode, _ := run.EvaluateTemplate(a.OrderDetails.PaymentSettings.PixConfig.Code)

convertedOrderTax, err := strconv.ParseFloat(evaluatedOrderTax, 64)
if err != nil {
logEvent(events.NewErrorf("error converting order tax %s to int: %v", evaluatedOrderTax, err))
return nil
}

convertedOrderShipping, err := strconv.ParseFloat(evaluatedOrderShipping, 64)
if err != nil {
logEvent(events.NewErrorf("error converting order shipping %s to int: %v", evaluatedOrderShipping, err))
return nil
}

convertedOrderDiscount, err := strconv.ParseFloat(evaluatedOrderDiscount, 64)
if err != nil {
logEvent(events.NewErrorf("error converting order discount %s to int: %v", evaluatedOrderDiscount, err))
return nil
}

subTotalValue := 0
for _, item := range orderItems {
if item.SaleAmount != 0 {
subTotalValue += item.SaleAmount * item.Quantity
} else {
subTotalValue += item.Amount * item.Quantity
}
}

taxValue := int(convertedOrderTax * 100)
shippingValue := int(convertedOrderShipping * 100)
discountValue := int(convertedOrderDiscount * 100)
totalValue := subTotalValue + taxValue + shippingValue - discountValue

orderDetailsMessage = flows.OrderDetailsMessage{
ReferenceID: evaluatedReferenceID,
PaymentSettings: &flows.OrderPaymentSettings{
Type: evaluatedOrderPaymentType,
PaymentLink: evaluatedOrderPaymentLink,
PixConfig: &flows.OrderPixConfig{
Key: evaluatedOrderPixKey,
KeyType: evaluatedOrderPixKeyType,
MerchantName: evaluatedOrderPixMerchantName,
Code: evaluatedOrderPixCode,
},
},
TotalAmount: totalValue,
Order: &flows.MessageOrder{
Items: &orderItems,
Subtotal: subTotalValue,
Tax: &flows.MessageOrderAmountWithDescription{
Value: taxValue,
Description: evaluatedOrderTaxDescription,
},
Shipping: &flows.MessageOrderAmountWithDescription{
Value: shippingValue,
Description: evaluatedOrderShippingDescription,
},
Discount: &flows.MessageOrderDiscount{
Value: discountValue,
Description: evaluatedOrderDiscountDescription,
ProgramName: evaluatedOrderDiscountProgramName,
},
},
}

}

destinations := run.Contact().ResolveDestinations(a.AllURNs)

for _, dest := range destinations {
Expand All @@ -150,14 +251,14 @@ func (a *SendWppMsgAction) Execute(run flows.FlowRun, step flows.Step, logModifi
channelRef = assets.NewChannelReference(dest.Channel.UUID(), dest.Channel.Name())
}

msg := flows.NewMsgWppOut(dest.URN.URN(), channelRef, a.InteractionType, a.HeaderType, evaluatedHeaderText, evaluatedText, evaluatedFooter, ctaMessage, listMessage, flowMessage, evaluatedAttachments, evaluatedReplyMessage, a.Topic)
msg := flows.NewMsgWppOut(dest.URN.URN(), channelRef, a.InteractionType, a.HeaderType, evaluatedHeaderText, evaluatedText, evaluatedFooter, ctaMessage, listMessage, flowMessage, orderDetailsMessage, evaluatedAttachments, evaluatedReplyMessage, a.Topic)
logEvent(events.NewMsgWppCreated(msg))
}

// if we couldn't find a destination, create a msg without a URN or channel and it's up to the caller
// to handle that as they want
if len(destinations) == 0 {
msg := flows.NewMsgWppOut(urns.NilURN, nil, a.InteractionType, a.HeaderType, evaluatedHeaderText, evaluatedText, evaluatedFooter, ctaMessage, listMessage, flowMessage, evaluatedAttachments, evaluatedReplyMessage, flows.NilMsgTopic)
msg := flows.NewMsgWppOut(urns.NilURN, nil, a.InteractionType, a.HeaderType, evaluatedHeaderText, evaluatedText, evaluatedFooter, ctaMessage, listMessage, flowMessage, orderDetailsMessage, evaluatedAttachments, evaluatedReplyMessage, flows.NilMsgTopic)
logEvent(events.NewMsgWppCreated(msg))
}

Expand Down
120 changes: 96 additions & 24 deletions flows/msg_wpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@ import (

type MsgWppOut struct {
BaseMsg
InteractionType_ string `json:"interaction_type,omitempty"`
HeaderType_ string `json:"header_type,omitempty"`
HeaderText_ string `json:"header_text,omitempty"`
Text_ string `json:"text,omitempty"`
Footer_ string `json:"footer,omitempty"`
Topic_ MsgTopic `json:"topic,omitempty"`
ListMessage_ ListMessage `json:"list_message,omitempty"`
Attachments_ []utils.Attachment `json:"attachments,omitempty"`
QuickReplies_ []string `json:"quick_replies,omitempty"`
TextLanguage envs.Language `json:"text_language,omitempty"`
CTAMessage_ CTAMessage `json:"cta_message,omitempty"`
FlowMessage_ FlowMessage `json:"flow_message,omitempty"`
InteractionType_ string `json:"interaction_type,omitempty"`
HeaderType_ string `json:"header_type,omitempty"`
HeaderText_ string `json:"header_text,omitempty"`
Text_ string `json:"text,omitempty"`
Footer_ string `json:"footer,omitempty"`
Topic_ MsgTopic `json:"topic,omitempty"`
ListMessage_ ListMessage `json:"list_message,omitempty"`
Attachments_ []utils.Attachment `json:"attachments,omitempty"`
QuickReplies_ []string `json:"quick_replies,omitempty"`
TextLanguage envs.Language `json:"text_language,omitempty"`
CTAMessage_ CTAMessage `json:"cta_message,omitempty"`
FlowMessage_ FlowMessage `json:"flow_message,omitempty"`
OrderDetailsMessage_ OrderDetailsMessage `json:"order_details_message,omitempty"`
}

type ListMessage struct {
Expand Down Expand Up @@ -50,24 +51,93 @@ type ListItems struct {
UUID string `json:"uuid,omitempty"`
}

func NewMsgWppOut(urn urns.URN, channel *assets.ChannelReference, interactionType, headerType, headerText, text, footer string, ctaMessage CTAMessage, listMessage ListMessage, flowMessage FlowMessage, attachments []utils.Attachment, replyButtons []string, topic MsgTopic) *MsgWppOut {
// Message order details structs, with string-like attributes to be evaluated and calculated
type OrderAmountWithDescription struct {
Value string `json:"value,omitempty"`
Description string `json:"description,omitempty"`
}

type OrderDiscount struct {
Value string `json:"value,omitempty"`
Description string `json:"description,omitempty"`
ProgramName string `json:"program_name,omitempty"`
}

type OrderPixConfig struct {
Key string `json:"key,omitempty"`
KeyType string `json:"key_type,omitempty"`
MerchantName string `json:"merchant_name,omitempty"`
Code string `json:"code,omitempty"`
}

type OrderPaymentSettings struct {
Type string `json:"type,omitempty"`
PaymentLink string `json:"payment_link,omitempty"`
PixConfig *OrderPixConfig `json:"pix_config,omitempty"`
}

type OrderDetails struct {
ReferenceID string `json:"re ference_id"`
Items string `json:"item_list"`
Tax *OrderAmountWithDescription `json:"tax"`
Shipping *OrderAmountWithDescription `json:"shipping"`
Discount *OrderDiscount `json:"discount"`
PaymentSettings *OrderPaymentSettings `json:"payment_settings"`
}

// Message for order details, with attribute types defined such as int values
type OrderDetailsMessage struct {
ReferenceID string `json:"reference_id,omitempty"`
PaymentSettings *OrderPaymentSettings `json:"payment_settings,omitempty"`
TotalAmount int `json:"total_amount,omitempty"`
Order *MessageOrder `json:"order,omitempty"`
}

type MessageOrder struct {
Items *[]MessageOrderItem `json:"items,omitempty"`
Subtotal int `json:"subtotal,omitempty"`
Tax *MessageOrderAmountWithDescription `json:"tax,omitempty"`
Shipping *MessageOrderAmountWithDescription `json:"shipping,omitempty"`
Discount *MessageOrderDiscount `json:"discount,omitempty"`
}

type MessageOrderItem struct {
RetailerID string `json:"retailer_id"`
Name string `json:"name"`
Amount int `json:"amount"`
Quantity int `json:"quantity"`
SaleAmount int `json:"sale_amount,omitempty"`
}
type MessageOrderAmountWithDescription struct {
Value int `json:"value,omitempty"`
Description string `json:"description,omitempty"`
}

type MessageOrderDiscount struct {
Value int `json:"value,omitempty"`
Description string `json:"description,omitempty"`
ProgramName string `json:"program_name,omitempty"`
}

func NewMsgWppOut(urn urns.URN, channel *assets.ChannelReference, interactionType, headerType, headerText, text, footer string, ctaMessage CTAMessage, listMessage ListMessage, flowMessage FlowMessage, orderDetailsMessage OrderDetailsMessage, attachments []utils.Attachment, replyButtons []string, topic MsgTopic) *MsgWppOut {
return &MsgWppOut{
BaseMsg: BaseMsg{
UUID_: MsgUUID(uuids.New()),
URN_: urn,
Channel_: channel,
},
HeaderType_: headerType,
InteractionType_: interactionType,
HeaderText_: headerText,
Text_: text,
Footer_: footer,
ListMessage_: listMessage,
Attachments_: attachments,
QuickReplies_: replyButtons,
Topic_: topic,
CTAMessage_: ctaMessage,
FlowMessage_: flowMessage,
HeaderType_: headerType,
InteractionType_: interactionType,
HeaderText_: headerText,
Text_: text,
Footer_: footer,
ListMessage_: listMessage,
Attachments_: attachments,
QuickReplies_: replyButtons,
Topic_: topic,
CTAMessage_: ctaMessage,
FlowMessage_: flowMessage,
OrderDetailsMessage_: orderDetailsMessage,
}
}

Expand All @@ -92,3 +162,5 @@ func (m *MsgWppOut) QuickReplies() []string { return m.QuickReplies_ }
func (m *MsgWppOut) CTAMessage() CTAMessage { return m.CTAMessage_ }

func (m *MsgWppOut) FlowMessage() FlowMessage { return m.FlowMessage_ }

func (m *MsgWppOut) OrderDetailsMessage() OrderDetailsMessage { return m.OrderDetailsMessage_ }
2 changes: 2 additions & 0 deletions flows/msg_wpp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func TestMsgWppOut(t *testing.T) {
flows.CTAMessage{},
flows.ListMessage{},
flows.FlowMessage{},
flows.OrderDetailsMessage{},
[]utils.Attachment{
utils.Attachment("image/jpeg:https://example.com/test.jpg"),
utils.Attachment("audio/mp3:https://example.com/test.mp3"),
Expand All @@ -49,6 +50,7 @@ func TestMsgWppOut(t *testing.T) {
"cta_message": {},
"list_message": {},
"flow_message": {},
"order_details_message": {},
"attachments": ["image/jpeg:https://example.com/test.jpg", "audio/mp3:https://example.com/test.mp3"],
"topic": "agent"
}`), marshaled, "JSON mismatch")
Expand Down

0 comments on commit 760e7af

Please sign in to comment.