Skip to content

Commit

Permalink
estimated, actual, and max are good for db func, need to address clos…
Browse files Browse the repository at this point in the history
…eout before tackling SIT
  • Loading branch information
danieljordan-caci committed Jan 15, 2025
1 parent aa6e280 commit 26610d9
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 143 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
-- function that calculates a ppm incentive given mileage, weight, and dates
-- this is used to calculate estimated, max, and actual incentives
CREATE OR REPLACE FUNCTION calculate_ppm_incentive(
ppm_id UUID,
pickup_address_id UUID,
destination_address_id UUID,
move_date DATE,
mileage INT,
weight INT,
is_estimated BOOLEAN,
is_actual BOOLEAN
is_actual BOOLEAN,
is_max BOOLEAN
) RETURNS NUMERIC AS
$$
DECLARE
Expand All @@ -24,11 +30,12 @@ DECLARE
cents_above_baseline NUMERIC;
BEGIN

IF NOT is_estimated AND NOT is_actual THEN
RAISE EXCEPTION 'Both is_estimated and is_actual cannot be FALSE. No update will be performed.';
IF NOT is_estimated AND NOT is_actual AND NOT is_max THEN
RAISE EXCEPTION 'is_estimated, is_actual, and is_max cannot all be FALSE. No update will be performed.';
END IF;

SELECT ppms.id, ppms.pickup_postal_address_id, ppms.destination_postal_address_id, ppms.expected_departure_date
-- validating it's a real PPM
SELECT ppms.id
INTO ppm
FROM ppm_shipments ppms
WHERE ppms.id = ppm_id;
Expand All @@ -37,19 +44,19 @@ BEGIN
RAISE EXCEPTION 'PPM with ID % not found', ppm_id;
END IF;

contract_id := get_contract_id(ppm.expected_departure_date);
contract_id := get_contract_id(move_date);
IF contract_id IS NULL THEN
RAISE EXCEPTION 'Contract not found for date: %', ppm.expected_departure_date;
RAISE EXCEPTION 'Contract not found for date: %', move_date;
END IF;

o_rate_area_id := get_rate_area_id(ppm.pickup_postal_address_id, NULL, contract_id);
o_rate_area_id := get_rate_area_id(pickup_address_id, NULL, contract_id);
IF o_rate_area_id IS NULL THEN
RAISE EXCEPTION 'Origin rate area is NULL for address ID %', ppm.pickup_postal_address_id;
RAISE EXCEPTION 'Origin rate area is NULL for address ID %', pickup_address_id;
END IF;

d_rate_area_id := get_rate_area_id(ppm.destination_postal_address_id, NULL, contract_id);
d_rate_area_id := get_rate_area_id(destination_address_id, NULL, contract_id);
IF d_rate_area_id IS NULL THEN
RAISE EXCEPTION 'Destination rate area is NULL for address ID %', ppm.destination_postal_address_id;
RAISE EXCEPTION 'Destination rate area is NULL for address ID %', destination_address_id;
END IF;

-- ISLH calculation
Expand All @@ -61,7 +68,7 @@ BEGIN
service_id,
contract_id,
'ISLH',
ppm.expected_departure_date
move_date
) * (weight / 100)::NUMERIC * 100, 0
);
RAISE NOTICE 'Estimated price for ISLH: % cents', estimated_price_islh;
Expand All @@ -75,7 +82,7 @@ BEGIN
service_id,
contract_id,
'IHPK',
ppm.expected_departure_date
move_date
) * (weight / 100)::NUMERIC * 100, 0
);
RAISE NOTICE 'Estimated price for IHPK: % cents', estimated_price_ihpk;
Expand All @@ -89,14 +96,14 @@ BEGIN
service_id,
contract_id,
'IHUPK',
ppm.expected_departure_date
move_date
) * (weight / 100)::NUMERIC * 100, 0
);
RAISE NOTICE 'Estimated price for IHUPK: % cents', estimated_price_ihupk;

-- FSC calculation
estimated_fsc_multiplier := get_fsc_multiplier(weight);
fuel_price := get_fuel_price(ppm.expected_departure_date);
fuel_price := get_fuel_price(move_date);
price_difference := calculate_price_difference(fuel_price);
cents_above_baseline := mileage * estimated_fsc_multiplier;
estimated_price_fsc := ROUND((cents_above_baseline * price_difference) * 100);
Expand All @@ -109,7 +116,8 @@ BEGIN
-- now update the incentive value
UPDATE ppm_shipments
SET estimated_incentive = CASE WHEN is_estimated THEN total_incentive ELSE estimated_incentive END,
final_incentive = CASE WHEN is_actual THEN total_incentive ELSE final_incentive END
final_incentive = CASE WHEN is_actual THEN total_incentive ELSE final_incentive END,
max_incentive = CASE WHEN is_max THEN total_incentive ELSE max_incentive END
WHERE id = ppm_id;

RETURN total_incentive;
Expand Down
4 changes: 2 additions & 2 deletions pkg/models/ppm_shipment.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,10 @@ func FetchPPMShipmentByPPMShipmentID(db *pop.Connection, ppmShipmentID uuid.UUID

// a db stored proc that will handle updating the estimated_incentive value
// this simulates pricing of a basic iHHG shipment with ISLH, IHPK, IHUPK, and the CONUS portion for a FSC
func CalculatePPMIncentive(db *pop.Connection, ppmID uuid.UUID, mileage int, weight int, isEstimated bool, isActual bool) (int, error) {
func CalculatePPMIncentive(db *pop.Connection, ppmID uuid.UUID, pickupAddressID uuid.UUID, destAddressID uuid.UUID, moveDate time.Time, mileage int, weight int, isEstimated bool, isActual bool, isMax bool) (int, error) {
var incentive int

err := db.RawQuery("SELECT calculate_ppm_incentive($1, $2, $3, $4, $5)", ppmID, mileage, weight, isEstimated, isActual).
err := db.RawQuery("SELECT calculate_ppm_incentive($1, $2, $3, $4, $5, $6, $7, $8, $9)", ppmID, pickupAddressID, destAddressID, moveDate, mileage, weight, isEstimated, isActual, isMax).
First(&incentive)
if err != nil {
return 0, fmt.Errorf("error calculating PPM incentive for PPM ID %s: %w", ppmID, err)
Expand Down
177 changes: 111 additions & 66 deletions pkg/services/ppmshipment/ppm_estimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,13 @@ func (f *estimatePPM) estimateIncentive(appCtx appcontext.AppContext, oldPPMShip
}
}

// if the PPM is international, we will use a db stored proc
contractDate := newPPMShipment.ExpectedDepartureDate
contract, err := serviceparamvaluelookups.FetchContract(appCtx, contractDate)
if err != nil {
return nil, nil, err
}

// if the PPM is international, we will use a db func
if newPPMShipment.Shipment.MarketCode != models.MarketCodeInternational {

calculateSITEstimate := shouldCalculateSITCost(newPPMShipment, &oldPPMShipment)
Expand All @@ -219,12 +225,6 @@ func (f *estimatePPM) estimateIncentive(appCtx appcontext.AppContext, oldPPMShip
return oldPPMShipment.EstimatedIncentive, newPPMShipment.SITEstimatedCost, nil
}

contractDate := newPPMShipment.ExpectedDepartureDate
contract, err := serviceparamvaluelookups.FetchContract(appCtx, contractDate)
if err != nil {
return nil, nil, err
}

estimatedIncentive := oldPPMShipment.EstimatedIncentive
if !skipCalculatingEstimatedIncentive {
// Clear out advance and advance requested fields when the estimated incentive is reset.
Expand All @@ -248,45 +248,15 @@ func (f *estimatePPM) estimateIncentive(appCtx appcontext.AppContext, oldPPMShip
return estimatedIncentive, estimatedSITCost, nil

} else {
var mileage int
pickupAddress := newPPMShipment.PickupAddress
destinationAddress := newPPMShipment.DestinationAddress

// get the Tacoma, WA port (code: 3002) - this is the authorized port for PPMs
ppmPort, err := models.FetchPortLocationByCode(appCtx.DB(), "3002")
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch port location: %w", err)
}

// handling OCONUS/CONUS mileage logic to determine mileage checks
isPickupOconus := pickupAddress.IsOconus != nil && *pickupAddress.IsOconus
isDestinationOconus := destinationAddress.IsOconus != nil && *destinationAddress.IsOconus

switch {
case isPickupOconus && isDestinationOconus:
// OCONUS -> OCONUS: no mileage (set to 0)
mileage = 0
case isPickupOconus && !isDestinationOconus:
// OCONUS -> CONUS: get mileage from port ZIP to destination ZIP
mileage, err = f.planner.ZipTransitDistance(appCtx, ppmPort.UsPostRegionCity.UsprZipID, destinationAddress.PostalCode, true, true)
if err != nil {
return nil, nil, fmt.Errorf("failed to calculate OCONUS to CONUS mileage: %w", err)
}
case !isPickupOconus && isDestinationOconus:
// CONUS -> OCONUS: get mileage from pickup ZIP to port ZIP
mileage, err = f.planner.ZipTransitDistance(appCtx, pickupAddress.PostalCode, ppmPort.UsPostRegionCity.UsprZipID, true, true)
if err != nil {
return nil, nil, fmt.Errorf("failed to calculate CONUS to OCONUS mileage: %w", err)
}
}

// now we can calculate the incentive
estimatedIncentive, err := models.CalculatePPMIncentive(appCtx.DB(), newPPMShipment.ID, mileage, newPPMShipment.EstimatedWeight.Int(), true, false)
estimatedIncentive, err := f.calculateOCONUSIncentive(appCtx, newPPMShipment.ID, *pickupAddress, *destinationAddress, contractDate, newPPMShipment.EstimatedWeight.Int(), false, false, true)
if err != nil {
return nil, nil, fmt.Errorf("failed to calculate estimated PPM incentive: %w", err)
}

return (*unit.Cents)(&estimatedIncentive), nil, nil
return estimatedIncentive, nil, nil
}
}

Expand All @@ -306,7 +276,7 @@ func (f *estimatePPM) maxIncentive(appCtx appcontext.AppContext, oldPPMShipment
// we have access to the MoveTaskOrderID in the ppmShipment object so we can use that to get the customer's maximum weight entitlement
var move models.Move
err = appCtx.DB().Q().Eager(
"Orders.Entitlement",
"Orders.Entitlement", "Orders.OriginDutyLocation.Address", "Orders.NewDutyLocation.Address",
).Where("show = TRUE").Find(&move, newPPMShipment.Shipment.MoveTaskOrderID)
if err != nil {
return nil, apperror.NewNotFoundError(newPPMShipment.ID, " error querying move")
Expand All @@ -322,14 +292,27 @@ func (f *estimatePPM) maxIncentive(appCtx appcontext.AppContext, oldPPMShipment
return nil, err
}

// since the max incentive is based off of the authorized weight entitlement and that value CAN change
// we will calculate the max incentive each time it is called
maxIncentive, err := f.calculatePrice(appCtx, newPPMShipment, unit.Pound(*orders.Entitlement.DBAuthorizedWeight), contract, true)
if err != nil {
return nil, err
}
if newPPMShipment.Shipment.MarketCode != models.MarketCodeInternational {

// since the max incentive is based off of the authorized weight entitlement and that value CAN change
// we will calculate the max incentive each time it is called
maxIncentive, err := f.calculatePrice(appCtx, newPPMShipment, unit.Pound(*orders.Entitlement.DBAuthorizedWeight), contract, true)
if err != nil {
return nil, err
}

return maxIncentive, nil
} else {
pickupAddress := orders.OriginDutyLocation.Address
destinationAddress := orders.NewDutyLocation.Address

return maxIncentive, nil
maxIncentive, err := f.calculateOCONUSIncentive(appCtx, newPPMShipment.ID, pickupAddress, destinationAddress, contractDate, *orders.Entitlement.DBAuthorizedWeight, false, false, true)
if err != nil {
return nil, fmt.Errorf("failed to calculate estimated PPM incentive: %w", err)
}

return maxIncentive, nil
}
}

func (f *estimatePPM) finalIncentive(appCtx appcontext.AppContext, oldPPMShipment models.PPMShipment, newPPMShipment *models.PPMShipment, checks ...ppmShipmentValidator) (*unit.Cents, error) {
Expand All @@ -352,32 +335,51 @@ func (f *estimatePPM) finalIncentive(appCtx appcontext.AppContext, oldPPMShipmen
newTotalWeight = *newPPMShipment.AllowableWeight
}

isMissingInfo := shouldSetFinalIncentiveToNil(newPPMShipment, newTotalWeight)
var skipCalculateFinalIncentive bool
finalIncentive := oldPPMShipment.FinalIncentive
contractDate := newPPMShipment.ExpectedDepartureDate
if newPPMShipment.ActualMoveDate != nil {
contractDate = *newPPMShipment.ActualMoveDate
}
contract, err := serviceparamvaluelookups.FetchContract(appCtx, contractDate)
if err != nil {
return nil, err
}

if !isMissingInfo {
skipCalculateFinalIncentive = shouldSkipCalculatingFinalIncentive(newPPMShipment, &oldPPMShipment, originalTotalWeight, newTotalWeight)
if !skipCalculateFinalIncentive {
contractDate := newPPMShipment.ExpectedDepartureDate
if newPPMShipment.ActualMoveDate != nil {
contractDate = *newPPMShipment.ActualMoveDate
}
contract, err := serviceparamvaluelookups.FetchContract(appCtx, contractDate)
if err != nil {
return nil, err
if newPPMShipment.Shipment.MarketCode != models.MarketCodeInternational {
isMissingInfo := shouldSetFinalIncentiveToNil(newPPMShipment, newTotalWeight)
var skipCalculateFinalIncentive bool
finalIncentive := oldPPMShipment.FinalIncentive
if !isMissingInfo {
skipCalculateFinalIncentive = shouldSkipCalculatingFinalIncentive(newPPMShipment, &oldPPMShipment, originalTotalWeight, newTotalWeight)
if !skipCalculateFinalIncentive {

finalIncentive, err := f.calculatePrice(appCtx, newPPMShipment, newTotalWeight, contract, false)
if err != nil {
return nil, err
}
return finalIncentive, nil
}
} else {
finalIncentive = nil

finalIncentive, err = f.calculatePrice(appCtx, newPPMShipment, newTotalWeight, contract, false)
return finalIncentive, nil
}

return finalIncentive, nil
} else {
pickupAddress := newPPMShipment.PickupAddress
destinationAddress := newPPMShipment.DestinationAddress

// we can't calculate actual incentive without the weight
if newTotalWeight != 0 {
finalIncentive, err := f.calculateOCONUSIncentive(appCtx, newPPMShipment.ID, *pickupAddress, *destinationAddress, contractDate, newTotalWeight.Int(), false, true, false)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to calculate estimated PPM incentive: %w", err)
}
return finalIncentive, nil
} else {
return nil, nil
}
} else {
finalIncentive = nil
}

return finalIncentive, nil
}

// SumWeightTickets return the total weight of all weightTickets associated with a PPMShipment, returns 0 if there is no valid weight
Expand Down Expand Up @@ -738,6 +740,49 @@ func (f estimatePPM) priceBreakdown(appCtx appcontext.AppContext, ppmShipment *m
return linehaul, fuel, origin, dest, packing, unpacking, storage, nil
}

// function for calculating incentives for OCONUS PPM shipments
// this uses a db function that takes in values needed to come up with the estimated/actual/max incentives
// this simulates the reimbursement for an iHHG move with ISLH, IHPK, IHUPK, and CONUS portion of FSC
func (f *estimatePPM) calculateOCONUSIncentive(appCtx appcontext.AppContext, ppmShipmentID uuid.UUID, pickupAddress models.Address, destinationAddress models.Address, moveDate time.Time, weight int, isEstimated bool, isActual bool, isMax bool) (*unit.Cents, error) {
var mileage int
ppmPort, err := models.FetchPortLocationByCode(appCtx.DB(), "3002") // Tacoma, WA port
if err != nil {
return nil, fmt.Errorf("failed to fetch port location: %w", err)
}

// check if addresses are OCONUS or CONUS -> this determines how we check mileage to/from the authorized port
isPickupOconus := pickupAddress.IsOconus != nil && *pickupAddress.IsOconus
isDestinationOconus := destinationAddress.IsOconus != nil && *destinationAddress.IsOconus

switch {
case isPickupOconus && isDestinationOconus:
// OCONUS -> OCONUS, we only reimburse for the CONUS mileage of the PPM
mileage = 0
case isPickupOconus && !isDestinationOconus:
// OCONUS -> CONUS (port ZIP -> address ZIP)
mileage, err = f.planner.ZipTransitDistance(appCtx, ppmPort.UsPostRegionCity.UsprZipID, destinationAddress.PostalCode, true, true)
if err != nil {
return nil, fmt.Errorf("failed to calculate OCONUS to CONUS mileage: %w", err)
}
case !isPickupOconus && isDestinationOconus:
// CONUS -> OCONUS (address ZIP -> port ZIP)
mileage, err = f.planner.ZipTransitDistance(appCtx, pickupAddress.PostalCode, ppmPort.UsPostRegionCity.UsprZipID, true, true)
if err != nil {
return nil, fmt.Errorf("failed to calculate CONUS to OCONUS mileage: %w", err)
}
default:
// covering down on CONUS -> CONUS moves - they should not appear here
return nil, fmt.Errorf("invalid pickup and destination configuration: pickup isOconus=%v, destination isOconus=%v", isPickupOconus, isDestinationOconus)
}

incentive, err := models.CalculatePPMIncentive(appCtx.DB(), ppmShipmentID, pickupAddress.ID, destinationAddress.ID, moveDate, mileage, weight, isEstimated, isActual, isMax)
if err != nil {
return nil, fmt.Errorf("failed to calculate PPM incentive: %w", err)
}

return (*unit.Cents)(&incentive), nil
}

func CalculateSITCost(appCtx appcontext.AppContext, ppmShipment *models.PPMShipment, contract models.ReContract) (*unit.Cents, error) {
logger := appCtx.Logger()

Expand Down
Loading

0 comments on commit 26610d9

Please sign in to comment.