From 8b46a64ab1d8858b38daf95590a921f1e4c39d60 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 2 Aug 2024 19:43:57 +0000 Subject: [PATCH 01/46] first draft for new tpps paid invoice report table --- migrations/app/migrations_manifest.txt | 1 + ...40802161708_tpps_paid_invoice_table.up.sql | 75 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index dbc16554cbe..fc98cfeb430 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -969,3 +969,4 @@ 20240725190050_update_payment_request_status_tpps_received.up.sql 20240729162353_joseph_doye_cn_cac.up.sql 20240729164930_mai_do_cac.up.sql +20240802161708_tpps_paid_invoice_table.up.sql diff --git a/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql new file mode 100644 index 00000000000..d1a9ca34d9d --- /dev/null +++ b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql @@ -0,0 +1,75 @@ +CREATE TABLE IF NOT EXISTS tpps_paid_invoice_reports ( + id uuid not null primary key, + payment_request_id uuid not null, + -- CONSTRAINT tpps_paid_invoice_reports_payment_request_id_fkey + -- REFERENCES payment_requests, + invoice_number text not null, + -- CONSTRAINT tpps_paid_invoice_reports_invoice_id_fkey + -- REFERENCES tpps_paid_invoice_report_to_payment_request, + tpps_created_doc_date timestamp, + seller_paid_date timestamp, + invoice_total_charges varchar, + line_description varchar, -- service item code IE (DOP, DUPK, DLH, FSC, DDP) + product_description varchar, -- same values as above for line desciprtion service item code IE (DOP, DUPK, DLH, FSC, DDP) + line_billing_units varchar, + line_unit_price integer DEFAULT NULL, + line_net_charge integer DEFAULT NULL, + po_tcn varchar, + line_number varchar, + first_note_code varchar, + first_note_description varchar, + first_note_to varchar, + first_note_message varchar, + second_note_code varchar, + second_note_description varchar, + second_note_to varchar, + second_note_message varchar, + third_note_code varchar, + third_note_code_description varchar, + third_note_code_to varchar, + third_note_code_message varchar, + created_at timestamp not null, + updated_at timestamp not null +); +COMMENT ON TABLE tpps_paid_invoice_reports IS 'Contains data populated from processing the TPPS paid invoice report'; + +-- CREATE INDEX on edi_errors (payment_request_id); +-- CREATE INDEX on edi_errors (interchange_control_number_id); + +-- COMMENT ON TABLE edi_errors IS 'Stores errors when sending an EDI 858 or stores errors reported from EDI responses (997 & 824)'; +-- COMMENT ON COLUMN edi_errors.payment_request_id IS 'Payment Request ID associated with this error'; +-- COMMENT ON COLUMN edi_errors.interchange_control_number_id IS 'ID for payment_request_to_interchange_control_numbers associated with this error. This will identify the ICN for the payment request.'; +-- COMMENT ON COLUMN edi_errors.code IS 'Reported code from syncada for the EDI error encountered'; +-- COMMENT ON COLUMN edi_errors.description IS 'Description of the error. Can be used with the edi_errors.code.'; +-- COMMENT ON COLUMN edi_errors.edi_type IS 'Type of EDI reporting or causing the issue. Can be EDI 997, 824, and 858.'; + + +-- CREATE TABLE edi_errors ( +-- id uuid not null primary key, +-- payment_request_id uuid not null +-- CONSTRAINT edi_errors_payment_request_id_fkey +-- REFERENCES payment_requests, +-- interchange_control_number_id uuid not null +-- CONSTRAINT edi_errors_icn_id_fkey +-- REFERENCES payment_request_to_interchange_control_numbers, +-- code varchar, +-- description varchar, +-- edi_type varchar not null, +-- created_at timestamp not null, +-- updated_at timestamp not null +-- ); +-- CREATE INDEX on edi_errors (payment_request_id); +-- CREATE INDEX on edi_errors (interchange_control_number_id); + +-- ALTER TYPE payment_request_status +-- ADD VALUE 'EDI_ERROR'; + +-- COMMENT ON COLUMN payment_requests.status IS 'Track the status of the payment request through the system. PENDING by default at creation. Options: PENDING, REVIEWED, REVIEWED_AND_ALL_SERVICE_ITEMS_REJECTED, SENT_TO_GEX, RECEIVED_BY_GEX, PAID, EDI_ERROR'; + +-- COMMENT ON TABLE edi_errors IS 'Stores errors when sending an EDI 858 or stores errors reported from EDI responses (997 & 824)'; +-- COMMENT ON COLUMN edi_errors.payment_request_id IS 'Payment Request ID associated with this error'; +-- COMMENT ON COLUMN edi_errors.interchange_control_number_id IS 'ID for payment_request_to_interchange_control_numbers associated with this error. This will identify the ICN for the payment request.'; +-- COMMENT ON COLUMN edi_errors.code IS 'Reported code from syncada for the EDI error encountered'; +-- COMMENT ON COLUMN edi_errors.description IS 'Description of the error. Can be used with the edi_errors.code.'; +-- COMMENT ON COLUMN edi_errors.edi_type IS 'Type of EDI reporting or causing the issue. Can be EDI 997, 824, and 858.'; + From 328b614d86d7fde0e63cfba5814c59d067fc251e Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 2 Aug 2024 19:50:47 +0000 Subject: [PATCH 02/46] remove fluff from migration file --- ...40802161708_tpps_paid_invoice_table.up.sql | 47 +------------------ 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql index d1a9ca34d9d..9c748f801ce 100644 --- a/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql +++ b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql @@ -1,11 +1,7 @@ CREATE TABLE IF NOT EXISTS tpps_paid_invoice_reports ( id uuid not null primary key, payment_request_id uuid not null, - -- CONSTRAINT tpps_paid_invoice_reports_payment_request_id_fkey - -- REFERENCES payment_requests, invoice_number text not null, - -- CONSTRAINT tpps_paid_invoice_reports_invoice_id_fkey - -- REFERENCES tpps_paid_invoice_report_to_payment_request, tpps_created_doc_date timestamp, seller_paid_date timestamp, invoice_total_charges varchar, @@ -31,45 +27,4 @@ CREATE TABLE IF NOT EXISTS tpps_paid_invoice_reports ( created_at timestamp not null, updated_at timestamp not null ); -COMMENT ON TABLE tpps_paid_invoice_reports IS 'Contains data populated from processing the TPPS paid invoice report'; - --- CREATE INDEX on edi_errors (payment_request_id); --- CREATE INDEX on edi_errors (interchange_control_number_id); - --- COMMENT ON TABLE edi_errors IS 'Stores errors when sending an EDI 858 or stores errors reported from EDI responses (997 & 824)'; --- COMMENT ON COLUMN edi_errors.payment_request_id IS 'Payment Request ID associated with this error'; --- COMMENT ON COLUMN edi_errors.interchange_control_number_id IS 'ID for payment_request_to_interchange_control_numbers associated with this error. This will identify the ICN for the payment request.'; --- COMMENT ON COLUMN edi_errors.code IS 'Reported code from syncada for the EDI error encountered'; --- COMMENT ON COLUMN edi_errors.description IS 'Description of the error. Can be used with the edi_errors.code.'; --- COMMENT ON COLUMN edi_errors.edi_type IS 'Type of EDI reporting or causing the issue. Can be EDI 997, 824, and 858.'; - - --- CREATE TABLE edi_errors ( --- id uuid not null primary key, --- payment_request_id uuid not null --- CONSTRAINT edi_errors_payment_request_id_fkey --- REFERENCES payment_requests, --- interchange_control_number_id uuid not null --- CONSTRAINT edi_errors_icn_id_fkey --- REFERENCES payment_request_to_interchange_control_numbers, --- code varchar, --- description varchar, --- edi_type varchar not null, --- created_at timestamp not null, --- updated_at timestamp not null --- ); --- CREATE INDEX on edi_errors (payment_request_id); --- CREATE INDEX on edi_errors (interchange_control_number_id); - --- ALTER TYPE payment_request_status --- ADD VALUE 'EDI_ERROR'; - --- COMMENT ON COLUMN payment_requests.status IS 'Track the status of the payment request through the system. PENDING by default at creation. Options: PENDING, REVIEWED, REVIEWED_AND_ALL_SERVICE_ITEMS_REJECTED, SENT_TO_GEX, RECEIVED_BY_GEX, PAID, EDI_ERROR'; - --- COMMENT ON TABLE edi_errors IS 'Stores errors when sending an EDI 858 or stores errors reported from EDI responses (997 & 824)'; --- COMMENT ON COLUMN edi_errors.payment_request_id IS 'Payment Request ID associated with this error'; --- COMMENT ON COLUMN edi_errors.interchange_control_number_id IS 'ID for payment_request_to_interchange_control_numbers associated with this error. This will identify the ICN for the payment request.'; --- COMMENT ON COLUMN edi_errors.code IS 'Reported code from syncada for the EDI error encountered'; --- COMMENT ON COLUMN edi_errors.description IS 'Description of the error. Can be used with the edi_errors.code.'; --- COMMENT ON COLUMN edi_errors.edi_type IS 'Type of EDI reporting or causing the issue. Can be EDI 997, 824, and 858.'; - +COMMENT ON TABLE tpps_paid_invoice_reports IS 'Contains data populated from processing the TPPS paid invoice report'; \ No newline at end of file From faa3042285928bc630960335740112600e173b90 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 2 Aug 2024 21:15:25 +0000 Subject: [PATCH 03/46] bare bones of a tpps paid invoice parser --- cmd/milmove-tasks/process_edis.go | 11 +++ pkg/cli/gex_sftp.go | 6 ++ pkg/edi/tpps_paid_invoice_report/edi.go | 31 +++++++ pkg/edi/tpps_paid_invoice_report/parser.go | 86 +++++++++++++++++++ .../tpps_paid_invoice_report/parser_test.go | 3 + pkg/models/edi_type.go | 3 + .../process_tpps_paid_invoice_report.go | 39 +++++++++ 7 files changed, 179 insertions(+) create mode 100644 pkg/edi/tpps_paid_invoice_report/edi.go create mode 100644 pkg/edi/tpps_paid_invoice_report/parser.go create mode 100644 pkg/edi/tpps_paid_invoice_report/parser_test.go create mode 100644 pkg/services/invoice/process_tpps_paid_invoice_report.go diff --git a/cmd/milmove-tasks/process_edis.go b/cmd/milmove-tasks/process_edis.go index d026d3194da..6f174717e79 100644 --- a/cmd/milmove-tasks/process_edis.go +++ b/cmd/milmove-tasks/process_edis.go @@ -244,5 +244,16 @@ func processEDIs(_ *cobra.Command, _ []string) error { logger.Info("Successfully processed EDI824 application advice responses") } + // TODO: should this go in a new file as opposed to process_edis.go since it is NOT an EDI file? + + // Process TPPS paid invoice report + pathTPPSPaidInvoiceReport := v.GetString(cli.SFTPTPPSPaidInvoiceReportPickupDirectory) + _, err = syncadaSFTPSession.FetchAndProcessSyncadaFiles(appCtx, pathTPPSPaidInvoiceReport, lastReadTime, invoice.NewTPPSPaidInvoiceReportProcessor()) + if err != nil { + logger.Error("Error reading TPPS Paid Invoice Report application advice responses", zap.Error(err)) + } else { + logger.Info("Successfully processed TPPS Paid Invoice Report application advice responses") + } + return nil } diff --git a/pkg/cli/gex_sftp.go b/pkg/cli/gex_sftp.go index 576391250a0..2db0727c0f9 100644 --- a/pkg/cli/gex_sftp.go +++ b/pkg/cli/gex_sftp.go @@ -41,6 +41,12 @@ const ( GEXSFTP824PickupDirectory string = "gex-sftp-824-pickup-directory" ) +// Set of flags used for SFTPTPPSPaid +const ( + // SFTPTPPSPaidInvoiceReportPickupDirectory is the ENV var for the directory where TPPS delivers the TPPS paid invoice report + SFTPTPPSPaidInvoiceReportPickupDirectory string = "pending" +) + // InitGEXSFTPFlags initializes GEX SFTP command line flags func InitGEXSFTPFlags(flag *pflag.FlagSet) { flag.Int(GEXSFTPPortFlag, 22, "GEX SFTP Port") diff --git a/pkg/edi/tpps_paid_invoice_report/edi.go b/pkg/edi/tpps_paid_invoice_report/edi.go new file mode 100644 index 00000000000..07a9f3efc9f --- /dev/null +++ b/pkg/edi/tpps_paid_invoice_report/edi.go @@ -0,0 +1,31 @@ +package tppspaidinvoicereport + +import edisegment "github.com/transcom/mymove/pkg/edi/segment" + +// look to pkg/edi/edi824/edi.go for reference + +// TransactionSet holds the transaction set envelope for the 824 +type TransactionSet struct { + ST edisegment.ST // transaction set header (bump up counter for "ST" and create new TransactionSet) + BGN edisegment.BGN // beginning statement + OTIs []edisegment.OTI `validate:"min=1,dive"` // original transaction identifications + TEDs []edisegment.TED `validate:"dive"` // technical error descriptions + SE edisegment.SE // transaction set trailer +} + +type functionalGroupEnvelope struct { + GS edisegment.GS // functional group header (bump up counter for "GS" and create new functionalGroupEnvelope) + TransactionSets []TransactionSet `validate:"min=1,dive"` + GE edisegment.GE // functional group trailer +} + +type interchangeControlEnvelope struct { + ISA edisegment.ISA // interchange control header + FunctionalGroups []functionalGroupEnvelope `validate:"min=1,dive"` + IEA edisegment.IEA // interchange control trailer +} + +// EDI holds all the segments to parse TPPS paid invoice report +type EDI struct { + InterchangeControlEnvelope interchangeControlEnvelope +} diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go new file mode 100644 index 00000000000..fadea79a18c --- /dev/null +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -0,0 +1,86 @@ +package tppspaidinvoicereport + +import ( + "encoding/csv" + "os" + "path/filepath" +) + +// TPPSData represents TPPS paid invoice report data +type TPPSData struct { + InvoiceNumber string + TPPSCreatedDocumentDate string + SellerPaidDate string + InvoiceTotalCharges string + LineDescription string + ProductDescription string + LineBillingUnits string + LineUnitPrice string // convert to int + LineNetCharge string // convert to int + POTCN string + LineNumber string + FirstNoteCode string + FirstNoteDescription string + FirstNoteTo string + FirstNoteMessage string + SecondNoteCode string + SecondNoteDescription string + SecondNoteTo string + SecondNoteMessage string + ThirdNoteCode string + ThirdNoteDescription string + ThirdNoteTo string + ThirdNoteMessage string +} + +// Parse takes in a string representation of a TPPS paid invoice report and reads it into a TPPS paid invoice report struct +func (e *EDI) Parse(stringTPPSPaidInvoiceReport string) ([]TPPSData, error) { + // var err error + // counter := counterData{} + + // scanner := bufio.NewScanner(strings.NewReader(stringTPPSPaidInvoiceReport)) + + var tppsData []TPPSData + + csvFile, err := os.Open(filepath.Clean(stringTPPSPaidInvoiceReport)) + if err != nil { + return tppsData, err + } + r := csv.NewReader(csvFile) + + // Skip the first header row + dataRows, err := r.ReadAll() + if err != nil { + return tppsData, err + } + for _, row := range dataRows[1:] { + parsed := TPPSData{ + InvoiceNumber: row[0], + TPPSCreatedDocumentDate: row[1], + SellerPaidDate: row[2], + InvoiceTotalCharges: row[3], + LineDescription: row[4], + ProductDescription: row[5], + LineBillingUnits: row[6], + LineUnitPrice: row[7], + LineNetCharge: row[8], + POTCN: row[9], + LineNumber: row[10], + FirstNoteCode: row[11], + FirstNoteDescription: row[12], + FirstNoteTo: row[13], + FirstNoteMessage: row[14], + SecondNoteCode: row[15], + SecondNoteDescription: row[16], + SecondNoteTo: row[17], + SecondNoteMessage: row[18], + ThirdNoteCode: row[19], + ThirdNoteDescription: row[20], + ThirdNoteTo: row[21], + ThirdNoteMessage: row[22], + } + tppsData = append(tppsData, parsed) + } + + return tppsData, nil +} diff --git a/pkg/edi/tpps_paid_invoice_report/parser_test.go b/pkg/edi/tpps_paid_invoice_report/parser_test.go new file mode 100644 index 00000000000..b88248d6d77 --- /dev/null +++ b/pkg/edi/tpps_paid_invoice_report/parser_test.go @@ -0,0 +1,3 @@ +package tppspaidinvoicereport + +// TODO add some tests for parsing the TPPS paid invoice report diff --git a/pkg/models/edi_type.go b/pkg/models/edi_type.go index 35e8559a483..f91649d8a37 100644 --- a/pkg/models/edi_type.go +++ b/pkg/models/edi_type.go @@ -12,6 +12,8 @@ const ( EDIType858 EDIType = "858" // EDIType997 captures enum value "997" EDIType997 EDIType = "997" + // TODO (maybe put this somewhere else as TPPS paid report is not an EDI type) + TPPSPaidInvoiceReport EDIType = "TPPSPaidInvoiceReport" ) var allowedEDITypes = []string{ @@ -19,6 +21,7 @@ var allowedEDITypes = []string{ string(EDIType824), string(EDIType858), string(EDIType997), + string(TPPSPaidInvoiceReport), } // String returns a string representation of the admin role diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go new file mode 100644 index 00000000000..7595a48afc7 --- /dev/null +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -0,0 +1,39 @@ +package invoice + +import ( + "fmt" + + "go.uber.org/zap" + + "github.com/transcom/mymove/pkg/appcontext" + tppsReponse "github.com/transcom/mymove/pkg/edi/tpps_paid_invoice_report" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" +) + +type tppsPaidInvoiceReportProcessor struct { +} + +// NewEDITPPSInvoiceProcessor returns a new TPPS paid invoice report processor +func NewTPPSPaidInvoiceReportProcessor() services.SyncadaFileProcessor { + + return &tppsPaidInvoiceReportProcessor{} +} + +// ProcessFile parses an TPPS paid invoice report response and updates the payment request status +func (e *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContext, _ string, stringTPPSPaidInvoiceReport string) error { + tppsPaidInvoiceReport := tppsReponse.EDI{} + + // TODO instead of _, use the return value here as it will contain the struct with tpps paid data + _, err := tppsPaidInvoiceReport.Parse(stringTPPSPaidInvoiceReport) + if err != nil { + appCtx.Logger().Error("unable to parse TPPS paid invoice report", zap.Error(err)) + return fmt.Errorf("unable to parse TPPS paid invoice report") + } + + return nil +} + +func (e *tppsPaidInvoiceReportProcessor) EDIType() models.EDIType { + return models.TPPSPaidInvoiceReport +} From 475f59bc6a6d60a6c46abd429cb4422f1939affb Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 5 Aug 2024 19:27:29 +0000 Subject: [PATCH 04/46] add test for string version of the csv file --- pkg/edi/tpps_paid_invoice_report/parser.go | 98 +++++++++---------- .../process_tpps_paid_invoice_report.go | 4 +- .../process_tpps_paid_invoice_report_test.go | 59 +++++++++++ 3 files changed, 110 insertions(+), 51 deletions(-) create mode 100644 pkg/services/invoice/process_tpps_paid_invoice_report_test.go diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index fadea79a18c..9cd23f2ab66 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -1,9 +1,8 @@ package tppspaidinvoicereport import ( - "encoding/csv" - "os" - "path/filepath" + "bufio" + "strings" ) // TPPSData represents TPPS paid invoice report data @@ -15,8 +14,8 @@ type TPPSData struct { LineDescription string ProductDescription string LineBillingUnits string - LineUnitPrice string // convert to int - LineNetCharge string // convert to int + LineUnitPrice string + LineNetCharge string POTCN string LineNumber string FirstNoteCode string @@ -33,54 +32,53 @@ type TPPSData struct { ThirdNoteMessage string } -// Parse takes in a string representation of a TPPS paid invoice report and reads it into a TPPS paid invoice report struct -func (e *EDI) Parse(stringTPPSPaidInvoiceReport string) ([]TPPSData, error) { - // var err error - // counter := counterData{} - - // scanner := bufio.NewScanner(strings.NewReader(stringTPPSPaidInvoiceReport)) - - var tppsData []TPPSData - - csvFile, err := os.Open(filepath.Clean(stringTPPSPaidInvoiceReport)) - if err != nil { - return tppsData, err +func ProcessTPPSReportEntryForOnePaymentRequest(tppsReportEntryForOnePaymentRequest []string) TPPSData { + var tppsData TPPSData + if len(tppsReportEntryForOnePaymentRequest) > 0 { + tppsData.InvoiceNumber = tppsReportEntryForOnePaymentRequest[0] + tppsData.TPPSCreatedDocumentDate = tppsReportEntryForOnePaymentRequest[1] + tppsData.SellerPaidDate = tppsReportEntryForOnePaymentRequest[2] + tppsData.InvoiceTotalCharges = tppsReportEntryForOnePaymentRequest[3] + tppsData.LineDescription = tppsReportEntryForOnePaymentRequest[4] + tppsData.ProductDescription = tppsReportEntryForOnePaymentRequest[5] + tppsData.LineBillingUnits = tppsReportEntryForOnePaymentRequest[6] + tppsData.LineUnitPrice = tppsReportEntryForOnePaymentRequest[7] + tppsData.LineNetCharge = tppsReportEntryForOnePaymentRequest[8] + tppsData.POTCN = tppsReportEntryForOnePaymentRequest[9] + tppsData.LineNumber = tppsReportEntryForOnePaymentRequest[10] + tppsData.FirstNoteCode = tppsReportEntryForOnePaymentRequest[11] + tppsData.FirstNoteDescription = tppsReportEntryForOnePaymentRequest[12] + tppsData.FirstNoteTo = tppsReportEntryForOnePaymentRequest[13] + tppsData.FirstNoteMessage = tppsReportEntryForOnePaymentRequest[14] + tppsData.SecondNoteCode = tppsReportEntryForOnePaymentRequest[15] + tppsData.SecondNoteDescription = tppsReportEntryForOnePaymentRequest[16] + tppsData.SecondNoteTo = tppsReportEntryForOnePaymentRequest[17] + tppsData.SecondNoteMessage = tppsReportEntryForOnePaymentRequest[18] + tppsData.ThirdNoteCode = tppsReportEntryForOnePaymentRequest[19] + tppsData.ThirdNoteDescription = tppsReportEntryForOnePaymentRequest[20] + tppsData.ThirdNoteTo = tppsReportEntryForOnePaymentRequest[21] + tppsData.ThirdNoteMessage = tppsReportEntryForOnePaymentRequest[22] } - r := csv.NewReader(csvFile) + return tppsData +} - // Skip the first header row - dataRows, err := r.ReadAll() - if err != nil { - return tppsData, err - } - for _, row := range dataRows[1:] { - parsed := TPPSData{ - InvoiceNumber: row[0], - TPPSCreatedDocumentDate: row[1], - SellerPaidDate: row[2], - InvoiceTotalCharges: row[3], - LineDescription: row[4], - ProductDescription: row[5], - LineBillingUnits: row[6], - LineUnitPrice: row[7], - LineNetCharge: row[8], - POTCN: row[9], - LineNumber: row[10], - FirstNoteCode: row[11], - FirstNoteDescription: row[12], - FirstNoteTo: row[13], - FirstNoteMessage: row[14], - SecondNoteCode: row[15], - SecondNoteDescription: row[16], - SecondNoteTo: row[17], - SecondNoteMessage: row[18], - ThirdNoteCode: row[19], - ThirdNoteDescription: row[20], - ThirdNoteTo: row[21], - ThirdNoteMessage: row[22], +// Parse takes in a string representation of a 997 EDI file and reads it into a 997 EDI struct +func (e *EDI) Parse(stringTPPSPaidInvoiceReport string) ([]TPPSData, error) { + var tppsDataFile []TPPSData + + scanner := bufio.NewScanner(strings.NewReader(stringTPPSPaidInvoiceReport)) + for scanner.Scan() { + row := strings.Split(scanner.Text(), "\n") + if row != nil { + rowSplitIntoColumns := strings.Split(row[0], "\t") + if rowSplitIntoColumns[0] == "Invoice Number From Invoice" { + // move past the header row to the actual TPPS data + continue + } + tppsReportEntryForOnePaymentRequest := ProcessTPPSReportEntryForOnePaymentRequest(rowSplitIntoColumns) + tppsDataFile = append(tppsDataFile, tppsReportEntryForOnePaymentRequest) } - tppsData = append(tppsData, parsed) } - return tppsData, nil + return tppsDataFile, nil } diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index 7595a48afc7..bc61c8961fe 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -25,10 +25,12 @@ func (e *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContex tppsPaidInvoiceReport := tppsReponse.EDI{} // TODO instead of _, use the return value here as it will contain the struct with tpps paid data - _, err := tppsPaidInvoiceReport.Parse(stringTPPSPaidInvoiceReport) + tppsData, err := tppsPaidInvoiceReport.Parse(stringTPPSPaidInvoiceReport) if err != nil { appCtx.Logger().Error("unable to parse TPPS paid invoice report", zap.Error(err)) return fmt.Errorf("unable to parse TPPS paid invoice report") + } else { + appCtx.Logger().Info("Successfully parsed TPPS Paid Invoice Report and found ", zap.Int("entries", len(tppsData))) } return nil diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go new file mode 100644 index 00000000000..f3b8bdf434e --- /dev/null +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -0,0 +1,59 @@ +package invoice + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/transcom/mymove/pkg/factory" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/testingsuite" +) + +type ProcessTPPSPaidInvoiceReportSuite struct { + *testingsuite.PopTestSuite +} + +func TestProcessTPPSPaidInvoiceReportSuite(t *testing.T) { + ts := &ProcessTPPSPaidInvoiceReportSuite{ + PopTestSuite: testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), + testingsuite.WithPerTestTransaction()), + } + + suite.Run(t, ts) + ts.PopTestSuite.TearDown() +} + +type FakeTPPSFile struct { + contents string +} + +func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport() { + tppsPaidInvoiceReportProcessor := NewTPPSPaidInvoiceReportProcessor() + + suite.Run("successfully proccesses a valid TPPSPaidInvoiceReport", func() { + paymentRequest := factory.BuildPaymentRequest(suite.DB(), nil, nil) + sampleTPPSPaidInvoiceReportString := FakeTPPSFile{ + `Invoice Number From Invoice Document Create Date Seller Paid Date Invoice Total Charges Line Description Product Description Line Billing Units Line Unit Price Line Net Charge PO/TCN Line Number First Note Code First Note Code Description First Note To First Note Message Second Note Code Second Note Code Description Second Note To Second Note Message Third Note Code Third Note Code Description Third Note To Third Note Message +1841-7267-3 2024-07-29 2024-07-30 1151.55 DDP DDP 3760 0.0077 28.95 1841-7267-826285fc 1 INT Notes to My Company - INT CARR HQ50066 +1841-7267-3 2024-07-29 2024-07-30 1151.55 FSC FSC 3760 0.0014 5.39 1841-7267-aeb3cfea 4 INT Notes to My Company - INT CARR HQ50066 +1841-7267-3 2024-07-29 2024-07-30 1151.55 DLH DLH 3760 0.2656 998.77 1841-7267-c8ea170b 2 INT Notes to My Company - INT CARR HQ50066 +1841-7267-3 2024-07-29 2024-07-30 1151.55 DUPK DUPK 3760 0.0315 118.44 1841-7267-265c16d7 3 INT Notes to My Company - INT CARR HQ50066 +9436-4123-3 2024-07-29 2024-07-30 125.25 DDP DDP 7500 0.0167 125.25 9436-4123-93761f93 1 INT Notes to My Company - INT CARR HQ50057 +`} + factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequestToInterchangeControlNumber{ + InterchangeControlNumber: 100001251, + EDIType: models.EDIType858, + }, + }, + { + Model: paymentRequest, + LinkOnly: true, + }, + }, nil) + err := tppsPaidInvoiceReportProcessor.ProcessFile(suite.AppContextForTest(), "", sampleTPPSPaidInvoiceReportString.contents) + suite.NoError(err) + }) +} From 7b43e8ed4d7c16cfd94d0fcff4f44f7a732e3ee4 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 5 Aug 2024 19:51:17 +0000 Subject: [PATCH 05/46] update comments to reference tpps --- pkg/edi/tpps_paid_invoice_report/parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index 9cd23f2ab66..e2409ff2ba3 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -62,7 +62,7 @@ func ProcessTPPSReportEntryForOnePaymentRequest(tppsReportEntryForOnePaymentRequ return tppsData } -// Parse takes in a string representation of a 997 EDI file and reads it into a 997 EDI struct +// Parse takes in a string representation of a TPPS paid invoice report file and reads it into a TPPSData struct func (e *EDI) Parse(stringTPPSPaidInvoiceReport string) ([]TPPSData, error) { var tppsDataFile []TPPSData From 30ff7c78ae89f2b9c3a023dc6e1384aac356fecb Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Tue, 6 Aug 2024 20:15:24 +0000 Subject: [PATCH 06/46] parse test csv into TPPSData struct --- pkg/edi/tpps_paid_invoice_report/parser.go | 182 ++++++++++++------ .../process_tpps_paid_invoice_report.go | 1 - 2 files changed, 126 insertions(+), 57 deletions(-) diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index e2409ff2ba3..9186835650b 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -2,81 +2,151 @@ package tppspaidinvoicereport import ( "bufio" + "os" + "path/filepath" "strings" ) // TPPSData represents TPPS paid invoice report data type TPPSData struct { - InvoiceNumber string - TPPSCreatedDocumentDate string - SellerPaidDate string - InvoiceTotalCharges string - LineDescription string - ProductDescription string - LineBillingUnits string - LineUnitPrice string - LineNetCharge string - POTCN string - LineNumber string - FirstNoteCode string - FirstNoteDescription string - FirstNoteTo string - FirstNoteMessage string - SecondNoteCode string - SecondNoteDescription string - SecondNoteTo string - SecondNoteMessage string - ThirdNoteCode string - ThirdNoteDescription string - ThirdNoteTo string - ThirdNoteMessage string + InvoiceNumber string + TPPSCreatedDocumentDate string + SellerPaidDate string + InvoiceTotalCharges string + LineDescription string + ProductDescription string + LineBillingUnits string + LineUnitPrice string + LineNetCharge string + POTCN string + LineNumber string + FirstNoteCode string + FirstNoteCodeDescription string + FirstNoteTo string + FirstNoteMessage string + SecondNoteCode string + SecondNoteCodeDescription string + SecondNoteTo string + SecondNoteMessage string + ThirdNoteCode string + ThirdNoteCodeDescription string + ThirdNoteTo string + ThirdNoteMessage string } -func ProcessTPPSReportEntryForOnePaymentRequest(tppsReportEntryForOnePaymentRequest []string) TPPSData { +func VerifyHeadersParsedCorrectly(parsedHeadersFromFile TPPSData) bool { + allHeadersWereProcessedCorrectly := false + + if parsedHeadersFromFile.InvoiceNumber == "Invoice Number From Invoice" && + parsedHeadersFromFile.TPPSCreatedDocumentDate == "Document Create Date" && + parsedHeadersFromFile.SellerPaidDate == "Seller Paid Date" && + parsedHeadersFromFile.InvoiceTotalCharges == "Invoice Total Charges" && + parsedHeadersFromFile.LineDescription == "Line Description" && + parsedHeadersFromFile.ProductDescription == "Product Description" && + parsedHeadersFromFile.LineBillingUnits == "Line Billing Units" && + parsedHeadersFromFile.LineUnitPrice == "Line Unit Price" && + parsedHeadersFromFile.LineNetCharge == "Line Net Charge" && + parsedHeadersFromFile.POTCN == "PO/TCN" && + parsedHeadersFromFile.LineNumber == "Line Number" && + parsedHeadersFromFile.FirstNoteCode == "First Note Code" && + parsedHeadersFromFile.FirstNoteCodeDescription == "First Note Code Description" && + parsedHeadersFromFile.FirstNoteTo == "First Note To" && + parsedHeadersFromFile.FirstNoteMessage == "First Note Message" && + parsedHeadersFromFile.SecondNoteCode == "Second Note Code" && + parsedHeadersFromFile.SecondNoteCodeDescription == "Second Note Code Description" && + parsedHeadersFromFile.SecondNoteTo == "Second Note To" && + parsedHeadersFromFile.SecondNoteMessage == "Second Note Message" && + parsedHeadersFromFile.ThirdNoteCode == "Third Note Code" && + parsedHeadersFromFile.ThirdNoteCodeDescription == "Third Note Code Description" && + parsedHeadersFromFile.ThirdNoteTo == "Third Note To" && + parsedHeadersFromFile.ThirdNoteMessage == "Third Note Message" { + allHeadersWereProcessedCorrectly = true + } + + return allHeadersWereProcessedCorrectly +} + +// ProcessTPPSReportEntryForOneRow takes one data row, cleans it, and parses it into a string representation of the TPPSData struct +func ProcessTPPSReportEntryForOneRow(row []string) TPPSData { + tppsReportEntryForOnePaymentRequest := strings.Split(row[0], "\t") var tppsData TPPSData + var processedTPPSReportEntryForOnePaymentRequest []string + if len(tppsReportEntryForOnePaymentRequest) > 0 { - tppsData.InvoiceNumber = tppsReportEntryForOnePaymentRequest[0] - tppsData.TPPSCreatedDocumentDate = tppsReportEntryForOnePaymentRequest[1] - tppsData.SellerPaidDate = tppsReportEntryForOnePaymentRequest[2] - tppsData.InvoiceTotalCharges = tppsReportEntryForOnePaymentRequest[3] - tppsData.LineDescription = tppsReportEntryForOnePaymentRequest[4] - tppsData.ProductDescription = tppsReportEntryForOnePaymentRequest[5] - tppsData.LineBillingUnits = tppsReportEntryForOnePaymentRequest[6] - tppsData.LineUnitPrice = tppsReportEntryForOnePaymentRequest[7] - tppsData.LineNetCharge = tppsReportEntryForOnePaymentRequest[8] - tppsData.POTCN = tppsReportEntryForOnePaymentRequest[9] - tppsData.LineNumber = tppsReportEntryForOnePaymentRequest[10] - tppsData.FirstNoteCode = tppsReportEntryForOnePaymentRequest[11] - tppsData.FirstNoteDescription = tppsReportEntryForOnePaymentRequest[12] - tppsData.FirstNoteTo = tppsReportEntryForOnePaymentRequest[13] - tppsData.FirstNoteMessage = tppsReportEntryForOnePaymentRequest[14] - tppsData.SecondNoteCode = tppsReportEntryForOnePaymentRequest[15] - tppsData.SecondNoteDescription = tppsReportEntryForOnePaymentRequest[16] - tppsData.SecondNoteTo = tppsReportEntryForOnePaymentRequest[17] - tppsData.SecondNoteMessage = tppsReportEntryForOnePaymentRequest[18] - tppsData.ThirdNoteCode = tppsReportEntryForOnePaymentRequest[19] - tppsData.ThirdNoteDescription = tppsReportEntryForOnePaymentRequest[20] - tppsData.ThirdNoteTo = tppsReportEntryForOnePaymentRequest[21] - tppsData.ThirdNoteMessage = tppsReportEntryForOnePaymentRequest[22] + + for indexOfOneEntry := range tppsReportEntryForOnePaymentRequest { + var processedEntry string + if tppsReportEntryForOnePaymentRequest[indexOfOneEntry] != "" { + // Remove any NULL characters + entryWithoutNulls := strings.Split(tppsReportEntryForOnePaymentRequest[indexOfOneEntry], "\x00") + for indexCleanedUp := range len(entryWithoutNulls) { + // Clean up extra characters + cleanedUpEntryString := strings.Split(entryWithoutNulls[indexCleanedUp], ("\xff\xfe")) + for index := range cleanedUpEntryString { + if cleanedUpEntryString[index] != "" { + processedEntry += cleanedUpEntryString[index] + } + } + } + } + // After we have fully processed an entry and have built a string, store it + processedTPPSReportEntryForOnePaymentRequest = append(processedTPPSReportEntryForOnePaymentRequest, processedEntry) + } + + tppsData.InvoiceNumber = processedTPPSReportEntryForOnePaymentRequest[0] + tppsData.TPPSCreatedDocumentDate = processedTPPSReportEntryForOnePaymentRequest[1] + tppsData.SellerPaidDate = processedTPPSReportEntryForOnePaymentRequest[2] + tppsData.InvoiceTotalCharges = processedTPPSReportEntryForOnePaymentRequest[3] + tppsData.LineDescription = processedTPPSReportEntryForOnePaymentRequest[4] + tppsData.ProductDescription = processedTPPSReportEntryForOnePaymentRequest[5] + tppsData.LineBillingUnits = processedTPPSReportEntryForOnePaymentRequest[6] + tppsData.LineUnitPrice = processedTPPSReportEntryForOnePaymentRequest[7] + tppsData.LineNetCharge = processedTPPSReportEntryForOnePaymentRequest[8] + tppsData.POTCN = processedTPPSReportEntryForOnePaymentRequest[9] + tppsData.LineNumber = processedTPPSReportEntryForOnePaymentRequest[10] + tppsData.FirstNoteCode = processedTPPSReportEntryForOnePaymentRequest[11] + tppsData.FirstNoteCodeDescription = processedTPPSReportEntryForOnePaymentRequest[12] + tppsData.FirstNoteTo = processedTPPSReportEntryForOnePaymentRequest[13] + tppsData.FirstNoteMessage = processedTPPSReportEntryForOnePaymentRequest[14] + tppsData.SecondNoteCode = processedTPPSReportEntryForOnePaymentRequest[15] + tppsData.SecondNoteCodeDescription = processedTPPSReportEntryForOnePaymentRequest[16] + tppsData.SecondNoteTo = processedTPPSReportEntryForOnePaymentRequest[17] + tppsData.SecondNoteMessage = processedTPPSReportEntryForOnePaymentRequest[18] + tppsData.ThirdNoteCode = processedTPPSReportEntryForOnePaymentRequest[19] + tppsData.ThirdNoteCodeDescription = processedTPPSReportEntryForOnePaymentRequest[20] + tppsData.ThirdNoteTo = processedTPPSReportEntryForOnePaymentRequest[21] + tppsData.ThirdNoteMessage = processedTPPSReportEntryForOnePaymentRequest[22] } return tppsData } -// Parse takes in a string representation of a TPPS paid invoice report file and reads it into a TPPSData struct +// Parse takes in a TPPS paid invoice report file and parses it into an array of TPPSData structs func (e *EDI) Parse(stringTPPSPaidInvoiceReport string) ([]TPPSData, error) { var tppsDataFile []TPPSData - scanner := bufio.NewScanner(strings.NewReader(stringTPPSPaidInvoiceReport)) + filename := "/Users/m.traskowsky_cn/Desktop/MILMOVE-en20240731.csv" // test file 1 + // filename := "/Users/m.traskowsky_cn/Desktop/MILMOVE-en20240801.csv" // test file 2 + csvFile, _ := os.Open(filepath.Clean(filename)) + endOfFile := false + headersAreCorrect := false + + scanner := bufio.NewScanner(csvFile) for scanner.Scan() { + rowIsHeader := false row := strings.Split(scanner.Text(), "\n") - if row != nil { - rowSplitIntoColumns := strings.Split(row[0], "\t") - if rowSplitIntoColumns[0] == "Invoice Number From Invoice" { - // move past the header row to the actual TPPS data - continue + // if we have reached a NULL at the end of the file and, return the tppsData + if row[0] == "\x00" { + endOfFile = true + } + if row != nil && !endOfFile { + tppsReportEntryForOnePaymentRequest := ProcessTPPSReportEntryForOneRow(row) + if tppsReportEntryForOnePaymentRequest.InvoiceNumber == "Invoice Number From Invoice" { + rowIsHeader = true + headersAreCorrect = VerifyHeadersParsedCorrectly(tppsReportEntryForOnePaymentRequest) + } + if !rowIsHeader && headersAreCorrect { // don't append the header row to result set + tppsDataFile = append(tppsDataFile, tppsReportEntryForOnePaymentRequest) } - tppsReportEntryForOnePaymentRequest := ProcessTPPSReportEntryForOnePaymentRequest(rowSplitIntoColumns) - tppsDataFile = append(tppsDataFile, tppsReportEntryForOnePaymentRequest) } } diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index bc61c8961fe..e426604a8f4 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -24,7 +24,6 @@ func NewTPPSPaidInvoiceReportProcessor() services.SyncadaFileProcessor { func (e *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContext, _ string, stringTPPSPaidInvoiceReport string) error { tppsPaidInvoiceReport := tppsReponse.EDI{} - // TODO instead of _, use the return value here as it will contain the struct with tpps paid data tppsData, err := tppsPaidInvoiceReport.Parse(stringTPPSPaidInvoiceReport) if err != nil { appCtx.Logger().Error("unable to parse TPPS paid invoice report", zap.Error(err)) From fe8616af03c704d9d4f54a3d9e972d5315d84a10 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Tue, 6 Aug 2024 20:17:44 +0000 Subject: [PATCH 07/46] parse test csv into TPPSData struct --- pkg/edi/tpps_paid_invoice_report/parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index 9186835650b..26838fd7bd6 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -79,7 +79,7 @@ func ProcessTPPSReportEntryForOneRow(row []string) TPPSData { if tppsReportEntryForOnePaymentRequest[indexOfOneEntry] != "" { // Remove any NULL characters entryWithoutNulls := strings.Split(tppsReportEntryForOnePaymentRequest[indexOfOneEntry], "\x00") - for indexCleanedUp := range len(entryWithoutNulls) { + for indexCleanedUp := range entryWithoutNulls { // Clean up extra characters cleanedUpEntryString := strings.Split(entryWithoutNulls[indexCleanedUp], ("\xff\xfe")) for index := range cleanedUpEntryString { From 097c161a628979c9a03e615579ecb306e9a38a97 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Tue, 6 Aug 2024 20:27:20 +0000 Subject: [PATCH 08/46] fix a few small typos --- pkg/edi/tpps_paid_invoice_report/parser.go | 4 ++-- pkg/services/invoice/process_tpps_paid_invoice_report.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index 26838fd7bd6..ae39d153bdf 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -134,7 +134,7 @@ func (e *EDI) Parse(stringTPPSPaidInvoiceReport string) ([]TPPSData, error) { for scanner.Scan() { rowIsHeader := false row := strings.Split(scanner.Text(), "\n") - // if we have reached a NULL at the end of the file and, return the tppsData + // If we have reached a NULL at the end of the file, do not continue parsing if row[0] == "\x00" { endOfFile = true } @@ -144,7 +144,7 @@ func (e *EDI) Parse(stringTPPSPaidInvoiceReport string) ([]TPPSData, error) { rowIsHeader = true headersAreCorrect = VerifyHeadersParsedCorrectly(tppsReportEntryForOnePaymentRequest) } - if !rowIsHeader && headersAreCorrect { // don't append the header row to result set + if !rowIsHeader && headersAreCorrect { // No need to append the header row to result set tppsDataFile = append(tppsDataFile, tppsReportEntryForOnePaymentRequest) } } diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index e426604a8f4..49becaa37ba 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -14,13 +14,13 @@ import ( type tppsPaidInvoiceReportProcessor struct { } -// NewEDITPPSInvoiceProcessor returns a new TPPS paid invoice report processor +// NewTPPSPaidInvoiceReportProcessor returns a new TPPS paid invoice report processor func NewTPPSPaidInvoiceReportProcessor() services.SyncadaFileProcessor { return &tppsPaidInvoiceReportProcessor{} } -// ProcessFile parses an TPPS paid invoice report response and updates the payment request status +// ProcessFile parses a TPPS paid invoice report response and updates the payment request status func (e *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContext, _ string, stringTPPSPaidInvoiceReport string) error { tppsPaidInvoiceReport := tppsReponse.EDI{} From 6b32ff61730d80c8d79791183d13e44fd7ae7c31 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 9 Aug 2024 13:59:57 +0000 Subject: [PATCH 09/46] store tpps report entries to database --- ...40802161708_tpps_paid_invoice_table.up.sql | 18 +- pkg/edi/tpps_paid_invoice_report/parser.go | 5 +- pkg/models/tpps_paid_invoice_report.go | 74 +++++++ .../process_tpps_paid_invoice_report.go | 184 +++++++++++++++++- .../process_tpps_paid_invoice_report_test.go | 110 +++++++++++ 5 files changed, 379 insertions(+), 12 deletions(-) create mode 100644 pkg/models/tpps_paid_invoice_report.go diff --git a/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql index 9c748f801ce..b6be9fca5b6 100644 --- a/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql +++ b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql @@ -1,15 +1,15 @@ CREATE TABLE IF NOT EXISTS tpps_paid_invoice_reports ( - id uuid not null primary key, - payment_request_id uuid not null, - invoice_number text not null, + id uuid NOT NULL, + -- payment_request_id uuid, + invoice_number varchar not null, tpps_created_doc_date timestamp, seller_paid_date timestamp, - invoice_total_charges varchar, + invoice_total_charges_in_millicents integer, line_description varchar, -- service item code IE (DOP, DUPK, DLH, FSC, DDP) product_description varchar, -- same values as above for line desciprtion service item code IE (DOP, DUPK, DLH, FSC, DDP) - line_billing_units varchar, - line_unit_price integer DEFAULT NULL, - line_net_charge integer DEFAULT NULL, + line_billing_units integer, + line_unit_price_in_millicents integer, + line_net_charge_in_millicents integer, po_tcn varchar, line_number varchar, first_note_code varchar, @@ -27,4 +27,8 @@ CREATE TABLE IF NOT EXISTS tpps_paid_invoice_reports ( created_at timestamp not null, updated_at timestamp not null ); + +COMMENT on COLUMN tpps_paid_invoice_reports.invoice_number IS 'Invoice number from the report that should match a payment_request_number'; + + COMMENT ON TABLE tpps_paid_invoice_reports IS 'Contains data populated from processing the TPPS paid invoice report'; \ No newline at end of file diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index ae39d153bdf..169d8f9609a 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -67,7 +67,7 @@ func VerifyHeadersParsedCorrectly(parsedHeadersFromFile TPPSData) bool { } // ProcessTPPSReportEntryForOneRow takes one data row, cleans it, and parses it into a string representation of the TPPSData struct -func ProcessTPPSReportEntryForOneRow(row []string) TPPSData { +func ParseTPPSReportEntryForOneRow(row []string) TPPSData { tppsReportEntryForOnePaymentRequest := strings.Split(row[0], "\t") var tppsData TPPSData var processedTPPSReportEntryForOnePaymentRequest []string @@ -89,6 +89,7 @@ func ProcessTPPSReportEntryForOneRow(row []string) TPPSData { } } } + processedEntry = strings.TrimSpace(processedEntry) // After we have fully processed an entry and have built a string, store it processedTPPSReportEntryForOnePaymentRequest = append(processedTPPSReportEntryForOnePaymentRequest, processedEntry) } @@ -139,7 +140,7 @@ func (e *EDI) Parse(stringTPPSPaidInvoiceReport string) ([]TPPSData, error) { endOfFile = true } if row != nil && !endOfFile { - tppsReportEntryForOnePaymentRequest := ProcessTPPSReportEntryForOneRow(row) + tppsReportEntryForOnePaymentRequest := ParseTPPSReportEntryForOneRow(row) if tppsReportEntryForOnePaymentRequest.InvoiceNumber == "Invoice Number From Invoice" { rowIsHeader = true headersAreCorrect = VerifyHeadersParsedCorrectly(tppsReportEntryForOnePaymentRequest) diff --git a/pkg/models/tpps_paid_invoice_report.go b/pkg/models/tpps_paid_invoice_report.go new file mode 100644 index 00000000000..96834ac0414 --- /dev/null +++ b/pkg/models/tpps_paid_invoice_report.go @@ -0,0 +1,74 @@ +package models + +import ( + "time" + + "github.com/gobuffalo/pop/v6" + "github.com/gobuffalo/validate/v3" + "github.com/gobuffalo/validate/v3/validators" + "github.com/gofrs/uuid" + "go.uber.org/zap/zapcore" + + "github.com/transcom/mymove/pkg/unit" +) + +// tppsPaidInvoiceReport stores the entries found from processing a TPPS paid invoice report +type TPPSPaidInvoiceReportEntry struct { + ID uuid.UUID `db:"id"` + // PaymentRequestID uuid.UUID `db:"payment_request_id"` + InvoiceNumber string `db:"invoice_number"` + TPPSCreatedDocumentDate time.Time `json:"tpps_created_doc_date" db:"tpps_created_doc_date"` + SellerPaidDate time.Time `json:"seller_paid_date" db:"seller_paid_date"` + InvoiceTotalChargesInMillicents unit.Millicents `json:"invoice_total_charges_in_millicents" db:"invoice_total_charges_in_millicents"` + LineDescription string `json:"line_description" db:"line_description"` + ProductDescription string `json:"product_description" db:"product_description"` + LineBillingUnits int `json:"line_billing_units" db:"line_billing_units"` + LineUnitPrice unit.Millicents `json:"line_unit_price_in_millicents" db:"line_unit_price_in_millicents"` + LineNetCharge unit.Millicents `json:"line_net_charge_in_millicents" db:"line_net_charge_in_millicents"` + POTCN string `json:"po_tcn" db:"po_tcn"` + LineNumber string `json:"line_number" db:"line_number"` + FirstNoteCode string `json:"first_note_code" db:"first_note_code"` + FirstNoteDescription string `json:"first_note_description" db:"first_note_description"` + FirstNoteCodeTo string `json:"first_note_to" db:"first_note_to"` + FirstNoteCodeMessage string `json:"first_note_message" db:"first_note_message"` + SecondNoteCode string `json:"second_note_code" db:"second_note_code"` + SecondNoteDescription string `json:"second_note_description" db:"second_note_description"` + SecondNoteCodeTo string `json:"second_note_to" db:"second_note_to"` + SecondNoteCodeMessage string `json:"second_note_message" db:"second_note_message"` + ThirdNoteCode string `json:"third_note_code" db:"third_note_code"` + ThirdNoteDescription string `json:"third_note_code_description" db:"third_note_code_description"` + ThirdNoteCodeTo string `json:"third_note_code_to" db:"third_note_code_to"` + ThirdNoteCodeMessage string `json:"third_note_code_message" db:"third_note_code_message"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` +} + +// TableName overrides the table name used by Pop. +func (t TPPSPaidInvoiceReportEntry) TableName() string { + return "tpps_paid_invoice_reports" +} + +// PaymentRequests is a slice of PaymentRequest +type TPPSPaidInvoiceReportEntrys []TPPSPaidInvoiceReportEntry + +// Validate gets run every time you call a "pop.Validate*" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method. +func (t *TPPSPaidInvoiceReportEntry) Validate(_ *pop.Connection) (*validate.Errors, error) { + var vs []validate.Validator + // vs = append(vs, &validators.UUIDIsPresent{Field: t.PaymentRequestID, Name: "PaymentRequestID"}) + if t.InvoiceNumber != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.InvoiceNumber, Name: "InvoiceNumber", Message: "InvoiceNumber string if present should not be empty"}) + } + vs = append(vs, &validators.TimeIsPresent{Field: t.TPPSCreatedDocumentDate, Name: "TPPSCreatedDocumentDate"}) + + return validate.Validate(vs...), nil +} + +// MarshalLogObject is required to be able to zap.Object log this model. +func (t *TPPSPaidInvoiceReportEntry) MarshalLogObject(encoder zapcore.ObjectEncoder) error { + // encoder.AddString("EDIType", e.EDIType.String()) + // encoder.AddInt("NumEDIsProcessed", e.NumEDIsProcessed) + // encoder.AddTime("ProcessStartedAt", e.ProcessStartedAt) + // encoder.AddTime("ProcessEndedAt", e.ProcessEndedAt) + encoder.AddString("InvoiceNumber", t.InvoiceNumber) + return nil +} diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index 49becaa37ba..3a3a33eea6b 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -2,18 +2,50 @@ package invoice import ( "fmt" + "strconv" + "strings" + "time" + "github.com/gobuffalo/validate/v3" "go.uber.org/zap" "github.com/transcom/mymove/pkg/appcontext" tppsReponse "github.com/transcom/mymove/pkg/edi/tpps_paid_invoice_report" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/unit" ) type tppsPaidInvoiceReportProcessor struct { } +// TPPSData represents TPPS paid invoice report data +type TPPSData struct { + InvoiceNumber string + TPPSCreatedDocumentDate string + SellerPaidDate string + InvoiceTotalCharges string + LineDescription string + ProductDescription string + LineBillingUnits string + LineUnitPrice string + LineNetCharge string + POTCN string + LineNumber string + FirstNoteCode string + FirstNoteCodeDescription string + FirstNoteTo string + FirstNoteMessage string + SecondNoteCode string + SecondNoteCodeDescription string + SecondNoteTo string + SecondNoteMessage string + ThirdNoteCode string + ThirdNoteCodeDescription string + ThirdNoteTo string + ThirdNoteMessage string +} + // NewTPPSPaidInvoiceReportProcessor returns a new TPPS paid invoice report processor func NewTPPSPaidInvoiceReportProcessor() services.SyncadaFileProcessor { @@ -21,7 +53,7 @@ func NewTPPSPaidInvoiceReportProcessor() services.SyncadaFileProcessor { } // ProcessFile parses a TPPS paid invoice report response and updates the payment request status -func (e *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContext, _ string, stringTPPSPaidInvoiceReport string) error { +func (t *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContext, _ string, stringTPPSPaidInvoiceReport string) error { tppsPaidInvoiceReport := tppsReponse.EDI{} tppsData, err := tppsPaidInvoiceReport.Parse(stringTPPSPaidInvoiceReport) @@ -29,12 +61,158 @@ func (e *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContex appCtx.Logger().Error("unable to parse TPPS paid invoice report", zap.Error(err)) return fmt.Errorf("unable to parse TPPS paid invoice report") } else { - appCtx.Logger().Info("Successfully parsed TPPS Paid Invoice Report and found ", zap.Int("entries", len(tppsData))) + appCtx.Logger().Info("Successfully parsed TPPS Paid Invoice Report") + } + + appCtx.Logger().Info("RECEIVED: TPPS Paid Invoice Report Processor received a TPPS Paid Invoice Report") + + if tppsData != nil { + verrs, errs := t.StoreTPPSPaidInvoiceReportInDatabase(appCtx, tppsData, "", stringTPPSPaidInvoiceReport) + if err != nil { + return errs + } + if verrs.HasAny() { + return verrs + } } return nil } -func (e *tppsPaidInvoiceReportProcessor) EDIType() models.EDIType { +func (t *tppsPaidInvoiceReportProcessor) EDIType() models.EDIType { return models.TPPSPaidInvoiceReport } + +func getPriceParts(rawPrice string) (int, int, int, error) { + // Get rid of a dollar sign if there is one. + basePrice := strings.Replace(rawPrice, "$", "", -1) + + // Split the string on the decimal point. + priceParts := strings.Split(basePrice, ".") + if len(priceParts) != 2 { + return 0, 0, 0, fmt.Errorf("expected 2 price parts but found %d for price [%s]", len(priceParts), rawPrice) + } + + integerPart, err := strconv.Atoi(priceParts[0]) + if err != nil { + return 0, 0, 0, fmt.Errorf("could not convert integer part of price [%s]", rawPrice) + } + + expectedDecimalPlaces := 0 + if len(priceParts[1]) > 0 { + expectedDecimalPlaces = len(priceParts[1]) + } + + fractionalPart, err := strconv.Atoi(priceParts[1]) + if err != nil { + return 0, 0, 0, fmt.Errorf("could not convert fractional part of price [%s]", rawPrice) + } + + return integerPart, fractionalPart, expectedDecimalPlaces, nil +} + +func priceToMillicents(rawPrice string) (int, error) { + integerPart, fractionalPart, expectedDecimalPlaces, err := getPriceParts(rawPrice) + if err != nil { + return 0, fmt.Errorf("could not parse price [%s]: %w", rawPrice, err) + } + var millicents int + if expectedDecimalPlaces == 0 { + // . cents = 0 millicents + millicents = (integerPart * 100000) + } else if expectedDecimalPlaces == 1 { + // .5 cents = 50000 millicents + millicents = (integerPart * 100000) + (fractionalPart * 10000) + } else if expectedDecimalPlaces == 2 { + // .25 cents = 25000 millicents + millicents = (integerPart * 100000) + (fractionalPart * 1000) + } else if expectedDecimalPlaces == 3 { + // .025 cents = 25000 millicents + millicents = (integerPart * 100000) + (fractionalPart * 100) + } else if expectedDecimalPlaces == 4 { + // .0025 cents = 250 millicents + millicents = (integerPart * 100000) + (fractionalPart * 10) + } + return millicents, nil +} + +func (t *tppsPaidInvoiceReportProcessor) StoreTPPSPaidInvoiceReportInDatabase(appCtx appcontext.AppContext, tppsData []tppsReponse.TPPSData, _ string, stringTPPSPaidInvoiceReport string) (*validate.Errors, error) { + var verrs *validate.Errors + + transactionError := appCtx.NewTransaction(func(txnAppCtx appcontext.AppContext) error { + + DateParamFormat := "2006-01-02" + + for _, tppsEntry := range tppsData { + timeOfTPPSCreatedDocumentDate, err := time.Parse(DateParamFormat, tppsEntry.TPPSCreatedDocumentDate) + if err != nil { + appCtx.Logger().Info("unable to parse TPPSCreatedDocumentDate from TPPS paid invoice report", zap.Error(err)) + } + timeOfSellerPaidDate, err := time.Parse(DateParamFormat, tppsEntry.SellerPaidDate) + if err != nil { + appCtx.Logger().Info("unable to parse SellerPaidDate from TPPS paid invoice report", zap.Error(err)) + } + invoiceTotalChargesInMillicents, err := priceToMillicents(tppsEntry.InvoiceTotalCharges) + if err != nil { + appCtx.Logger().Info("unable to parse InvoiceTotalCharges from TPPS paid invoice report", zap.Error(err)) + } + intLineBillingUnits, err := strconv.Atoi(tppsEntry.LineBillingUnits) + if err != nil { + appCtx.Logger().Info("unable to parse LineBillingUnits from TPPS paid invoice report", zap.Error(err)) + } + lineUnitPriceInMillicents, err := priceToMillicents(tppsEntry.LineUnitPrice) + if err != nil { + appCtx.Logger().Info("unable to parse LineUnitPrice from TPPS paid invoice report", zap.Error(err)) + } + lineNetChargeInMillicents, err := priceToMillicents(tppsEntry.LineNetCharge) + if err != nil { + appCtx.Logger().Info("unable to parse LineNetCharge from TPPS paid invoice report", zap.Error(err)) + } + + tppsEntryModel := models.TPPSPaidInvoiceReportEntry{ + InvoiceNumber: tppsEntry.InvoiceNumber, + TPPSCreatedDocumentDate: timeOfTPPSCreatedDocumentDate, + SellerPaidDate: timeOfSellerPaidDate, + InvoiceTotalChargesInMillicents: unit.Millicents(invoiceTotalChargesInMillicents), + LineDescription: tppsEntry.LineDescription, + ProductDescription: tppsEntry.ProductDescription, + LineBillingUnits: intLineBillingUnits, + LineUnitPrice: unit.Millicents(lineUnitPriceInMillicents), + LineNetCharge: unit.Millicents(lineNetChargeInMillicents), + POTCN: tppsEntry.POTCN, + LineNumber: tppsEntry.LineNumber, + FirstNoteCode: tppsEntry.FirstNoteCode, + FirstNoteDescription: tppsEntry.FirstNoteCodeDescription, + FirstNoteCodeTo: tppsEntry.FirstNoteTo, + FirstNoteCodeMessage: tppsEntry.FirstNoteMessage, + SecondNoteCode: tppsEntry.SecondNoteCode, + SecondNoteDescription: tppsEntry.SecondNoteCodeDescription, + SecondNoteCodeTo: tppsEntry.SecondNoteTo, + SecondNoteCodeMessage: tppsEntry.SecondNoteMessage, + ThirdNoteCode: tppsEntry.ThirdNoteCode, + ThirdNoteDescription: tppsEntry.ThirdNoteCodeDescription, + ThirdNoteCodeTo: tppsEntry.ThirdNoteTo, + ThirdNoteCodeMessage: tppsEntry.ThirdNoteMessage, + } + + verrs, err = txnAppCtx.DB().ValidateAndSave(&tppsEntryModel) + if err != nil { + appCtx.Logger().Error("failure saving entry from TPPS paid invoice report", zap.Error(err)) + return err + } + } + + return nil + }) + + if transactionError != nil { + appCtx.Logger().Error(transactionError.Error()) + return verrs, transactionError + } + if verrs.HasAny() { + appCtx.Logger().Error("unable to process TPPS paid invoice report", zap.Error(verrs)) + return verrs, nil + } + + return nil, nil +} diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index f3b8bdf434e..767e2432c70 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -2,12 +2,14 @@ package invoice import ( "testing" + "time" "github.com/stretchr/testify/suite" "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/testingsuite" + "github.com/transcom/mymove/pkg/unit" ) type ProcessTPPSPaidInvoiceReportSuite struct { @@ -53,7 +55,115 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport LinkOnly: true, }, }, nil) + err := tppsPaidInvoiceReportProcessor.ProcessFile(suite.AppContextForTest(), "", sampleTPPSPaidInvoiceReportString.contents) suite.NoError(err) + + tppsEntries := []models.TPPSPaidInvoiceReportEntry{} + err = suite.DB().All(&tppsEntries) + suite.NoError(err) + for tppsEntryIndex := range tppsEntries { + + if tppsEntryIndex == 0 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(115155000)) // 1151.55 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 3760) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(770)) // 0.0077 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(2895000)) // 28.95 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-826285fc") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") + } + if tppsEntryIndex == 1 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(115155000)) // 1151.55 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "FSC") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "FSC") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 3760) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(140)) // 0.0014 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(539000)) // 5.39 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-aeb3cfea") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "4") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") + + } + if tppsEntryIndex == 2 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(115155000)) // 1151.55 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DLH") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DLH") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 3760) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(26560)) // 0.2656 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(99877000)) // 998.77 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-c8ea170b") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "2") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") + + } + if tppsEntryIndex == 3 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(115155000)) // 1151.55 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DUPK") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DUPK") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 3760) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(3150)) // 0.0315 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(11844000)) // 118.44 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-265c16d7") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "3") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") + + } + if tppsEntryIndex == 4 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "9436-4123-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(12525000)) // 125.25 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 7500) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(1670)) // 0.0167 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(12525000)) // 125.25 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "9436-4123-93761f93") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50057") + + } + suite.NotNil(tppsEntries[tppsEntryIndex].ID) + suite.NotNil(tppsEntries[tppsEntryIndex].CreatedAt) + suite.NotNil(tppsEntries[tppsEntryIndex].UpdatedAt) + suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteCode, "") + suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteDescription, "") + suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteCodeTo, "") + suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteCodeMessage, "") + suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteCode, "") + suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteDescription, "") + suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteCodeTo, "") + suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteCodeMessage, "") + } }) } From 4a1527a768fd751f3646a2896d7216e72d0b6a77 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 9 Aug 2024 15:11:23 +0000 Subject: [PATCH 10/46] add path to test csv for parsing and storing --- ...40802161708_tpps_paid_invoice_table.up.sql | 28 ++++++++++++--- pkg/edi/tpps_paid_invoice_report/parser.go | 6 ++-- .../process_tpps_paid_invoice_report.go | 26 ++++++-------- .../process_tpps_paid_invoice_report_test.go | 33 +++--------------- .../tpps_paid_invoice_report_testfile.csv | Bin 0 -> 3938 bytes 5 files changed, 41 insertions(+), 52 deletions(-) create mode 100644 pkg/services/invoice/tpps_paid_invoice_report_testfile.csv diff --git a/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql index b6be9fca5b6..87f42befd0d 100644 --- a/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql +++ b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql @@ -1,12 +1,11 @@ CREATE TABLE IF NOT EXISTS tpps_paid_invoice_reports ( id uuid NOT NULL, - -- payment_request_id uuid, invoice_number varchar not null, tpps_created_doc_date timestamp, seller_paid_date timestamp, invoice_total_charges_in_millicents integer, - line_description varchar, -- service item code IE (DOP, DUPK, DLH, FSC, DDP) - product_description varchar, -- same values as above for line desciprtion service item code IE (DOP, DUPK, DLH, FSC, DDP) + line_description varchar, + product_description varchar, line_billing_units integer, line_unit_price_in_millicents integer, line_net_charge_in_millicents integer, @@ -29,6 +28,27 @@ CREATE TABLE IF NOT EXISTS tpps_paid_invoice_reports ( ); COMMENT on COLUMN tpps_paid_invoice_reports.invoice_number IS 'Invoice number from the report that should match a payment_request_number'; - +COMMENT on COLUMN tpps_paid_invoice_reports.tpps_created_doc_date IS 'Date that TPPS created the invoice report'; +COMMENT on COLUMN tpps_paid_invoice_reports.seller_paid_date IS 'Seller paid date'; +COMMENT on COLUMN tpps_paid_invoice_reports.invoice_total_charges_in_millicents IS 'Total charges for the invoice represented in millicents'; +COMMENT on COLUMN tpps_paid_invoice_reports.line_description IS 'Reservice code for the service item'; +COMMENT on COLUMN tpps_paid_invoice_reports.product_description IS 'Reservice code for the service item'; +COMMENT on COLUMN tpps_paid_invoice_reports.line_billing_units IS 'Line billing units'; +COMMENT on COLUMN tpps_paid_invoice_reports.line_unit_price_in_millicents IS 'Unit price represented in millicents'; +COMMENT on COLUMN tpps_paid_invoice_reports.line_net_charge_in_millicents IS 'Net charge represented in millicents'; +COMMENT on COLUMN tpps_paid_invoice_reports.po_tcn IS 'PO/TCN'; +COMMENT on COLUMN tpps_paid_invoice_reports.line_number IS 'Line number'; +COMMENT on COLUMN tpps_paid_invoice_reports.first_note_code IS 'Code of the first note'; +COMMENT on COLUMN tpps_paid_invoice_reports.first_note_description IS 'Description of the first note'; +COMMENT on COLUMN tpps_paid_invoice_reports.first_note_to IS 'Note of the first note'; +COMMENT on COLUMN tpps_paid_invoice_reports.first_note_message IS 'Message of the first note'; +COMMENT on COLUMN tpps_paid_invoice_reports.second_note_code IS 'Code of the second note'; +COMMENT on COLUMN tpps_paid_invoice_reports.second_note_description IS 'Description of the second note'; +COMMENT on COLUMN tpps_paid_invoice_reports.second_note_to IS 'Note of the second note'; +COMMENT on COLUMN tpps_paid_invoice_reports.second_note_message IS 'Message of the second note'; +COMMENT on COLUMN tpps_paid_invoice_reports.third_note_code IS 'Code of the third note'; +COMMENT on COLUMN tpps_paid_invoice_reports.third_note_code_description IS 'Description of the third note'; +COMMENT on COLUMN tpps_paid_invoice_reports.third_note_code_to IS 'Note of the third note'; +COMMENT on COLUMN tpps_paid_invoice_reports.third_note_code_message IS 'Message of the third note'; COMMENT ON TABLE tpps_paid_invoice_reports IS 'Contains data populated from processing the TPPS paid invoice report'; \ No newline at end of file diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index 169d8f9609a..5fd92555b98 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -122,12 +122,10 @@ func ParseTPPSReportEntryForOneRow(row []string) TPPSData { } // Parse takes in a TPPS paid invoice report file and parses it into an array of TPPSData structs -func (e *EDI) Parse(stringTPPSPaidInvoiceReport string) ([]TPPSData, error) { +func (e *EDI) Parse(stringTPPSPaidInvoiceReportFilePath string) ([]TPPSData, error) { var tppsDataFile []TPPSData - filename := "/Users/m.traskowsky_cn/Desktop/MILMOVE-en20240731.csv" // test file 1 - // filename := "/Users/m.traskowsky_cn/Desktop/MILMOVE-en20240801.csv" // test file 2 - csvFile, _ := os.Open(filepath.Clean(filename)) + csvFile, _ := os.Open(filepath.Clean(stringTPPSPaidInvoiceReportFilePath)) endOfFile := false headersAreCorrect := false diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index 3a3a33eea6b..6d0f46e6406 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -53,10 +53,10 @@ func NewTPPSPaidInvoiceReportProcessor() services.SyncadaFileProcessor { } // ProcessFile parses a TPPS paid invoice report response and updates the payment request status -func (t *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContext, _ string, stringTPPSPaidInvoiceReport string) error { +func (t *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContext, TPPSPaidInvoiceReportFilePath string, stringTPPSPaidInvoiceReport string) error { tppsPaidInvoiceReport := tppsReponse.EDI{} - tppsData, err := tppsPaidInvoiceReport.Parse(stringTPPSPaidInvoiceReport) + tppsData, err := tppsPaidInvoiceReport.Parse(TPPSPaidInvoiceReportFilePath) if err != nil { appCtx.Logger().Error("unable to parse TPPS paid invoice report", zap.Error(err)) return fmt.Errorf("unable to parse TPPS paid invoice report") @@ -67,7 +67,7 @@ func (t *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContex appCtx.Logger().Info("RECEIVED: TPPS Paid Invoice Report Processor received a TPPS Paid Invoice Report") if tppsData != nil { - verrs, errs := t.StoreTPPSPaidInvoiceReportInDatabase(appCtx, tppsData, "", stringTPPSPaidInvoiceReport) + verrs, errs := t.StoreTPPSPaidInvoiceReportInDatabase(appCtx, tppsData) if err != nil { return errs } @@ -117,26 +117,20 @@ func priceToMillicents(rawPrice string) (int, error) { return 0, fmt.Errorf("could not parse price [%s]: %w", rawPrice, err) } var millicents int - if expectedDecimalPlaces == 0 { - // . cents = 0 millicents - millicents = (integerPart * 100000) - } else if expectedDecimalPlaces == 1 { - // .5 cents = 50000 millicents - millicents = (integerPart * 100000) + (fractionalPart * 10000) + millicents = (integerPart * 100000) + if expectedDecimalPlaces == 1 { + millicents += (fractionalPart * 10000) } else if expectedDecimalPlaces == 2 { - // .25 cents = 25000 millicents - millicents = (integerPart * 100000) + (fractionalPart * 1000) + millicents += (fractionalPart * 1000) } else if expectedDecimalPlaces == 3 { - // .025 cents = 25000 millicents - millicents = (integerPart * 100000) + (fractionalPart * 100) + millicents += (fractionalPart * 100) } else if expectedDecimalPlaces == 4 { - // .0025 cents = 250 millicents - millicents = (integerPart * 100000) + (fractionalPart * 10) + millicents += (fractionalPart * 10) } return millicents, nil } -func (t *tppsPaidInvoiceReportProcessor) StoreTPPSPaidInvoiceReportInDatabase(appCtx appcontext.AppContext, tppsData []tppsReponse.TPPSData, _ string, stringTPPSPaidInvoiceReport string) (*validate.Errors, error) { +func (t *tppsPaidInvoiceReportProcessor) StoreTPPSPaidInvoiceReportInDatabase(appCtx appcontext.AppContext, tppsData []tppsReponse.TPPSData) (*validate.Errors, error) { var verrs *validate.Errors transactionError := appCtx.NewTransaction(func(txnAppCtx appcontext.AppContext) error { diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index 767e2432c70..7842f23ff57 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/testingsuite" "github.com/transcom/mymove/pkg/unit" @@ -26,42 +25,20 @@ func TestProcessTPPSPaidInvoiceReportSuite(t *testing.T) { ts.PopTestSuite.TearDown() } -type FakeTPPSFile struct { - contents string -} - func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport() { tppsPaidInvoiceReportProcessor := NewTPPSPaidInvoiceReportProcessor() suite.Run("successfully proccesses a valid TPPSPaidInvoiceReport", func() { - paymentRequest := factory.BuildPaymentRequest(suite.DB(), nil, nil) - sampleTPPSPaidInvoiceReportString := FakeTPPSFile{ - `Invoice Number From Invoice Document Create Date Seller Paid Date Invoice Total Charges Line Description Product Description Line Billing Units Line Unit Price Line Net Charge PO/TCN Line Number First Note Code First Note Code Description First Note To First Note Message Second Note Code Second Note Code Description Second Note To Second Note Message Third Note Code Third Note Code Description Third Note To Third Note Message -1841-7267-3 2024-07-29 2024-07-30 1151.55 DDP DDP 3760 0.0077 28.95 1841-7267-826285fc 1 INT Notes to My Company - INT CARR HQ50066 -1841-7267-3 2024-07-29 2024-07-30 1151.55 FSC FSC 3760 0.0014 5.39 1841-7267-aeb3cfea 4 INT Notes to My Company - INT CARR HQ50066 -1841-7267-3 2024-07-29 2024-07-30 1151.55 DLH DLH 3760 0.2656 998.77 1841-7267-c8ea170b 2 INT Notes to My Company - INT CARR HQ50066 -1841-7267-3 2024-07-29 2024-07-30 1151.55 DUPK DUPK 3760 0.0315 118.44 1841-7267-265c16d7 3 INT Notes to My Company - INT CARR HQ50066 -9436-4123-3 2024-07-29 2024-07-30 125.25 DDP DDP 7500 0.0167 125.25 9436-4123-93761f93 1 INT Notes to My Company - INT CARR HQ50057 -`} - factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ - { - Model: models.PaymentRequestToInterchangeControlNumber{ - InterchangeControlNumber: 100001251, - EDIType: models.EDIType858, - }, - }, - { - Model: paymentRequest, - LinkOnly: true, - }, - }, nil) - - err := tppsPaidInvoiceReportProcessor.ProcessFile(suite.AppContextForTest(), "", sampleTPPSPaidInvoiceReportString.contents) + + testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/tpps_paid_invoice_report_testfile.csv" + + err := tppsPaidInvoiceReportProcessor.ProcessFile(suite.AppContextForTest(), testTPPSPaidInvoiceReportFilePath, "") suite.NoError(err) tppsEntries := []models.TPPSPaidInvoiceReportEntry{} err = suite.DB().All(&tppsEntries) suite.NoError(err) + suite.Equal(len(tppsEntries), 5) for tppsEntryIndex := range tppsEntries { if tppsEntryIndex == 0 { diff --git a/pkg/services/invoice/tpps_paid_invoice_report_testfile.csv b/pkg/services/invoice/tpps_paid_invoice_report_testfile.csv new file mode 100644 index 0000000000000000000000000000000000000000..fabaeec7d869a6e99072fd503b256c332307ac8f GIT binary patch literal 3938 zcmeH}+iu!G5QgV=^AvsoQX6Bymn~M5stu)W@&Lrp#8N`4Kr8j-+kP_+H4bY)C`ogH zR(qVo{+|6kyMKPamzn&OJ1HcWsRS~VA95>~jPIG9(B3f*q^{>AV?8(BDIOMR4A7m* zn6diF9!5^SGQXje9!IPfGO20QeFt)m?*)1{=JlG<@4zN24Y zHA7=$m?vP%4OeEC>w1-^ci1XI^?b#i7xV?T_5GHsf$dPnYZcFseabjgh!xEzdBYbC zh-_WEinAlm1*qp{KeVsc7!DXs@VZT|(e}r}?^om7300mDv2UIx6@queQMqpotRd!FwwUjfCImH%zM8=5s zdY!fH4%Bx2ZPsH|?`=obx0!OL%welmJ{&1%^j#NmbL#l;I;%MF{G Date: Fri, 9 Aug 2024 16:08:18 +0000 Subject: [PATCH 11/46] add parser test, clean up --- cmd/milmove-tasks/process_edis.go | 2 - pkg/cli/gex_sftp.go | 3 +- pkg/edi/tpps_paid_invoice_report/edi.go | 31 ---- pkg/edi/tpps_paid_invoice_report/parser.go | 45 ++---- .../tpps_paid_invoice_report/parser_test.go | 138 +++++++++++++++++- pkg/edi/tpps_paid_invoice_report/tpps_data.go | 28 ++++ .../process_tpps_paid_invoice_report.go | 4 +- 7 files changed, 182 insertions(+), 69 deletions(-) delete mode 100644 pkg/edi/tpps_paid_invoice_report/edi.go create mode 100644 pkg/edi/tpps_paid_invoice_report/tpps_data.go diff --git a/cmd/milmove-tasks/process_edis.go b/cmd/milmove-tasks/process_edis.go index 6f174717e79..7a12fbf25be 100644 --- a/cmd/milmove-tasks/process_edis.go +++ b/cmd/milmove-tasks/process_edis.go @@ -244,8 +244,6 @@ func processEDIs(_ *cobra.Command, _ []string) error { logger.Info("Successfully processed EDI824 application advice responses") } - // TODO: should this go in a new file as opposed to process_edis.go since it is NOT an EDI file? - // Process TPPS paid invoice report pathTPPSPaidInvoiceReport := v.GetString(cli.SFTPTPPSPaidInvoiceReportPickupDirectory) _, err = syncadaSFTPSession.FetchAndProcessSyncadaFiles(appCtx, pathTPPSPaidInvoiceReport, lastReadTime, invoice.NewTPPSPaidInvoiceReportProcessor()) diff --git a/pkg/cli/gex_sftp.go b/pkg/cli/gex_sftp.go index 2db0727c0f9..aecb87cf013 100644 --- a/pkg/cli/gex_sftp.go +++ b/pkg/cli/gex_sftp.go @@ -44,7 +44,7 @@ const ( // Set of flags used for SFTPTPPSPaid const ( // SFTPTPPSPaidInvoiceReportPickupDirectory is the ENV var for the directory where TPPS delivers the TPPS paid invoice report - SFTPTPPSPaidInvoiceReportPickupDirectory string = "pending" + SFTPTPPSPaidInvoiceReportPickupDirectory string = "pending" // pending completion of B-20560 ) // InitGEXSFTPFlags initializes GEX SFTP command line flags @@ -57,6 +57,7 @@ func InitGEXSFTPFlags(flag *pflag.FlagSet) { flag.String(GEXSFTPHostKeyFlag, "", "GEX SFTP Host Key") flag.String(GEXSFTP997PickupDirectory, "", "GEX 997 SFTP Pickup Directory") flag.String(GEXSFTP824PickupDirectory, "", "GEX 834 SFTP Pickup Directory") + // flag.String(SFTPTPPSPaidInvoiceReportPickupDirectory, "", "TPPS Paid Invoice SFTP Pickup Directory") // pending completion of B-20560 } // CheckGEXSFTP validates GEX SFTP command line flags diff --git a/pkg/edi/tpps_paid_invoice_report/edi.go b/pkg/edi/tpps_paid_invoice_report/edi.go deleted file mode 100644 index 07a9f3efc9f..00000000000 --- a/pkg/edi/tpps_paid_invoice_report/edi.go +++ /dev/null @@ -1,31 +0,0 @@ -package tppspaidinvoicereport - -import edisegment "github.com/transcom/mymove/pkg/edi/segment" - -// look to pkg/edi/edi824/edi.go for reference - -// TransactionSet holds the transaction set envelope for the 824 -type TransactionSet struct { - ST edisegment.ST // transaction set header (bump up counter for "ST" and create new TransactionSet) - BGN edisegment.BGN // beginning statement - OTIs []edisegment.OTI `validate:"min=1,dive"` // original transaction identifications - TEDs []edisegment.TED `validate:"dive"` // technical error descriptions - SE edisegment.SE // transaction set trailer -} - -type functionalGroupEnvelope struct { - GS edisegment.GS // functional group header (bump up counter for "GS" and create new functionalGroupEnvelope) - TransactionSets []TransactionSet `validate:"min=1,dive"` - GE edisegment.GE // functional group trailer -} - -type interchangeControlEnvelope struct { - ISA edisegment.ISA // interchange control header - FunctionalGroups []functionalGroupEnvelope `validate:"min=1,dive"` - IEA edisegment.IEA // interchange control trailer -} - -// EDI holds all the segments to parse TPPS paid invoice report -type EDI struct { - InterchangeControlEnvelope interchangeControlEnvelope -} diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index 5fd92555b98..36d9fb66e01 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -2,38 +2,12 @@ package tppspaidinvoicereport import ( "bufio" + "io" "os" "path/filepath" "strings" ) -// TPPSData represents TPPS paid invoice report data -type TPPSData struct { - InvoiceNumber string - TPPSCreatedDocumentDate string - SellerPaidDate string - InvoiceTotalCharges string - LineDescription string - ProductDescription string - LineBillingUnits string - LineUnitPrice string - LineNetCharge string - POTCN string - LineNumber string - FirstNoteCode string - FirstNoteCodeDescription string - FirstNoteTo string - FirstNoteMessage string - SecondNoteCode string - SecondNoteCodeDescription string - SecondNoteTo string - SecondNoteMessage string - ThirdNoteCode string - ThirdNoteCodeDescription string - ThirdNoteTo string - ThirdNoteMessage string -} - func VerifyHeadersParsedCorrectly(parsedHeadersFromFile TPPSData) bool { allHeadersWereProcessedCorrectly := false @@ -122,19 +96,26 @@ func ParseTPPSReportEntryForOneRow(row []string) TPPSData { } // Parse takes in a TPPS paid invoice report file and parses it into an array of TPPSData structs -func (e *EDI) Parse(stringTPPSPaidInvoiceReportFilePath string) ([]TPPSData, error) { +func (t *TPPSData) Parse(stringTPPSPaidInvoiceReportFilePath string, testTPPSInvoiceString string) ([]TPPSData, error) { var tppsDataFile []TPPSData - csvFile, _ := os.Open(filepath.Clean(stringTPPSPaidInvoiceReportFilePath)) + var dataToParse io.Reader + + if stringTPPSPaidInvoiceReportFilePath != "" { + csvFile, _ := os.Open(filepath.Clean(stringTPPSPaidInvoiceReportFilePath)) + dataToParse = csvFile + } else { + dataToParse = strings.NewReader(testTPPSInvoiceString) + } endOfFile := false headersAreCorrect := false - scanner := bufio.NewScanner(csvFile) + scanner := bufio.NewScanner(dataToParse) for scanner.Scan() { rowIsHeader := false row := strings.Split(scanner.Text(), "\n") - // If we have reached a NULL at the end of the file, do not continue parsing - if row[0] == "\x00" { + // If we have reached a NULL or empty row at the end of the file, do not continue parsing + if row[0] == "\x00" || row[0] == "" { endOfFile = true } if row != nil && !endOfFile { diff --git a/pkg/edi/tpps_paid_invoice_report/parser_test.go b/pkg/edi/tpps_paid_invoice_report/parser_test.go index b88248d6d77..d2bbaaca7b1 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser_test.go +++ b/pkg/edi/tpps_paid_invoice_report/parser_test.go @@ -1,3 +1,139 @@ package tppspaidinvoicereport -// TODO add some tests for parsing the TPPS paid invoice report +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/transcom/mymove/pkg/testingsuite" +) + +type TPPSPaidInvoiceSuite struct { + testingsuite.BaseTestSuite +} + +func TestTPPSPaidInvoiceSuite(t *testing.T) { + hs := &TPPSPaidInvoiceSuite{} + + suite.Run(t, hs) +} + +func (suite *TPPSPaidInvoiceSuite) TestParse() { + + suite.Run("successfully parse simple TPPS Paid Invoice string", func() { + sampleTPPSPaidInvoiceString := `Invoice Number From Invoice Document Create Date Seller Paid Date Invoice Total Charges Line Description Product Description Line Billing Units Line Unit Price Line Net Charge PO/TCN Line Number First Note Code First Note Code Description First Note To First Note Message Second Note Code Second Note Code Description Second Note To Second Note Message Third Note Code Third Note Code Description Third Note To Third Note Message +1841-7267-3 2024-07-29 2024-07-30 1151.55 DDP DDP 3760 0.0077 28.95 1841-7267-826285fc 1 INT Notes to My Company - INT CARR HQ50066 +1841-7267-3 2024-07-29 2024-07-30 1151.55 FSC FSC 3760 0.0014 5.39 1841-7267-aeb3cfea 4 INT Notes to My Company - INT CARR HQ50066 +1841-7267-3 2024-07-29 2024-07-30 1151.55 DLH DLH 3760 0.2656 998.77 1841-7267-c8ea170b 2 INT Notes to My Company - INT CARR HQ50066 +1841-7267-3 2024-07-29 2024-07-30 1151.55 DUPK DUPK 3760 0.0315 118.44 1841-7267-265c16d7 3 INT Notes to My Company - INT CARR HQ50066 +9436-4123-3 2024-07-29 2024-07-30 125.25 DDP DDP 7500 0.0167 125.25 9436-4123-93761f93 1 INT Notes to My Company - INT CARR HQ50057 + +` + + tppsPaidInvoice := TPPSData{} + tppsEntries, err := tppsPaidInvoice.Parse("", sampleTPPSPaidInvoiceString) + suite.NoError(err, "Successful parse of 997") + suite.Equal(len(tppsEntries), 5) + + for tppsEntryIndex := range tppsEntries { + if tppsEntryIndex == 0 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, "2024-07-29") + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, "2024-07-30") + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalCharges, "1151.55") + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, "3760") + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, "0.0077") + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, "28.95") + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-826285fc") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteMessage, "HQ50066") + } + if tppsEntryIndex == 1 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, "2024-07-29") + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, "2024-07-30") + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalCharges, "1151.55") + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "FSC") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "FSC") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, "3760") + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, "0.0014") + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, "5.39") + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-aeb3cfea") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "4") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteMessage, "HQ50066") + + } + if tppsEntryIndex == 2 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, "2024-07-29") + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, "2024-07-30") + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalCharges, "1151.55") + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DLH") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DLH") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, "3760") + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, "0.2656") + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, "998.77") + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-c8ea170b") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "2") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteMessage, "HQ50066") + + } + if tppsEntryIndex == 3 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, "2024-07-29") + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, "2024-07-30") + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalCharges, "1151.55") + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DUPK") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DUPK") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, "3760") + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, "0.0315") + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, "118.44") + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-265c16d7") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "3") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteMessage, "HQ50066") + + } + if tppsEntryIndex == 4 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "9436-4123-3") + suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, "2024-07-29") + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, "2024-07-30") + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalCharges, "125.25") + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, "7500") + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, "0.0167") + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, "125.25") + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "9436-4123-93761f93") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeDescription, "Notes to My Company - INT") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteTo, "CARR") + suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteMessage, "HQ50057") + + } + suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteCode, "") + suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteCodeDescription, "") + suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteTo, "") + suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteMessage, "") + suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteCode, "") + suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteCodeDescription, "") + suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteTo, "") + suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteMessage, "") + } + }) + +} diff --git a/pkg/edi/tpps_paid_invoice_report/tpps_data.go b/pkg/edi/tpps_paid_invoice_report/tpps_data.go new file mode 100644 index 00000000000..c9c707740fd --- /dev/null +++ b/pkg/edi/tpps_paid_invoice_report/tpps_data.go @@ -0,0 +1,28 @@ +package tppspaidinvoicereport + +// TPPSData represents TPPS paid invoice report data +type TPPSData struct { + InvoiceNumber string + TPPSCreatedDocumentDate string + SellerPaidDate string + InvoiceTotalCharges string + LineDescription string + ProductDescription string + LineBillingUnits string + LineUnitPrice string + LineNetCharge string + POTCN string + LineNumber string + FirstNoteCode string + FirstNoteCodeDescription string + FirstNoteTo string + FirstNoteMessage string + SecondNoteCode string + SecondNoteCodeDescription string + SecondNoteTo string + SecondNoteMessage string + ThirdNoteCode string + ThirdNoteCodeDescription string + ThirdNoteTo string + ThirdNoteMessage string +} diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index 6d0f46e6406..e3ca4487ad4 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -54,9 +54,9 @@ func NewTPPSPaidInvoiceReportProcessor() services.SyncadaFileProcessor { // ProcessFile parses a TPPS paid invoice report response and updates the payment request status func (t *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContext, TPPSPaidInvoiceReportFilePath string, stringTPPSPaidInvoiceReport string) error { - tppsPaidInvoiceReport := tppsReponse.EDI{} + tppsPaidInvoiceReport := tppsReponse.TPPSData{} - tppsData, err := tppsPaidInvoiceReport.Parse(TPPSPaidInvoiceReportFilePath) + tppsData, err := tppsPaidInvoiceReport.Parse(TPPSPaidInvoiceReportFilePath, "") if err != nil { appCtx.Logger().Error("unable to parse TPPS paid invoice report", zap.Error(err)) return fmt.Errorf("unable to parse TPPS paid invoice report") From c02f42edf760b38fff7de42161668fd87343b98a Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 9 Aug 2024 16:13:26 +0000 Subject: [PATCH 12/46] fix parser test --- pkg/edi/tpps_paid_invoice_report/parser_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/edi/tpps_paid_invoice_report/parser_test.go b/pkg/edi/tpps_paid_invoice_report/parser_test.go index d2bbaaca7b1..82c03dc5168 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser_test.go +++ b/pkg/edi/tpps_paid_invoice_report/parser_test.go @@ -22,11 +22,11 @@ func (suite *TPPSPaidInvoiceSuite) TestParse() { suite.Run("successfully parse simple TPPS Paid Invoice string", func() { sampleTPPSPaidInvoiceString := `Invoice Number From Invoice Document Create Date Seller Paid Date Invoice Total Charges Line Description Product Description Line Billing Units Line Unit Price Line Net Charge PO/TCN Line Number First Note Code First Note Code Description First Note To First Note Message Second Note Code Second Note Code Description Second Note To Second Note Message Third Note Code Third Note Code Description Third Note To Third Note Message -1841-7267-3 2024-07-29 2024-07-30 1151.55 DDP DDP 3760 0.0077 28.95 1841-7267-826285fc 1 INT Notes to My Company - INT CARR HQ50066 -1841-7267-3 2024-07-29 2024-07-30 1151.55 FSC FSC 3760 0.0014 5.39 1841-7267-aeb3cfea 4 INT Notes to My Company - INT CARR HQ50066 -1841-7267-3 2024-07-29 2024-07-30 1151.55 DLH DLH 3760 0.2656 998.77 1841-7267-c8ea170b 2 INT Notes to My Company - INT CARR HQ50066 -1841-7267-3 2024-07-29 2024-07-30 1151.55 DUPK DUPK 3760 0.0315 118.44 1841-7267-265c16d7 3 INT Notes to My Company - INT CARR HQ50066 -9436-4123-3 2024-07-29 2024-07-30 125.25 DDP DDP 7500 0.0167 125.25 9436-4123-93761f93 1 INT Notes to My Company - INT CARR HQ50057 +1841-7267-3 2024-07-29 2024-07-30 1151.55 DDP DDP 3760 0.0077 28.95 1841-7267-826285fc 1 INT Notes to My Company - INT CARR HQ50066 +1841-7267-3 2024-07-29 2024-07-30 1151.55 FSC FSC 3760 0.0014 5.39 1841-7267-aeb3cfea 4 INT Notes to My Company - INT CARR HQ50066 +1841-7267-3 2024-07-29 2024-07-30 1151.55 DLH DLH 3760 0.2656 998.77 1841-7267-c8ea170b 2 INT Notes to My Company - INT CARR HQ50066 +1841-7267-3 2024-07-29 2024-07-30 1151.55 DUPK DUPK 3760 0.0315 118.44 1841-7267-265c16d7 3 INT Notes to My Company - INT CARR HQ50066 +9436-4123-3 2024-07-29 2024-07-30 125.25 DDP DDP 7500 0.0167 125.25 9436-4123-93761f93 1 INT Notes to My Company - INT CARR HQ50057 ` From b76c4bcfbe825dc1d32b6e4c1205e5b7a0075887 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 9 Aug 2024 16:29:16 +0000 Subject: [PATCH 13/46] add validators for tpps paid fields --- pkg/models/edi_type.go | 2 +- pkg/models/tpps_paid_invoice_report.go | 70 ++++++++++++++++++++------ 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/pkg/models/edi_type.go b/pkg/models/edi_type.go index f91649d8a37..8280014ed53 100644 --- a/pkg/models/edi_type.go +++ b/pkg/models/edi_type.go @@ -12,7 +12,7 @@ const ( EDIType858 EDIType = "858" // EDIType997 captures enum value "997" EDIType997 EDIType = "997" - // TODO (maybe put this somewhere else as TPPS paid report is not an EDI type) + // TPPSPaidInvoiceReport captures enum value "TPPSPaidInvoiceReport" TPPSPaidInvoiceReport EDIType = "TPPSPaidInvoiceReport" ) diff --git a/pkg/models/tpps_paid_invoice_report.go b/pkg/models/tpps_paid_invoice_report.go index 96834ac0414..21cea64af84 100644 --- a/pkg/models/tpps_paid_invoice_report.go +++ b/pkg/models/tpps_paid_invoice_report.go @@ -7,15 +7,13 @@ import ( "github.com/gobuffalo/validate/v3" "github.com/gobuffalo/validate/v3/validators" "github.com/gofrs/uuid" - "go.uber.org/zap/zapcore" "github.com/transcom/mymove/pkg/unit" ) -// tppsPaidInvoiceReport stores the entries found from processing a TPPS paid invoice report +// TPPSPaidInvoiceReportEntry stores the entries found from processing a TPPS paid invoice report type TPPSPaidInvoiceReportEntry struct { - ID uuid.UUID `db:"id"` - // PaymentRequestID uuid.UUID `db:"payment_request_id"` + ID uuid.UUID `db:"id"` InvoiceNumber string `db:"invoice_number"` TPPSCreatedDocumentDate time.Time `json:"tpps_created_doc_date" db:"tpps_created_doc_date"` SellerPaidDate time.Time `json:"seller_paid_date" db:"seller_paid_date"` @@ -54,21 +52,63 @@ type TPPSPaidInvoiceReportEntrys []TPPSPaidInvoiceReportEntry // Validate gets run every time you call a "pop.Validate*" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method. func (t *TPPSPaidInvoiceReportEntry) Validate(_ *pop.Connection) (*validate.Errors, error) { var vs []validate.Validator - // vs = append(vs, &validators.UUIDIsPresent{Field: t.PaymentRequestID, Name: "PaymentRequestID"}) if t.InvoiceNumber != "" { vs = append(vs, &validators.StringIsPresent{Field: t.InvoiceNumber, Name: "InvoiceNumber", Message: "InvoiceNumber string if present should not be empty"}) } vs = append(vs, &validators.TimeIsPresent{Field: t.TPPSCreatedDocumentDate, Name: "TPPSCreatedDocumentDate"}) + vs = append(vs, &validators.TimeIsPresent{Field: t.SellerPaidDate, Name: "SellerPaidDate"}) + vs = append(vs, &validators.IntIsGreaterThan{Field: t.InvoiceTotalChargesInMillicents.Int(), Name: "InvoiceTotalChargesInMillicents", Compared: 0}) + if t.LineDescription != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.LineDescription, Name: "LineDescription", Message: "LineDescription string if present should not be empty"}) + } + if t.ProductDescription != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.ProductDescription, Name: "ProductDescription", Message: "ProductDescription string if present should not be empty"}) + } + vs = append(vs, &validators.IntIsGreaterThan{Field: int(t.LineBillingUnits), Name: "LineBillingUnits", Compared: -1}) + vs = append(vs, &validators.IntIsGreaterThan{Field: t.LineUnitPrice.Int(), Name: "LineUnitPrice", Compared: 0}) + vs = append(vs, &validators.IntIsGreaterThan{Field: t.LineNetCharge.Int(), Name: "LineNetCharge", Compared: 0}) + if t.POTCN != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.POTCN, Name: "POTCN", Message: "POTCN string if present should not be empty"}) + } + if t.LineNumber != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.LineNumber, Name: "LineNumber", Message: "LineNumber string if present should not be empty"}) + } + if t.FirstNoteCode != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.FirstNoteCode, Name: "FirstNoteCode", Message: "FirstNoteCode string if present should not be empty"}) + } + if t.FirstNoteDescription != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.FirstNoteDescription, Name: "FirstNoteDescription", Message: "FirstNoteDescription string if present should not be empty"}) + } + if t.FirstNoteCodeTo != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.FirstNoteCodeTo, Name: "FirstNoteCodeTo", Message: "FirstNoteCodeTo string if present should not be empty"}) + } + if t.FirstNoteCodeMessage != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.FirstNoteCodeMessage, Name: "FirstNoteCodeMessage", Message: "FirstNoteCodeMessage string if present should not be empty"}) + } + if t.SecondNoteCode != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.SecondNoteCode, Name: "SecondNoteCode", Message: "SecondNoteCode string if present should not be empty"}) + } + if t.SecondNoteDescription != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.SecondNoteDescription, Name: "SecondNoteDescription", Message: "SecondNoteDescription string if present should not be empty"}) + } + if t.SecondNoteCodeTo != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.SecondNoteCodeTo, Name: "SecondNoteCodeTo", Message: "SecondNoteCodeTo string if present should not be empty"}) + } + if t.SecondNoteCodeMessage != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.SecondNoteCodeMessage, Name: "SecondNoteCodeMessage", Message: "SecondNoteCodeMessage string if present should not be empty"}) + } + if t.ThirdNoteCode != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.ThirdNoteCode, Name: "ThirdNoteCode", Message: "ThirdNoteCode string if present should not be empty"}) + } + if t.ThirdNoteDescription != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.ThirdNoteDescription, Name: "ThirdNoteDescription", Message: "ThirdNoteDescription string if present should not be empty"}) + } + if t.ThirdNoteCodeTo != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.ThirdNoteCodeTo, Name: "ThirdNoteCodeTo", Message: "ThirdNoteCodeTo string if present should not be empty"}) + } + if t.ThirdNoteCodeMessage != "" { + vs = append(vs, &validators.StringIsPresent{Field: t.ThirdNoteCodeMessage, Name: "ThirdNoteCodeMessage", Message: "ThirdNoteCodeMessage string if present should not be empty"}) + } return validate.Validate(vs...), nil } - -// MarshalLogObject is required to be able to zap.Object log this model. -func (t *TPPSPaidInvoiceReportEntry) MarshalLogObject(encoder zapcore.ObjectEncoder) error { - // encoder.AddString("EDIType", e.EDIType.String()) - // encoder.AddInt("NumEDIsProcessed", e.NumEDIsProcessed) - // encoder.AddTime("ProcessStartedAt", e.ProcessStartedAt) - // encoder.AddTime("ProcessEndedAt", e.ProcessEndedAt) - encoder.AddString("InvoiceNumber", t.InvoiceNumber) - return nil -} From ac6870dbff54df53e34a770fa925545abfb4fd69 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 9 Aug 2024 16:44:06 +0000 Subject: [PATCH 14/46] add TPPSPaidInvoiceReport to editype lists --- pkg/models/edi_errors_test.go | 4 ++-- pkg/models/edi_processing_test.go | 4 ++-- .../payment_request_to_interchange_control_number_test.go | 2 +- scripts/run-server-test | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/models/edi_errors_test.go b/pkg/models/edi_errors_test.go index 89ab524bd26..3df78582238 100644 --- a/pkg/models/edi_errors_test.go +++ b/pkg/models/edi_errors_test.go @@ -29,7 +29,7 @@ func (suite *ModelSuite) TestEdiErrors() { "description": {"Both Description and Code cannot be nil, one must be valid"}, "code": {"Both Code and Description cannot be nil, one must be valid"}, "payment_request_id": {"PaymentRequestID can not be blank."}, - "editype": {"EDIType is not in the list [810, 824, 858, 997]."}, + "editype": {"EDIType is not in the list [810, 824, 858, 997, TPPSPaidInvoiceReport]."}, }, }, "Message Type Invalid": { @@ -42,7 +42,7 @@ func (suite *ModelSuite) TestEdiErrors() { Description: models.StringPointer("EDI Error happened to field 123"), }, expectedErrs: map[string][]string{ - "editype": {"EDIType is not in the list [810, 824, 858, 997]."}, + "editype": {"EDIType is not in the list [810, 824, 858, 997, TPPSPaidInvoiceReport]."}, }, }, "At least one valid Code or Description": { diff --git a/pkg/models/edi_processing_test.go b/pkg/models/edi_processing_test.go index 23e1ba1e69b..d4002845904 100644 --- a/pkg/models/edi_processing_test.go +++ b/pkg/models/edi_processing_test.go @@ -28,7 +28,7 @@ func (suite *ModelSuite) TestBasicEDIProcessingInstantiation() { expectedErrs: map[string][]string{ "process_started_at": {"ProcessStartedAt can not be blank."}, "process_ended_at": {"ProcessEndedAt can not be blank."}, - "editype": {"EDIType is not in the list [810, 824, 858, 997]."}, + "editype": {"EDIType is not in the list [810, 824, 858, 997, TPPSPaidInvoiceReport]."}, }, }, "Other Errors": { @@ -41,7 +41,7 @@ func (suite *ModelSuite) TestBasicEDIProcessingInstantiation() { }, expectedErrs: map[string][]string{ "num_edis_processed": {"-1 is not greater than -1."}, - "editype": {"EDIType is not in the list [810, 824, 858, 997]."}, + "editype": {"EDIType is not in the list [810, 824, 858, 997, TPPSPaidInvoiceReport]."}, }, }, } diff --git a/pkg/models/payment_request_to_interchange_control_number_test.go b/pkg/models/payment_request_to_interchange_control_number_test.go index 6e648abc60d..66d9e617470 100644 --- a/pkg/models/payment_request_to_interchange_control_number_test.go +++ b/pkg/models/payment_request_to_interchange_control_number_test.go @@ -26,7 +26,7 @@ func (suite *ModelSuite) TestPaymentRequestToInterchangeControlNumber() { expErrors := map[string][]string{ "payment_request_id": {"PaymentRequestID can not be blank."}, "interchange_control_number": {"0 is not greater than 0."}, - "editype": {"EDIType is not in the list [810, 824, 858, 997]."}, + "editype": {"EDIType is not in the list [810, 824, 858, 997, TPPSPaidInvoiceReport]."}, } suite.verifyValidationErrors(&validPR2ICN, expErrors) }) diff --git a/scripts/run-server-test b/scripts/run-server-test index 0ead8674b81..1fd1e90764b 100755 --- a/scripts/run-server-test +++ b/scripts/run-server-test @@ -53,9 +53,9 @@ fi # Check if the operating system is macOS before running command # this uses the classic linker when running make server_test aka go test # this addresses issues we were having with the default linker on macOS -if [[ "$(uname)" == "Darwin" ]]; then - gotest_args+=("-ldflags=-extldflags=-Wl,-ld_classic") -fi +# if [[ "$(uname)" == "Darwin" ]]; then +# gotest_args+=("-ldflags=-extldflags=-Wl,-ld_classic") +# fi # Try to compile tests, but don't run them. if [[ "${DRY_RUN:-}" == "1" ]]; then From 8f74ed4fc0341c488b44385348d03fa6dc3c76b8 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 9 Aug 2024 16:46:05 +0000 Subject: [PATCH 15/46] undo commenting out of ld_classic --- scripts/run-server-test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/run-server-test b/scripts/run-server-test index 1fd1e90764b..0ead8674b81 100755 --- a/scripts/run-server-test +++ b/scripts/run-server-test @@ -53,9 +53,9 @@ fi # Check if the operating system is macOS before running command # this uses the classic linker when running make server_test aka go test # this addresses issues we were having with the default linker on macOS -# if [[ "$(uname)" == "Darwin" ]]; then -# gotest_args+=("-ldflags=-extldflags=-Wl,-ld_classic") -# fi +if [[ "$(uname)" == "Darwin" ]]; then + gotest_args+=("-ldflags=-extldflags=-Wl,-ld_classic") +fi # Try to compile tests, but don't run them. if [[ "${DRY_RUN:-}" == "1" ]]; then From b7bdf63189abc5db197f85ced564a7df57a1e15a Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 9 Aug 2024 16:50:12 +0000 Subject: [PATCH 16/46] clean up comments and test names --- pkg/models/tpps_paid_invoice_report.go | 2 +- pkg/services/invoice/process_tpps_paid_invoice_report_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/models/tpps_paid_invoice_report.go b/pkg/models/tpps_paid_invoice_report.go index 21cea64af84..1d645bef1f2 100644 --- a/pkg/models/tpps_paid_invoice_report.go +++ b/pkg/models/tpps_paid_invoice_report.go @@ -46,7 +46,7 @@ func (t TPPSPaidInvoiceReportEntry) TableName() string { return "tpps_paid_invoice_reports" } -// PaymentRequests is a slice of PaymentRequest +// TPPSPaidInvoiceReportEntrys is a slice of TPPSPaidInvoiceReportEntry type TPPSPaidInvoiceReportEntrys []TPPSPaidInvoiceReportEntry // Validate gets run every time you call a "pop.Validate*" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method. diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index 7842f23ff57..eb0f54c1594 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -28,7 +28,7 @@ func TestProcessTPPSPaidInvoiceReportSuite(t *testing.T) { func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport() { tppsPaidInvoiceReportProcessor := NewTPPSPaidInvoiceReportProcessor() - suite.Run("successfully proccesses a valid TPPSPaidInvoiceReport", func() { + suite.Run("successfully proccesses a valid TPPSPaidInvoiceReport and stores it in the database", func() { testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/tpps_paid_invoice_report_testfile.csv" From 552625344cbcf07521837dea46bb454c9d10bf88 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 9 Aug 2024 17:36:25 +0000 Subject: [PATCH 17/46] exclude from tpps_paid_invoice_report/parser_test from hitting the trim trailing whitespace pre commit hook --- .pre-commit-config.yaml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 55781afb6f1..b1606a88e7c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -55,7 +55,11 @@ repos: pkg/cli/auth.go$| )$ - id: trailing-whitespace - exclude: ^public/swagger-ui/ + exclude: > + (?x)^( + public/swagger-ui/| + pkg/edi/tpps_paid_invoice_report/parser_test.go| + )$ - repo: https://github.com/golangci/golangci-lint rev: v1.57.2 @@ -134,11 +138,13 @@ repos: language: script files: swagger/.* types: [yaml] - exclude: swagger-def/.* # These are partial swagger files that are compiled into the ones in swagger/*. - # They will be checked unless explicitly excluded, but they will fail. - # By only validating the compiled files, we are effectively checking these files as well. - require_serial: true # Make sure that we only call the script once with all affected YAML files as CI seems to - # have sporadic failures if we call this script in parallel. + exclude: + swagger-def/.* # These are partial swagger files that are compiled into the ones in swagger/*. + # They will be checked unless explicitly excluded, but they will fail. + # By only validating the compiled files, we are effectively checking these files as well. + require_serial: + true # Make sure that we only call the script once with all affected YAML files as CI seems to + # have sporadic failures if we call this script in parallel. - repo: local hooks: From 51951dffa99f2ae13447f3168b5bbfcde328691a Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 12 Aug 2024 18:42:56 +0000 Subject: [PATCH 18/46] catch err for os.open, test for failure to open file --- pkg/edi/tpps_paid_invoice_report/parser.go | 8 +++++++- .../process_tpps_paid_invoice_report_test.go | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index 36d9fb66e01..763f8af09c5 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -2,10 +2,13 @@ package tppspaidinvoicereport import ( "bufio" + "fmt" "io" "os" "path/filepath" "strings" + + "github.com/pkg/errors" ) func VerifyHeadersParsedCorrectly(parsedHeadersFromFile TPPSData) bool { @@ -102,7 +105,10 @@ func (t *TPPSData) Parse(stringTPPSPaidInvoiceReportFilePath string, testTPPSInv var dataToParse io.Reader if stringTPPSPaidInvoiceReportFilePath != "" { - csvFile, _ := os.Open(filepath.Clean(stringTPPSPaidInvoiceReportFilePath)) + csvFile, err := os.Open(filepath.Clean(stringTPPSPaidInvoiceReportFilePath)) + if err != nil { + return nil, errors.Wrap(err, (fmt.Sprintf("Unable to read TPPS paid invoice report from path %s", stringTPPSPaidInvoiceReportFilePath))) + } dataToParse = csvFile } else { dataToParse = strings.NewReader(testTPPSInvoiceString) diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index eb0f54c1594..1916b1b798d 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -143,4 +143,19 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteCodeMessage, "") } }) + + suite.Run("error opening filepath returns descriptive error for failing to parse TPPS paid invoice report", func() { + // given a path to a nonexistent file + testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/AFileThatDoesNotExist.csv" + + err := tppsPaidInvoiceReportProcessor.ProcessFile(suite.AppContextForTest(), testTPPSPaidInvoiceReportFilePath, "") + // ensure parse fails and returns error + suite.Error(err, "unable to parse TPPS paid invoice report") + + // verify no entries were logged in the database + tppsEntries := []models.TPPSPaidInvoiceReportEntry{} + err = suite.DB().All(&tppsEntries) + suite.NoError(err) + suite.Equal(len(tppsEntries), 0) + }) } From 6f680948b60c91a3893262e0ecc576dfae021a25 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 12 Aug 2024 21:59:28 +0000 Subject: [PATCH 19/46] make column headers dynamic rather than hard-coded --- pkg/edi/tpps_paid_invoice_report/parser.go | 73 ++++++++++++------- .../tpps_paid_invoice_report/parser_test.go | 2 +- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index 763f8af09c5..e4781e3771e 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -44,10 +44,11 @@ func VerifyHeadersParsedCorrectly(parsedHeadersFromFile TPPSData) bool { } // ProcessTPPSReportEntryForOneRow takes one data row, cleans it, and parses it into a string representation of the TPPSData struct -func ParseTPPSReportEntryForOneRow(row []string) TPPSData { +func ParseTPPSReportEntryForOneRow(row []string, columnIndexes map[string]int, headerIndicesNeedDefined bool) (TPPSData, map[string]int, bool) { tppsReportEntryForOnePaymentRequest := strings.Split(row[0], "\t") var tppsData TPPSData var processedTPPSReportEntryForOnePaymentRequest []string + var columnHeaderIndices map[string]int if len(tppsReportEntryForOnePaymentRequest) > 0 { @@ -70,32 +71,41 @@ func ParseTPPSReportEntryForOneRow(row []string) TPPSData { // After we have fully processed an entry and have built a string, store it processedTPPSReportEntryForOnePaymentRequest = append(processedTPPSReportEntryForOnePaymentRequest, processedEntry) } - - tppsData.InvoiceNumber = processedTPPSReportEntryForOnePaymentRequest[0] - tppsData.TPPSCreatedDocumentDate = processedTPPSReportEntryForOnePaymentRequest[1] - tppsData.SellerPaidDate = processedTPPSReportEntryForOnePaymentRequest[2] - tppsData.InvoiceTotalCharges = processedTPPSReportEntryForOnePaymentRequest[3] - tppsData.LineDescription = processedTPPSReportEntryForOnePaymentRequest[4] - tppsData.ProductDescription = processedTPPSReportEntryForOnePaymentRequest[5] - tppsData.LineBillingUnits = processedTPPSReportEntryForOnePaymentRequest[6] - tppsData.LineUnitPrice = processedTPPSReportEntryForOnePaymentRequest[7] - tppsData.LineNetCharge = processedTPPSReportEntryForOnePaymentRequest[8] - tppsData.POTCN = processedTPPSReportEntryForOnePaymentRequest[9] - tppsData.LineNumber = processedTPPSReportEntryForOnePaymentRequest[10] - tppsData.FirstNoteCode = processedTPPSReportEntryForOnePaymentRequest[11] - tppsData.FirstNoteCodeDescription = processedTPPSReportEntryForOnePaymentRequest[12] - tppsData.FirstNoteTo = processedTPPSReportEntryForOnePaymentRequest[13] - tppsData.FirstNoteMessage = processedTPPSReportEntryForOnePaymentRequest[14] - tppsData.SecondNoteCode = processedTPPSReportEntryForOnePaymentRequest[15] - tppsData.SecondNoteCodeDescription = processedTPPSReportEntryForOnePaymentRequest[16] - tppsData.SecondNoteTo = processedTPPSReportEntryForOnePaymentRequest[17] - tppsData.SecondNoteMessage = processedTPPSReportEntryForOnePaymentRequest[18] - tppsData.ThirdNoteCode = processedTPPSReportEntryForOnePaymentRequest[19] - tppsData.ThirdNoteCodeDescription = processedTPPSReportEntryForOnePaymentRequest[20] - tppsData.ThirdNoteTo = processedTPPSReportEntryForOnePaymentRequest[21] - tppsData.ThirdNoteMessage = processedTPPSReportEntryForOnePaymentRequest[22] + if headerIndicesNeedDefined { + columnHeaderIndices = make(map[string]int) + for i, columnHeader := range processedTPPSReportEntryForOnePaymentRequest { + columnHeaderIndices[columnHeader] = i + } + // only need to define the column header indices once per read of a file, so set to false after first pass through + headerIndicesNeedDefined = false + } else { + columnHeaderIndices = columnIndexes + } + tppsData.InvoiceNumber = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Invoice Number From Invoice"]] + tppsData.TPPSCreatedDocumentDate = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Document Create Date"]] + tppsData.SellerPaidDate = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Seller Paid Date"]] + tppsData.InvoiceTotalCharges = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Invoice Total Charges"]] + tppsData.LineDescription = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Line Description"]] + tppsData.ProductDescription = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Product Description"]] + tppsData.LineBillingUnits = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Line Billing Units"]] + tppsData.LineUnitPrice = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Line Unit Price"]] + tppsData.LineNetCharge = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Line Net Charge"]] + tppsData.POTCN = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["PO/TCN"]] + tppsData.LineNumber = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Line Number"]] + tppsData.FirstNoteCode = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["First Note Code"]] + tppsData.FirstNoteCodeDescription = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["First Note Code Description"]] + tppsData.FirstNoteTo = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["First Note To"]] + tppsData.FirstNoteMessage = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["First Note Message"]] + tppsData.SecondNoteCode = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Second Note Code"]] + tppsData.SecondNoteCodeDescription = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Second Note Code Description"]] + tppsData.SecondNoteTo = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Second Note To"]] + tppsData.SecondNoteMessage = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Second Note Message"]] + tppsData.ThirdNoteCode = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Third Note Code"]] + tppsData.ThirdNoteCodeDescription = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Third Note Code Description"]] + tppsData.ThirdNoteTo = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Third Note To"]] + tppsData.ThirdNoteMessage = processedTPPSReportEntryForOnePaymentRequest[columnHeaderIndices["Third Note Message"]] } - return tppsData + return tppsData, columnHeaderIndices, headerIndicesNeedDefined } // Parse takes in a TPPS paid invoice report file and parses it into an array of TPPSData structs @@ -115,6 +125,8 @@ func (t *TPPSData) Parse(stringTPPSPaidInvoiceReportFilePath string, testTPPSInv } endOfFile := false headersAreCorrect := false + needToDefineColumnIndices := true + var headerColumnIndices map[string]int scanner := bufio.NewScanner(dataToParse) for scanner.Scan() { @@ -125,7 +137,14 @@ func (t *TPPSData) Parse(stringTPPSPaidInvoiceReportFilePath string, testTPPSInv endOfFile = true } if row != nil && !endOfFile { - tppsReportEntryForOnePaymentRequest := ParseTPPSReportEntryForOneRow(row) + tppsReportEntryForOnePaymentRequest, columnIndicesFound, keepFindingColumnIndices := ParseTPPSReportEntryForOneRow(row, headerColumnIndices, needToDefineColumnIndices) + // For first data row of file (headers), find indices of the columns + // For the rest of the file, use those same indices to parse in the data + if needToDefineColumnIndices { + // Only want to define header column indices once per file read + headerColumnIndices = columnIndicesFound + } + needToDefineColumnIndices = keepFindingColumnIndices if tppsReportEntryForOnePaymentRequest.InvoiceNumber == "Invoice Number From Invoice" { rowIsHeader = true headersAreCorrect = VerifyHeadersParsedCorrectly(tppsReportEntryForOnePaymentRequest) diff --git a/pkg/edi/tpps_paid_invoice_report/parser_test.go b/pkg/edi/tpps_paid_invoice_report/parser_test.go index 82c03dc5168..dfd041d24b7 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser_test.go +++ b/pkg/edi/tpps_paid_invoice_report/parser_test.go @@ -32,7 +32,7 @@ func (suite *TPPSPaidInvoiceSuite) TestParse() { tppsPaidInvoice := TPPSData{} tppsEntries, err := tppsPaidInvoice.Parse("", sampleTPPSPaidInvoiceString) - suite.NoError(err, "Successful parse of 997") + suite.NoError(err, "Successful parse of TPPS Paid Invoice string") suite.Equal(len(tppsEntries), 5) for tppsEntryIndex := range tppsEntries { From d0a097ff7cbe6ae96016ab18aa42c0acc656b347 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 12 Aug 2024 22:00:57 +0000 Subject: [PATCH 20/46] add comment notating tab-delimiter --- pkg/edi/tpps_paid_invoice_report/parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index e4781e3771e..a2806236d1f 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -43,7 +43,7 @@ func VerifyHeadersParsedCorrectly(parsedHeadersFromFile TPPSData) bool { return allHeadersWereProcessedCorrectly } -// ProcessTPPSReportEntryForOneRow takes one data row, cleans it, and parses it into a string representation of the TPPSData struct +// ProcessTPPSReportEntryForOneRow takes one tab-delimited data row, cleans it, and parses it into a string representation of the TPPSData struct func ParseTPPSReportEntryForOneRow(row []string, columnIndexes map[string]int, headerIndicesNeedDefined bool) (TPPSData, map[string]int, bool) { tppsReportEntryForOnePaymentRequest := strings.Split(row[0], "\t") var tppsData TPPSData From 0f667643d20b7015a44cfd97939c95d4913bf472 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 12 Aug 2024 22:03:17 +0000 Subject: [PATCH 21/46] add comment for test string delimiters --- pkg/edi/tpps_paid_invoice_report/parser_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/edi/tpps_paid_invoice_report/parser_test.go b/pkg/edi/tpps_paid_invoice_report/parser_test.go index dfd041d24b7..a36e28394af 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser_test.go +++ b/pkg/edi/tpps_paid_invoice_report/parser_test.go @@ -21,6 +21,7 @@ func TestTPPSPaidInvoiceSuite(t *testing.T) { func (suite *TPPSPaidInvoiceSuite) TestParse() { suite.Run("successfully parse simple TPPS Paid Invoice string", func() { + // This is a string representation of a test .csv file. Rows are new-line delimited, columns in each row are tab delimited, file ends in a empty row. sampleTPPSPaidInvoiceString := `Invoice Number From Invoice Document Create Date Seller Paid Date Invoice Total Charges Line Description Product Description Line Billing Units Line Unit Price Line Net Charge PO/TCN Line Number First Note Code First Note Code Description First Note To First Note Message Second Note Code Second Note Code Description Second Note To Second Note Message Third Note Code Third Note Code Description Third Note To Third Note Message 1841-7267-3 2024-07-29 2024-07-30 1151.55 DDP DDP 3760 0.0077 28.95 1841-7267-826285fc 1 INT Notes to My Company - INT CARR HQ50066 1841-7267-3 2024-07-29 2024-07-30 1151.55 FSC FSC 3760 0.0014 5.39 1841-7267-aeb3cfea 4 INT Notes to My Company - INT CARR HQ50066 From 7c3355ae705ec5ef892c62e163f88fa4b1462130 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 12 Aug 2024 22:17:23 +0000 Subject: [PATCH 22/46] reformat validators and only keep necessary validators in --- pkg/models/tpps_paid_invoice_report.go | 73 +++++--------------------- 1 file changed, 13 insertions(+), 60 deletions(-) diff --git a/pkg/models/tpps_paid_invoice_report.go b/pkg/models/tpps_paid_invoice_report.go index 1d645bef1f2..852d97290b5 100644 --- a/pkg/models/tpps_paid_invoice_report.go +++ b/pkg/models/tpps_paid_invoice_report.go @@ -51,64 +51,17 @@ type TPPSPaidInvoiceReportEntrys []TPPSPaidInvoiceReportEntry // Validate gets run every time you call a "pop.Validate*" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method. func (t *TPPSPaidInvoiceReportEntry) Validate(_ *pop.Connection) (*validate.Errors, error) { - var vs []validate.Validator - if t.InvoiceNumber != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.InvoiceNumber, Name: "InvoiceNumber", Message: "InvoiceNumber string if present should not be empty"}) - } - vs = append(vs, &validators.TimeIsPresent{Field: t.TPPSCreatedDocumentDate, Name: "TPPSCreatedDocumentDate"}) - vs = append(vs, &validators.TimeIsPresent{Field: t.SellerPaidDate, Name: "SellerPaidDate"}) - vs = append(vs, &validators.IntIsGreaterThan{Field: t.InvoiceTotalChargesInMillicents.Int(), Name: "InvoiceTotalChargesInMillicents", Compared: 0}) - if t.LineDescription != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.LineDescription, Name: "LineDescription", Message: "LineDescription string if present should not be empty"}) - } - if t.ProductDescription != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.ProductDescription, Name: "ProductDescription", Message: "ProductDescription string if present should not be empty"}) - } - vs = append(vs, &validators.IntIsGreaterThan{Field: int(t.LineBillingUnits), Name: "LineBillingUnits", Compared: -1}) - vs = append(vs, &validators.IntIsGreaterThan{Field: t.LineUnitPrice.Int(), Name: "LineUnitPrice", Compared: 0}) - vs = append(vs, &validators.IntIsGreaterThan{Field: t.LineNetCharge.Int(), Name: "LineNetCharge", Compared: 0}) - if t.POTCN != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.POTCN, Name: "POTCN", Message: "POTCN string if present should not be empty"}) - } - if t.LineNumber != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.LineNumber, Name: "LineNumber", Message: "LineNumber string if present should not be empty"}) - } - if t.FirstNoteCode != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.FirstNoteCode, Name: "FirstNoteCode", Message: "FirstNoteCode string if present should not be empty"}) - } - if t.FirstNoteDescription != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.FirstNoteDescription, Name: "FirstNoteDescription", Message: "FirstNoteDescription string if present should not be empty"}) - } - if t.FirstNoteCodeTo != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.FirstNoteCodeTo, Name: "FirstNoteCodeTo", Message: "FirstNoteCodeTo string if present should not be empty"}) - } - if t.FirstNoteCodeMessage != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.FirstNoteCodeMessage, Name: "FirstNoteCodeMessage", Message: "FirstNoteCodeMessage string if present should not be empty"}) - } - if t.SecondNoteCode != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.SecondNoteCode, Name: "SecondNoteCode", Message: "SecondNoteCode string if present should not be empty"}) - } - if t.SecondNoteDescription != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.SecondNoteDescription, Name: "SecondNoteDescription", Message: "SecondNoteDescription string if present should not be empty"}) - } - if t.SecondNoteCodeTo != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.SecondNoteCodeTo, Name: "SecondNoteCodeTo", Message: "SecondNoteCodeTo string if present should not be empty"}) - } - if t.SecondNoteCodeMessage != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.SecondNoteCodeMessage, Name: "SecondNoteCodeMessage", Message: "SecondNoteCodeMessage string if present should not be empty"}) - } - if t.ThirdNoteCode != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.ThirdNoteCode, Name: "ThirdNoteCode", Message: "ThirdNoteCode string if present should not be empty"}) - } - if t.ThirdNoteDescription != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.ThirdNoteDescription, Name: "ThirdNoteDescription", Message: "ThirdNoteDescription string if present should not be empty"}) - } - if t.ThirdNoteCodeTo != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.ThirdNoteCodeTo, Name: "ThirdNoteCodeTo", Message: "ThirdNoteCodeTo string if present should not be empty"}) - } - if t.ThirdNoteCodeMessage != "" { - vs = append(vs, &validators.StringIsPresent{Field: t.ThirdNoteCodeMessage, Name: "ThirdNoteCodeMessage", Message: "ThirdNoteCodeMessage string if present should not be empty"}) - } - - return validate.Validate(vs...), nil + return validate.Validate( + &validators.StringIsPresent{Field: t.InvoiceNumber, Name: "InvoiceNumber", Message: "InvoiceNumber string if present should not be empty"}, + &validators.TimeIsPresent{Field: t.TPPSCreatedDocumentDate, Name: "TPPSCreatedDocumentDate"}, + &validators.TimeIsPresent{Field: t.SellerPaidDate, Name: "SellerPaidDate"}, + &validators.IntIsGreaterThan{Field: t.InvoiceTotalChargesInMillicents.Int(), Name: "InvoiceTotalChargesInMillicents", Compared: 0}, + &validators.StringIsPresent{Field: t.LineDescription, Name: "LineDescription", Message: "LineDescription string if present should not be empty"}, + &validators.StringIsPresent{Field: t.ProductDescription, Name: "ProductDescription", Message: "ProductDescription string if present should not be empty"}, + &validators.IntIsGreaterThan{Field: int(t.LineBillingUnits), Name: "LineBillingUnits", Compared: -1}, + &validators.IntIsGreaterThan{Field: t.LineUnitPrice.Int(), Name: "LineUnitPrice", Compared: 0}, + &validators.IntIsGreaterThan{Field: t.LineNetCharge.Int(), Name: "LineNetCharge", Compared: 0}, + &validators.StringIsPresent{Field: t.POTCN, Name: "POTCN", Message: "POTCN string if present should not be empty"}, + &validators.StringIsPresent{Field: t.LineNumber, Name: "LineNumber", Message: "LineNumber string if present should not be empty"}, + ), nil } From ec06c72f17e473cd092b030e77d4cef7340b1e8b Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Tue, 13 Aug 2024 16:20:36 +0000 Subject: [PATCH 23/46] move tpps_paid_invoice_report_testfile.csv to fixtures folder --- .../tpps_paid_invoice_report_testfile.csv | Bin .../process_tpps_paid_invoice_report_test.go | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename pkg/services/invoice/{ => fixtures}/tpps_paid_invoice_report_testfile.csv (100%) diff --git a/pkg/services/invoice/tpps_paid_invoice_report_testfile.csv b/pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile.csv similarity index 100% rename from pkg/services/invoice/tpps_paid_invoice_report_testfile.csv rename to pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile.csv diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index 1916b1b798d..fff6d78dd1f 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -30,7 +30,7 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport suite.Run("successfully proccesses a valid TPPSPaidInvoiceReport and stores it in the database", func() { - testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/tpps_paid_invoice_report_testfile.csv" + testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile.csv" err := tppsPaidInvoiceReportProcessor.ProcessFile(suite.AppContextForTest(), testTPPSPaidInvoiceReportFilePath, "") suite.NoError(err) From 9fa33e8bdca009e5e39b0bf8206aecbaf2e9e820 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Tue, 13 Aug 2024 21:39:00 +0000 Subject: [PATCH 24/46] be explicit about which fields are required for processing --- ...40802161708_tpps_paid_invoice_table.up.sql | 20 +++++++++---------- pkg/models/tpps_paid_invoice_report.go | 1 - .../process_tpps_paid_invoice_report.go | 6 +++++- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql index 87f42befd0d..52958ae7516 100644 --- a/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql +++ b/migrations/app/schema/20240802161708_tpps_paid_invoice_table.up.sql @@ -1,16 +1,16 @@ CREATE TABLE IF NOT EXISTS tpps_paid_invoice_reports ( id uuid NOT NULL, - invoice_number varchar not null, + invoice_number varchar NOT NULL, tpps_created_doc_date timestamp, - seller_paid_date timestamp, - invoice_total_charges_in_millicents integer, - line_description varchar, - product_description varchar, - line_billing_units integer, - line_unit_price_in_millicents integer, - line_net_charge_in_millicents integer, - po_tcn varchar, - line_number varchar, + seller_paid_date timestamp NOT NULL, + invoice_total_charges_in_millicents integer NOT NULL, + line_description varchar NOT NULL, + product_description varchar NOT NULL, + line_billing_units integer NOT NULL, + line_unit_price_in_millicents integer NOT NULL, + line_net_charge_in_millicents integer NOT NULL, + po_tcn varchar NOT NULL, + line_number varchar NOT NULL, first_note_code varchar, first_note_description varchar, first_note_to varchar, diff --git a/pkg/models/tpps_paid_invoice_report.go b/pkg/models/tpps_paid_invoice_report.go index 852d97290b5..6d1be0b0e07 100644 --- a/pkg/models/tpps_paid_invoice_report.go +++ b/pkg/models/tpps_paid_invoice_report.go @@ -53,7 +53,6 @@ type TPPSPaidInvoiceReportEntrys []TPPSPaidInvoiceReportEntry func (t *TPPSPaidInvoiceReportEntry) Validate(_ *pop.Connection) (*validate.Errors, error) { return validate.Validate( &validators.StringIsPresent{Field: t.InvoiceNumber, Name: "InvoiceNumber", Message: "InvoiceNumber string if present should not be empty"}, - &validators.TimeIsPresent{Field: t.TPPSCreatedDocumentDate, Name: "TPPSCreatedDocumentDate"}, &validators.TimeIsPresent{Field: t.SellerPaidDate, Name: "SellerPaidDate"}, &validators.IntIsGreaterThan{Field: t.InvoiceTotalChargesInMillicents.Int(), Name: "InvoiceTotalChargesInMillicents", Compared: 0}, &validators.StringIsPresent{Field: t.LineDescription, Name: "LineDescription", Message: "LineDescription string if present should not be empty"}, diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index e3ca4487ad4..a59c2053d12 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -132,7 +132,6 @@ func priceToMillicents(rawPrice string) (int, error) { func (t *tppsPaidInvoiceReportProcessor) StoreTPPSPaidInvoiceReportInDatabase(appCtx appcontext.AppContext, tppsData []tppsReponse.TPPSData) (*validate.Errors, error) { var verrs *validate.Errors - transactionError := appCtx.NewTransaction(func(txnAppCtx appcontext.AppContext) error { DateParamFormat := "2006-01-02" @@ -145,22 +144,27 @@ func (t *tppsPaidInvoiceReportProcessor) StoreTPPSPaidInvoiceReportInDatabase(ap timeOfSellerPaidDate, err := time.Parse(DateParamFormat, tppsEntry.SellerPaidDate) if err != nil { appCtx.Logger().Info("unable to parse SellerPaidDate from TPPS paid invoice report", zap.Error(err)) + return verrs } invoiceTotalChargesInMillicents, err := priceToMillicents(tppsEntry.InvoiceTotalCharges) if err != nil { appCtx.Logger().Info("unable to parse InvoiceTotalCharges from TPPS paid invoice report", zap.Error(err)) + return verrs } intLineBillingUnits, err := strconv.Atoi(tppsEntry.LineBillingUnits) if err != nil { appCtx.Logger().Info("unable to parse LineBillingUnits from TPPS paid invoice report", zap.Error(err)) + return verrs } lineUnitPriceInMillicents, err := priceToMillicents(tppsEntry.LineUnitPrice) if err != nil { appCtx.Logger().Info("unable to parse LineUnitPrice from TPPS paid invoice report", zap.Error(err)) + return verrs } lineNetChargeInMillicents, err := priceToMillicents(tppsEntry.LineNetCharge) if err != nil { appCtx.Logger().Info("unable to parse LineNetCharge from TPPS paid invoice report", zap.Error(err)) + return verrs } tppsEntryModel := models.TPPSPaidInvoiceReportEntry{ From 13174d68974e96bc392e9b73ee8e59da15ee8622 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Tue, 13 Aug 2024 22:24:48 +0000 Subject: [PATCH 25/46] add test file for pkg/models/tpps_paid_invoice_report.go --- pkg/models/tpps_paid_invoice_report.go | 16 ++-- pkg/models/tpps_paid_invoice_report_test.go | 99 +++++++++++++++++++++ 2 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 pkg/models/tpps_paid_invoice_report_test.go diff --git a/pkg/models/tpps_paid_invoice_report.go b/pkg/models/tpps_paid_invoice_report.go index 6d1be0b0e07..1c2e2450c6e 100644 --- a/pkg/models/tpps_paid_invoice_report.go +++ b/pkg/models/tpps_paid_invoice_report.go @@ -52,15 +52,15 @@ type TPPSPaidInvoiceReportEntrys []TPPSPaidInvoiceReportEntry // Validate gets run every time you call a "pop.Validate*" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method. func (t *TPPSPaidInvoiceReportEntry) Validate(_ *pop.Connection) (*validate.Errors, error) { return validate.Validate( - &validators.StringIsPresent{Field: t.InvoiceNumber, Name: "InvoiceNumber", Message: "InvoiceNumber string if present should not be empty"}, + &validators.StringIsPresent{Field: t.InvoiceNumber, Name: "InvoiceNumber"}, &validators.TimeIsPresent{Field: t.SellerPaidDate, Name: "SellerPaidDate"}, - &validators.IntIsGreaterThan{Field: t.InvoiceTotalChargesInMillicents.Int(), Name: "InvoiceTotalChargesInMillicents", Compared: 0}, - &validators.StringIsPresent{Field: t.LineDescription, Name: "LineDescription", Message: "LineDescription string if present should not be empty"}, - &validators.StringIsPresent{Field: t.ProductDescription, Name: "ProductDescription", Message: "ProductDescription string if present should not be empty"}, + &validators.IntIsGreaterThan{Field: t.InvoiceTotalChargesInMillicents.Int(), Name: "InvoiceTotalChargesInMillicents", Compared: -1}, + &validators.StringIsPresent{Field: t.LineDescription, Name: "LineDescription"}, + &validators.StringIsPresent{Field: t.ProductDescription, Name: "ProductDescription"}, &validators.IntIsGreaterThan{Field: int(t.LineBillingUnits), Name: "LineBillingUnits", Compared: -1}, - &validators.IntIsGreaterThan{Field: t.LineUnitPrice.Int(), Name: "LineUnitPrice", Compared: 0}, - &validators.IntIsGreaterThan{Field: t.LineNetCharge.Int(), Name: "LineNetCharge", Compared: 0}, - &validators.StringIsPresent{Field: t.POTCN, Name: "POTCN", Message: "POTCN string if present should not be empty"}, - &validators.StringIsPresent{Field: t.LineNumber, Name: "LineNumber", Message: "LineNumber string if present should not be empty"}, + &validators.IntIsGreaterThan{Field: t.LineUnitPrice.Int(), Name: "LineUnitPrice", Compared: -1}, + &validators.IntIsGreaterThan{Field: t.LineNetCharge.Int(), Name: "LineNetCharge", Compared: -1}, + &validators.StringIsPresent{Field: t.POTCN, Name: "POTCN"}, + &validators.StringIsPresent{Field: t.LineNumber, Name: "LineNumber"}, ), nil } diff --git a/pkg/models/tpps_paid_invoice_report_test.go b/pkg/models/tpps_paid_invoice_report_test.go new file mode 100644 index 00000000000..7669b10c002 --- /dev/null +++ b/pkg/models/tpps_paid_invoice_report_test.go @@ -0,0 +1,99 @@ +package models_test + +import ( + "time" + + "github.com/gofrs/uuid" + + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/unit" +) + +func (suite *ModelSuite) TestBasicTPPSPaidInvoiceReportInstantiation() { + testCases := map[string]struct { + tppsPaidInvoiceReport models.TPPSPaidInvoiceReportEntry + expectedErrs map[string][]string + }{ + "Successful Create": { + tppsPaidInvoiceReport: models.TPPSPaidInvoiceReportEntry{ + ID: uuid.Must(uuid.NewV4()), + InvoiceNumber: "1841-7267-3", + TPPSCreatedDocumentDate: time.Now(), + SellerPaidDate: time.Now(), + InvoiceTotalChargesInMillicents: unit.Millicents(115155000), + LineDescription: "DDP", + ProductDescription: "DDP", + LineBillingUnits: 3760, + LineUnitPrice: unit.Millicents(770), + LineNetCharge: unit.Millicents(2895000), + POTCN: "1841-7267-826285fc", + LineNumber: "1", + FirstNoteCode: "INT", + FirstNoteDescription: "Notes to My Company - INT", + FirstNoteCodeTo: "CARR", + FirstNoteCodeMessage: "HQ50066", + SecondNoteCode: "INT", + SecondNoteDescription: "Notes to My Company - INT", + SecondNoteCodeTo: "CARR", + SecondNoteCodeMessage: "HQ50066", + ThirdNoteCode: "INT", + ThirdNoteDescription: "Notes to My Company - INT", + ThirdNoteCodeTo: "CARR", + ThirdNoteCodeMessage: "HQ50066", + }, + expectedErrs: nil, + }, + "Empty Fields": { + tppsPaidInvoiceReport: models.TPPSPaidInvoiceReportEntry{}, + expectedErrs: map[string][]string{ + "invoice_number": {"InvoiceNumber can not be blank."}, + "seller_paid_date": {"SellerPaidDate can not be blank."}, + "line_description": {"LineDescription can not be blank."}, + "product_description": {"ProductDescription can not be blank."}, + "line_number": {"LineNumber can not be blank."}, + "potcn": {"POTCN can not be blank."}, + }, + }, + "Other Errors": { + tppsPaidInvoiceReport: models.TPPSPaidInvoiceReportEntry{ + ID: uuid.Must(uuid.NewV4()), + InvoiceNumber: "1841-7267-3", + TPPSCreatedDocumentDate: time.Now(), + SellerPaidDate: time.Now(), + InvoiceTotalChargesInMillicents: -1, + LineDescription: "DDP", + ProductDescription: "DDP", + LineBillingUnits: -1, + LineUnitPrice: -1, + LineNetCharge: -1, + POTCN: "1841-7267-826285fc", + LineNumber: "1", + FirstNoteCode: "INT", + FirstNoteDescription: "Notes to My Company - INT", + FirstNoteCodeTo: "CARR", + FirstNoteCodeMessage: "HQ50066", + SecondNoteCode: "INT", + SecondNoteDescription: "Notes to My Company - INT", + SecondNoteCodeTo: "CARR", + SecondNoteCodeMessage: "HQ50066", + ThirdNoteCode: "INT", + ThirdNoteDescription: "Notes to My Company - INT", + ThirdNoteCodeTo: "CARR", + ThirdNoteCodeMessage: "HQ50066", + }, + expectedErrs: map[string][]string{ + "invoice_total_charges_in_millicents": {"-1 is not greater than -1."}, + "line_billing_units": {"-1 is not greater than -1."}, + "line_unit_price": {"-1 is not greater than -1."}, + "line_net_charge": {"-1 is not greater than -1."}, + }, + }, + } + + for name, test := range testCases { + suite.Run(name, func() { + suite.verifyValidationErrors(&test.tppsPaidInvoiceReport, test.expectedErrs) //#nosec G601 + }) + } + +} From 3a297669b75e7abc8e7d91f4731142039d6e168f Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Wed, 14 Aug 2024 16:07:45 +0000 Subject: [PATCH 26/46] nullable value handling --- pkg/models/tpps_paid_invoice_report.go | 26 +++---- pkg/models/tpps_paid_invoice_report_test.go | 69 +++++++++++-------- .../process_tpps_paid_invoice_report.go | 26 +++---- .../process_tpps_paid_invoice_report_test.go | 66 +++++++++--------- 4 files changed, 100 insertions(+), 87 deletions(-) diff --git a/pkg/models/tpps_paid_invoice_report.go b/pkg/models/tpps_paid_invoice_report.go index 1c2e2450c6e..b6a3b7e333b 100644 --- a/pkg/models/tpps_paid_invoice_report.go +++ b/pkg/models/tpps_paid_invoice_report.go @@ -15,7 +15,7 @@ import ( type TPPSPaidInvoiceReportEntry struct { ID uuid.UUID `db:"id"` InvoiceNumber string `db:"invoice_number"` - TPPSCreatedDocumentDate time.Time `json:"tpps_created_doc_date" db:"tpps_created_doc_date"` + TPPSCreatedDocumentDate *time.Time `json:"tpps_created_doc_date" db:"tpps_created_doc_date"` SellerPaidDate time.Time `json:"seller_paid_date" db:"seller_paid_date"` InvoiceTotalChargesInMillicents unit.Millicents `json:"invoice_total_charges_in_millicents" db:"invoice_total_charges_in_millicents"` LineDescription string `json:"line_description" db:"line_description"` @@ -25,18 +25,18 @@ type TPPSPaidInvoiceReportEntry struct { LineNetCharge unit.Millicents `json:"line_net_charge_in_millicents" db:"line_net_charge_in_millicents"` POTCN string `json:"po_tcn" db:"po_tcn"` LineNumber string `json:"line_number" db:"line_number"` - FirstNoteCode string `json:"first_note_code" db:"first_note_code"` - FirstNoteDescription string `json:"first_note_description" db:"first_note_description"` - FirstNoteCodeTo string `json:"first_note_to" db:"first_note_to"` - FirstNoteCodeMessage string `json:"first_note_message" db:"first_note_message"` - SecondNoteCode string `json:"second_note_code" db:"second_note_code"` - SecondNoteDescription string `json:"second_note_description" db:"second_note_description"` - SecondNoteCodeTo string `json:"second_note_to" db:"second_note_to"` - SecondNoteCodeMessage string `json:"second_note_message" db:"second_note_message"` - ThirdNoteCode string `json:"third_note_code" db:"third_note_code"` - ThirdNoteDescription string `json:"third_note_code_description" db:"third_note_code_description"` - ThirdNoteCodeTo string `json:"third_note_code_to" db:"third_note_code_to"` - ThirdNoteCodeMessage string `json:"third_note_code_message" db:"third_note_code_message"` + FirstNoteCode *string `json:"first_note_code" db:"first_note_code"` + FirstNoteDescription *string `json:"first_note_description" db:"first_note_description"` + FirstNoteCodeTo *string `json:"first_note_to" db:"first_note_to"` + FirstNoteCodeMessage *string `json:"first_note_message" db:"first_note_message"` + SecondNoteCode *string `json:"second_note_code" db:"second_note_code"` + SecondNoteDescription *string `json:"second_note_description" db:"second_note_description"` + SecondNoteCodeTo *string `json:"second_note_to" db:"second_note_to"` + SecondNoteCodeMessage *string `json:"second_note_message" db:"second_note_message"` + ThirdNoteCode *string `json:"third_note_code" db:"third_note_code"` + ThirdNoteDescription *string `json:"third_note_code_description" db:"third_note_code_description"` + ThirdNoteCodeTo *string `json:"third_note_code_to" db:"third_note_code_to"` + ThirdNoteCodeMessage *string `json:"third_note_code_message" db:"third_note_code_message"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } diff --git a/pkg/models/tpps_paid_invoice_report_test.go b/pkg/models/tpps_paid_invoice_report_test.go index 7669b10c002..dd4f8db3969 100644 --- a/pkg/models/tpps_paid_invoice_report_test.go +++ b/pkg/models/tpps_paid_invoice_report_test.go @@ -10,6 +10,19 @@ import ( ) func (suite *ModelSuite) TestBasicTPPSPaidInvoiceReportInstantiation() { + currTime := time.Now() + firstNoteCode := "INT" + firstNoteDescription := "Notes to My Company - INT" + firstNoteCodeTo := "CARR" + firstNoteCodeMessage := "HQ50066" + SecondNoteCode := "INT" + SecondNoteDescription := "Notes to My Company - INT" + SecondNoteCodeTo := "CARR" + SecondNoteCodeMessage := "HQ50066" + ThirdNoteCode := "INT" + ThirdNoteDescription := "Notes to My Company - INT" + ThirdNoteCodeTo := "CARR" + ThirdNoteCodeMessage := "HQ50066" testCases := map[string]struct { tppsPaidInvoiceReport models.TPPSPaidInvoiceReportEntry expectedErrs map[string][]string @@ -18,8 +31,8 @@ func (suite *ModelSuite) TestBasicTPPSPaidInvoiceReportInstantiation() { tppsPaidInvoiceReport: models.TPPSPaidInvoiceReportEntry{ ID: uuid.Must(uuid.NewV4()), InvoiceNumber: "1841-7267-3", - TPPSCreatedDocumentDate: time.Now(), - SellerPaidDate: time.Now(), + TPPSCreatedDocumentDate: &currTime, + SellerPaidDate: currTime, InvoiceTotalChargesInMillicents: unit.Millicents(115155000), LineDescription: "DDP", ProductDescription: "DDP", @@ -28,18 +41,18 @@ func (suite *ModelSuite) TestBasicTPPSPaidInvoiceReportInstantiation() { LineNetCharge: unit.Millicents(2895000), POTCN: "1841-7267-826285fc", LineNumber: "1", - FirstNoteCode: "INT", - FirstNoteDescription: "Notes to My Company - INT", - FirstNoteCodeTo: "CARR", - FirstNoteCodeMessage: "HQ50066", - SecondNoteCode: "INT", - SecondNoteDescription: "Notes to My Company - INT", - SecondNoteCodeTo: "CARR", - SecondNoteCodeMessage: "HQ50066", - ThirdNoteCode: "INT", - ThirdNoteDescription: "Notes to My Company - INT", - ThirdNoteCodeTo: "CARR", - ThirdNoteCodeMessage: "HQ50066", + FirstNoteCode: &firstNoteCode, + FirstNoteDescription: &firstNoteDescription, + FirstNoteCodeTo: &firstNoteCodeTo, + FirstNoteCodeMessage: &firstNoteCodeMessage, + SecondNoteCode: &SecondNoteCode, + SecondNoteDescription: &SecondNoteDescription, + SecondNoteCodeTo: &SecondNoteCodeTo, + SecondNoteCodeMessage: &SecondNoteCodeMessage, + ThirdNoteCode: &ThirdNoteCode, + ThirdNoteDescription: &ThirdNoteDescription, + ThirdNoteCodeTo: &ThirdNoteCodeTo, + ThirdNoteCodeMessage: &ThirdNoteCodeMessage, }, expectedErrs: nil, }, @@ -58,8 +71,8 @@ func (suite *ModelSuite) TestBasicTPPSPaidInvoiceReportInstantiation() { tppsPaidInvoiceReport: models.TPPSPaidInvoiceReportEntry{ ID: uuid.Must(uuid.NewV4()), InvoiceNumber: "1841-7267-3", - TPPSCreatedDocumentDate: time.Now(), - SellerPaidDate: time.Now(), + TPPSCreatedDocumentDate: &currTime, + SellerPaidDate: currTime, InvoiceTotalChargesInMillicents: -1, LineDescription: "DDP", ProductDescription: "DDP", @@ -68,18 +81,18 @@ func (suite *ModelSuite) TestBasicTPPSPaidInvoiceReportInstantiation() { LineNetCharge: -1, POTCN: "1841-7267-826285fc", LineNumber: "1", - FirstNoteCode: "INT", - FirstNoteDescription: "Notes to My Company - INT", - FirstNoteCodeTo: "CARR", - FirstNoteCodeMessage: "HQ50066", - SecondNoteCode: "INT", - SecondNoteDescription: "Notes to My Company - INT", - SecondNoteCodeTo: "CARR", - SecondNoteCodeMessage: "HQ50066", - ThirdNoteCode: "INT", - ThirdNoteDescription: "Notes to My Company - INT", - ThirdNoteCodeTo: "CARR", - ThirdNoteCodeMessage: "HQ50066", + FirstNoteCode: &firstNoteCode, + FirstNoteDescription: &firstNoteDescription, + FirstNoteCodeTo: &firstNoteCodeTo, + FirstNoteCodeMessage: &firstNoteCodeMessage, + SecondNoteCode: &SecondNoteCode, + SecondNoteDescription: &SecondNoteDescription, + SecondNoteCodeTo: &SecondNoteCodeTo, + SecondNoteCodeMessage: &SecondNoteCodeMessage, + ThirdNoteCode: &ThirdNoteCode, + ThirdNoteDescription: &ThirdNoteDescription, + ThirdNoteCodeTo: &ThirdNoteCodeTo, + ThirdNoteCodeMessage: &ThirdNoteCodeMessage, }, expectedErrs: map[string][]string{ "invoice_total_charges_in_millicents": {"-1 is not greater than -1."}, diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index a59c2053d12..9f7f0bdc94a 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -169,7 +169,7 @@ func (t *tppsPaidInvoiceReportProcessor) StoreTPPSPaidInvoiceReportInDatabase(ap tppsEntryModel := models.TPPSPaidInvoiceReportEntry{ InvoiceNumber: tppsEntry.InvoiceNumber, - TPPSCreatedDocumentDate: timeOfTPPSCreatedDocumentDate, + TPPSCreatedDocumentDate: &timeOfTPPSCreatedDocumentDate, SellerPaidDate: timeOfSellerPaidDate, InvoiceTotalChargesInMillicents: unit.Millicents(invoiceTotalChargesInMillicents), LineDescription: tppsEntry.LineDescription, @@ -179,18 +179,18 @@ func (t *tppsPaidInvoiceReportProcessor) StoreTPPSPaidInvoiceReportInDatabase(ap LineNetCharge: unit.Millicents(lineNetChargeInMillicents), POTCN: tppsEntry.POTCN, LineNumber: tppsEntry.LineNumber, - FirstNoteCode: tppsEntry.FirstNoteCode, - FirstNoteDescription: tppsEntry.FirstNoteCodeDescription, - FirstNoteCodeTo: tppsEntry.FirstNoteTo, - FirstNoteCodeMessage: tppsEntry.FirstNoteMessage, - SecondNoteCode: tppsEntry.SecondNoteCode, - SecondNoteDescription: tppsEntry.SecondNoteCodeDescription, - SecondNoteCodeTo: tppsEntry.SecondNoteTo, - SecondNoteCodeMessage: tppsEntry.SecondNoteMessage, - ThirdNoteCode: tppsEntry.ThirdNoteCode, - ThirdNoteDescription: tppsEntry.ThirdNoteCodeDescription, - ThirdNoteCodeTo: tppsEntry.ThirdNoteTo, - ThirdNoteCodeMessage: tppsEntry.ThirdNoteMessage, + FirstNoteCode: &tppsEntry.FirstNoteCode, // #nosec G601 + FirstNoteDescription: &tppsEntry.FirstNoteCodeDescription, // #nosec G601 + FirstNoteCodeTo: &tppsEntry.FirstNoteTo, // #nosec G601 + FirstNoteCodeMessage: &tppsEntry.FirstNoteMessage, // #nosec G601 + SecondNoteCode: &tppsEntry.SecondNoteCode, // #nosec G601 + SecondNoteDescription: &tppsEntry.SecondNoteCodeDescription, // #nosec G601 + SecondNoteCodeTo: &tppsEntry.SecondNoteTo, // #nosec G601 + SecondNoteCodeMessage: &tppsEntry.SecondNoteMessage, // #nosec G601 + ThirdNoteCode: &tppsEntry.ThirdNoteCode, // #nosec G601 + ThirdNoteDescription: &tppsEntry.ThirdNoteCodeDescription, // #nosec G601 + ThirdNoteCodeTo: &tppsEntry.ThirdNoteTo, // #nosec G601 + ThirdNoteCodeMessage: &tppsEntry.ThirdNoteMessage, // #nosec G601 } verrs, err = txnAppCtx.DB().ValidateAndSave(&tppsEntryModel) diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index fff6d78dd1f..0835d93f23c 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -43,7 +43,7 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport if tppsEntryIndex == 0 { suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") - suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(115155000)) // 1151.55 suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DDP") @@ -53,14 +53,14 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(2895000)) // 28.95 suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-826285fc") suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") } if tppsEntryIndex == 1 { suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") - suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(115155000)) // 1151.55 suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "FSC") @@ -70,15 +70,15 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(539000)) // 5.39 suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-aeb3cfea") suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "4") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") } if tppsEntryIndex == 2 { suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") - suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(115155000)) // 1151.55 suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DLH") @@ -88,15 +88,15 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(99877000)) // 998.77 suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-c8ea170b") suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "2") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") } if tppsEntryIndex == 3 { suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1841-7267-3") - suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(115155000)) // 1151.55 suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DUPK") @@ -106,15 +106,15 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(11844000)) // 118.44 suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1841-7267-265c16d7") suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "3") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50066") } if tppsEntryIndex == 4 { suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "9436-4123-3") - suite.Equal(tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.July, 29, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.July, 30, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(12525000)) // 125.25 suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DDP") @@ -124,23 +124,23 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(12525000)) // 125.25 suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "9436-4123-93761f93") suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") - suite.Equal(tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50057") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCode, "INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteDescription, "Notes to My Company - INT") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "CARR") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "HQ50057") } suite.NotNil(tppsEntries[tppsEntryIndex].ID) suite.NotNil(tppsEntries[tppsEntryIndex].CreatedAt) suite.NotNil(tppsEntries[tppsEntryIndex].UpdatedAt) - suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteCode, "") - suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteDescription, "") - suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteCodeTo, "") - suite.Equal(tppsEntries[tppsEntryIndex].SecondNoteCodeMessage, "") - suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteCode, "") - suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteDescription, "") - suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteCodeTo, "") - suite.Equal(tppsEntries[tppsEntryIndex].ThirdNoteCodeMessage, "") + suite.Equal(*tppsEntries[tppsEntryIndex].SecondNoteCode, "") + suite.Equal(*tppsEntries[tppsEntryIndex].SecondNoteDescription, "") + suite.Equal(*tppsEntries[tppsEntryIndex].SecondNoteCodeTo, "") + suite.Equal(*tppsEntries[tppsEntryIndex].SecondNoteCodeMessage, "") + suite.Equal(*tppsEntries[tppsEntryIndex].ThirdNoteCode, "") + suite.Equal(*tppsEntries[tppsEntryIndex].ThirdNoteDescription, "") + suite.Equal(*tppsEntries[tppsEntryIndex].ThirdNoteCodeTo, "") + suite.Equal(*tppsEntries[tppsEntryIndex].ThirdNoteCodeMessage, "") } }) From 2b3b46bccd80f8230ad268687bd59ee0613650b3 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Thu, 15 Aug 2024 20:26:33 +0000 Subject: [PATCH 27/46] add fk to reference payment_request_number from tpps invoice number --- migrations/app/migrations_manifest.txt | 1 + ...20240815195730_add_fk_to_tpps_paid_invoice_reports.up.sql | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 migrations/app/schema/20240815195730_add_fk_to_tpps_paid_invoice_reports.up.sql diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index dd20db8c24a..8588743af22 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -976,3 +976,4 @@ 20240801135833_alter_mto_shipment_type_motorhome.up.sql 20240802161708_tpps_paid_invoice_table.up.sql 20240814144527_remove_allow_pptas_client.up.sql +20240815195730_add_fk_to_tpps_paid_invoice_reports.up.sql diff --git a/migrations/app/schema/20240815195730_add_fk_to_tpps_paid_invoice_reports.up.sql b/migrations/app/schema/20240815195730_add_fk_to_tpps_paid_invoice_reports.up.sql new file mode 100644 index 00000000000..0472e62b31e --- /dev/null +++ b/migrations/app/schema/20240815195730_add_fk_to_tpps_paid_invoice_reports.up.sql @@ -0,0 +1,5 @@ +ALTER TABLE tpps_paid_invoice_reports +ADD CONSTRAINT tpps_paid_invoice_reports_invoice_number_fkey +FOREIGN KEY (invoice_number) +REFERENCES payment_requests(payment_request_number) +ON DELETE CASCADE; \ No newline at end of file From 6af196888830ba43a839e108e1c196471f4317d9 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Thu, 15 Aug 2024 23:00:31 +0000 Subject: [PATCH 28/46] fetch tpps invoice report info for payment request --- pkg/gen/ghcapi/embedded_spec.go | 36 ++++++++++++++ pkg/gen/ghcmessages/payment_request.go | 23 +++++++++ pkg/gen/ghcmessages/payment_service_item.go | 3 ++ pkg/models/payment_request.go | 11 +++-- .../process_tpps_paid_invoice_report_test.go | 47 +++++++++++++++++++ .../payment_request_list_fetcher.go | 32 +++++++++++++ scripts/run-server-test | 6 +-- .../PaymentRequestCard/PaymentRequestCard.jsx | 2 +- swagger-def/ghc.yaml | 15 ++++++ swagger/ghc.yaml | 17 +++++++ 10 files changed, 183 insertions(+), 9 deletions(-) diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 0ea8c3f1a44..f1e0f093f1d 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -10840,6 +10840,18 @@ func init() { }, "status": { "$ref": "#/definitions/PaymentRequestStatus" + }, + "tppsPaidReportAmountPaidTotal": { + "type": "integer", + "format": "cents", + "title": "Total amount that TPPS paid for the service items on the payment request in cents", + "x-nullable": true + }, + "tppsPaidReportSellerPaidDate": { + "type": "string", + "format": "date", + "title": "Date that TPPS paid HS for the payment request", + "x-nullable": true } } }, @@ -10927,6 +10939,12 @@ func init() { }, "status": { "$ref": "#/definitions/PaymentServiceItemStatus" + }, + "tppsPaidReportAmountPaidForServiceItem": { + "type": "integer", + "format": "cents", + "title": "Amount that TPPS paid for the individual service item in cents", + "x-nullable": true } } }, @@ -25839,6 +25857,18 @@ func init() { }, "status": { "$ref": "#/definitions/PaymentRequestStatus" + }, + "tppsPaidReportAmountPaidTotal": { + "type": "integer", + "format": "cents", + "title": "Total amount that TPPS paid for the service items on the payment request in cents", + "x-nullable": true + }, + "tppsPaidReportSellerPaidDate": { + "type": "string", + "format": "date", + "title": "Date that TPPS paid HS for the payment request", + "x-nullable": true } } }, @@ -25926,6 +25956,12 @@ func init() { }, "status": { "$ref": "#/definitions/PaymentServiceItemStatus" + }, + "tppsPaidReportAmountPaidForServiceItem": { + "type": "integer", + "format": "cents", + "title": "Amount that TPPS paid for the individual service item in cents", + "x-nullable": true } } }, diff --git a/pkg/gen/ghcmessages/payment_request.go b/pkg/gen/ghcmessages/payment_request.go index 67707f972bf..e43f0700cbb 100644 --- a/pkg/gen/ghcmessages/payment_request.go +++ b/pkg/gen/ghcmessages/payment_request.go @@ -87,6 +87,13 @@ type PaymentRequest struct { // status Status PaymentRequestStatus `json:"status,omitempty"` + + // Total amount that TPPS paid for the service items on the payment request in cents + TppsPaidReportAmountPaidTotal *int64 `json:"tppsPaidReportAmountPaidTotal,omitempty"` + + // Date that TPPS paid HS for the payment request + // Format: date + TppsPaidReportSellerPaidDate *strfmt.Date `json:"tppsPaidReportSellerPaidDate,omitempty"` } // Validate validates this payment request @@ -137,6 +144,10 @@ func (m *PaymentRequest) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateTppsPaidReportSellerPaidDate(formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -297,6 +308,18 @@ func (m *PaymentRequest) validateStatus(formats strfmt.Registry) error { return nil } +func (m *PaymentRequest) validateTppsPaidReportSellerPaidDate(formats strfmt.Registry) error { + if swag.IsZero(m.TppsPaidReportSellerPaidDate) { // not required + return nil + } + + if err := validate.FormatOf("tppsPaidReportSellerPaidDate", "body", "date", m.TppsPaidReportSellerPaidDate.String(), formats); err != nil { + return err + } + + return nil +} + // ContextValidate validate this payment request based on the context it is used func (m *PaymentRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error diff --git a/pkg/gen/ghcmessages/payment_service_item.go b/pkg/gen/ghcmessages/payment_service_item.go index 3ec79d74844..ed2102ad3a6 100644 --- a/pkg/gen/ghcmessages/payment_service_item.go +++ b/pkg/gen/ghcmessages/payment_service_item.go @@ -75,6 +75,9 @@ type PaymentServiceItem struct { // status Status PaymentServiceItemStatus `json:"status,omitempty"` + + // Amount that TPPS paid for the individual service item in cents + TppsPaidReportAmountPaidForServiceItem *int64 `json:"tppsPaidReportAmountPaidForServiceItem,omitempty"` } // Validate validates this payment service item diff --git a/pkg/models/payment_request.go b/pkg/models/payment_request.go index 181738a14c7..120f7a1d4fb 100644 --- a/pkg/models/payment_request.go +++ b/pkg/models/payment_request.go @@ -81,11 +81,12 @@ type PaymentRequest struct { RecalculationOfPaymentRequestID *uuid.UUID `json:"recalculation_of_payment_request_id" db:"recalculation_of_payment_request_id"` // Associations - MoveTaskOrder Move `belongs_to:"moves" fk_id:"move_id"` - PaymentServiceItems PaymentServiceItems `has_many:"payment_service_items" fk_id:"payment_request_id"` - ProofOfServiceDocs ProofOfServiceDocs `has_many:"proof_of_service_docs" fk_id:"payment_request_id"` - EdiErrors EdiErrors `has_many:"edi_errors" fk_id:"payment_request_id"` - RecalculationOfPaymentRequest *PaymentRequest `belongs_to:"payment_requests" fk_id:"recalculation_of_payment_request_id"` + MoveTaskOrder Move `belongs_to:"moves" fk_id:"move_id"` + PaymentServiceItems PaymentServiceItems `has_many:"payment_service_items" fk_id:"payment_request_id"` + ProofOfServiceDocs ProofOfServiceDocs `has_many:"proof_of_service_docs" fk_id:"payment_request_id"` + EdiErrors EdiErrors `has_many:"edi_errors" fk_id:"payment_request_id"` + RecalculationOfPaymentRequest *PaymentRequest `belongs_to:"payment_requests" fk_id:"recalculation_of_payment_request_id"` + TPPSPaidInvoiceReports TPPSPaidInvoiceReportEntrys `has_many:"tpps_paid_invoice_reports" fk_id:"payment_request_number"` } // TableName overrides the table name used by Pop. diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index 0835d93f23c..8e2fe01a73b 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/testingsuite" "github.com/transcom/mymove/pkg/unit" @@ -29,6 +30,52 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport tppsPaidInvoiceReportProcessor := NewTPPSPaidInvoiceReportProcessor() suite.Run("successfully proccesses a valid TPPSPaidInvoiceReport and stores it in the database", func() { + // payment request with a payment request number of 1841-7267-3 must exist + // because the TPPS invoice report invoice's number references the + // payment request payment_request_number as a foreign key + paymentRequestOne := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusPaid, + PaymentRequestNumber: "1841-7267-3", + }, + }, + }, nil) + factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequestToInterchangeControlNumber{ + InterchangeControlNumber: 100001251, + EDIType: models.EDIType858, + }, + }, + { + Model: paymentRequestOne, + LinkOnly: true, + }, + }, nil) + // payment request with a payment request number of 9436-4123-3 must exist + // because the TPPS invoice report invoice's number references the + // payment request payment_request_number as a foreign key + paymentRequestTwo := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusPaid, + PaymentRequestNumber: "9436-4123-3", + }, + }, + }, nil) + factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequestToInterchangeControlNumber{ + InterchangeControlNumber: 100001251, + EDIType: models.EDIType858, + }, + }, + { + Model: paymentRequestTwo, + LinkOnly: true, + }, + }, nil) testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile.csv" diff --git a/pkg/services/payment_request/payment_request_list_fetcher.go b/pkg/services/payment_request/payment_request_list_fetcher.go index 7d1d979dbe6..8c5c24375cf 100644 --- a/pkg/services/payment_request/payment_request_list_fetcher.go +++ b/pkg/services/payment_request/payment_request_list_fetcher.go @@ -196,7 +196,15 @@ func (f *paymentRequestListFetcher) FetchPaymentRequestListByMove(appCtx appcont if errFetchingEdiError != nil { return nil, errFetchingEdiError } + + tppsReportEntryList, errFetchingTPPSInformation := fetchTPPSPaidInvoiceReportDataPaymentRequest(appCtx, &paymentRequests[i]) + if errFetchingTPPSInformation != nil { + return nil, errFetchingTPPSInformation + } + paymentRequests[i].EdiErrors = append(paymentRequests[i].EdiErrors, mostRecentEdiErrorForPaymentRequest) + paymentRequests[i].TPPSPaidInvoiceReports = tppsReportEntryList + } return &paymentRequests, nil @@ -229,6 +237,30 @@ func fetchEDIErrorsForPaymentRequest(appCtx appcontext.AppContext, pr *models.Pa return ediErrorInfo, nil } +// fetchTPPSPaidInvoiceReportDataPaymentRequest returns entries in the tpps_paid_invoice_reports +// for a payment request by matching the payment request number to the TPPS invoice number +func fetchTPPSPaidInvoiceReportDataPaymentRequest(appCtx appcontext.AppContext, pr *models.PaymentRequest) (models.TPPSPaidInvoiceReportEntrys, error) { + + var tppsPaidInvoiceReport []models.TPPSPaidInvoiceReportEntry + tppsPaidInformation := models.TPPSPaidInvoiceReportEntrys{} + + err := appCtx.DB().Q(). + Where("tpps_paid_invoice_reports.invoice_number = $1", pr.PaymentRequestNumber). + All(&tppsPaidInvoiceReport) + + if err != nil { + return tppsPaidInformation, err + } else if len(tppsPaidInvoiceReport) == 0 { + return tppsPaidInformation, nil + } + if len(tppsPaidInvoiceReport) > 0 { + tppsPaidInformation = tppsPaidInvoiceReport + return tppsPaidInformation, nil + } + + return tppsPaidInformation, nil +} + func orderName(query *pop.Query, order *string) *pop.Query { query.Order(fmt.Sprintf("service_members.last_name %s, service_members.first_name %s", *order, *order)) return query diff --git a/scripts/run-server-test b/scripts/run-server-test index 0ead8674b81..1fd1e90764b 100755 --- a/scripts/run-server-test +++ b/scripts/run-server-test @@ -53,9 +53,9 @@ fi # Check if the operating system is macOS before running command # this uses the classic linker when running make server_test aka go test # this addresses issues we were having with the default linker on macOS -if [[ "$(uname)" == "Darwin" ]]; then - gotest_args+=("-ldflags=-extldflags=-Wl,-ld_classic") -fi +# if [[ "$(uname)" == "Darwin" ]]; then +# gotest_args+=("-ldflags=-extldflags=-Wl,-ld_classic") +# fi # Try to compile tests, but don't run them. if [[ "${DRY_RUN:-}" == "1" ]]; then diff --git a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx index 4cfaa275233..64beae364cc 100644 --- a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx +++ b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx @@ -31,7 +31,7 @@ const paymentRequestStatusLabel = (status) => { case PAYMENT_REQUEST_STATUS.REVIEWED_AND_ALL_SERVICE_ITEMS_REJECTED: return 'Rejected'; case PAYMENT_REQUEST_STATUS.PAID: - return 'Paid'; + return 'TPPS Paid'; case PAYMENT_REQUEST_STATUS.EDI_ERROR: return 'EDI Error'; default: diff --git a/swagger-def/ghc.yaml b/swagger-def/ghc.yaml index d0e958caa38..193902eaa15 100644 --- a/swagger-def/ghc.yaml +++ b/swagger-def/ghc.yaml @@ -5490,6 +5490,16 @@ definitions: description: The reason the services counselor has excluded or rejected the item. type: string x-nullable: true + tppsPaidReportAmountPaidTotal: + type: integer + format: cents + title: Total amount that TPPS paid for the service items on the payment request in cents + x-nullable: true + tppsPaidReportSellerPaidDate: + type: string + format: date + title: Date that TPPS paid HS for the payment request + x-nullable: true type: object PaymentRequests: items: @@ -5549,6 +5559,11 @@ definitions: $ref: 'definitions/PaymentServiceItemParams.yaml' eTag: type: string + tppsPaidReportAmountPaidForServiceItem: + type: integer + format: cents + title: Amount that TPPS paid for the individual service item in cents + x-nullable: true type: object PaymentRequestStatus: $ref: 'definitions/PaymentRequestStatus.yaml' diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index 38e6b541409..ac374e2276c 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -5706,6 +5706,18 @@ definitions: description: The reason the services counselor has excluded or rejected the item. type: string x-nullable: true + tppsPaidReportAmountPaidTotal: + type: integer + format: cents + title: >- + Total amount that TPPS paid for the service items on the payment + request in cents + x-nullable: true + tppsPaidReportSellerPaidDate: + type: string + format: date + title: Date that TPPS paid HS for the payment request + x-nullable: true type: object PaymentRequests: items: @@ -5765,6 +5777,11 @@ definitions: $ref: '#/definitions/PaymentServiceItemParams' eTag: type: string + tppsPaidReportAmountPaidForServiceItem: + type: integer + format: cents + title: Amount that TPPS paid for the individual service item in cents + x-nullable: true type: object PaymentRequestStatus: type: string From fc0a0d70783c5ca09ee204ad5bada8f149263331 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 16 Aug 2024 14:09:39 +0000 Subject: [PATCH 29/46] remove commenting out of ldclassic --- scripts/run-server-test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/run-server-test b/scripts/run-server-test index 1fd1e90764b..0ead8674b81 100755 --- a/scripts/run-server-test +++ b/scripts/run-server-test @@ -53,9 +53,9 @@ fi # Check if the operating system is macOS before running command # this uses the classic linker when running make server_test aka go test # this addresses issues we were having with the default linker on macOS -# if [[ "$(uname)" == "Darwin" ]]; then -# gotest_args+=("-ldflags=-extldflags=-Wl,-ld_classic") -# fi +if [[ "$(uname)" == "Darwin" ]]; then + gotest_args+=("-ldflags=-extldflags=-Wl,-ld_classic") +fi # Try to compile tests, but don't run them. if [[ "${DRY_RUN:-}" == "1" ]]; then From 0b9b86002a62d5886d942c74a7a47495d722617e Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 16 Aug 2024 20:24:28 +0000 Subject: [PATCH 30/46] add relevant tpps report data to pr and pr si payloads --- pkg/gen/ghcapi/embedded_spec.go | 32 +++++------ pkg/gen/ghcmessages/payment_request.go | 16 +++--- pkg/gen/ghcmessages/payment_service_item.go | 4 +- .../internal/payloads/model_to_payload.go | 57 ++++++++++++------- pkg/models/payment_request.go | 12 ++-- .../payment_request_list_fetcher.go | 2 +- src/types/order.js | 3 + swagger-def/ghc.yaml | 16 +++--- swagger/ghc.yaml | 18 +++--- 9 files changed, 91 insertions(+), 69 deletions(-) diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index f1e0f093f1d..af1470d2a33 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -10841,15 +10841,15 @@ func init() { "status": { "$ref": "#/definitions/PaymentRequestStatus" }, - "tppsPaidReportAmountPaidTotal": { + "tppsInvoiceAmountPaidTotalMillicents": { "type": "integer", - "format": "cents", - "title": "Total amount that TPPS paid for the service items on the payment request in cents", + "format": "millients", + "title": "Total amount that TPPS paid for all service items on the payment request in millicents", "x-nullable": true }, - "tppsPaidReportSellerPaidDate": { + "tppsInvoiceSellerPaidDate": { "type": "string", - "format": "date", + "format": "date-time", "title": "Date that TPPS paid HS for the payment request", "x-nullable": true } @@ -10940,10 +10940,10 @@ func init() { "status": { "$ref": "#/definitions/PaymentServiceItemStatus" }, - "tppsPaidReportAmountPaidForServiceItem": { + "tppsInvoiceAmountPaidPerServiceItemMillicents": { "type": "integer", - "format": "cents", - "title": "Amount that TPPS paid for the individual service item in cents", + "format": "millicents", + "title": "Amount that TPPS paid for the individual service item in millicents", "x-nullable": true } } @@ -25858,15 +25858,15 @@ func init() { "status": { "$ref": "#/definitions/PaymentRequestStatus" }, - "tppsPaidReportAmountPaidTotal": { + "tppsInvoiceAmountPaidTotalMillicents": { "type": "integer", - "format": "cents", - "title": "Total amount that TPPS paid for the service items on the payment request in cents", + "format": "millients", + "title": "Total amount that TPPS paid for all service items on the payment request in millicents", "x-nullable": true }, - "tppsPaidReportSellerPaidDate": { + "tppsInvoiceSellerPaidDate": { "type": "string", - "format": "date", + "format": "date-time", "title": "Date that TPPS paid HS for the payment request", "x-nullable": true } @@ -25957,10 +25957,10 @@ func init() { "status": { "$ref": "#/definitions/PaymentServiceItemStatus" }, - "tppsPaidReportAmountPaidForServiceItem": { + "tppsInvoiceAmountPaidPerServiceItemMillicents": { "type": "integer", - "format": "cents", - "title": "Amount that TPPS paid for the individual service item in cents", + "format": "millicents", + "title": "Amount that TPPS paid for the individual service item in millicents", "x-nullable": true } } diff --git a/pkg/gen/ghcmessages/payment_request.go b/pkg/gen/ghcmessages/payment_request.go index e43f0700cbb..6e4ba8100c6 100644 --- a/pkg/gen/ghcmessages/payment_request.go +++ b/pkg/gen/ghcmessages/payment_request.go @@ -88,12 +88,12 @@ type PaymentRequest struct { // status Status PaymentRequestStatus `json:"status,omitempty"` - // Total amount that TPPS paid for the service items on the payment request in cents - TppsPaidReportAmountPaidTotal *int64 `json:"tppsPaidReportAmountPaidTotal,omitempty"` + // Total amount that TPPS paid for all service items on the payment request in millicents + TppsInvoiceAmountPaidTotalMillicents *int64 `json:"tppsInvoiceAmountPaidTotalMillicents,omitempty"` // Date that TPPS paid HS for the payment request - // Format: date - TppsPaidReportSellerPaidDate *strfmt.Date `json:"tppsPaidReportSellerPaidDate,omitempty"` + // Format: date-time + TppsInvoiceSellerPaidDate *strfmt.DateTime `json:"tppsInvoiceSellerPaidDate,omitempty"` } // Validate validates this payment request @@ -144,7 +144,7 @@ func (m *PaymentRequest) Validate(formats strfmt.Registry) error { res = append(res, err) } - if err := m.validateTppsPaidReportSellerPaidDate(formats); err != nil { + if err := m.validateTppsInvoiceSellerPaidDate(formats); err != nil { res = append(res, err) } @@ -308,12 +308,12 @@ func (m *PaymentRequest) validateStatus(formats strfmt.Registry) error { return nil } -func (m *PaymentRequest) validateTppsPaidReportSellerPaidDate(formats strfmt.Registry) error { - if swag.IsZero(m.TppsPaidReportSellerPaidDate) { // not required +func (m *PaymentRequest) validateTppsInvoiceSellerPaidDate(formats strfmt.Registry) error { + if swag.IsZero(m.TppsInvoiceSellerPaidDate) { // not required return nil } - if err := validate.FormatOf("tppsPaidReportSellerPaidDate", "body", "date", m.TppsPaidReportSellerPaidDate.String(), formats); err != nil { + if err := validate.FormatOf("tppsInvoiceSellerPaidDate", "body", "date-time", m.TppsInvoiceSellerPaidDate.String(), formats); err != nil { return err } diff --git a/pkg/gen/ghcmessages/payment_service_item.go b/pkg/gen/ghcmessages/payment_service_item.go index ed2102ad3a6..8dce225f76c 100644 --- a/pkg/gen/ghcmessages/payment_service_item.go +++ b/pkg/gen/ghcmessages/payment_service_item.go @@ -76,8 +76,8 @@ type PaymentServiceItem struct { // status Status PaymentServiceItemStatus `json:"status,omitempty"` - // Amount that TPPS paid for the individual service item in cents - TppsPaidReportAmountPaidForServiceItem *int64 `json:"tppsPaidReportAmountPaidForServiceItem,omitempty"` + // Amount that TPPS paid for the individual service item in millicents + TppsInvoiceAmountPaidPerServiceItemMillicents *int64 `json:"tppsInvoiceAmountPaidPerServiceItemMillicents,omitempty"` } // Validate validates this payment service item diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index 45cc5c51fc9..a3799827887 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -1512,25 +1512,37 @@ func PaymentRequest(appCtx appcontext.AppContext, pr *models.PaymentRequest, sto } } + TPPSPaidInvoiceReportsForPR := *pr.TPPSPaidInvoiceReports + totalTPPSPaidInvoicePriceMillicents := int64(0) + var tppsPaidInvoiceSellerPaidDate *time.Time + if len(TPPSPaidInvoiceReportsForPR) > 0 { + if TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents > -1 { + totalTPPSPaidInvoicePriceMillicents = TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents.Int64() + tppsPaidInvoiceSellerPaidDate = &TPPSPaidInvoiceReportsForPR[0].SellerPaidDate + } + } + return &ghcmessages.PaymentRequest{ - ID: *handlers.FmtUUID(pr.ID), - IsFinal: &pr.IsFinal, - MoveTaskOrderID: *handlers.FmtUUID(pr.MoveTaskOrderID), - MoveTaskOrder: move, - PaymentRequestNumber: pr.PaymentRequestNumber, - RecalculationOfPaymentRequestID: handlers.FmtUUIDPtr(pr.RecalculationOfPaymentRequestID), - RejectionReason: pr.RejectionReason, - Status: ghcmessages.PaymentRequestStatus(pr.Status), - ETag: etag.GenerateEtag(pr.UpdatedAt), - ServiceItems: *PaymentServiceItems(&pr.PaymentServiceItems), - ReviewedAt: handlers.FmtDateTimePtr(pr.ReviewedAt), - ProofOfServiceDocs: serviceDocs, - CreatedAt: strfmt.DateTime(pr.CreatedAt), - SentToGexAt: (*strfmt.DateTime)(pr.SentToGexAt), - ReceivedByGexAt: (*strfmt.DateTime)(pr.ReceivedByGexAt), - EdiErrorType: &ediErrorInfoEDIType, - EdiErrorCode: &ediErrorInfoEDICode, - EdiErrorDescription: &ediErrorInfoEDIDescription, + ID: *handlers.FmtUUID(pr.ID), + IsFinal: &pr.IsFinal, + MoveTaskOrderID: *handlers.FmtUUID(pr.MoveTaskOrderID), + MoveTaskOrder: move, + PaymentRequestNumber: pr.PaymentRequestNumber, + RecalculationOfPaymentRequestID: handlers.FmtUUIDPtr(pr.RecalculationOfPaymentRequestID), + RejectionReason: pr.RejectionReason, + Status: ghcmessages.PaymentRequestStatus(pr.Status), + ETag: etag.GenerateEtag(pr.UpdatedAt), + ServiceItems: *PaymentServiceItems(&pr.PaymentServiceItems, &TPPSPaidInvoiceReportsForPR), + ReviewedAt: handlers.FmtDateTimePtr(pr.ReviewedAt), + ProofOfServiceDocs: serviceDocs, + CreatedAt: strfmt.DateTime(pr.CreatedAt), + SentToGexAt: (*strfmt.DateTime)(pr.SentToGexAt), + ReceivedByGexAt: (*strfmt.DateTime)(pr.ReceivedByGexAt), + EdiErrorType: &ediErrorInfoEDIType, + EdiErrorCode: &ediErrorInfoEDICode, + EdiErrorDescription: &ediErrorInfoEDIDescription, + TppsInvoiceAmountPaidTotalMillicents: &totalTPPSPaidInvoicePriceMillicents, + TppsInvoiceSellerPaidDate: (*strfmt.DateTime)(tppsPaidInvoiceSellerPaidDate), }, nil } @@ -1559,11 +1571,18 @@ func PaymentServiceItem(ps *models.PaymentServiceItem) *ghcmessages.PaymentServi } // PaymentServiceItems payload -func PaymentServiceItems(paymentServiceItems *models.PaymentServiceItems) *ghcmessages.PaymentServiceItems { +func PaymentServiceItems(paymentServiceItems *models.PaymentServiceItems, tppsPaidReportData *models.TPPSPaidInvoiceReportEntrys) *ghcmessages.PaymentServiceItems { payload := make(ghcmessages.PaymentServiceItems, len(*paymentServiceItems)) for i, m := range *paymentServiceItems { copyOfPaymentServiceItem := m // Make copy to avoid implicit memory aliasing of items from a range statement. payload[i] = PaymentServiceItem(©OfPaymentServiceItem) + + tppsDataForPaymentRequest := *tppsPaidReportData + for tppsDataRowIndex := range tppsDataForPaymentRequest { + if tppsDataForPaymentRequest[tppsDataRowIndex].ProductDescription == payload[i].MtoServiceItemCode { + payload[i].TppsInvoiceAmountPaidPerServiceItemMillicents = handlers.FmtMilliCentsPtr(&tppsDataForPaymentRequest[tppsDataRowIndex].LineNetCharge) + } + } } return &payload } diff --git a/pkg/models/payment_request.go b/pkg/models/payment_request.go index 120f7a1d4fb..525c5ebb636 100644 --- a/pkg/models/payment_request.go +++ b/pkg/models/payment_request.go @@ -81,12 +81,12 @@ type PaymentRequest struct { RecalculationOfPaymentRequestID *uuid.UUID `json:"recalculation_of_payment_request_id" db:"recalculation_of_payment_request_id"` // Associations - MoveTaskOrder Move `belongs_to:"moves" fk_id:"move_id"` - PaymentServiceItems PaymentServiceItems `has_many:"payment_service_items" fk_id:"payment_request_id"` - ProofOfServiceDocs ProofOfServiceDocs `has_many:"proof_of_service_docs" fk_id:"payment_request_id"` - EdiErrors EdiErrors `has_many:"edi_errors" fk_id:"payment_request_id"` - RecalculationOfPaymentRequest *PaymentRequest `belongs_to:"payment_requests" fk_id:"recalculation_of_payment_request_id"` - TPPSPaidInvoiceReports TPPSPaidInvoiceReportEntrys `has_many:"tpps_paid_invoice_reports" fk_id:"payment_request_number"` + MoveTaskOrder Move `belongs_to:"moves" fk_id:"move_id"` + PaymentServiceItems PaymentServiceItems `has_many:"payment_service_items" fk_id:"payment_request_id"` + ProofOfServiceDocs ProofOfServiceDocs `has_many:"proof_of_service_docs" fk_id:"payment_request_id"` + EdiErrors EdiErrors `has_many:"edi_errors" fk_id:"payment_request_id"` + RecalculationOfPaymentRequest *PaymentRequest `belongs_to:"payment_requests" fk_id:"recalculation_of_payment_request_id"` + TPPSPaidInvoiceReports *TPPSPaidInvoiceReportEntrys `has_many:"tpps_paid_invoice_reports" fk_id:"payment_request_number"` } // TableName overrides the table name used by Pop. diff --git a/pkg/services/payment_request/payment_request_list_fetcher.go b/pkg/services/payment_request/payment_request_list_fetcher.go index 8c5c24375cf..592a012368f 100644 --- a/pkg/services/payment_request/payment_request_list_fetcher.go +++ b/pkg/services/payment_request/payment_request_list_fetcher.go @@ -203,7 +203,7 @@ func (f *paymentRequestListFetcher) FetchPaymentRequestListByMove(appCtx appcont } paymentRequests[i].EdiErrors = append(paymentRequests[i].EdiErrors, mostRecentEdiErrorForPaymentRequest) - paymentRequests[i].TPPSPaidInvoiceReports = tppsReportEntryList + paymentRequests[i].TPPSPaidInvoiceReports = &tppsReportEntryList } diff --git a/src/types/order.js b/src/types/order.js index ce0b2f63b36..a178a3295dd 100644 --- a/src/types/order.js +++ b/src/types/order.js @@ -124,6 +124,7 @@ export const PaymentServiceItemShape = PropTypes.shape({ status: PropTypes.string, rejectionReason: PropTypes.string, paymentServiceItemParams: PropTypes.arrayOf(PaymentServiceItemParam), + tppsInvoiceAmountPaidPerServiceItemMillicents: PropTypes.number, }); export const PaymentRequestShape = PropTypes.shape({ @@ -139,6 +140,8 @@ export const PaymentRequestShape = PropTypes.shape({ ediErrorType: PropTypes.string, ediErrorCode: PropTypes.string, ediErrorDescription: PropTypes.string, + tppsInvoiceAmountPaidTotalMillicents: PropTypes.number, + tppsInvoiceSellerPaidDate: PropTypes.string, }); export const OrdersLOAShape = PropTypes.shape({ diff --git a/swagger-def/ghc.yaml b/swagger-def/ghc.yaml index 193902eaa15..42220abea3b 100644 --- a/swagger-def/ghc.yaml +++ b/swagger-def/ghc.yaml @@ -5490,14 +5490,14 @@ definitions: description: The reason the services counselor has excluded or rejected the item. type: string x-nullable: true - tppsPaidReportAmountPaidTotal: + tppsInvoiceAmountPaidTotalMillicents: type: integer - format: cents - title: Total amount that TPPS paid for the service items on the payment request in cents + format: millients + title: Total amount that TPPS paid for all service items on the payment request in millicents x-nullable: true - tppsPaidReportSellerPaidDate: + tppsInvoiceSellerPaidDate: type: string - format: date + format: date-time title: Date that TPPS paid HS for the payment request x-nullable: true type: object @@ -5559,10 +5559,10 @@ definitions: $ref: 'definitions/PaymentServiceItemParams.yaml' eTag: type: string - tppsPaidReportAmountPaidForServiceItem: + tppsInvoiceAmountPaidPerServiceItemMillicents: type: integer - format: cents - title: Amount that TPPS paid for the individual service item in cents + format: millicents + title: Amount that TPPS paid for the individual service item in millicents x-nullable: true type: object PaymentRequestStatus: diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index ac374e2276c..97733f20fa4 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -5706,16 +5706,16 @@ definitions: description: The reason the services counselor has excluded or rejected the item. type: string x-nullable: true - tppsPaidReportAmountPaidTotal: + tppsInvoiceAmountPaidTotalMillicents: type: integer - format: cents + format: millients title: >- - Total amount that TPPS paid for the service items on the payment - request in cents + Total amount that TPPS paid for all service items on the payment + request in millicents x-nullable: true - tppsPaidReportSellerPaidDate: + tppsInvoiceSellerPaidDate: type: string - format: date + format: date-time title: Date that TPPS paid HS for the payment request x-nullable: true type: object @@ -5777,10 +5777,10 @@ definitions: $ref: '#/definitions/PaymentServiceItemParams' eTag: type: string - tppsPaidReportAmountPaidForServiceItem: + tppsInvoiceAmountPaidPerServiceItemMillicents: type: integer - format: cents - title: Amount that TPPS paid for the individual service item in cents + format: millicents + title: Amount that TPPS paid for the individual service item in millicents x-nullable: true type: object PaymentRequestStatus: From 1649418e39dc14f19b55ee0bc0710556ef530332 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 19 Aug 2024 15:37:09 +0000 Subject: [PATCH 31/46] display tpps info on UI for payment requests --- .../internal/payloads/model_to_payload.go | 17 +-- .../ExpandableServiceItemRow.jsx | 9 +- .../PaymentRequestCard/PaymentRequestCard.jsx | 44 +++++++- .../PaymentRequestDetails.jsx | 101 +++++++++++++----- 4 files changed, 131 insertions(+), 40 deletions(-) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index a3799827887..6c6d7014729 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -1512,13 +1512,16 @@ func PaymentRequest(appCtx appcontext.AppContext, pr *models.PaymentRequest, sto } } - TPPSPaidInvoiceReportsForPR := *pr.TPPSPaidInvoiceReports - totalTPPSPaidInvoicePriceMillicents := int64(0) + var totalTPPSPaidInvoicePriceMillicents *int64 var tppsPaidInvoiceSellerPaidDate *time.Time - if len(TPPSPaidInvoiceReportsForPR) > 0 { - if TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents > -1 { - totalTPPSPaidInvoicePriceMillicents = TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents.Int64() - tppsPaidInvoiceSellerPaidDate = &TPPSPaidInvoiceReportsForPR[0].SellerPaidDate + var TPPSPaidInvoiceReportsForPR models.TPPSPaidInvoiceReportEntrys + if pr.TPPSPaidInvoiceReports != nil { + TPPSPaidInvoiceReportsForPR = *pr.TPPSPaidInvoiceReports + if len(TPPSPaidInvoiceReportsForPR) > 0 { + if TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents > -1 { + totalTPPSPaidInvoicePriceMillicents = models.Int64Pointer(int64(TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents)) + tppsPaidInvoiceSellerPaidDate = &TPPSPaidInvoiceReportsForPR[0].SellerPaidDate + } } } @@ -1541,7 +1544,7 @@ func PaymentRequest(appCtx appcontext.AppContext, pr *models.PaymentRequest, sto EdiErrorType: &ediErrorInfoEDIType, EdiErrorCode: &ediErrorInfoEDICode, EdiErrorDescription: &ediErrorInfoEDIDescription, - TppsInvoiceAmountPaidTotalMillicents: &totalTPPSPaidInvoicePriceMillicents, + TppsInvoiceAmountPaidTotalMillicents: totalTPPSPaidInvoicePriceMillicents, TppsInvoiceSellerPaidDate: (*strfmt.DateTime)(tppsPaidInvoiceSellerPaidDate), }, nil } diff --git a/src/components/Office/ExpandableServiceItemRow/ExpandableServiceItemRow.jsx b/src/components/Office/ExpandableServiceItemRow/ExpandableServiceItemRow.jsx index d5d39cf7a63..585ef6b44d6 100644 --- a/src/components/Office/ExpandableServiceItemRow/ExpandableServiceItemRow.jsx +++ b/src/components/Office/ExpandableServiceItemRow/ExpandableServiceItemRow.jsx @@ -9,7 +9,7 @@ import { PAYMENT_SERVICE_ITEM_STATUS } from 'shared/constants'; import { allowedServiceItemCalculations } from 'constants/serviceItems'; import { PaymentServiceItemShape } from 'types'; import { MTOServiceItemShape } from 'types/order'; -import { toDollarString, formatCents } from 'utils/formatters'; +import { toDollarString, formatCents, formatDollarFromMillicents } from 'utils/formatters'; import ServiceItemCalculations from 'components/Office/ServiceItemCalculations/ServiceItemCalculations'; const ExpandableServiceItemRow = ({ @@ -18,6 +18,7 @@ const ExpandableServiceItemRow = ({ index, paymentIsDeprecated, serviceItem, + tppsDataExists, }) => { const [isExpanded, setIsExpanded] = useState(false); const canClickToExpandContent = (canShowExpandableContent, item) => { @@ -59,6 +60,12 @@ const ExpandableServiceItemRow = ({ {additionalServiceItemData.standaloneCrate && ' - Standalone'} {toDollarString(formatCents(serviceItem.priceCents))} + {tppsDataExists && ( + + {serviceItem.tppsInvoiceAmountPaidPerServiceItemMillicents > 0 && + toDollarString(formatDollarFromMillicents(serviceItem.tppsInvoiceAmountPaidPerServiceItemMillicents))} + + )} {paymentIsDeprecated && (
diff --git a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx index 64beae364cc..a7a1c5fb591 100644 --- a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx +++ b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx @@ -11,12 +11,13 @@ import styles from './PaymentRequestCard.module.scss'; import { PaymentRequestShape } from 'types'; import { LOA_TYPE, PAYMENT_REQUEST_STATUS } from 'shared/constants'; import { nonWeightReliantServiceItems } from 'content/serviceItems'; -import { toDollarString, formatDateFromIso, formatCents } from 'utils/formatters'; +import { toDollarString, formatDateFromIso, formatCents, formatDollarFromMillicents } from 'utils/formatters'; import PaymentRequestDetails from 'components/Office/PaymentRequestDetails/PaymentRequestDetails'; import ConnectedAcountingCodesModal from 'components/Office/AccountingCodesModal/AccountingCodesModal'; import { groupByShipment } from 'utils/serviceItems'; import Restricted from 'components/Restricted/Restricted'; import { permissionTypes } from 'constants/permissions'; +import { formatDateWithUTC } from 'shared/dates'; const paymentRequestStatusLabel = (status) => { switch (status) { @@ -133,9 +134,15 @@ const PaymentRequestCard = ({ const showErrorDetailsChevron = showErrorDetails ? 'chevron-up' : 'chevron-down'; const showErrorDetailsText = showErrorDetails ? 'Hide EDI error details' : 'Show EDI error details'; const handleToggleErrorDetails = () => setShowErrorDetails((prevState) => !prevState); - const { ediErrorCode, ediErrorDescription, ediErrorType } = paymentRequest; + const { + ediErrorCode, + ediErrorDescription, + ediErrorType, + tppsInvoiceAmountPaidTotalMillicents, + tppsInvoiceSellerPaidDate, + } = paymentRequest; const ediErrorsExistForPaymentRequest = ediErrorCode || ediErrorDescription || ediErrorType; - + const tppsDataExistsForPaymentRequest = tppsInvoiceAmountPaidTotalMillicents !== undefined; const showViewDocuments = uploads.length > 0 ? ViewDocuments : No documents provided; const tacs = { HHG: tac, NTS: ntsTac }; @@ -259,6 +266,36 @@ const PaymentRequestCard = ({ }; const renderPaymentRequestDetailsForStatus = (paymentRequestStatus) => { + if ( + (paymentRequestStatus === PAYMENT_REQUEST_STATUS.PAID || + paymentRequestStatus === PAYMENT_REQUEST_STATUS.EDI_ERROR) && + tppsInvoiceSellerPaidDate + ) { + return ( +
+ {tppsInvoiceAmountPaidTotalMillicents > 0 && ( +
+ +
+

{toDollarString(formatDollarFromMillicents(tppsInvoiceAmountPaidTotalMillicents))}

+ TPPS Paid + on {formatDateWithUTC(tppsInvoiceSellerPaidDate, 'DD MMM YYYY')} +
+
+ )} + {approvedAmount > 0 && ( +
+ +
+

{toDollarString(formatCents(approvedAmount))}

+ Accepted + on {formatDateFromIso(paymentRequest.reviewedAt, 'DD MMM YYYY')} +
+
+ )} +
+ ); + } if ( (paymentRequestStatus === PAYMENT_REQUEST_STATUS.TPPS_RECEIVED || paymentRequestStatus === PAYMENT_REQUEST_STATUS.EDI_ERROR) && @@ -428,6 +465,7 @@ const PaymentRequestCard = ({ tacs={tacs} sacs={sacs} onEditClick={onEditClick} + tppsDataExists={tppsDataExistsForPaymentRequest} /> ); })} diff --git a/src/components/Office/PaymentRequestDetails/PaymentRequestDetails.jsx b/src/components/Office/PaymentRequestDetails/PaymentRequestDetails.jsx index a97adfe6ba4..4dbd4e2f0b0 100644 --- a/src/components/Office/PaymentRequestDetails/PaymentRequestDetails.jsx +++ b/src/components/Office/PaymentRequestDetails/PaymentRequestDetails.jsx @@ -93,7 +93,15 @@ PaymentRequestAccountingCodes.defaultProps = { onEditClick: () => {}, }; -const PaymentRequestDetails = ({ serviceItems, shipment, paymentRequestStatus, tacs, sacs, onEditClick }) => { +const PaymentRequestDetails = ({ + serviceItems, + shipment, + paymentRequestStatus, + tacs, + sacs, + onEditClick, + tppsDataExists, +}) => { const mtoShipmentType = serviceItems?.[0]?.mtoShipmentType; const [headingType, shipmentStyle] = shipmentHeadingAndStyle(mtoShipmentType); const { modificationType, departureDate, address, mtoServiceItems } = shipment; @@ -138,34 +146,69 @@ const PaymentRequestDetails = ({ serviceItems, shipment, paymentRequestStatus, t

- - - - - - - - - - - - - - - {serviceItems.map((item, index) => { - return ( - - ); - })} - -
Service itemAmountStatus
+ {tppsDataExists === true ? ( + + + + + + + + + + + + + + + + + {serviceItems.map((item, index) => { + return ( + + ); + })} + +
Service itemAmountTPPS PaidStatus
+ ) : ( + + + + + + + + + + + + + + + {serviceItems.map((item, index) => { + return ( + + ); + })} + +
Service itemAmountStatus
+ )} ) ); From 984da8ee30b53ab8fd75aa4c2e87ea4f1dc726e4 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 19 Aug 2024 17:23:10 +0000 Subject: [PATCH 32/46] fix failing server tests --- .../ghcapi/internal/payloads/model_to_payload.go | 2 +- pkg/models/payment_request.go | 12 ++++++------ .../payment_request/payment_request_list_fetcher.go | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index 6c6d7014729..a6eaba6f19f 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -1516,7 +1516,7 @@ func PaymentRequest(appCtx appcontext.AppContext, pr *models.PaymentRequest, sto var tppsPaidInvoiceSellerPaidDate *time.Time var TPPSPaidInvoiceReportsForPR models.TPPSPaidInvoiceReportEntrys if pr.TPPSPaidInvoiceReports != nil { - TPPSPaidInvoiceReportsForPR = *pr.TPPSPaidInvoiceReports + TPPSPaidInvoiceReportsForPR = pr.TPPSPaidInvoiceReports if len(TPPSPaidInvoiceReportsForPR) > 0 { if TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents > -1 { totalTPPSPaidInvoicePriceMillicents = models.Int64Pointer(int64(TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents)) diff --git a/pkg/models/payment_request.go b/pkg/models/payment_request.go index 525c5ebb636..120f7a1d4fb 100644 --- a/pkg/models/payment_request.go +++ b/pkg/models/payment_request.go @@ -81,12 +81,12 @@ type PaymentRequest struct { RecalculationOfPaymentRequestID *uuid.UUID `json:"recalculation_of_payment_request_id" db:"recalculation_of_payment_request_id"` // Associations - MoveTaskOrder Move `belongs_to:"moves" fk_id:"move_id"` - PaymentServiceItems PaymentServiceItems `has_many:"payment_service_items" fk_id:"payment_request_id"` - ProofOfServiceDocs ProofOfServiceDocs `has_many:"proof_of_service_docs" fk_id:"payment_request_id"` - EdiErrors EdiErrors `has_many:"edi_errors" fk_id:"payment_request_id"` - RecalculationOfPaymentRequest *PaymentRequest `belongs_to:"payment_requests" fk_id:"recalculation_of_payment_request_id"` - TPPSPaidInvoiceReports *TPPSPaidInvoiceReportEntrys `has_many:"tpps_paid_invoice_reports" fk_id:"payment_request_number"` + MoveTaskOrder Move `belongs_to:"moves" fk_id:"move_id"` + PaymentServiceItems PaymentServiceItems `has_many:"payment_service_items" fk_id:"payment_request_id"` + ProofOfServiceDocs ProofOfServiceDocs `has_many:"proof_of_service_docs" fk_id:"payment_request_id"` + EdiErrors EdiErrors `has_many:"edi_errors" fk_id:"payment_request_id"` + RecalculationOfPaymentRequest *PaymentRequest `belongs_to:"payment_requests" fk_id:"recalculation_of_payment_request_id"` + TPPSPaidInvoiceReports TPPSPaidInvoiceReportEntrys `has_many:"tpps_paid_invoice_reports" fk_id:"payment_request_number"` } // TableName overrides the table name used by Pop. diff --git a/pkg/services/payment_request/payment_request_list_fetcher.go b/pkg/services/payment_request/payment_request_list_fetcher.go index 592a012368f..8c5c24375cf 100644 --- a/pkg/services/payment_request/payment_request_list_fetcher.go +++ b/pkg/services/payment_request/payment_request_list_fetcher.go @@ -203,7 +203,7 @@ func (f *paymentRequestListFetcher) FetchPaymentRequestListByMove(appCtx appcont } paymentRequests[i].EdiErrors = append(paymentRequests[i].EdiErrors, mostRecentEdiErrorForPaymentRequest) - paymentRequests[i].TPPSPaidInvoiceReports = &tppsReportEntryList + paymentRequests[i].TPPSPaidInvoiceReports = tppsReportEntryList } From 1e772c4690f3683a9e82d9baa9b219398a2b2615 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 19 Aug 2024 17:25:20 +0000 Subject: [PATCH 33/46] add rejected amount for TPPS paid and TPPS received statuses --- .../PaymentRequestCard/PaymentRequestCard.jsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx index a7a1c5fb591..0cd32774e98 100644 --- a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx +++ b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx @@ -293,6 +293,16 @@ const PaymentRequestCard = ({ )} + {rejectedAmount > 0 && ( +
+ +
+

{toDollarString(formatCents(rejectedAmount))}

+ Rejected + on {formatDateFromIso(paymentRequest.reviewedAt, 'DD MMM YYYY')} +
+
+ )} ); } @@ -313,6 +323,16 @@ const PaymentRequestCard = ({ )} + {rejectedAmount > 0 && ( +
+ +
+

{toDollarString(formatCents(rejectedAmount))}

+ Rejected + on {formatDateFromIso(paymentRequest.receivedByGexAt, 'DD MMM YYYY')} +
+
+ )} ); } From c0887d14a5f06888773fc454b953e1185d076f25 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 19 Aug 2024 18:31:03 +0000 Subject: [PATCH 34/46] add client tests for showing tpps info --- .../PaymentRequestCard/PaymentRequestCard.jsx | 8 +- .../PaymentRequestCard.test.jsx | 12 ++- .../PaymentRequestDetails.test.jsx | 87 +++++++++++++++++++ 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx index 0cd32774e98..b0cf942db32 100644 --- a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx +++ b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx @@ -272,11 +272,11 @@ const PaymentRequestCard = ({ tppsInvoiceSellerPaidDate ) { return ( -
+
{tppsInvoiceAmountPaidTotalMillicents > 0 && (
-
+

{toDollarString(formatDollarFromMillicents(tppsInvoiceAmountPaidTotalMillicents))}

TPPS Paid on {formatDateWithUTC(tppsInvoiceSellerPaidDate, 'DD MMM YYYY')} @@ -284,7 +284,7 @@ const PaymentRequestCard = ({
)} {approvedAmount > 0 && ( -
+

{toDollarString(formatCents(approvedAmount))}

@@ -294,7 +294,7 @@ const PaymentRequestCard = ({
)} {rejectedAmount > 0 && ( -
+

{toDollarString(formatCents(rejectedAmount))}

diff --git a/src/components/Office/PaymentRequestCard/PaymentRequestCard.test.jsx b/src/components/Office/PaymentRequestCard/PaymentRequestCard.test.jsx index 0aa5f0bb13d..04486d323c7 100644 --- a/src/components/Office/PaymentRequestCard/PaymentRequestCard.test.jsx +++ b/src/components/Office/PaymentRequestCard/PaymentRequestCard.test.jsx @@ -697,7 +697,7 @@ describe('PaymentRequestCard', () => { createdAt: '2020-12-01T00:00:00.000Z', mtoServiceItemID: 'f8c2f97f-99e7-4fb1-9cc4-473debd24dbc', priceCents: 2000001, - status: 'DENIED', + status: 'APPROVED', }, { id: '39474c6a-69b6-4501-8e08-670a12512a5f', @@ -708,6 +708,8 @@ describe('PaymentRequestCard', () => { rejectionReason: 'duplicate charge', }, ], + tppsInvoiceAmountPaidTotalMillicents: 115155000, + tppsInvoiceSellerPaidDate: '2024-07-30T00:00:00.000Z', }; const paid = mount( @@ -718,7 +720,13 @@ describe('PaymentRequestCard', () => { /> , ); - expect(paid.find({ 'data-testid': 'tag' }).contains('Paid')).toBe(true); + expect(paid.find({ 'data-testid': 'tag' }).contains('TPPS Paid')).toBe(true); + expect(paid.find({ 'data-testid': 'tppsPaidDetails' }).exists()).toBe(true); + expect(paid.find({ 'data-testid': 'tppsPaidDetailsDollarAmountTotal' }).exists()).toBe(true); + // displays the tpps paid sum, milmove accepted amount, and milmove rejected amount + expect(paid.find({ 'data-testid': 'tppsPaidDetailsDollarAmountTotal' }).contains('$1,151.55')).toBe(true); + expect(paid.find({ 'data-testid': 'milMoveAcceptedDetailsDollarAmountTotal' }).contains('$20,000.01')).toBe(true); + expect(paid.find({ 'data-testid': 'milMoveRejectedDetailsDollarAmountTotal' }).contains('$40,000.01')).toBe(true); }); }); diff --git a/src/components/Office/PaymentRequestDetails/PaymentRequestDetails.test.jsx b/src/components/Office/PaymentRequestDetails/PaymentRequestDetails.test.jsx index 2a078027150..367f36684d2 100644 --- a/src/components/Office/PaymentRequestDetails/PaymentRequestDetails.test.jsx +++ b/src/components/Office/PaymentRequestDetails/PaymentRequestDetails.test.jsx @@ -52,6 +52,7 @@ const hhgServiceItems = [ status: PAYMENT_SERVICE_ITEM_STATUS.REQUESTED, mtoShipmentType: SHIPMENT_OPTIONS.HHG, mtoServiceItemName: 'Domestic linehaul', + tppsInvoiceAmountPaidPerServiceItemMillicents: 100001000, }, { id: '09474c6a-69b6-4501-8e08-670a12512a5b', @@ -62,6 +63,7 @@ const hhgServiceItems = [ status: PAYMENT_SERVICE_ITEM_STATUS.REQUESTED, mtoShipmentType: SHIPMENT_OPTIONS.HHG, mtoServiceItemName: 'Fuel surcharge', + tppsInvoiceAmountPaidPerServiceItemMillicents: 200001000, }, { id: '09474c6a-69b6-4501-8e08-670a12512a5c', @@ -72,6 +74,7 @@ const hhgServiceItems = [ status: PAYMENT_SERVICE_ITEM_STATUS.APPROVED, mtoShipmentType: SHIPMENT_OPTIONS.HHG, mtoServiceItemName: 'Domestic origin price', + tppsInvoiceAmountPaidPerServiceItemMillicents: 300001000, }, { id: '09474c6a-69b6-4501-8e08-670a12512a5d', @@ -82,6 +85,7 @@ const hhgServiceItems = [ status: PAYMENT_SERVICE_ITEM_STATUS.APPROVED, mtoShipmentType: SHIPMENT_OPTIONS.HHG, mtoServiceItemName: 'Domestic destination price', + tppsInvoiceAmountPaidPerServiceItemMillicents: 400001000, }, { id: '09474c6a-69b6-4501-8e08-670a12512a5e', @@ -92,6 +96,7 @@ const hhgServiceItems = [ status: PAYMENT_SERVICE_ITEM_STATUS.DENIED, mtoShipmentType: SHIPMENT_OPTIONS.HHG, mtoServiceItemName: 'Domestic packing', + tppsInvoiceAmountPaidPerServiceItemMillicents: 500001000, }, { id: '09474c6a-69b6-4501-8e08-670a12512a5f', @@ -102,6 +107,7 @@ const hhgServiceItems = [ status: PAYMENT_SERVICE_ITEM_STATUS.DENIED, mtoShipmentType: SHIPMENT_OPTIONS.HHG, mtoServiceItemName: 'Domestic unpacking', + tppsInvoiceAmountPaidPerServiceItemMillicents: 600001000, }, ]; @@ -453,4 +459,85 @@ describe('PaymentRequestDetails', () => { expect(wrapper.find('ShipmentModificationTag').text()).toBe(shipmentModificationTypes.DIVERSION); }); }); + + describe('when a payment request has TPPS data that needs to be displayed ', () => { + const wrapper = mount( + + + , + ); + + it('renders the service item names', () => { + const serviceItemNames = wrapper.find({ 'data-testid': 'serviceItemName' }); + expect(serviceItemNames.at(0).text()).toEqual('Domestic linehaul'); + expect(serviceItemNames.at(1).text()).toEqual('Fuel surcharge'); + expect(serviceItemNames.at(2).text()).toEqual('Domestic origin price'); + expect(serviceItemNames.at(3).text()).toEqual('Domestic destination price'); + expect(serviceItemNames.at(4).text()).toEqual('Domestic packing'); + expect(serviceItemNames.at(5).text()).toEqual('Domestic unpacking'); + }); + + it('renders the service item amounts', () => { + const serviceItemAmounts = wrapper.find({ 'data-testid': 'serviceItemAmount' }); + expect(serviceItemAmounts.at(0).text()).toEqual('$1,000.01'); + expect(serviceItemAmounts.at(1).text()).toEqual('$2,000.01'); + expect(serviceItemAmounts.at(2).text()).toEqual('$3,000.01'); + expect(serviceItemAmounts.at(3).text()).toEqual('$4,000.01'); + expect(serviceItemAmounts.at(4).text()).toEqual('$5,000.01'); + expect(serviceItemAmounts.at(5).text()).toEqual('$6,000.01'); + }); + + it('renders the service item TPPS Paid amounts', () => { + const serviceItemTPPSPaidAmounts = wrapper.find({ 'data-testid': 'serviceItemTPPSPaidAmount' }); + expect(serviceItemTPPSPaidAmounts.at(0).text()).toEqual('$1,000.01'); + expect(serviceItemTPPSPaidAmounts.at(1).text()).toEqual('$2,000.01'); + expect(serviceItemTPPSPaidAmounts.at(2).text()).toEqual('$3,000.01'); + expect(serviceItemTPPSPaidAmounts.at(3).text()).toEqual('$4,000.01'); + expect(serviceItemTPPSPaidAmounts.at(4).text()).toEqual('$5,000.01'); + expect(serviceItemTPPSPaidAmounts.at(5).text()).toEqual('$6,000.01'); + }); + }); + + describe('does not render TPPS data when a payment request does not have TPPS data that needs to be displayed', () => { + const wrapper = mount( + + + , + ); + + it('renders the service item names', () => { + const serviceItemNames = wrapper.find({ 'data-testid': 'serviceItemName' }); + expect(serviceItemNames.at(0).text()).toEqual('Domestic linehaul'); + expect(serviceItemNames.at(1).text()).toEqual('Fuel surcharge'); + expect(serviceItemNames.at(2).text()).toEqual('Domestic origin price'); + expect(serviceItemNames.at(3).text()).toEqual('Domestic destination price'); + expect(serviceItemNames.at(4).text()).toEqual('Domestic packing'); + expect(serviceItemNames.at(5).text()).toEqual('Domestic unpacking'); + }); + + it('renders the service item amounts', () => { + const serviceItemAmounts = wrapper.find({ 'data-testid': 'serviceItemAmount' }); + expect(serviceItemAmounts.at(0).text()).toEqual('$1,000.01'); + expect(serviceItemAmounts.at(1).text()).toEqual('$2,000.01'); + expect(serviceItemAmounts.at(2).text()).toEqual('$3,000.01'); + expect(serviceItemAmounts.at(3).text()).toEqual('$4,000.01'); + expect(serviceItemAmounts.at(4).text()).toEqual('$5,000.01'); + expect(serviceItemAmounts.at(5).text()).toEqual('$6,000.01'); + }); + + it('does not render the service item TPPS Paid amounts', () => { + const serviceItemTPPSPaidAmounts = wrapper.find({ 'data-testid': 'serviceItemTPPSPaidAmount' }); + expect(serviceItemTPPSPaidAmounts.exists()).toBe(false); + }); + }); }); From 3feb96fd3bfe896d72d1c7b67840a0a2fe02832a Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Mon, 19 Aug 2024 18:50:47 +0000 Subject: [PATCH 35/46] reorder payment request details for tpps paid --- .../PaymentRequestCard/PaymentRequestCard.jsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx index b0cf942db32..8e1ed250eec 100644 --- a/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx +++ b/src/components/Office/PaymentRequestCard/PaymentRequestCard.jsx @@ -273,16 +273,6 @@ const PaymentRequestCard = ({ ) { return (
- {tppsInvoiceAmountPaidTotalMillicents > 0 && ( -
- -
-

{toDollarString(formatDollarFromMillicents(tppsInvoiceAmountPaidTotalMillicents))}

- TPPS Paid - on {formatDateWithUTC(tppsInvoiceSellerPaidDate, 'DD MMM YYYY')} -
-
- )} {approvedAmount > 0 && (
@@ -303,6 +293,16 @@ const PaymentRequestCard = ({
)} + {tppsInvoiceAmountPaidTotalMillicents > 0 && ( +
+ +
+

{toDollarString(formatDollarFromMillicents(tppsInvoiceAmountPaidTotalMillicents))}

+ TPPS Paid + on {formatDateWithUTC(tppsInvoiceSellerPaidDate, 'DD MMM YYYY')} +
+
+ )}
); } From 37e71f8c3b9233fdfb8771c9301332a68dba5280 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Tue, 20 Aug 2024 23:11:29 +0000 Subject: [PATCH 36/46] add parsing test for encoded version of file, fix a few issues in parser --- pkg/edi/tpps_paid_invoice_report/parser.go | 1 + ...nvoice_report_testfile_tpps_pickup_dir.csv | Bin 0 -> 2976 bytes .../process_tpps_paid_invoice_report.go | 11 +- .../process_tpps_paid_invoice_report_test.go | 294 ++++++++++++++++++ 4 files changed, 301 insertions(+), 5 deletions(-) create mode 100644 pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile_tpps_pickup_dir.csv diff --git a/pkg/edi/tpps_paid_invoice_report/parser.go b/pkg/edi/tpps_paid_invoice_report/parser.go index a2806236d1f..c4cb9d6ef77 100644 --- a/pkg/edi/tpps_paid_invoice_report/parser.go +++ b/pkg/edi/tpps_paid_invoice_report/parser.go @@ -68,6 +68,7 @@ func ParseTPPSReportEntryForOneRow(row []string, columnIndexes map[string]int, h } } processedEntry = strings.TrimSpace(processedEntry) + processedEntry = strings.TrimLeft(processedEntry, "�") // After we have fully processed an entry and have built a string, store it processedTPPSReportEntryForOnePaymentRequest = append(processedTPPSReportEntryForOnePaymentRequest, processedEntry) } diff --git a/pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile_tpps_pickup_dir.csv b/pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile_tpps_pickup_dir.csv new file mode 100644 index 0000000000000000000000000000000000000000..40a601c5cb11ab181385d66fb1d31601c6f7f820 GIT binary patch literal 2976 zcmd6pZ*JN^5XI-KdkQW91!H6QTCAv56N=R20AL_NN=X&c-Y&|Ka)tihE-o>wQxJ_5 zt>T^Coj>o*%r1F;dVGF*{9-HnZFjb`!sgbnk^QnCHbuT?b;kFOb;GK9k>%_cPIr!n z2Q-H0&TWD$Klw(A>^tjQhW9vTe`&LdM%8!2F7f?<-mUX`gLH4#*sm?JbF5dJ2-+z) zTiQ?bmsqXP7(2`}uoVth?kxB9ww^v=D}?fVP0TCiCAQUg~E z`DPzsG$gZC?QNbT^1KA~+S!jBd*yJ5G{ft^&Xw(OF2a75-#VWc&eNKlE91U;h0CmJ z7Q&|bzNS%+uFlfqt2^CAcxg8-dv*9;Kd(m@w)5D+XH{e$tYdA<7)?u@G(hT-)0kDv zUX!`+zG>}lf@a5B=w;~S^yb*URwPB~x(E?c%dI-tMC^Lo)Y*p66x4Z2kM+>aoJ~xX z4;c~uL*s2&=dc!gKkqYc+u$!p{u@tu&Txnr8y0^ZU(gl!|pRs6+Q^;8{GQ1n)V$|<(0?**@A`BeqW{c;Y)niG9N6;m*%j{k#GA75=)G-0C-?mFqX&Xr|?A9Z8` zs+^b5SJBVnC*L}ccjQp~1zJ-^f=$Q0O{do`6h~N&xuN?!%Ny!v@rVm{{sFtYNBjT) literal 0 HcmV?d00001 diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index 9f7f0bdc94a..4ffcc13dfe1 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -89,21 +89,22 @@ func getPriceParts(rawPrice string) (int, int, int, error) { // Split the string on the decimal point. priceParts := strings.Split(basePrice, ".") - if len(priceParts) != 2 { - return 0, 0, 0, fmt.Errorf("expected 2 price parts but found %d for price [%s]", len(priceParts), rawPrice) - } integerPart, err := strconv.Atoi(priceParts[0]) if err != nil { return 0, 0, 0, fmt.Errorf("could not convert integer part of price [%s]", rawPrice) } + decimalsExistOnDollarAmount := len(priceParts) == 2 expectedDecimalPlaces := 0 - if len(priceParts[1]) > 0 { + if decimalsExistOnDollarAmount && len(priceParts[1]) > 0 { expectedDecimalPlaces = len(priceParts[1]) } - fractionalPart, err := strconv.Atoi(priceParts[1]) + fractionalPart := 0 + if decimalsExistOnDollarAmount { + fractionalPart, err = strconv.Atoi(priceParts[1]) + } if err != nil { return 0, 0, 0, fmt.Errorf("could not convert fractional part of price [%s]", rawPrice) } diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index 0835d93f23c..1e91f017e74 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/testingsuite" "github.com/transcom/mymove/pkg/unit" @@ -29,6 +30,49 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport tppsPaidInvoiceReportProcessor := NewTPPSPaidInvoiceReportProcessor() suite.Run("successfully proccesses a valid TPPSPaidInvoiceReport and stores it in the database", func() { + // payment requests 1-4 with a payment request numbers of 1841-7267-3 and 9436-4123-3, + // must exist because the TPPS invoice report's invoice number references the payment + // request payment_request_number as a foreign key + paymentRequestOne := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusPaid, + PaymentRequestNumber: "1841-7267-3", + }, + }, + }, nil) + factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequestToInterchangeControlNumber{ + InterchangeControlNumber: 100001251, + EDIType: models.EDIType858, + }, + }, + { + Model: paymentRequestOne, + LinkOnly: true, + }, + }, nil) + paymentRequestTwo := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusPaid, + PaymentRequestNumber: "9436-4123-3", + }, + }, + }, nil) + factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequestToInterchangeControlNumber{ + InterchangeControlNumber: 100001251, + EDIType: models.EDIType858, + }, + }, + { + Model: paymentRequestTwo, + LinkOnly: true, + }, + }, nil) testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile.csv" @@ -144,6 +188,256 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport } }) + suite.Run("successfully proccesses a TPPSPaidInvoiceReport from a file directly from the TPPS pickup directory and stores it in the database", func() { + // payment requests 1-4 with a payment request numbers of 1841-7267-3, 1208-5962-1, + // 8801-2773-2, and 8801-2773-3 must exist because the TPPS invoice report's invoice + // number references the payment request payment_request_number as a foreign key + paymentRequestOne := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusPaid, + PaymentRequestNumber: "1077-4079-3", + }, + }, + }, nil) + factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequestToInterchangeControlNumber{ + InterchangeControlNumber: 100001251, + EDIType: models.EDIType858, + }, + }, + { + Model: paymentRequestOne, + LinkOnly: true, + }, + }, nil) + paymentRequestTwo := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusPaid, + PaymentRequestNumber: "1208-5962-1", + }, + }, + }, nil) + factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequestToInterchangeControlNumber{ + InterchangeControlNumber: 100001251, + EDIType: models.EDIType858, + }, + }, + { + Model: paymentRequestTwo, + LinkOnly: true, + }, + }, nil) + paymentRequestThree := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusPaid, + PaymentRequestNumber: "8801-2773-2", + }, + }, + }, nil) + factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequestToInterchangeControlNumber{ + InterchangeControlNumber: 100001251, + EDIType: models.EDIType858, + }, + }, + { + Model: paymentRequestThree, + LinkOnly: true, + }, + }, nil) + paymentRequestFour := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusPaid, + PaymentRequestNumber: "8801-2773-3", + }, + }, + }, nil) + factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequestToInterchangeControlNumber{ + InterchangeControlNumber: 100001251, + EDIType: models.EDIType858, + }, + }, + { + Model: paymentRequestFour, + LinkOnly: true, + }, + }, nil) + + testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile_tpps_pickup_dir.csv" + // The above test file is formatted exactly as it appears in the TPPS pickup directory, encoded. + // Below is a comment which shows the decoded version of the file, for information and to see expected values for testing + // Invoice Number From Invoice Document Create Date Seller Paid Date Invoice Total Charges Line Description Product Description Line Billing Units Line Unit Price Line Net Charge PO/TCN Line Number First Note Code First Note Code Description First Note To First Note Message Second Note Code Second Note Code Description Second Note To Second Note Message Third Note Code Third Note Code Description Third Note To Third Note Message + // 1077-4079-3 2024-08-05 2024-08-05 421.87 DUPK DUPK 10340 0.0311 321.57 1077-4079-cabd6371 2 + // 1077-4079-3 2024-08-05 2024-08-05 421.87 DDP DDP 10340 0.0097 100.3 1077-4079-a4e717fd 1 + // 1208-5962-1 2024-08-05 2024-08-05 557 MS MS 1 557 557 1208-5962-e0fb5863 1 + // 8801-2773-2 2024-08-05 2024-08-05 2748.04 DOP DOP 1 77.02 77.02 8801-2773-f2bb471e 1 + // 8801-2773-2 2024-08-05 2024-08-05 2748.04 DPK DPK 1 2671.02 2671.02 8801-2773-fdaee177 2 + // 8801-2773-3 2024-08-05 2024-08-05 1397.74 DDP DDP 1 91.31 91.31 8801-2773-2e54e07d 2 + // 8801-2773-3 2024-08-05 2024-08-05 1397.74 DLH DLH 1 1052.84 1052.84 8801-2773-27961d7f 1 + // 8801-2773-3 2024-08-05 2024-08-05 1397.74 FSC FSC 1 6.66 6.66 8801-2773-f9e0672c 3 + // 8801-2773-3 2024-08-05 2024-08-05 1397.74 DUPK DUPK 1 246.93 246.93 8801-2773-c6c78cf9 4 + // (file ends in a empty line) + + err := tppsPaidInvoiceReportProcessor.ProcessFile(suite.AppContextForTest(), testTPPSPaidInvoiceReportFilePath, "") + suite.NoError(err) + + tppsEntries := []models.TPPSPaidInvoiceReportEntry{} + err = suite.DB().All(&tppsEntries) + suite.NoError(err) + suite.Equal(len(tppsEntries), 9) + for tppsEntryIndex := range tppsEntries { + + if tppsEntryIndex == 0 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1077-4079-3") + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(42187000)) // 421.87 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DUPK") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DUPK") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 10340) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(3110)) // 0.0311 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(32157000)) // 321.57 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1077-4079-cabd6371") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "2") + } + if tppsEntryIndex == 1 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1077-4079-3") + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(42187000)) // 421.87 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 10340) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(970)) // 0.0097 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(10030000)) // 100.3 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1077-4079-a4e717fd") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") + } + if tppsEntryIndex == 2 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "1208-5962-1") + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(55700000)) // 557 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "MS") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "MS") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 1) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(55700000)) // 557 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(55700000)) // 557 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "1208-5962-e0fb5863") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") + } + if tppsEntryIndex == 3 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "8801-2773-2") + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(274804000)) // 2748.04 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DOP") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DOP") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 1) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(7702000)) // 77.02 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(7702000)) // 77.02 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "8801-2773-f2bb471e") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") + + } + if tppsEntryIndex == 4 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "8801-2773-2") + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(274804000)) // 2748.04 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DPK") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DPK") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 1) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(267102000)) // 2671.02 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(267102000)) // 2671.02 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "8801-2773-fdaee177") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "2") + + } + if tppsEntryIndex == 5 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "8801-2773-3") + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(139774000)) // 1397.74 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DDP") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 1) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(9131000)) // 91.31 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(9131000)) // 91.31 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "8801-2773-2e54e07d") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "2") + + } + if tppsEntryIndex == 6 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "8801-2773-3") + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(139774000)) // 1397.74 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DLH") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DLH") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 1) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(105284000)) // 1052.84 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(105284000)) // 1052.84 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "8801-2773-27961d7f") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "1") + + } + if tppsEntryIndex == 7 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "8801-2773-3") + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(139774000)) // 1397.74 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "FSC") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "FSC") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 1) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(666000)) // 6.66 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(666000)) // 6.66 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "8801-2773-f9e0672c") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "3") + + } + if tppsEntryIndex == 8 { + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceNumber, "8801-2773-3") + suite.Equal(*tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].SellerPaidDate, time.Date(2024, time.August, 5, 0, 0, 0, 0, tppsEntries[tppsEntryIndex].TPPSCreatedDocumentDate.Location())) + suite.Equal(tppsEntries[tppsEntryIndex].InvoiceTotalChargesInMillicents, unit.Millicents(139774000)) // 1397.74 + suite.Equal(tppsEntries[tppsEntryIndex].LineDescription, "DUPK") + suite.Equal(tppsEntries[tppsEntryIndex].ProductDescription, "DUPK") + suite.Equal(tppsEntries[tppsEntryIndex].LineBillingUnits, 1) + suite.Equal(tppsEntries[tppsEntryIndex].LineUnitPrice, unit.Millicents(24693000)) // 246.93 + suite.Equal(tppsEntries[tppsEntryIndex].LineNetCharge, unit.Millicents(24693000)) // 246.93 + suite.Equal(tppsEntries[tppsEntryIndex].POTCN, "8801-2773-c6c78cf9") + suite.Equal(tppsEntries[tppsEntryIndex].LineNumber, "4") + + } + suite.NotNil(tppsEntries[tppsEntryIndex].ID) + suite.NotNil(tppsEntries[tppsEntryIndex].CreatedAt) + suite.NotNil(tppsEntries[tppsEntryIndex].UpdatedAt) + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCode, "") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteDescription, "") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeTo, "") + suite.Equal(*tppsEntries[tppsEntryIndex].FirstNoteCodeMessage, "") + suite.Equal(*tppsEntries[tppsEntryIndex].SecondNoteCode, "") + suite.Equal(*tppsEntries[tppsEntryIndex].SecondNoteDescription, "") + suite.Equal(*tppsEntries[tppsEntryIndex].SecondNoteCodeTo, "") + suite.Equal(*tppsEntries[tppsEntryIndex].SecondNoteCodeMessage, "") + suite.Equal(*tppsEntries[tppsEntryIndex].ThirdNoteCode, "") + suite.Equal(*tppsEntries[tppsEntryIndex].ThirdNoteDescription, "") + suite.Equal(*tppsEntries[tppsEntryIndex].ThirdNoteCodeTo, "") + suite.Equal(*tppsEntries[tppsEntryIndex].ThirdNoteCodeMessage, "") + } + }) + suite.Run("error opening filepath returns descriptive error for failing to parse TPPS paid invoice report", func() { // given a path to a nonexistent file testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/AFileThatDoesNotExist.csv" From 3829afb0c7df34151db0eabf038edb95975cc911 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Wed, 21 Aug 2024 15:15:27 +0000 Subject: [PATCH 37/46] change negative one to zero --- pkg/handlers/ghcapi/internal/payloads/model_to_payload.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index 9524abb877e..bf4fe0bcc53 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -1521,7 +1521,7 @@ func PaymentRequest(appCtx appcontext.AppContext, pr *models.PaymentRequest, sto if pr.TPPSPaidInvoiceReports != nil { TPPSPaidInvoiceReportsForPR = pr.TPPSPaidInvoiceReports if len(TPPSPaidInvoiceReportsForPR) > 0 { - if TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents > -1 { + if TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents >= 0 { totalTPPSPaidInvoicePriceMillicents = models.Int64Pointer(int64(TPPSPaidInvoiceReportsForPR[0].InvoiceTotalChargesInMillicents)) tppsPaidInvoiceSellerPaidDate = &TPPSPaidInvoiceReportsForPR[0].SellerPaidDate } From 770931e717125c4aa060c71e21d37fe94551d063 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Wed, 21 Aug 2024 15:17:33 +0000 Subject: [PATCH 38/46] add nil check on tppsPaidReportData --- .../ghcapi/internal/payloads/model_to_payload.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index bf4fe0bcc53..9c9eb68a57a 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -1583,10 +1583,12 @@ func PaymentServiceItems(paymentServiceItems *models.PaymentServiceItems, tppsPa copyOfPaymentServiceItem := m // Make copy to avoid implicit memory aliasing of items from a range statement. payload[i] = PaymentServiceItem(©OfPaymentServiceItem) - tppsDataForPaymentRequest := *tppsPaidReportData - for tppsDataRowIndex := range tppsDataForPaymentRequest { - if tppsDataForPaymentRequest[tppsDataRowIndex].ProductDescription == payload[i].MtoServiceItemCode { - payload[i].TppsInvoiceAmountPaidPerServiceItemMillicents = handlers.FmtMilliCentsPtr(&tppsDataForPaymentRequest[tppsDataRowIndex].LineNetCharge) + if *tppsPaidReportData != nil { + tppsDataForPaymentRequest := *tppsPaidReportData + for tppsDataRowIndex := range tppsDataForPaymentRequest { + if tppsDataForPaymentRequest[tppsDataRowIndex].ProductDescription == payload[i].MtoServiceItemCode { + payload[i].TppsInvoiceAmountPaidPerServiceItemMillicents = handlers.FmtMilliCentsPtr(&tppsDataForPaymentRequest[tppsDataRowIndex].LineNetCharge) + } } } } From fd6db799bc77b67a62bc25a176acf9b932dc0ffe Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Wed, 21 Aug 2024 16:06:03 +0000 Subject: [PATCH 39/46] add comments to explain TPPS invoice relationship to PR --- pkg/handlers/ghcapi/internal/payloads/model_to_payload.go | 2 ++ pkg/services/payment_request/payment_request_list_fetcher.go | 3 +++ 2 files changed, 5 insertions(+) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index 9c9eb68a57a..662d3656adb 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -1583,6 +1583,8 @@ func PaymentServiceItems(paymentServiceItems *models.PaymentServiceItems, tppsPa copyOfPaymentServiceItem := m // Make copy to avoid implicit memory aliasing of items from a range statement. payload[i] = PaymentServiceItem(©OfPaymentServiceItem) + // We process TPPS Paid Invoice Reports to get payment information for each payment service item + // This report tells us how much TPPS paid HS for each item, then we store and display it if *tppsPaidReportData != nil { tppsDataForPaymentRequest := *tppsPaidReportData for tppsDataRowIndex := range tppsDataForPaymentRequest { diff --git a/pkg/services/payment_request/payment_request_list_fetcher.go b/pkg/services/payment_request/payment_request_list_fetcher.go index 8c5c24375cf..6071c11a9cc 100644 --- a/pkg/services/payment_request/payment_request_list_fetcher.go +++ b/pkg/services/payment_request/payment_request_list_fetcher.go @@ -197,6 +197,9 @@ func (f *paymentRequestListFetcher) FetchPaymentRequestListByMove(appCtx appcont return nil, errFetchingEdiError } + // We process TPPS Paid Invoice Reports to get payment information for each payment service item + // As well as the total amount paid for the overall payment request, and the date it was paid + // This report tells us how much TPPS paid HS, then we store and display it tppsReportEntryList, errFetchingTPPSInformation := fetchTPPSPaidInvoiceReportDataPaymentRequest(appCtx, &paymentRequests[i]) if errFetchingTPPSInformation != nil { return nil, errFetchingTPPSInformation From b8da51cfdcd61c1810f9798e80afff55aad694e5 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Thu, 22 Aug 2024 16:34:46 +0000 Subject: [PATCH 40/46] fix typo on processes --- pkg/services/invoice/process_edi824_test.go | 2 +- pkg/services/invoice/process_tpps_paid_invoice_report_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/services/invoice/process_edi824_test.go b/pkg/services/invoice/process_edi824_test.go index a1c8e8da876..6f544c7f2eb 100644 --- a/pkg/services/invoice/process_edi824_test.go +++ b/pkg/services/invoice/process_edi824_test.go @@ -30,7 +30,7 @@ func TestProcessEDI824Suite(t *testing.T) { func (suite *ProcessEDI824Suite) TestParsingEDI824() { edi824Processor := NewEDI824Processor() - suite.Run("successfully proccesses a valid EDI824", func() { + suite.Run("successfully processes a valid EDI824", func() { paymentRequest := factory.BuildPaymentRequest(suite.DB(), nil, nil) sample824EDIString := fmt.Sprintf(` ISA*00*0084182369*00*0000000000*ZZ*MILMOVE *12*8004171844 *201002*1504*U*00401*00000995*0*T*| diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index 1e91f017e74..5dbd5c122dd 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -29,7 +29,7 @@ func TestProcessTPPSPaidInvoiceReportSuite(t *testing.T) { func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport() { tppsPaidInvoiceReportProcessor := NewTPPSPaidInvoiceReportProcessor() - suite.Run("successfully proccesses a valid TPPSPaidInvoiceReport and stores it in the database", func() { + suite.Run("successfully processes a valid TPPSPaidInvoiceReport and stores it in the database", func() { // payment requests 1-4 with a payment request numbers of 1841-7267-3 and 9436-4123-3, // must exist because the TPPS invoice report's invoice number references the payment // request payment_request_number as a foreign key @@ -188,7 +188,7 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport } }) - suite.Run("successfully proccesses a TPPSPaidInvoiceReport from a file directly from the TPPS pickup directory and stores it in the database", func() { + suite.Run("successfully processes a TPPSPaidInvoiceReport from a file directly from the TPPS pickup directory and stores it in the database", func() { // payment requests 1-4 with a payment request numbers of 1841-7267-3, 1208-5962-1, // 8801-2773-2, and 8801-2773-3 must exist because the TPPS invoice report's invoice // number references the payment request payment_request_number as a foreign key From 2d6a6ddfba2aeb335ccce809e499ad7ab653b3a6 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 23 Aug 2024 15:47:17 +0000 Subject: [PATCH 41/46] update PR status to PAID, expand tests --- .../process_tpps_paid_invoice_report.go | 56 ++++- .../process_tpps_paid_invoice_report_test.go | 209 +++++++++++------- 2 files changed, 186 insertions(+), 79 deletions(-) diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index 4ffcc13dfe1..ed691bd65ac 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -70,10 +70,54 @@ func (t *tppsPaidInvoiceReportProcessor) ProcessFile(appCtx appcontext.AppContex verrs, errs := t.StoreTPPSPaidInvoiceReportInDatabase(appCtx, tppsData) if err != nil { return errs - } - if verrs.HasAny() { + } else if verrs.HasAny() { return verrs + } else { + appCtx.Logger().Info("Successfully stored TPPS Paid Invoice Report information in the database") + } + + transactionError := appCtx.NewTransaction(func(txnAppCtx appcontext.AppContext) error { + var paymentRequestWithStatusUpdatedToPaid = map[string]string{} + + // For the data in the TPPS Paid Invoice Report, find the payment requests that match the + // invoice numbers of the rows in the report and update the payment request status to PAID + for _, tppsDataForOnePaymentRequest := range tppsData { + var paymentRequest models.PaymentRequest + + err = txnAppCtx.DB().Q(). + Where("payment_requests.payment_request_number = ?", tppsDataForOnePaymentRequest.InvoiceNumber). + First(&paymentRequest) + + if err != nil { + return err + } + + // Since there can be many rows in a TPPS report that reference the same payment request, we want + // to keep track of which payment requests we've already updated the status to PAID for and + // only update it's status once, using a map to keep track of already updated payment requests + _, paymentRequestExistsInUpdatedStatusMap := paymentRequestWithStatusUpdatedToPaid[paymentRequest.ID.String()] + if !paymentRequestExistsInUpdatedStatusMap { + paymentRequest.Status = models.PaymentRequestStatusPaid + err = txnAppCtx.DB().Update(&paymentRequest) + if err != nil { + txnAppCtx.Logger().Error("failure updating payment request to PAID", zap.Error(err)) + return fmt.Errorf("failure updating payment request status to PAID: %w", err) + } + + txnAppCtx.Logger().Info("SUCCESS: TPPS Paid Invoice Report Processor updated Payment Request to PAID status") + t.logTPPSInvoiceReportWithPaymentRequest(txnAppCtx, tppsDataForOnePaymentRequest, paymentRequest) + + paymentRequestWithStatusUpdatedToPaid[paymentRequest.ID.String()] = paymentRequest.PaymentRequestNumber + } + } + return nil + }) + + if transactionError != nil { + appCtx.Logger().Error(transactionError.Error()) + return transactionError } + return nil } return nil @@ -83,6 +127,14 @@ func (t *tppsPaidInvoiceReportProcessor) EDIType() models.EDIType { return models.TPPSPaidInvoiceReport } +func (t *tppsPaidInvoiceReportProcessor) logTPPSInvoiceReportWithPaymentRequest(appCtx appcontext.AppContext, tppsResponse tppsReponse.TPPSData, paymentRequest models.PaymentRequest) { + appCtx.Logger().Info("TPPS Paid Invoice Report log", + zap.String("PaymentRequestNumber", paymentRequest.PaymentRequestNumber), + zap.String("PaymentRequest.Status", string(paymentRequest.Status)), + zap.String("TPPSPaidInvoiceReportEntry.InvoiceNumber", tppsResponse.InvoiceNumber), + ) +} + func getPriceParts(rawPrice string) (int, int, int, error) { // Get rid of a dollar sign if there is one. basePrice := strings.Replace(rawPrice, "$", "", -1) diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go index 5dbd5c122dd..eb074b672a9 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report_test.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report_test.go @@ -30,7 +30,7 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport tppsPaidInvoiceReportProcessor := NewTPPSPaidInvoiceReportProcessor() suite.Run("successfully processes a valid TPPSPaidInvoiceReport and stores it in the database", func() { - // payment requests 1-4 with a payment request numbers of 1841-7267-3 and 9436-4123-3, + // payment requests with payment request numbers of 1841-7267-3 and 9436-4123-3 // must exist because the TPPS invoice report's invoice number references the payment // request payment_request_number as a foreign key paymentRequestOne := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ @@ -41,18 +41,7 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport }, }, }, nil) - factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ - { - Model: models.PaymentRequestToInterchangeControlNumber{ - InterchangeControlNumber: 100001251, - EDIType: models.EDIType858, - }, - }, - { - Model: paymentRequestOne, - LinkOnly: true, - }, - }, nil) + suite.NotNil(paymentRequestOne) paymentRequestTwo := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ { Model: models.PaymentRequest{ @@ -61,18 +50,7 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport }, }, }, nil) - factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ - { - Model: models.PaymentRequestToInterchangeControlNumber{ - InterchangeControlNumber: 100001251, - EDIType: models.EDIType858, - }, - }, - { - Model: paymentRequestTwo, - LinkOnly: true, - }, - }, nil) + suite.NotNil(paymentRequestTwo) testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile.csv" @@ -83,6 +61,17 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport err = suite.DB().All(&tppsEntries) suite.NoError(err) suite.Equal(len(tppsEntries), 5) + + // find the paymentRequests and verify that they have all been updated to have a status of PAID after processing the report + paymentRequests := []models.PaymentRequest{} + err = suite.DB().All(&paymentRequests) + suite.NoError(err) + suite.Equal(len(paymentRequests), 2) + + for _, paymentRequest := range paymentRequests { + suite.Equal(models.PaymentRequestStatusPaid, paymentRequest.Status) + } + for tppsEntryIndex := range tppsEntries { if tppsEntryIndex == 0 { @@ -195,83 +184,39 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport paymentRequestOne := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ { Model: models.PaymentRequest{ - Status: models.PaymentRequestStatusPaid, + Status: models.PaymentRequestStatusEDIError, PaymentRequestNumber: "1077-4079-3", }, }, }, nil) - factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ - { - Model: models.PaymentRequestToInterchangeControlNumber{ - InterchangeControlNumber: 100001251, - EDIType: models.EDIType858, - }, - }, - { - Model: paymentRequestOne, - LinkOnly: true, - }, - }, nil) + suite.NotNil(paymentRequestOne) paymentRequestTwo := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ { Model: models.PaymentRequest{ - Status: models.PaymentRequestStatusPaid, + Status: models.PaymentRequestStatusReviewed, PaymentRequestNumber: "1208-5962-1", }, }, }, nil) - factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ - { - Model: models.PaymentRequestToInterchangeControlNumber{ - InterchangeControlNumber: 100001251, - EDIType: models.EDIType858, - }, - }, - { - Model: paymentRequestTwo, - LinkOnly: true, - }, - }, nil) + suite.NotNil(paymentRequestTwo) paymentRequestThree := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ { Model: models.PaymentRequest{ - Status: models.PaymentRequestStatusPaid, + Status: models.PaymentRequestStatusSentToGex, PaymentRequestNumber: "8801-2773-2", }, }, }, nil) - factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ - { - Model: models.PaymentRequestToInterchangeControlNumber{ - InterchangeControlNumber: 100001251, - EDIType: models.EDIType858, - }, - }, - { - Model: paymentRequestThree, - LinkOnly: true, - }, - }, nil) + suite.NotNil(paymentRequestThree) paymentRequestFour := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ { Model: models.PaymentRequest{ - Status: models.PaymentRequestStatusPaid, + Status: models.PaymentRequestStatusTppsReceived, PaymentRequestNumber: "8801-2773-3", }, }, }, nil) - factory.BuildPaymentRequestToInterchangeControlNumber(suite.DB(), []factory.Customization{ - { - Model: models.PaymentRequestToInterchangeControlNumber{ - InterchangeControlNumber: 100001251, - EDIType: models.EDIType858, - }, - }, - { - Model: paymentRequestFour, - LinkOnly: true, - }, - }, nil) + suite.NotNil(paymentRequestFour) testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile_tpps_pickup_dir.csv" // The above test file is formatted exactly as it appears in the TPPS pickup directory, encoded. @@ -295,6 +240,17 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport err = suite.DB().All(&tppsEntries) suite.NoError(err) suite.Equal(len(tppsEntries), 9) + + // find the paymentRequests and verify that they have all been updated to have a status of PAID after processing the report + paymentRequests := []models.PaymentRequest{} + err = suite.DB().All(&paymentRequests) + suite.NoError(err) + suite.Equal(len(paymentRequests), 4) + + for _, paymentRequest := range paymentRequests { + suite.Equal(models.PaymentRequestStatusPaid, paymentRequest.Status) + } + for tppsEntryIndex := range tppsEntries { if tppsEntryIndex == 0 { @@ -438,6 +394,105 @@ func (suite *ProcessTPPSPaidInvoiceReportSuite) TestParsingTPPSPaidInvoiceReport } }) + suite.Run("successfully updates payment request status to PAID when processing a TPPS Invoice Report", func() { + // payment requests 1-4 with a payment request numbers of 1841-7267-3, 1208-5962-1, + // 8801-2773-2, and 8801-2773-3 must exist because the TPPS invoice report's invoice + // number references the payment request payment_request_number as a foreign key + paymentRequestOne := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusEDIError, + PaymentRequestNumber: "1077-4079-3", + }, + }, + }, nil) + suite.NotNil(paymentRequestOne) + paymentRequestTwo := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusReviewed, + PaymentRequestNumber: "1208-5962-1", + }, + }, + }, nil) + suite.NotNil(paymentRequestTwo) + paymentRequestThree := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusSentToGex, + PaymentRequestNumber: "8801-2773-2", + }, + }, + }, nil) + suite.NotNil(paymentRequestThree) + paymentRequestFour := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: models.PaymentRequest{ + Status: models.PaymentRequestStatusTppsReceived, + PaymentRequestNumber: "8801-2773-3", + }, + }, + }, nil) + suite.NotNil(paymentRequestFour) + + testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile_tpps_pickup_dir.csv" + // The above test file is formatted exactly as it appears in the TPPS pickup directory, encoded. + // Below is a comment which shows the decoded version of the file, for information and to see expected values for testing + // Invoice Number From Invoice Document Create Date Seller Paid Date Invoice Total Charges Line Description Product Description Line Billing Units Line Unit Price Line Net Charge PO/TCN Line Number First Note Code First Note Code Description First Note To First Note Message Second Note Code Second Note Code Description Second Note To Second Note Message Third Note Code Third Note Code Description Third Note To Third Note Message + // 1077-4079-3 2024-08-05 2024-08-05 421.87 DUPK DUPK 10340 0.0311 321.57 1077-4079-cabd6371 2 + // 1077-4079-3 2024-08-05 2024-08-05 421.87 DDP DDP 10340 0.0097 100.3 1077-4079-a4e717fd 1 + // 1208-5962-1 2024-08-05 2024-08-05 557 MS MS 1 557 557 1208-5962-e0fb5863 1 + // 8801-2773-2 2024-08-05 2024-08-05 2748.04 DOP DOP 1 77.02 77.02 8801-2773-f2bb471e 1 + // 8801-2773-2 2024-08-05 2024-08-05 2748.04 DPK DPK 1 2671.02 2671.02 8801-2773-fdaee177 2 + // 8801-2773-3 2024-08-05 2024-08-05 1397.74 DDP DDP 1 91.31 91.31 8801-2773-2e54e07d 2 + // 8801-2773-3 2024-08-05 2024-08-05 1397.74 DLH DLH 1 1052.84 1052.84 8801-2773-27961d7f 1 + // 8801-2773-3 2024-08-05 2024-08-05 1397.74 FSC FSC 1 6.66 6.66 8801-2773-f9e0672c 3 + // 8801-2773-3 2024-08-05 2024-08-05 1397.74 DUPK DUPK 1 246.93 246.93 8801-2773-c6c78cf9 4 + // (file ends in a empty line) + + err := tppsPaidInvoiceReportProcessor.ProcessFile(suite.AppContextForTest(), testTPPSPaidInvoiceReportFilePath, "") + suite.NoError(err) + + tppsEntries := []models.TPPSPaidInvoiceReportEntry{} + err = suite.DB().All(&tppsEntries) + suite.NoError(err) + suite.Equal(len(tppsEntries), 9) + + // find the paymentRequests and verify that they have all been updated to have a status of PAID after processing the report + paymentRequests := []models.PaymentRequest{} + err = suite.DB().All(&paymentRequests) + suite.NoError(err) + suite.Equal(len(paymentRequests), 4) + + // verify that all of the payment requests now have a status of PAID + for _, updatedStatusPaymentRequest := range paymentRequests { + if updatedStatusPaymentRequest.PaymentRequestNumber == "1077-4079-3" { + previousStatusPaymentRequest := paymentRequestOne + suite.Equal(previousStatusPaymentRequest.PaymentRequestNumber, updatedStatusPaymentRequest.PaymentRequestNumber) + suite.Equal(models.PaymentRequestStatusEDIError, previousStatusPaymentRequest.Status) + suite.Equal(models.PaymentRequestStatusPaid, updatedStatusPaymentRequest.Status) + } + if updatedStatusPaymentRequest.PaymentRequestNumber == "1208-5962-1" { + previousStatusPaymentRequest := paymentRequestTwo + suite.Equal(previousStatusPaymentRequest.PaymentRequestNumber, updatedStatusPaymentRequest.PaymentRequestNumber) + suite.Equal(models.PaymentRequestStatusReviewed, previousStatusPaymentRequest.Status) + suite.Equal(models.PaymentRequestStatusPaid, updatedStatusPaymentRequest.Status) + } + if updatedStatusPaymentRequest.PaymentRequestNumber == "8801-2773-2" { + previousStatusPaymentRequest := paymentRequestThree + suite.Equal(previousStatusPaymentRequest.PaymentRequestNumber, updatedStatusPaymentRequest.PaymentRequestNumber) + suite.Equal(models.PaymentRequestStatusSentToGex, previousStatusPaymentRequest.Status) + suite.Equal(models.PaymentRequestStatusPaid, updatedStatusPaymentRequest.Status) + } + if updatedStatusPaymentRequest.PaymentRequestNumber == "8801-2773-3" { + previousStatusPaymentRequest := paymentRequestFour + suite.Equal(previousStatusPaymentRequest.PaymentRequestNumber, updatedStatusPaymentRequest.PaymentRequestNumber) + suite.Equal(models.PaymentRequestStatusTppsReceived, previousStatusPaymentRequest.Status) + suite.Equal(models.PaymentRequestStatusPaid, updatedStatusPaymentRequest.Status) + } + } + }) + suite.Run("error opening filepath returns descriptive error for failing to parse TPPS paid invoice report", func() { // given a path to a nonexistent file testTPPSPaidInvoiceReportFilePath := "../../../pkg/services/invoice/AFileThatDoesNotExist.csv" From b3b4c2104d593b39ae3f5720bb426c829731446e Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Wed, 28 Aug 2024 14:12:14 +0000 Subject: [PATCH 42/46] add payment request ID to logging --- pkg/services/invoice/process_edi824.go | 3 ++- pkg/services/invoice/process_tpps_paid_invoice_report.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/services/invoice/process_edi824.go b/pkg/services/invoice/process_edi824.go index 6fa6d6661d7..ed981f4a0a9 100644 --- a/pkg/services/invoice/process_edi824.go +++ b/pkg/services/invoice/process_edi824.go @@ -163,7 +163,8 @@ func (e *edi824Processor) ProcessFile(appCtx appcontext.AppContext, _ string, st paymentRequest.Status = models.PaymentRequestStatusEDIError err = txnAppCtx.DB().Update(&paymentRequest) if err != nil { - txnAppCtx.Logger().Error("failure updating payment request status:", zap.Error(err)) + txnAppCtx.Logger().Error(fmt.Sprintf("failure updating payment request status : %v", paymentRequest.ID)) + return fmt.Errorf("failure updating payment request status: %w", err) } txnAppCtx.Logger().Info("SUCCESS: 824 Processor updated Payment Request to new status") diff --git a/pkg/services/invoice/process_tpps_paid_invoice_report.go b/pkg/services/invoice/process_tpps_paid_invoice_report.go index ed691bd65ac..ee192fd3e1b 100644 --- a/pkg/services/invoice/process_tpps_paid_invoice_report.go +++ b/pkg/services/invoice/process_tpps_paid_invoice_report.go @@ -129,9 +129,10 @@ func (t *tppsPaidInvoiceReportProcessor) EDIType() models.EDIType { func (t *tppsPaidInvoiceReportProcessor) logTPPSInvoiceReportWithPaymentRequest(appCtx appcontext.AppContext, tppsResponse tppsReponse.TPPSData, paymentRequest models.PaymentRequest) { appCtx.Logger().Info("TPPS Paid Invoice Report log", + zap.String("TPPSPaidInvoiceReportEntry.InvoiceNumber", tppsResponse.InvoiceNumber), zap.String("PaymentRequestNumber", paymentRequest.PaymentRequestNumber), zap.String("PaymentRequest.Status", string(paymentRequest.Status)), - zap.String("TPPSPaidInvoiceReportEntry.InvoiceNumber", tppsResponse.InvoiceNumber), + zap.String("PaymentRequest.ID", paymentRequest.ID.String()), ) } From 9fe7e5d452967bfec98a63837f954f2030154268 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Wed, 28 Aug 2024 14:30:00 +0000 Subject: [PATCH 43/46] maintain zap error --- pkg/services/invoice/process_edi824.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/services/invoice/process_edi824.go b/pkg/services/invoice/process_edi824.go index ed981f4a0a9..a67a8012e75 100644 --- a/pkg/services/invoice/process_edi824.go +++ b/pkg/services/invoice/process_edi824.go @@ -163,8 +163,7 @@ func (e *edi824Processor) ProcessFile(appCtx appcontext.AppContext, _ string, st paymentRequest.Status = models.PaymentRequestStatusEDIError err = txnAppCtx.DB().Update(&paymentRequest) if err != nil { - txnAppCtx.Logger().Error(fmt.Sprintf("failure updating payment request status : %v", paymentRequest.ID)) - + txnAppCtx.Logger().Error(fmt.Sprintf("failure updating payment request status : %v", paymentRequest.ID), zap.Error(err)) return fmt.Errorf("failure updating payment request status: %w", err) } txnAppCtx.Logger().Info("SUCCESS: 824 Processor updated Payment Request to new status") From 25769533396813450daa297110584404705df32d Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Thu, 29 Aug 2024 19:08:46 +0000 Subject: [PATCH 44/46] create new simulate-process-tpps cmd to process tpps paid invoice report --- Makefile | 4 ++ cmd/simulate-process-tpps/main.go | 107 ++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 cmd/simulate-process-tpps/main.go diff --git a/Makefile b/Makefile index b739a1b9189..ef98c54a56d 100644 --- a/Makefile +++ b/Makefile @@ -312,6 +312,9 @@ bin/tls-checker: $(shell find cmd/tls-checker -name '*.go') $(PKG_GOSRC) .check_ bin/generate-payment-request-edi: $(shell find cmd/generate-payment-request-edi -name '*.go') $(PKG_GOSRC) .check_go_version.stamp .check_gopath.stamp go build -ldflags "$(LDFLAGS)" -o bin/generate-payment-request-edi ./cmd/generate-payment-request-edi +bin/simulate-process-tpps: $(shell find cmd/simulate-process-tpps -name '*.go') $(PKG_GOSRC) .check_go_version.stamp .check_gopath.stamp + go build -ldflags "$(LDFLAGS)" -o bin/simulate-process-tpps ./cmd/simulate-process-tpps + # # ----- END BIN TARGETS ----- # @@ -404,6 +407,7 @@ build_tools: bin/gin \ bin/webhook-client \ bin/read-alb-logs \ bin/send-to-gex \ + bin/simulate-process-tpps \ bin/tls-checker ## Build all tools .PHONY: build diff --git a/cmd/simulate-process-tpps/main.go b/cmd/simulate-process-tpps/main.go new file mode 100644 index 00000000000..6b39c75981b --- /dev/null +++ b/cmd/simulate-process-tpps/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "fmt" + "log" + "os" + "strings" + + "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" + processor "github.com/transcom/mymove/pkg/services/invoice" +) + +// Call this from command line with go run ./cmd/simulate-process-tpps/ + +func checkConfig(v *viper.Viper, logger *zap.Logger) error { + + err := cli.CheckDatabase(v, logger) + if err != nil { + return err + } + + return nil +} + +func initFlags(flag *pflag.FlagSet) { + // DB Config + cli.InitDatabaseFlags(flag) + + // Logging Levels + cli.InitLoggingFlags(flag) + + // Don't sort flags + flag.SortFlags = false +} + +func main() { + flag := pflag.CommandLine + initFlags(flag) + parseErr := flag.Parse(os.Args[1:]) + if parseErr != nil { + log.Fatal("failed to parse flags", zap.Error(parseErr)) + } + + v := viper.New() + bindErr := v.BindPFlags(flag) + if bindErr != nil { + log.Fatal("failed to bind flags", zap.Error(bindErr)) + } + v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + v.AutomaticEnv() + + dbEnv := v.GetString(cli.DbEnvFlag) + + logger, _, err := logging.Config(logging.WithEnvironment(dbEnv), logging.WithLoggingLevel(v.GetString(cli.LoggingLevelFlag))) + if err != nil { + log.Fatalf("failed to initialize Zap logging due to %v", err) + } + zap.ReplaceGlobals(logger) + + err = checkConfig(v, logger) + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", err.Error()) + fmt.Fprintln(os.Stderr, "Usage:") + flag.PrintDefaults() + os.Exit(1) + } + + // DB connection + dbConnection, err := cli.InitDatabase(v, logger) + if err != nil { + logger.Fatal("Connecting to DB", zap.Error(err)) + } + + // Create new TPPS Paid Invoice Report Processor + processor := processor.NewTPPSPaidInvoiceReportProcessor() + + appCtx := appcontext.NewAppContext(dbConnection, logger, nil) + + testTPPSPaidInvoiceReportFilePath := "pkg/services/invoice/fixtures/tpps_paid_invoice_report_testfile_tpps_pickup_dir.csv" + + err = processor.ProcessFile(appCtx, testTPPSPaidInvoiceReportFilePath, "") + if err != nil { + appCtx.Logger().Error("Error while processing TPPS Paid Invoice Report file", zap.String("path", testTPPSPaidInvoiceReportFilePath), zap.Error(err)) + } + + testTPPSPaidInvoiceData := ` +1077-4079-3 2024-08-05 2024-08-05 421.87 DUPK DUPK 10340 0.0311 321.57 1077-4079-cabd6371 2 +1077-4079-3 2024-08-05 2024-08-05 421.87 DDP DDP 10340 0.0097 100.3 1077-4079-a4e717fd 1 +1208-5962-1 2024-08-05 2024-08-05 557 MS MS 1 557 557 1208-5962-e0fb5863 1 +8801-2773-2 2024-08-05 2024-08-05 2748.04 DOP DOP 1 77.02 77.02 8801-2773-f2bb471e 1 +8801-2773-2 2024-08-05 2024-08-05 2748.04 DPK DPK 1 2671.02 2671.02 8801-2773-fdaee177 2 +8801-2773-3 2024-08-05 2024-08-05 1397.74 DDP DDP 1 91.31 91.31 8801-2773-2e54e07d 2 +8801-2773-3 2024-08-05 2024-08-05 1397.74 DLH DLH 1 1052.84 1052.84 8801-2773-27961d7f 1 +8801-2773-3 2024-08-05 2024-08-05 1397.74 FSC FSC 1 6.66 6.66 8801-2773-f9e0672c 3 +8801-2773-3 2024-08-05 2024-08-05 1397.74 DUPK DUPK 1 246.93 246.93 8801-2773-c6c78cf9 4 + +` + + appCtx.Logger().Info("The tpps_paid_invoice_reports table should now have the following data: ") + appCtx.Logger().Info(testTPPSPaidInvoiceData) +} From 913c71d5be340066f98d0d9b9d11a4d31cbefaf1 Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Fri, 30 Aug 2024 19:49:45 +0000 Subject: [PATCH 45/46] comment out unused code until B-20560 is started --- cmd/milmove-tasks/process_edis.go | 20 +++++++++++--------- pkg/cli/gex_sftp.go | 3 +++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/cmd/milmove-tasks/process_edis.go b/cmd/milmove-tasks/process_edis.go index 7a12fbf25be..31cf87d9c23 100644 --- a/cmd/milmove-tasks/process_edis.go +++ b/cmd/milmove-tasks/process_edis.go @@ -244,14 +244,16 @@ func processEDIs(_ *cobra.Command, _ []string) error { logger.Info("Successfully processed EDI824 application advice responses") } - // Process TPPS paid invoice report - pathTPPSPaidInvoiceReport := v.GetString(cli.SFTPTPPSPaidInvoiceReportPickupDirectory) - _, err = syncadaSFTPSession.FetchAndProcessSyncadaFiles(appCtx, pathTPPSPaidInvoiceReport, lastReadTime, invoice.NewTPPSPaidInvoiceReportProcessor()) - if err != nil { - logger.Error("Error reading TPPS Paid Invoice Report application advice responses", zap.Error(err)) - } else { - logger.Info("Successfully processed TPPS Paid Invoice Report application advice responses") - } - + // Pending completion of B-20560, uncomment the code below + /* + // Process TPPS paid invoice report + pathTPPSPaidInvoiceReport := v.GetString(cli.SFTPTPPSPaidInvoiceReportPickupDirectory) + _, err = syncadaSFTPSession.FetchAndProcessSyncadaFiles(appCtx, pathTPPSPaidInvoiceReport, lastReadTime, invoice.NewTPPSPaidInvoiceReportProcessor()) + if err != nil { + logger.Error("Error reading TPPS Paid Invoice Report application advice responses", zap.Error(err)) + } else { + logger.Info("Successfully processed TPPS Paid Invoice Report application advice responses") + } + */ return nil } diff --git a/pkg/cli/gex_sftp.go b/pkg/cli/gex_sftp.go index aecb87cf013..00239275c52 100644 --- a/pkg/cli/gex_sftp.go +++ b/pkg/cli/gex_sftp.go @@ -41,11 +41,14 @@ const ( GEXSFTP824PickupDirectory string = "gex-sftp-824-pickup-directory" ) +// Pending completion of B-20560, uncomment the code below +/* // Set of flags used for SFTPTPPSPaid const ( // SFTPTPPSPaidInvoiceReportPickupDirectory is the ENV var for the directory where TPPS delivers the TPPS paid invoice report SFTPTPPSPaidInvoiceReportPickupDirectory string = "pending" // pending completion of B-20560 ) +*/ // InitGEXSFTPFlags initializes GEX SFTP command line flags func InitGEXSFTPFlags(flag *pflag.FlagSet) { From e910331f522d685a0abe771ae926c44425a74a7a Mon Sep 17 00:00:00 2001 From: Maria Traskowsky Date: Tue, 3 Sep 2024 13:22:02 +0000 Subject: [PATCH 46/46] add comment explaining purpose --- cmd/simulate-process-tpps/main.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/simulate-process-tpps/main.go b/cmd/simulate-process-tpps/main.go index 6b39c75981b..47cf6c356a7 100644 --- a/cmd/simulate-process-tpps/main.go +++ b/cmd/simulate-process-tpps/main.go @@ -17,6 +17,10 @@ import ( ) // Call this from command line with go run ./cmd/simulate-process-tpps/ +// This binary will be explicitly for simulation and testing purposes for the scenario of +// payment request numbers 1077-4079-3, 1208-5962-1, 8801-2773-2, 8801-2773-3 +// Those payment request numbers must exist in the payment_requests table in order for +// this binary to be used properly func checkConfig(v *viper.Viper, logger *zap.Logger) error {