Skip to content

Commit

Permalink
Merge pull request #14471 from transcom/B-21458
Browse files Browse the repository at this point in the history
B-21458 Main
  • Loading branch information
brianmanley-caci authored Dec 26, 2024
2 parents d7d715b + 91bfafd commit f266d06
Show file tree
Hide file tree
Showing 10 changed files with 444 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p><strong>***</strong> DO NOT REPLY directly to this email <strong>***</strong></p>

<p>This is a reminder that your PPM with the <strong>assigned move code {{.Locator}}</strong> from
<strong>{{.OriginDutyLocation}}</strong> to <strong>{{.DestinationDutyLocation}}</strong> is awaiting action in MilMove.</p>
<strong>{{.OriginDutyLocation}}</strong> to <strong>{{.DestinationLocation}}</strong> is awaiting action in MilMove.</p>

<p>Next steps:</p>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*** DO NOT REPLY directly to this email ***

This is a reminder that your PPM with the assigned move code {{.Locator}} from {{.OriginDutyLocation}}
to {{.DestinationDutyLocation}} is awaiting action in MilMove.
to {{.DestinationLocation}} is awaiting action in MilMove.

Next steps:

Expand Down
14 changes: 14 additions & 0 deletions pkg/models/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,20 @@ func (a *Address) LineFormat() string {
return strings.Join(parts, ", ")
}

// LineDisplayFormat returns the address in a single line representation of the US mailing address format
func (a *Address) LineDisplayFormat() string {
optionalStreetAddress2 := ""
if a.StreetAddress2 != nil && len(*a.StreetAddress2) > 0 {
optionalStreetAddress2 = " " + *a.StreetAddress2
}
optionalStreetAddress3 := ""
if a.StreetAddress3 != nil && len(*a.StreetAddress3) > 0 {
optionalStreetAddress3 = " " + *a.StreetAddress3
}

return fmt.Sprintf("%s%s%s, %s, %s %s", a.StreetAddress1, optionalStreetAddress2, optionalStreetAddress3, a.City, a.State, a.PostalCode)
}

// NotImplementedCountryCode is the default for unimplemented country code lookup
type NotImplementedCountryCode struct {
message string
Expand Down
36 changes: 36 additions & 0 deletions pkg/models/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,40 @@ func (suite *ModelSuite) TestAddressFormat() {
formattedAddress = newAddress.LineFormat()

suite.Equal("street 1, street 2, street 3, city, state, 90210, UNITED STATES", formattedAddress)

formattedAddress = newAddress.LineDisplayFormat()

suite.Equal("street 1 street 2 street 3, city, state 90210", formattedAddress)
}

func (suite *ModelSuite) TestPartialAddressFormat() {
country := factory.FetchOrBuildCountry(suite.DB(), nil, nil)
newAddress := &m.Address{
StreetAddress1: "street 1",
StreetAddress2: nil,
StreetAddress3: nil,
City: "city",
State: "state",
PostalCode: "90210",
County: m.StringPointer("County"),
Country: &country,
CountryId: &country.ID,
}

verrs, err := newAddress.Validate(nil)

suite.NoError(err)
suite.False(verrs.HasAny(), "Error validating model")

formattedAddress := newAddress.Format()

suite.Equal("street 1\ncity, state 90210", formattedAddress)

formattedAddress = newAddress.LineFormat()

suite.Equal("street 1, city, state, 90210, UNITED STATES", formattedAddress)

formattedAddress = newAddress.LineDisplayFormat()

suite.Equal("street 1, city, state 90210", formattedAddress)
}
18 changes: 9 additions & 9 deletions pkg/notifications/move_counseled.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,16 @@ func (m MoveCounseled) emails(appCtx appcontext.AppContext) ([]emailContent, err
}

destinationAddress := orders.NewDutyLocation.Name
isSeparateeOrRetireeOrder := orders.OrdersType == internalmessages.OrdersTypeRETIREMENT || orders.OrdersType == internalmessages.OrdersTypeSEPARATION
if isSeparateeOrRetireeOrder && len(move.MTOShipments) > 0 && move.MTOShipments[0].DestinationAddress != nil {
mtoShipDestinationAddress, streetAddr2, streetAddr3 := *move.MTOShipments[0].DestinationAddress, "", ""
if mtoShipDestinationAddress.StreetAddress2 != nil {
streetAddr2 = " " + *mtoShipDestinationAddress.StreetAddress2
isSeparateeRetiree := orders.OrdersType == internalmessages.OrdersTypeRETIREMENT || orders.OrdersType == internalmessages.OrdersTypeSEPARATION
if isSeparateeRetiree && len(move.MTOShipments) > 0 {
mtoShipment := move.MTOShipments[0]
if mtoShipment.DestinationAddress != nil {
destAddr := mtoShipment.DestinationAddress
destinationAddress = destAddr.LineDisplayFormat()
} else if mtoShipment.ShipmentType == models.MTOShipmentTypePPM {
destAddr := models.FetchAddressByID(appCtx.DB(), mtoShipment.PPMShipment.DestinationAddressID)
destinationAddress = destAddr.LineDisplayFormat()
}
if mtoShipDestinationAddress.StreetAddress3 != nil {
streetAddr3 = " " + *mtoShipDestinationAddress.StreetAddress3
}
destinationAddress = fmt.Sprintf("%s%s%s, %s, %s %s", mtoShipDestinationAddress.StreetAddress1, streetAddr2, streetAddr3, mtoShipDestinationAddress.City, mtoShipDestinationAddress.State, mtoShipDestinationAddress.PostalCode)
}

if serviceMember.PersonalEmail == nil {
Expand Down
74 changes: 74 additions & 0 deletions pkg/notifications/move_counseled_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,77 @@ func (suite *NotificationSuite) TestCounselorApprovedMoveForRetiree() {
suite.Contains(email.textBody, move.MTOShipments[0].DestinationAddress.State)
suite.Contains(email.textBody, move.MTOShipments[0].DestinationAddress.PostalCode)
}

func (suite *NotificationSuite) TestMoveCounseledDestinationIsShipmentForPpmSeparatee() {
move := SetupPpmMove(suite, internalmessages.OrdersTypeSEPARATION)
notification := NewMoveCounseled(move.ID)
expectedSubject := "Your counselor has approved your move details"

emails, err := notification.emails(suite.AppContextWithSessionForTest(&auth.Session{
ServiceMemberID: move.Orders.ServiceMember.ID,
ApplicationName: auth.MilApp,
}))

suite.NoError(err)
suite.Equal(len(emails), 1)

email := emails[0]
sm := move.Orders.ServiceMember
mtoShipment := move.MTOShipments[0]
ppmShipment := mtoShipment.PPMShipment
destinationAddress := ppmShipment.DestinationAddress
suite.Equal(email.recipientEmail, *sm.PersonalEmail)
suite.Equal(email.subject, expectedSubject)
suite.Contains(email.htmlBody, "from "+move.Orders.OriginDutyLocation.Name+" to "+destinationAddress.LineDisplayFormat()+" in the ")
suite.Contains(email.textBody, "from "+move.Orders.OriginDutyLocation.Name+" to "+destinationAddress.LineDisplayFormat()+" in the ")
}

func (suite *NotificationSuite) TestMoveCounseledDestinationIsShipmentForPpmRetiree() {
move := SetupPpmMove(suite, internalmessages.OrdersTypeRETIREMENT)
notification := NewMoveCounseled(move.ID)
expectedSubject := "Your counselor has approved your move details"

emails, err := notification.emails(suite.AppContextWithSessionForTest(&auth.Session{
ServiceMemberID: move.Orders.ServiceMember.ID,
ApplicationName: auth.MilApp,
}))

suite.NoError(err)
suite.Equal(len(emails), 1)

email := emails[0]
sm := move.Orders.ServiceMember
mtoShipment := move.MTOShipments[0]
ppmShipment := mtoShipment.PPMShipment
destinationAddress := ppmShipment.DestinationAddress
suite.Equal(email.recipientEmail, *sm.PersonalEmail)
suite.Equal(email.subject, expectedSubject)
suite.Contains(email.htmlBody, "from "+move.Orders.OriginDutyLocation.Name+" to "+destinationAddress.LineDisplayFormat()+" in the ")
suite.Contains(email.textBody, "from "+move.Orders.OriginDutyLocation.Name+" to "+destinationAddress.LineDisplayFormat()+" in the ")
}

func (suite *NotificationSuite) TestMoveCounseledDestinationIsDutyStationForPpmPcsType() {
move := SetupPpmMove(suite, internalmessages.OrdersTypePERMANENTCHANGEOFSTATION)
notification := NewMoveCounseled(move.ID)
expectedSubject := "Your counselor has approved your move details"

emails, err := notification.emails(suite.AppContextWithSessionForTest(&auth.Session{
ServiceMemberID: move.Orders.ServiceMember.ID,
ApplicationName: auth.MilApp,
}))

suite.NoError(err)
suite.Equal(len(emails), 1)

email := emails[0]
sm := move.Orders.ServiceMember
mtoShipment := move.MTOShipments[0]
ppmShipment := mtoShipment.PPMShipment
destinationAddress := ppmShipment.DestinationAddress
suite.Equal(email.recipientEmail, *sm.PersonalEmail)
suite.Equal(email.subject, expectedSubject)
suite.NotContains(email.htmlBody, destinationAddress.LineDisplayFormat())
suite.NotContains(email.textBody, destinationAddress.LineDisplayFormat())
suite.Contains(email.htmlBody, "from "+move.Orders.OriginDutyLocation.Name+" to "+move.Orders.NewDutyLocation.Name+" in the ")
suite.Contains(email.textBody, "from "+move.Orders.OriginDutyLocation.Name+" to "+move.Orders.NewDutyLocation.Name+" in the ")
}
81 changes: 62 additions & 19 deletions pkg/notifications/move_payment_reminder.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/transcom/mymove/pkg/appcontext"
"github.com/transcom/mymove/pkg/assets"
"github.com/transcom/mymove/pkg/gen/internalmessages"
"github.com/transcom/mymove/pkg/models"
"github.com/transcom/mymove/pkg/unit"
)
Expand Down Expand Up @@ -46,14 +47,21 @@ type PaymentReminderEmailInfos []PaymentReminderEmailInfo

// PaymentReminderEmailInfo contains payment reminder data for rendering a template
type PaymentReminderEmailInfo struct {
ServiceMemberID uuid.UUID `db:"id"`
Email *string `db:"personal_email"`
NewDutyLocationName string `db:"new_duty_location_name"`
OriginDutyLocationName string `db:"origin_duty_location_name"`
MoveDate string `db:"move_date"`
Locator string `db:"locator"`
WeightEstimate *unit.Pound `db:"weight_estimate"`
IncentiveEstimate *unit.Cents `db:"incentive_estimate"`
ServiceMemberID uuid.UUID `db:"id"`
Email *string `db:"personal_email"`
NewDutyLocationName string `db:"new_duty_location_name"`
OriginDutyLocationName string `db:"origin_duty_location_name"`
MoveDate string `db:"move_date"`
Locator string `db:"locator"`
WeightEstimate *unit.Pound `db:"weight_estimate"`
IncentiveEstimate *unit.Cents `db:"incentive_estimate"`
DestinationStreet1 *string `db:"destination_street_address_1"`
DestinationStreet2 *string `db:"destination_street_address_2"`
DestinationStreet3 *string `db:"destination_street_address_3"`
DestinationCity *string `db:"destination_city"`
DestinationState *string `db:"destination_state"`
DestinationPostalCode *string `db:"destination_postal_code"`
OrdersType internalmessages.OrdersType `db:"orders_type"`
}

// GetEmailInfo fetches payment email information
Expand All @@ -65,14 +73,22 @@ func (m PaymentReminder) GetEmailInfo(appCtx appcontext.AppContext) (PaymentRemi
ps.expected_departure_date as move_date,
dln.name AS new_duty_location_name,
dln2.name AS origin_duty_location_name,
m.locator
m.locator,
da.street_address_1 AS destination_street_address_1,
da.street_address_2 AS destination_street_address_2,
da.street_address_3 AS destination_street_address_3,
da.city AS destination_city,
da.state AS destination_state,
da.postal_code AS destination_postal_code,
o.orders_type
FROM ppm_shipments ps
JOIN mto_shipments ms on ms.id = ps.shipment_id
JOIN moves m ON ms.move_id = m.id
JOIN orders o ON m.orders_id = o.id
JOIN service_members sm ON o.service_member_id = sm.id
JOIN duty_locations dln ON o.new_duty_location_id = dln.id
JOIN duty_locations dln2 ON o.origin_duty_location_id = dln2.id
JOIN addresses da ON ps.destination_postal_address_id = da.id
WHERE ps.status = 'WAITING_ON_CUSTOMER'::public."ppm_shipment_status"
AND ms.status = 'APPROVED'::public."mto_shipment_status"
AND ps.expected_departure_date <= now() - ($1)::interval
Expand Down Expand Up @@ -109,11 +125,11 @@ func (m PaymentReminder) formatEmails(appCtx appcontext.AppContext, PaymentRemin
var emails []emailContent
for _, PaymentReminderEmailInfo := range PaymentReminderEmailInfos {
htmlBody, textBody, err := m.renderTemplates(appCtx, PaymentReminderEmailData{
OriginDutyLocation: PaymentReminderEmailInfo.OriginDutyLocationName,
DestinationDutyLocation: PaymentReminderEmailInfo.NewDutyLocationName,
Locator: PaymentReminderEmailInfo.Locator,
OneSourceLink: OneSourceTransportationOfficeLink,
MyMoveLink: MyMoveLink,
OriginDutyLocation: PaymentReminderEmailInfo.OriginDutyLocationName,
DestinationLocation: getDestinationLocation(PaymentReminderEmailInfo),
Locator: PaymentReminderEmailInfo.Locator,
OneSourceLink: OneSourceTransportationOfficeLink,
MyMoveLink: MyMoveLink,
})
if err != nil {
appCtx.Logger().Error("error rendering template", zap.Error(err))
Expand Down Expand Up @@ -152,6 +168,33 @@ func (m PaymentReminder) renderTemplates(appCtx appcontext.AppContext, data Paym
return htmlBody, textBody, nil
}

func getDestinationLocation(PaymentReminderEmailInfo PaymentReminderEmailInfo) string {
destinationLocation := PaymentReminderEmailInfo.NewDutyLocationName
ordersType := PaymentReminderEmailInfo.OrdersType
street1 := PaymentReminderEmailInfo.DestinationStreet1
isSeparateeOrRetireeOrder := ordersType == internalmessages.OrdersTypeRETIREMENT || ordersType == internalmessages.OrdersTypeSEPARATION
if isSeparateeOrRetireeOrder && street1 != nil {
street2, street3, city, state, postalCode := "", "", "", "", ""
if PaymentReminderEmailInfo.DestinationStreet2 != nil {
street2 = " " + *PaymentReminderEmailInfo.DestinationStreet2
}
if PaymentReminderEmailInfo.DestinationStreet3 != nil {
street3 = " " + *PaymentReminderEmailInfo.DestinationStreet3
}
if PaymentReminderEmailInfo.DestinationCity != nil {
city = ", " + *PaymentReminderEmailInfo.DestinationCity
}
if PaymentReminderEmailInfo.DestinationState != nil {
state = ", " + *PaymentReminderEmailInfo.DestinationState
}
if PaymentReminderEmailInfo.DestinationPostalCode != nil {
postalCode = " " + *PaymentReminderEmailInfo.DestinationPostalCode
}
destinationLocation = fmt.Sprintf("%s%s%s%s%s%s", *street1, street2, street3, city, state, postalCode)
}
return destinationLocation
}

// OnSuccess callback passed to be invoked by NewNotificationSender when an email successfully sent
// saves the svs the email info along with the SES mail id to the notifications table
func (m PaymentReminder) OnSuccess(appCtx appcontext.AppContext, PaymentReminderEmailInfo PaymentReminderEmailInfo) func(string) error {
Expand All @@ -174,11 +217,11 @@ func (m PaymentReminder) OnSuccess(appCtx appcontext.AppContext, PaymentReminder

// PaymentReminderEmailData is used to render an email template
type PaymentReminderEmailData struct {
OriginDutyLocation string
DestinationDutyLocation string
Locator string
OneSourceLink string
MyMoveLink string
OriginDutyLocation string
DestinationLocation string
Locator string
OneSourceLink string
MyMoveLink string
}

// RenderHTML renders the html for the email
Expand Down
Loading

0 comments on commit f266d06

Please sign in to comment.