Skip to content

Commit

Permalink
Merge pull request #12148 from transcom/B-19035-Update-Payment-Remind…
Browse files Browse the repository at this point in the history
…er-Email-Template

B 19035 update payment reminder email template
  • Loading branch information
JamesHawks224 authored Mar 14, 2024
2 parents 41da512 + 07615a1 commit 8acfdec
Show file tree
Hide file tree
Showing 7 changed files with 501 additions and 94 deletions.
10 changes: 10 additions & 0 deletions cmd/milmove-tasks/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ func main() {
initSaveGHCFuelPriceFlags(saveGHCFuelPriceDataCommand.Flags())
root.AddCommand(saveGHCFuelPriceDataCommand)

sendPaymentReminderCommand := &cobra.Command{
Use: "send-payment-reminder",
Short: "sends payment reminder email",
Long: "sends payment reminder email",
RunE: sendPaymentReminder,
SilenceUsage: true,
}
initPaymentReminderFlags(sendPaymentReminderCommand.Flags())
root.AddCommand(sendPaymentReminderCommand)

postFileToGEXCommand := &cobra.Command{
Use: "post-file-to-gex",
Short: "posts a file to GEX",
Expand Down
102 changes: 102 additions & 0 deletions cmd/milmove-tasks/send_payment_reminder_email.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package main

import (
"log"
"strings"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"go.uber.org/zap"

"github.com/transcom/mymove/pkg/appcontext"
"github.com/transcom/mymove/pkg/cli"
"github.com/transcom/mymove/pkg/logging"
"github.com/transcom/mymove/pkg/notifications"
)

func checkPaymentReminderConfig(v *viper.Viper, logger *zap.Logger) error {

logger.Debug("checking config")

err := cli.CheckDatabase(v, logger)
if err != nil {
return err
}

return cli.CheckEmail(v)
}

func initPaymentReminderFlags(flag *pflag.FlagSet) {

// DB Config
cli.InitDatabaseFlags(flag)

// Logging Levels
cli.InitLoggingFlags(flag)

// Email
cli.InitEmailFlags(flag)

// Don't sort flags
flag.SortFlags = false
}

// Command (test eamil): go run ./cmd/milmove-tasks send-payment-reminder
// Command (send email): go run ./cmd/milmove-tasks send-payment-reminder --email-backend=ses --aws-ses-domain=devlocal.dp3.us --aws-ses-region=us-gov-west-1
func sendPaymentReminder(cmd *cobra.Command, args []string) error {
err := cmd.ParseFlags(args)
if err != nil {
return errors.Wrap(err, "Could not parse args")
}
flags := cmd.Flags()
v := viper.New()
err = v.BindPFlags(flags)
if err != nil {
return errors.Wrap(err, "Could not bind flags")
}
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
v.AutomaticEnv()

dbEnv := v.GetString(cli.DbEnvFlag)

logger, _, err := logging.Config(
logging.WithEnvironment(dbEnv),
logging.WithLoggingLevel(v.GetString(cli.LoggingLevelFlag)),
logging.WithStacktraceLength(v.GetInt(cli.StacktraceLengthFlag)),
)
if err != nil {
log.Fatalf("Failed to initialize Zap logging due to %v", err)
}
zap.ReplaceGlobals(logger)

err = checkPaymentReminderConfig(v, logger)
if err != nil {
logger.Fatal("invalid configuration", zap.Error(err))
}

// Create a connection to the DB
dbConnection, err := cli.InitDatabase(v, logger)
if err != nil {
logger.Fatal("Connecting to DB", zap.Error(err))
}

appCtx := appcontext.NewAppContext(dbConnection, logger, nil)

notificationSender, notificationSenderErr := notifications.InitEmail(v, logger)
if notificationSenderErr != nil {
logger.Fatal("notification sender sending not enabled", zap.Error(notificationSenderErr))
}

movePaymentReminderNotifier, err := notifications.NewPaymentReminder()
if err != nil {
logger.Fatal("initializing MoveReviewed", zap.Error(err))
}

err = notificationSender.SendNotification(appCtx, movePaymentReminderNotifier)
if err != nil {
logger.Fatal("Emails failed to send", zap.Error(err))
}
return nil
}
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
<p>We hope your move to {{.DestinationDutyLocation}} went well.</p>
<p><strong>***</strong> DO NOT REPLY directly to this email <strong>***</strong></p>

<p>It’s been a couple of weeks, so we want to make sure you get paid for that move. {{.IncentiveTxt}}</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>

<p>To get your incentive, you need to request payment.</p>
<p>Next steps:</p>

<p>Log in to MilMove and request payment</p>
<p>To get your payment, you need to login to MilMove, document expenses, and request payment.</p>

<p>We want to pay you for your PPM, but we can’t do that until you document expenses and request payment.</p>

<p>To do that</p>
<p>To do that:</p>

<p>
<ul>
<li><a href="{{.MyMoveLink}}">Log in to MilMove</a></li>
<li>Click Request Payment</li>
<li>Follow the instructions.</li>
<li>Log into <a href={{.MyMoveLink}}>MilMove</a></li>
<li>Click on "Upload PPM Documents"</li>
<li>Follow the instructions</li>
</ul>

<p>What documents do you need?</p>
</p>

<p>To request payment, you should have copies of:</p>

<ul>
<li>Weight tickets from certified scales, documenting empty and full weights for all vehicles and trailers you used for your move</li>
<li>Receipts for reimbursable expenses (see our moving tips PDF for more info)</li>
<li> Weight tickets from certified scales, documenting empty and full weights for all vehicles and trailers you used for your move.</li>
<li> Receipts for reimbursable expenses.</li>
</ul>

<p>MilMove will ask you to upload copies of your documents as you complete your payment request.</p>

<p>What if you’re missing documents?</p>

<p>If you’re missing receipts, you can still request payment. You might not get reimbursement or a tax credit for those expenses.</p>
{{ if .TOName }}
<p>If you’re missing certified weight tickets, your PPPO will have to help. Call {{.TOName}}{{if .TOPhone}} at {{.TOPhone}}{{end}} to have them walk you through it. Reference your move locator code: {{.Locator}}.</p>
{{- end -}}
<p>MilMove will ask you to upload copies of your documents as you complete your payment request.

{{ if not .TOName }}
<p>If you are missing weight tickets, someone from the government will have to help. Consult Military OneSource's <a href="https://www.militaryonesource.mil/moving-housing/moving/planning-your-move/customer-service-contacts-for-military-pcs/">directory of PCS-related contacts</a> to find your best contact and reference your move code {{.Locator}}.</p>
{{- end }}
<p>If you are missing reciepts, you may still be able to request payment, but you will need assistance from your transportation office.</p>

<p>Log in to MilMove to complete your request and get paid.</p>
<p>Payment request must be submitted within 45 days of your move date.</p>

<p>Request payment within 45 days of your move date or you might not be able to get paid.</p>
<p>If you have any questions, contact a government transportation office. You can see a listing of
transportation offices on Military OneSource here: &lt;<a href="{{.OneSourceLink}}">{{.OneSourceLink}}</a>&gt;</p>

{{if .TOName}}<p>If you have any questions or concerns, you can talk to a human! Call your local PPPO at {{.TOName}}{{if .TOPhone}} at {{.TOPhone}}{{end}}. Reference your move locator code: {{.Locator}}.</p>{{end}}
<p>Thank you,</p>
<p>USTRANSCOM MilMove Team</p>
<p>The information contained in this email may contain Privacy Act information and is therefore protected
under the Privacy Act of 1974. Failure to protect Privacy Act information could result in a $5,000 fine.</p>
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
We hope your move to {{.DestinationDutyLocation}} went well.
*** DO NOT REPLY directly to this email ***

It’s been a couple of weeks, so we want to make sure you get paid for that move. {{.IncentiveTxt}}
This is a reminder that your PPM with the assigned move code {{.Locator}} from {{.OriginDutyLocation}}
to {{.DestinationDutyLocation}} is awaiting action in MilMove.

To get your incentive, you need to request payment.
Next steps:

Log in to MilMove and request payment
To get your payment, you need to login to MilMove, document expenses, and request payment.

We want to pay you for your PPM, but we can’t do that until you document expenses and request payment.
To do that:

To do that

* Log in to MilMove
* Click Request Payment
* Follow the instructions.

What documents do you need?
* Log into MilMove<{{.MyMoveLink}}>
* Click on "Upload PPM Documents"
* Follow the instructions

To request payment, you should have copies of:
* Weight tickets from certified scales, documenting empty and full weights for all vehicles and trailers you used for your move
* Receipts for reimbursable expenses (see our moving tips PDF for more info)

* Weight tickets from certified scales, documenting empty and full weights for all vehicles and trailers you used for your move.
* Receipts for reimbursable expenses.

MilMove will ask you to upload copies of your documents as you complete your payment request.

What if you’re missing documents?
If you are missing reciepts, you may still be able to request payment, but you will need assistance from your transportation office.

If you’re missing receipts, you can still request payment. You might not get reimbursement or a tax credit for those expenses.
Payment request must be submitted within 45 days of your move date.

If you’re missing certified weight tickets, your PPPO will have to help. {{if .TOName}}Call {{.TOName}}{{if .TOPhone}} at {{.TOPhone}}{{end}} to have them walk you through it. Reference your move locator code: {{.Locator}}.{{end}}
If you have any questions, contact a government transportation office. You can see a listing of transportation offices on Military OneSource here: <{{.OneSourceLink}}>

Log in to MilMove to complete your request and get paid.
Thank you,

Request payment within 45 days of your move date or you might not be able to get paid.
USTRANSCOM MilMove Team

{{if .TOName}}If you have any questions or concerns, you can talk to a human! Call your local PPPO at {{.TOName}}{{if .TOPhone}} at {{ .TOPhone}}{{end}}. Reference your move locator code: {{.Locator}}.{{end}}
The information contained in this email may contain Privacy Act information and is therefore protected
under the Privacy Act of 1974. Failure to protect Privacy Act information could result in a $5,000 fine.
25 changes: 25 additions & 0 deletions pkg/factory/ppm_shipment_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,31 @@ func AddSignedCertificationToPPMShipment(db *pop.Connection, ppmShipment *models
}
ppmShipment.SignedCertification = &signedCertification
}

func GetTraitPPMShipmentReadyForPaymentRequest() []Customization {
estimatedWeight := unit.Pound(200)
estimateIncentive := unit.Cents(1000)
return []Customization{
{
Model: models.PPMShipment{
Status: models.PPMShipmentStatusWaitingOnCustomer,
EstimatedWeight: &estimatedWeight,
EstimatedIncentive: &estimateIncentive,
},
},
{
Model: models.MTOShipment{
Status: models.MTOShipmentStatusApproved,
},
},
{
Model: models.Move{
Status: models.MoveStatusAPPROVED,
},
},
}
}

func GetTraitApprovedPPMWithActualInfo() []Customization {
submittedTime := time.Now()
approvedTime := submittedTime.AddDate(0, 0, 3)
Expand Down
62 changes: 16 additions & 46 deletions pkg/notifications/move_payment_reminder.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,14 @@ 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"`
WeightEstimate *unit.Pound `db:"weight_estimate"`
IncentiveEstimate *unit.Cents `db:"incentive_estimate"`
IncentiveTxt string
TOName *string `db:"transportation_office_name"`
TOPhone *string `db:"transportation_office_phone"`
MoveDate string `db:"move_date"`
Locator string `db:"locator"`
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"`
}

// GetEmailInfo fetches payment email information
Expand All @@ -66,23 +64,15 @@ func (m PaymentReminder) GetEmailInfo(appCtx appcontext.AppContext) (PaymentRemi
COALESCE(ps.estimated_incentive, 0) AS incentive_estimate,
ps.expected_departure_date as move_date,
dln.name AS new_duty_location_name,
tos.name AS transportation_office_name,
opl.number AS transportation_office_phone,
dln2.name AS origin_duty_location_name,
m.locator
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
LEFT JOIN duty_locations dln ON o.new_duty_location_id = dln.id
LEFT JOIN transportation_offices tos ON tos.id = dln.transportation_office_id
LEFT JOIN office_phone_lines opl on opl.transportation_office_id = tos.id and opl.id =
(
SELECT opl2.id FROM office_phone_lines opl2
WHERE opl2.is_dsn_number IS false
AND tos.id = opl2.transportation_office_id
LIMIT 1
)
JOIN duty_locations dln ON o.new_duty_location_id = dln.id
JOIN duty_locations dln2 ON o.origin_duty_location_id = dln2.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 @@ -118,28 +108,11 @@ func (m PaymentReminder) emails(appCtx appcontext.AppContext) ([]emailContent, e
func (m PaymentReminder) formatEmails(appCtx appcontext.AppContext, PaymentReminderEmailInfos PaymentReminderEmailInfos) ([]emailContent, error) {
var emails []emailContent
for _, PaymentReminderEmailInfo := range PaymentReminderEmailInfos {
incentiveTxt := ""
if PaymentReminderEmailInfo.WeightEstimate.Int() > 0 && PaymentReminderEmailInfo.IncentiveEstimate.Int() > 0 {
incentiveTxt = fmt.Sprintf("You expected to move about %d lbs, which gives you an estimated incentive of %s.", PaymentReminderEmailInfo.WeightEstimate.Int(), PaymentReminderEmailInfo.IncentiveEstimate.ToDollarString())
}
var toPhone *string
if PaymentReminderEmailInfo.TOPhone != nil {
toPhone = PaymentReminderEmailInfo.TOPhone
}

var toName *string
if PaymentReminderEmailInfo.TOPhone != nil {
toName = PaymentReminderEmailInfo.TOName
}

htmlBody, textBody, err := m.renderTemplates(appCtx, PaymentReminderEmailData{
OriginDutyLocation: PaymentReminderEmailInfo.OriginDutyLocationName,
DestinationDutyLocation: PaymentReminderEmailInfo.NewDutyLocationName,
WeightEstimate: fmt.Sprintf("%d", PaymentReminderEmailInfo.WeightEstimate.Int()),
IncentiveEstimate: PaymentReminderEmailInfo.IncentiveEstimate.ToDollarString(),
IncentiveTxt: incentiveTxt,
TOName: toName,
TOPhone: toPhone,
Locator: PaymentReminderEmailInfo.Locator,
OneSourceLink: OneSourceTransportationOfficeLink,
MyMoveLink: MyMoveLink,
})
if err != nil {
Expand All @@ -153,7 +126,7 @@ func (m PaymentReminder) formatEmails(appCtx appcontext.AppContext, PaymentRemin
}
smEmail := emailContent{
recipientEmail: *PaymentReminderEmailInfo.Email,
subject: fmt.Sprintf("[MilMove] Reminder: request payment for your move to %s (move %s)", PaymentReminderEmailInfo.NewDutyLocationName, PaymentReminderEmailInfo.Locator),
subject: "Complete your Personally Procured Move (PPM)",
htmlBody: htmlBody,
textBody: textBody,
onSuccess: m.OnSuccess(appCtx, PaymentReminderEmailInfo),
Expand Down Expand Up @@ -201,13 +174,10 @@ func (m PaymentReminder) OnSuccess(appCtx appcontext.AppContext, PaymentReminder

// PaymentReminderEmailData is used to render an email template
type PaymentReminderEmailData struct {
OriginDutyLocation string
DestinationDutyLocation string
WeightEstimate string
IncentiveEstimate string
IncentiveTxt string
TOName *string
TOPhone *string
Locator string
OneSourceLink string
MyMoveLink string
}

Expand Down
Loading

0 comments on commit 8acfdec

Please sign in to comment.