Skip to content

Commit

Permalink
Merge pull request #13748 from transcom/B-20654-main
Browse files Browse the repository at this point in the history
B 20654 main
  • Loading branch information
deandreJones authored Sep 25, 2024
2 parents 0255462 + 18d700d commit cc2beab
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 55 deletions.
19 changes: 17 additions & 2 deletions cmd/generate-payment-request-edi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"
"os"
"strings"
"time"

"github.com/benbjohnson/clock"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -51,6 +52,8 @@ func initFlags(flag *pflag.FlagSet) {
// Logging Levels
cli.InitLoggingFlags(flag)

cli.InitStorageFlags(flag)

// Don't sort flags
flag.SortFlags = false
}
Expand Down Expand Up @@ -125,7 +128,10 @@ func main() {
if envFlag == "production" || envFlag == "prod" || envFlag == "prd" {
isProd = true
}

if err != nil {
logger.Fatal("Failed to get next ICN value", zap.Error(err))
}
fileName := fmt.Sprintf("%s_edi858.txt", time.Now().Format("2006_01_02T15_04_05Z07_00"))
edi858c, err := generator.Generate(appCtx, paymentRequest, isProd)
if err != nil {
logger.Fatal(err.Error())
Expand All @@ -135,6 +141,15 @@ func main() {
if err != nil {
logger.Fatal(err.Error())
}
logger.Info(edi858String)
storeInvoice := invoice.StoreInvoice858C{}
verrs, err := storeInvoice.Call(appCtx, edi858String, &paymentRequest, fileName)
if verrs != nil && verrs.HasAny() {
logger.Fatal(verrs.Error())
}

fmt.Print(edi858String)
if err != nil {
logger.Fatal(err.Error())
}
logger.Info("Successfully stored invoice")
}
2 changes: 2 additions & 0 deletions migrations/app/migrations_manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,8 @@
20240819164156_update_pws_violations_pt3.up.sql
20240820125856_allow_pptas_migration.up.sql
20240820151043_add_gsr_role.up.sql
20240820204924_add_upload_type_application.up.sql
20240820210949_add_payment_request_edi_uploads_table.up.sql
20240822180409_adding_locked_price_cents_service_param.up.sql
20240909194514_pricing_unpriced_ms_cs_service_items.up.sql
20240910021542_populating_locked_price_cents_from_pricing_estimate_for_ms_and_cs_service_items.up.sql
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TYPE upload_type ADD VALUE 'APP';
COMMENT ON COLUMN uploads.upload_type IS 'Who created the upload: USER, PRIME, OFFICE, or APPLICATION';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS payment_request_edi_uploads (
id uuid primary key,
upload_id uuid not null constraint payment_request_edi_uploads_id_fkey references uploads,
created_at timestamp not null,
updated_at timestamp not null,
deleted_at timestamp with time zone
);

COMMENT ON TABLE payment_request_edi_uploads IS 'Stores uploads from the application that are run in background from generated EDI files';
COMMENT ON COLUMN payment_request_edi_uploads.upload_id IS 'Foreign key of the uploads table';
16 changes: 16 additions & 0 deletions pkg/models/payment_request_edi_upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package models

import (
"time"

"github.com/gofrs/uuid"
)

type PaymentRequestEdiUpload struct {
ID uuid.UUID `db:"id"`
UploadID uuid.UUID `db:"upload_id"`
Upload Upload `belongs_to:"uploads" fk_id:"upload_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt *time.Time `db:"deleted_at"`
}
4 changes: 4 additions & 0 deletions pkg/models/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const (
UploadTypePRIME UploadType = "PRIME"
// UploadTypePRIME string OFFICE
UploadTypeOFFICE UploadType = "OFFICE"
// UploadTypeAPP string APP
UploadTypeAPP UploadType = "APP"
)

// An Upload represents an uploaded file, such as an image or PDF.
Expand Down Expand Up @@ -54,6 +56,7 @@ func (u *Upload) Validate(_ *pop.Connection) (*validate.Errors, error) {
string(UploadTypeUSER),
string(UploadTypePRIME),
string(UploadTypeOFFICE),
string(UploadTypeAPP),
}})
vs = append(vs,
&validators.StringIsPresent{Field: u.Filename, Name: "Filename"},
Expand Down Expand Up @@ -117,6 +120,7 @@ func (ut UploadType) Valid() bool {
string(UploadTypeUSER),
string(UploadTypePRIME),
string(UploadTypeOFFICE),
string(UploadTypeAPP),
} {
if string(ut) == value {
return true
Expand Down
2 changes: 1 addition & 1 deletion pkg/models/upload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (suite *ModelSuite) Test_UploadValidationErrors() {
"bytes": {"Bytes can not be blank."},
"filename": {"Filename can not be blank."},
"content_type": {"ContentType can not be blank."},
"upload_type": {"UploadType is not in the list [USER, PRIME, OFFICE]."},
"upload_type": {"UploadType is not in the list [USER, PRIME, OFFICE, APP]."},
}

suite.verifyValidationErrors(upload, expErrors)
Expand Down
16 changes: 8 additions & 8 deletions pkg/payment_request/send_to_syncada.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,32 @@ import (
)

// SendToSyncada send EDI file to Syncada for processing
func SendToSyncada(appCtx appcontext.AppContext, edi string, icn int64, gexSender services.GexSender, sftpSender services.SyncadaSFTPSender, sendEDIFile bool) error {
func SendToSyncada(appCtx appcontext.AppContext, edi string, icn int64, gexSender services.GexSender, sftpSender services.SyncadaSFTPSender, sendEDIFile bool) (string, error) {
logger := appCtx.Logger()
syncadaFileName := fmt.Sprintf("%s_%d_edi858.txt", time.Now().Format("2006_01_02T15_04_05Z07_00"), icn)

if !sendEDIFile {
logger.Info("SendToSyncada() is in do not send mode, syncadaFileName: " + syncadaFileName + "")
return nil
return "", nil
}

if (gexSender == nil) && (sftpSender == nil) {
return fmt.Errorf("cannot send to Syncada, SendToSyncada() senders are nil")
return "", fmt.Errorf("cannot send to Syncada, SendToSyncada() senders are nil")
}
if gexSender != nil {
logger.Info("SendToSyncada() is in send mode using GEX, sending syncadaFileName: " + syncadaFileName)
resp, err := gexSender.SendToGex(services.GEXChannelInvoice, edi, syncadaFileName)
if err != nil {
logger.Error("GEX Sender encountered an error", zap.Error(err))
return fmt.Errorf("GEX sender encountered an error: %w", err)
return "", fmt.Errorf("GEX sender encountered an error: %w", err)
}
if resp == nil {
logger.Error("GEX Sender receieved no response from GEX")
return fmt.Errorf("no response when sending EDI to GEX")
return "", fmt.Errorf("no response when sending EDI to GEX")
}
if resp.StatusCode != http.StatusOK {
logger.Error("func SendToSyncada() failed send to GEX with", zap.Int("StatusCode", resp.StatusCode), zap.String("Status", resp.Status))
return fmt.Errorf("received error response when sending EDI to GEX %v", resp)
return "", fmt.Errorf("received error response when sending EDI to GEX %v", resp)
}
logger.Info(
"SUCCESS: EDI858 Processor sent a new file to syncada for Payment Request, using GEX",
Expand All @@ -50,9 +50,9 @@ func SendToSyncada(appCtx appcontext.AppContext, edi string, icn int64, gexSende
logger.Info("SendToSyncada() is in send mode, sending syncadaFileName: " + syncadaFileName + "")
_, err := sftpSender.SendToSyncadaViaSFTP(appCtx, edi858String, syncadaFileName)
if err != nil {
return err
return "", err
}
logger.Info("SUCCESS: EDI858 Processor sent new file to syncada for Payment Request", zap.String("syncadaFileName", syncadaFileName))
}
return nil
return syncadaFileName, nil
}
16 changes: 8 additions & 8 deletions pkg/payment_request/send_to_syncada_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() {
suite.Run("returns no error if send is false", func() {
sftpSender := services.SyncadaSFTPSender(nil)
gexSender := services.GexSender(nil)
err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, false)
_, err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, false)
suite.NoError(err)
})

suite.Run("returns error if no sender", func() {
sftpSender := services.SyncadaSFTPSender(nil)
gexSender := services.GexSender(nil)
err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, true)
_, err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, true)
suite.Error(err)
suite.Equal("cannot send to Syncada, SendToSyncada() senders are nil", err.Error())
})
Expand All @@ -40,7 +40,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() {
gexSender := &mocks.GexSender{}
gexSender.
On("SendToGex", services.GEXChannelInvoice, fakeEdi, filenameMatcher).Return(response, nil)
err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
_, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
suite.NoError(err)
})

Expand All @@ -51,7 +51,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() {
gexSender := &mocks.GexSender{}
gexSender.
On("SendToGex", services.GEXChannelInvoice, fakeEdi, filenameMatcher).Return(response, nil)
err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
_, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
suite.Error(err)
suite.Contains("received error response when sending EDI to GEX &{ 403 0 0 map[] <nil> 0 [] false false map[] <nil> <nil>}", err.Error())
})
Expand All @@ -62,7 +62,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() {
gexSender := &mocks.GexSender{}
gexSender.
On("SendToGex", services.GEXChannelInvoice, fakeEdi, filenameMatcher).Return(nil, nil)
err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
_, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
suite.Error(err)
suite.Contains("no response when sending EDI to GEX", err.Error())
})
Expand All @@ -73,7 +73,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() {
gexSender := &mocks.GexSender{}
gexSender.
On("SendToGex", services.GEXChannelInvoice, fakeEdi, filenameMatcher).Return(nil, fmt.Errorf("gex send threw error"))
err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
_, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
suite.Error(err)
suite.Contains("GEX sender encountered an error: gex send threw error", err.Error())
})
Expand All @@ -85,7 +85,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() {
sftpSender.
On("SendToSyncadaViaSFTP", mock.AnythingOfType("*appcontext.appContext"), mock.Anything, filenameMatcher).Return(bytesSent, fmt.Errorf("test error"))
gexSender := services.GexSender(nil)
err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, true)
_, err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, true)
suite.Error(err)
suite.Equal("test error", err.Error())
})
Expand All @@ -98,7 +98,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() {
sftpSender.
On("SendToSyncadaViaSFTP", mock.AnythingOfType("*appcontext.appContext"), strings.NewReader(fakeEdi), filenameMatcher).Return(bytesSent, nil)
gexSender := services.GexSender(nil)
err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
_, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true)
suite.NoError(err)
})
}
52 changes: 17 additions & 35 deletions pkg/services/invoice/store_invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/gobuffalo/validate/v3"
"github.com/gobuffalo/validate/v3/validators"
"github.com/gofrs/uuid"
"github.com/pkg/errors"
"github.com/spf13/afero"
"go.uber.org/zap"
Expand All @@ -19,25 +18,23 @@ import (

// StoreInvoice858C is a service object to store an invoice's EDI in S3.
type StoreInvoice858C struct {
Storer *storage.FileStorer
}

// Call stores the EDI/Invoice to S3.
func (s StoreInvoice858C) Call(appCtx appcontext.AppContext, edi string, invoice *models.Invoice, userID uuid.UUID) (*validate.Errors, error) {
func (s StoreInvoice858C) Call(appCtx appcontext.AppContext, edi string, paymentRequest *models.PaymentRequest, fileName string) (*validate.Errors, error) {
verrs := validate.NewErrors()

// Create path for EDI file
// {application-bucket}/app/invoice/{invoice_id}.edi
invoiceID := invoice.ID.String()
ediFilename := invoiceID + ".edi"
ediFilePath := "/app/invoice/"
ediTmpFile := path.Join(ediFilePath, ediFilename)

// {application-bucket}/app/payment-request-{payment_request_id}/mto-{move_id}/{payment_request_id}.txt
paymentRequestId := paymentRequest.ID.String()
ediFilePath := "app/payment-request-uploads/" + "payment-request-" + paymentRequestId + "/mto-" + paymentRequest.MoveTaskOrderID.String() + "/edi"
ediTmpFile := path.Join(ediFilePath, fileName)
appCtx.Logger().Warn("attemping to store it to:" + ediFilePath)
fs := afero.NewMemMapFs()

f, err := fs.Create(ediTmpFile)
if err != nil {
return verrs, errors.Wrapf(err, "afero.Create Failed in StoreInvoice858C() invoice ID: %s", invoiceID)
return verrs, errors.Wrapf(err, "afero.Create Failed in StoreInvoice858C() payment request ID: %s", paymentRequestId)
}

defer func() {
Expand All @@ -48,7 +45,7 @@ func (s StoreInvoice858C) Call(appCtx appcontext.AppContext, edi string, invoice

_, err = io.WriteString(f, edi)
if err != nil {
return verrs, errors.Wrapf(err, "io.WriteString(edi) Failed in StoreInvoice858C() invoice ID: %s", invoiceID)
return verrs, errors.Wrapf(err, "io.WriteString(edi) Failed in StoreInvoice858C() invoice ID: %s", paymentRequestId)
}

err = f.Sync()
Expand All @@ -57,42 +54,27 @@ func (s StoreInvoice858C) Call(appCtx appcontext.AppContext, edi string, invoice
}

// Create UserUpload
loader, err := uploader.NewUserUploader(*s.Storer, uploader.MaxCustomerUserUploadFileSizeLimit)
loader, err := uploader.NewPaymentRequestEDIUploader(storage.NewFilesystem(storage.FilesystemParams{}), uploader.MaxCustomerUserUploadFileSizeLimit)
if err != nil {
appCtx.Logger().Fatal("could not instantiate uploader", zap.Error(err))
}
// Set Storagekey path for S3
loader.SetUploadStorageKey(ediTmpFile)

// Delete of previous upload, if it exist
// If Delete of UserUpload fails, ignoring this error because we still have a new UserUpload that needs to be saved
// to the Invoice
err = UploadUpdater{UserUploader: loader}.DeleteUpload(appCtx, invoice)
if err != nil {
logStr := ""
if invoice != nil && invoice.UserUploadID != nil {
logStr = invoice.UserUploadID.String()
}
appCtx.Logger().Info("Errors encountered for while deleting previous UserUpload:"+logStr,
zap.Any("verrors", verrs.Error()))
// Create and save UserUpload to s3
filePointer := &uploader.File{
File: f,
}

// Create and save UserUpload to s3
userUpload, verrs2, err := loader.CreateUserUpload(appCtx, userID, uploader.File{File: f}, uploader.AllowedTypesText)
// Then pass the pointer to the function
appUpload, verrs2, err := loader.CreatePaymentRequestEDIUploadForDocument(appCtx, filePointer, uploader.AllowedTypesText)
verrs.Append(verrs2)
if err != nil {
return verrs, errors.Wrapf(err, "Failed to Create UserUpload for StoreInvoice858C(), invoice ID: %s", invoiceID)
}

if userUpload == nil {
return verrs, errors.New("Failed to Create and Save new UserUpload object in database, invoice ID: " + invoiceID)
return verrs, errors.Wrapf(err, "Failed to Create AppUpload for StoreInvoice858C(), invoice ID: %s", paymentRequestId)
}

// Save UserUpload to Invoice
verrs2, err = UploadUpdater{UserUploader: loader}.Call(appCtx, invoice, userUpload)
verrs.Append(verrs2)
if err != nil {
return verrs, errors.New("Failed to save UserUpload to Invoice: " + invoiceID)
if appUpload == nil {
return verrs, errors.New("Failed to Create and Save new ApUpload object in database, invoice ID: " + paymentRequestId)
}

if verrs.HasAny() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ func (p *paymentRequestReviewedProcessor) ProcessAndLockReviewedPR(appCtx appcon
)
// Send EDI string to Syncada
// If sent successfully to GEX, update payment request status to SENT_TO_GEX.
err = paymentrequesthelper.SendToSyncada(txnAppCtx, edi858cString, icn, p.gexSender, p.sftpSender, p.runSendToSyncada)
var fileName string
fileName, err = paymentrequesthelper.SendToSyncada(txnAppCtx, edi858cString, icn, p.gexSender, p.sftpSender, p.runSendToSyncada)
if err != nil {
return GexSendError{paymentRequestID: lockedPR.ID, err: err}
}
Expand All @@ -145,6 +146,15 @@ func (p *paymentRequestReviewedProcessor) ProcessAndLockReviewedPR(appCtx appcon
if err != nil {
return fmt.Errorf("failure updating payment request status: %w", err)
}
storeInvoice := invoice.StoreInvoice858C{}
verrs, err := storeInvoice.Call(appCtx, edi858cString, &lockedPR, fileName)

if err != nil {
return fmt.Errorf("failure storing invoice: %w", err)
}
if verrs != nil && verrs.HasAny() {
return fmt.Errorf("validation errors while storing invoice: %w", verrs)
}

return nil
})
Expand Down
Loading

0 comments on commit cc2beab

Please sign in to comment.