Skip to content

Commit

Permalink
Merge branch 'integrationTesting' into B-20989-INT
Browse files Browse the repository at this point in the history
  • Loading branch information
pambecker committed Sep 18, 2024
2 parents 54fe366 + f224276 commit faa0d66
Show file tree
Hide file tree
Showing 26 changed files with 481 additions and 111 deletions.
1 change: 1 addition & 0 deletions migrations/app/migrations_manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1000,4 +1000,5 @@
20240822180409_adding_locked_price_cents_service_param.up.sql
20240909194514_pricing_unpriced_ms_cs_service_items.up.sql
20240910021542_populating_locked_price_cents_from_pricing_estimate_for_ms_and_cs_service_items.up.sql
20240917132411_update_provides_ppm_closeout_transportation_offices.up.sql
20240917165710_update_duty_locations_provides_services_counseling.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- PPPO Fort Belvoir - USA
UPDATE transportation_offices SET provides_ppm_closeout = true WHERE id = 'a877a317-be5f-482b-a126-c91a34be9290';

-- Personal Property Activity HQ (PPA HQ) - USAF
UPDATE transportation_offices SET provides_ppm_closeout = false WHERE id = 'ebdacf64-353a-4014-91db-0d04d88320f0';

-- PPPO Base Miami - USCG
UPDATE transportation_offices SET provides_ppm_closeout = true WHERE id = '1b3e7496-efa7-48aa-ba22-b630d6fea98b';

-- JPPSO - North West (JEAT) - USA
UPDATE transportation_offices SET provides_ppm_closeout = false WHERE id = '5a3388e1-6d46-4639-ac8f-a8937dc26938';

-- PPPO NSWC Panama City Division - USN
UPDATE transportation_offices SET provides_ppm_closeout = true WHERE id = '57cf1e81-8113-4a52-bc50-3cb8902c2efd';

-- JPPSO - Mid Atlantic (BGAC) - USA
UPDATE transportation_offices SET provides_ppm_closeout = false WHERE id = '8e25ccc1-7891-4146-a9d0-cd0d48b59a50';

-- JPPSO - South West (LKNQ) - USN
UPDATE transportation_offices SET provides_ppm_closeout = false WHERE id = '27002d34-e9ea-4ef5-a086-f23d07c4088c';
8 changes: 4 additions & 4 deletions pkg/gen/ghcapi/embedded_spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion pkg/handlers/ghcapi/tranportation_offices.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ func (h GetTransportationOfficesHandler) Handle(params transportationofficeop.Ge
return h.AuditableAppContextFromRequestWithErrors(params.HTTPRequest,
func(appCtx appcontext.AppContext) (middleware.Responder, error) {

transportationOffices, err := h.TransportationOfficesFetcher.GetTransportationOffices(appCtx, params.Search, false)
// B-21022: forPpm param is set true. This is used by PPM closeout widget. Need to ensure certain offices are included/excluded
// if location has ppm closedout enabled.
transportationOffices, err := h.TransportationOfficesFetcher.GetTransportationOffices(appCtx, params.Search, true)

if err != nil {
appCtx.Logger().Error("Error searching for Transportation Offices: ", zap.Error(err))
return transportationofficeop.NewGetTransportationOfficesInternalServerError(), err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,6 @@ func calculatePendingSITBalance(appCtx appcontext.AppContext, paymentServiceItem
return err
}
sortedShipmentSIT := sitstatus.SortShipmentSITs(shipmentSIT, today)
totalSITAllowance, err := sitstatus.NewShipmentSITStatus().CalculateShipmentSITAllowance(appCtx, shipment)
if err != nil {
return err
}
calculateTotalDaysInSIT := sitstatus.CalculateTotalDaysInSIT(sortedShipmentSIT, today)

if shipmentSITBalance, ok := shipmentsSITBalances[shipment.ID.String()]; ok {
shipmentSITBalance.PendingSITDaysInvoiced = daysInSIT
Expand All @@ -181,9 +176,18 @@ func calculatePendingSITBalance(appCtx appcontext.AppContext, paymentServiceItem
// totalSITEndDate using this service item's entry date.
// Additionally retrieve the latest SIT Departure date from the current SIT if it exists. The first current SIT is chosen as there is not currently support for more than one SIT
// Per AC under B-20899
totalSITEndDate := sitstatus.CalculateSITAuthorizedEndDate(totalSITAllowance, daysInSIT, *paymentServiceItem.MTOServiceItem.SITEntryDate, calculateTotalDaysInSIT, sortedShipmentSIT.CurrentSITs[0].Summary.SITDepartureDate)

shipmentSITBalance.TotalSITEndDate = totalSITEndDate
shipmentSIT, err = sitstatus.NewShipmentSITStatus().RetrieveShipmentSIT(appCtx, shipment)
if err != nil {
return err
}
sortedShipmentSIT = sitstatus.SortShipmentSITs(shipmentSIT, today)
// Get the latest authorized end date
if len(sortedShipmentSIT.CurrentSITs) == 0 {
// No current SIT, get the most recent authorized end date
shipmentSITBalance.TotalSITEndDate = sortedShipmentSIT.PastSITs[len(sortedShipmentSIT.PastSITs)-1].Summary.SITAuthorizedEndDate
} else {
shipmentSITBalance.TotalSITEndDate = sortedShipmentSIT.CurrentSITs[0].Summary.SITAuthorizedEndDate
}
shipmentsSITBalances[shipment.ID.String()] = shipmentSITBalance
} else {
shipmentSITBalance := services.ShipmentPaymentSITBalance{
Expand All @@ -192,6 +196,12 @@ func calculatePendingSITBalance(appCtx appcontext.AppContext, paymentServiceItem
PendingBilledStartDate: start,
PendingBilledEndDate: end,
}
shipmentSIT, err = sitstatus.NewShipmentSITStatus().RetrieveShipmentSIT(appCtx, shipment)
if err != nil {
return err
}
sortedShipmentSIT = sitstatus.SortShipmentSITs(shipmentSIT, today)

totalSITDaysAuthorized, err := sitstatus.NewShipmentSITStatus().CalculateShipmentSITAllowance(appCtx, shipment)
if err != nil {
return err
Expand All @@ -201,11 +211,16 @@ func calculatePendingSITBalance(appCtx appcontext.AppContext, paymentServiceItem

// Retrieve the latest SIT Departure date from the current SIT if it exists. The first current SIT is chosen as there is not currently support for more than one SIT
// Per AC under B-20899
totalSITEndDate := sitstatus.CalculateSITAuthorizedEndDate(totalSITAllowance, daysInSIT, *paymentServiceItem.MTOServiceItem.SITEntryDate, calculateTotalDaysInSIT, sortedShipmentSIT.CurrentSITs[0].Summary.SITDepartureDate)

shipmentSITBalance.TotalSITDaysAuthorized = totalSITDaysAuthorized
shipmentSITBalance.TotalSITDaysRemaining = totalSITDaysRemaining
shipmentSITBalance.TotalSITEndDate = totalSITEndDate
// Get the latest authorized end date
if len(sortedShipmentSIT.CurrentSITs) == 0 {
// No current SIT, get the most recent authorized end date
shipmentSITBalance.TotalSITEndDate = sortedShipmentSIT.PastSITs[len(sortedShipmentSIT.PastSITs)-1].Summary.SITAuthorizedEndDate
} else {
shipmentSITBalance.TotalSITEndDate = sortedShipmentSIT.CurrentSITs[0].Summary.SITAuthorizedEndDate
}

shipmentsSITBalances[shipment.ID.String()] = shipmentSITBalance
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,11 @@ func (suite *PaymentRequestServiceSuite) TestListShipmentPaymentSITBalance() {
suite.Equal(paymentEndDate.String(), pendingSITBalance.PendingBilledEndDate.String())
suite.Equal(120, pendingSITBalance.TotalSITDaysAuthorized)
suite.Equal(89, pendingSITBalance.TotalSITDaysRemaining)
suite.Equal(doasit.SITEntryDate.AddDate(0, 0, 118).String(), pendingSITBalance.TotalSITEndDate.UTC().String())
// Authorized end date should be 120 days from the entry date.
// Only add 119 to the entry date because that last day counts as one too
// To understand this, use a date range calculator and set start date
// to AUG 12 2024, end date to DEC 9 2024, and select to "Include end date in calculation"
suite.Equal(doasit.SITEntryDate.AddDate(0, 0, 119).String(), pendingSITBalance.TotalSITEndDate.UTC().String())
})

suite.Run("calculates pending destination SIT balance when origin was invoiced previously", func() {
Expand Down Expand Up @@ -527,10 +531,11 @@ func (suite *PaymentRequestServiceSuite) TestListShipmentPaymentSITBalance() {
suite.Equal(destinationPaymentEndDate.String(), pendingSITBalance.PendingBilledEndDate.String())

suite.Equal(120, pendingSITBalance.TotalSITDaysAuthorized)
// 120 total authorized - 30 from origin SIT - 60 from destination SIT = 30 SIT days remaining
// 120 total authorized - 31 from origin SIT - 61 from destination SIT = 28 SIT days remaining
suite.Equal(28, pendingSITBalance.TotalSITDaysRemaining)

suite.Equal(ddasit.SITEntryDate.AddDate(0, 0, 87).String(), pendingSITBalance.TotalSITEndDate.UTC().String())
// 120 authorized - 31 already used - 1 to be inclusive of the last day = 88
suite.Equal(ddasit.SITEntryDate.AddDate(0, 0, 88).String(), pendingSITBalance.TotalSITEndDate.UTC().String())
})

suite.Run("ignores including previously denied service items in SIT balance", func() {
Expand Down
71 changes: 57 additions & 14 deletions pkg/services/sit_status/shipment_sit_status.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sitstatus

import (
"sort"
"time"

"github.com/gofrs/uuid"
Expand Down Expand Up @@ -75,6 +76,10 @@ func Clamp(input, min, max int) (int, error) {
// Each SIT grouping has a top-level summary of the grouped SIT
func (f shipmentSITStatus) RetrieveShipmentSIT(appCtx appcontext.AppContext, shipment models.MTOShipment) (models.SITServiceItemGroupings, error) {
var shipmentSITs models.SITServiceItemGroupings
var daysUsed int // Sum of all days used, this will increase after each SIT summary is generated for each group
// It is passed to the next grouping (sorted by entry date) in order to track proper authorized end dates
// by subtracting the days used from the allowance
var mostRecentSITAuthorizedEndDate *time.Time

year, month, day := time.Now().Date()
today := time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
Expand Down Expand Up @@ -119,12 +124,31 @@ func (f shipmentSITStatus) RetrieveShipmentSIT(appCtx appcontext.AppContext, shi
return nil, err
}

// Sort by entry date
var entryDates []time.Time
for entryDate := range groupedSITs {
// Extract map keys and insert into a slice
// This is done due to flaky issues that come from
// a map being unordered.
// By using a slice, we can ensure a proper sort
entryDates = append(entryDates, entryDate)
}
sort.Slice(entryDates, func(i, j int) bool {
return entryDates[i].Before(entryDates[j])
})

// Generate summaries for each group and append them to shipmentSITs
for _, group := range groupedSITs {
summary := f.generateSITSummary(*group, today, totalSITAllowance)
// Use the sorted entry date keys
for _, entryDate := range entryDates {
// We know all groups will be properly iterated over because
// entryDates are the extracted map keys from teh groupedSITs map
group := groupedSITs[entryDate]
summary := f.generateSITSummary(*group, today, totalSITAllowance, daysUsed, mostRecentSITAuthorizedEndDate)
if summary != nil {
group.Summary = *summary
mostRecentSITAuthorizedEndDate = &group.Summary.SITAuthorizedEndDate
shipmentSITs = append(shipmentSITs, *group)
daysUsed += summary.DaysInSIT
}
}

Expand All @@ -150,7 +174,13 @@ func containsReServiceCode(validCodes []models.ReServiceCode, code models.ReServ
// will have discrepancies and information spread across multiple items.
// This SIT summary is to make it readable down the line, and handle all complex calculations
// in one, central location.
func (f shipmentSITStatus) generateSITSummary(sit models.SITServiceItemGrouping, today time.Time, totalSitAllowance int) *models.SITSummary {
// - sit: The SIT service item grouping we are generating the summary for.
// - today: The current date, used for calculating days in SIT.
// - totalSitAllowance: The total number of days allowed for this shipment.
// - daysUsedSoFar: The number of SIT days that have already been used in previous SIT groupings.
// This is used to ensure the remaining SIT allowance is correctly applied to this grouping, and is
// generated by sorting SIT groupings by entry date, and adding to the sum of daysUsed
func (f shipmentSITStatus) generateSITSummary(sit models.SITServiceItemGrouping, today time.Time, totalSitAllowance int, daysUsedSoFar int, mostRecentSITAuthorizedEndDate *time.Time) *models.SITSummary {
if sit.ServiceItems == nil {
// Return nil if there are no service items
return nil
Expand Down Expand Up @@ -200,14 +230,10 @@ func (f shipmentSITStatus) generateSITSummary(sit models.SITServiceItemGrouping,
}
}

// Calculate the days in SIT based on the earliest SIT entry date and earliest SIT departure date if any were discovered from the SIT group
// Calculate the days in SIT and authorized end date based on the earliest SIT entry date and earliest SIT departure date if any were discovered from the SIT group
if earliestSITEntryDate != nil {
calculatedTotalDaysInSIT = daysInSIT(*earliestSITEntryDate, earliestSITDepartureDate, today)
}

// Calculate the SIT Authorized End Date
if earliestSITEntryDate != nil {
earliestSITAuthorizedEndDateValue := CalculateSITAuthorizedEndDate(totalSitAllowance, calculatedTotalDaysInSIT, *earliestSITEntryDate, calculatedTotalDaysInSIT, earliestSITDepartureDate)
earliestSITAuthorizedEndDateValue := calculateSITAuthorizedEndDate(totalSitAllowance, calculatedTotalDaysInSIT, *earliestSITEntryDate, daysUsedSoFar, earliestSITDepartureDate, mostRecentSITAuthorizedEndDate)
earliestSITAuthorizedEndDate = &earliestSITAuthorizedEndDateValue
}

Expand Down Expand Up @@ -284,7 +310,6 @@ func (f shipmentSITStatus) CalculateShipmentSITStatus(appCtx appcontext.AppConte
daysInSIT := daysInSIT(currentSIT.Summary.SITEntryDate, currentSIT.Summary.SITDepartureDate, today)
sitEntryDate := currentSIT.Summary.SITEntryDate
sitDepartureDate := currentSIT.Summary.SITDepartureDate
sitAuthorizedEndDate := CalculateSITAuthorizedEndDate(totalSITAllowance, daysInSIT, sitEntryDate, shipmentSITStatus.CalculatedTotalDaysInSIT, sitDepartureDate)
var sitCustomerContacted, sitRequestedDelivery *time.Time
sitCustomerContacted = currentSIT.Summary.SITCustomerContacted
sitRequestedDelivery = currentSIT.Summary.SITRequestedDelivery
Expand All @@ -295,7 +320,7 @@ func (f shipmentSITStatus) CalculateShipmentSITStatus(appCtx appcontext.AppConte
DaysInSIT: daysInSIT,
SITEntryDate: sitEntryDate,
SITDepartureDate: sitDepartureDate,
SITAuthorizedEndDate: sitAuthorizedEndDate,
SITAuthorizedEndDate: currentSIT.Summary.SITAuthorizedEndDate,
SITCustomerContacted: sitCustomerContacted,
SITRequestedDelivery: sitRequestedDelivery,
}
Expand Down Expand Up @@ -382,17 +407,35 @@ func CalculateTotalPastDaysInSIT(shipmentSITs SortedShipmentSITs, today time.Tim
return totalDays
}

func CalculateSITAuthorizedEndDate(totalSITAllowance int, currentDaysInSIT int, sitEntryDate time.Time, totalDaysInSITSoFar int, sitDepartureDate *time.Time) time.Time {
sitAuthorizedEndDate := sitEntryDate.AddDate(0, 0, (totalSITAllowance - (totalDaysInSITSoFar - currentDaysInSIT)))
// totalDaysUsedByPreviousSITs is the current SUM of past SITs days used
// currentDaysUsedWithinThisSIT is the amount of days spent in SIT for the current SIT so far
// This is a private helper func for the generation of the SIT summaries
func calculateSITAuthorizedEndDate(totalSITAllowance int, currentDaysUsedWithinThisSIT int, sitEntryDate time.Time, totalDaysUsedByPreviousSITs int, sitDepartureDate *time.Time, mostRecentSITAuthorizedEndDate *time.Time) time.Time {
// Get the remaining allowance for this SIT's authorized end date by
// subtracting the allowance by the days used by previous SITs.
remainingSITAllowance := totalSITAllowance - totalDaysUsedByPreviousSITs
// Use clamp to determine if this SIT was created already in violation of the authorized
// amount of days. This is a super rare case, but deserves the check nonetheless
_, err := Clamp(remainingSITAllowance-currentDaysUsedWithinThisSIT, 0, remainingSITAllowance)
if err != nil && mostRecentSITAuthorizedEndDate != nil {
// This error is only triggered when past SITs have already exceeded the allowed SIT days.
// In this case, the new SIT is starting already in violation of the SIT allowance.
// This should never be able to occur due to service object prevention, but just in case we default to
// the previous SITs authorized end date.
return *mostRecentSITAuthorizedEndDate
}

// The authorized end date will be the sum of days remaining in the allowance from the entry date
// Subtract the last day to be inclusive of counting it
// Eg, this func successfully counts totalSITAllowance days from SITEntryDate, but per customer requirements, they will
// then count that last day too. So if given 90 days of allowance, we'd get Aug 20 2024 thru Nov 18 2024. 90 days as expected
// but then if you count the last day, it gets 91. Thus making the calculation incorrect. This is why we subtract a day, to be inclusive of it
sitAuthorizedEndDate = sitAuthorizedEndDate.AddDate(0, 0, -1)
sitAuthorizedEndDate := sitEntryDate.AddDate(0, 0, remainingSITAllowance-1)
// Ensure that the authorized end date does not go before the entry date
if sitAuthorizedEndDate.Before(sitEntryDate) {
sitAuthorizedEndDate = sitEntryDate
}
// Now that we have our authorized end date, we need to compare it to the departure date.
// If the SIT departure date is set and it is before the currently authorized end date
// then the original SIT authorized end date should be updated to the departure date
if sitDepartureDate != nil && (sitDepartureDate.Before(sitAuthorizedEndDate) && sitDepartureDate.After(sitEntryDate)) {
Expand Down
Loading

0 comments on commit faa0d66

Please sign in to comment.