From d17499d6ff81c85d491b1f451e8e93003caa9147 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 9 Dec 2024 20:23:27 +0000 Subject: [PATCH 001/113] initial entitlement refactor migration --- migrations/app/migrations_manifest.txt | 1 + ...0241209121924_entitlements_refactor.up.sql | 812 ++++++++++++++++++ 2 files changed, 813 insertions(+) create mode 100644 migrations/app/schema/20241209121924_entitlements_refactor.up.sql diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index d016b8adfe5..fb4b51e7c4d 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1036,3 +1036,4 @@ 20241202163059_create_test_sequence_dev_env.up.sql 20241203024453_add_ppm_max_incentive_column.up.sql 20241204210208_retroactive_update_of_ppm_max_and_estimated_incentives_prd.up.sql +20241209121924_entitlements_refactor.up.sql diff --git a/migrations/app/schema/20241209121924_entitlements_refactor.up.sql b/migrations/app/schema/20241209121924_entitlements_refactor.up.sql new file mode 100644 index 00000000000..4535b73af22 --- /dev/null +++ b/migrations/app/schema/20241209121924_entitlements_refactor.up.sql @@ -0,0 +1,812 @@ +-- See https://dp3.atlassian.net/wiki/spaces/MT/pages/2738716677/HHG+and+UB+Entitlements +-- Prep entitlements table for holding weight restricted +ALTER TABLE entitlements +ADD COLUMN IF NOT EXISTS is_weight_restricted boolean NOT NULL DEFAULT false, + ADD COLUMN IF NOT EXISTS weight_restriction int; +-- Create pay grades table to get our static entitlements.go file to be db based +CREATE TABLE IF NOT EXISTS pay_grades ( + id uuid PRIMARY KEY NOT NULL, + grade text NOT NULL UNIQUE, + grade_description text +); +-- Create household goods allowances table +CREATE TABLE IF NOT EXISTS hhg_allowances ( + id uuid PRIMARY KEY NOT NULL, + pay_grade_id uuid NOT NULL UNIQUE REFERENCES pay_grades(id) ON DELETE CASCADE, + total_weight_self int NOT NULL, + total_weight_self_plus_dependents int NOT NULL, + pro_gear_weight int NOT NULL, + pro_gear_weight_spouse int NOT NULL +); +-- Insert Max HHG allowance app value +-- camel case to match the existing standaloneCrateCap parameter +INSERT INTO application_parameters (id, parameter_name, parameter_value) +VALUES ( + 'D246186B-E93B-4716-B82C-6A38EA5EAB8C', + 'maxHhgAllowance', + '18000' + ); +-- Insert pay_grades and hhg_allowances +-- ACADEMY_CADET +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '8D8C82EA-EA8F-4D7F-9D84-8D186AB7A7C0', + 'ACADEMY_CADET', + 'Academy Cadet' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '8A43128C-B080-4D22-9BEA-6F1CBF7F7123', + ( + SELECT id + FROM pay_grades + WHERE grade = 'ACADEMY_CADET' + ), + 350, + 350, + 0, + 0 + ); +-- MIDSHIPMAN +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '63998729-EF74-486E-BEEA-5B519FA3812F', + 'MIDSHIPMAN', + 'Midshipman' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'C95AA341-9261-4E14-B63B-9A7262FB8EA0', + ( + SELECT id + FROM pay_grades + WHERE grade = 'MIDSHIPMAN' + ), + 350, + 350, + 0, + 0 + ); +-- AVIATION_CADET +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + 'DF749D7E-5007-43CD-8715-2875D281F817', + 'AVIATION_CADET', + 'Aviation Cadet' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '23D6DEF4-975E-4075-A4B2-E4DC3DF3D6FF', + ( + SELECT id + FROM pay_grades + WHERE grade = 'AVIATION_CADET' + ), + 7000, + 8000, + 2000, + 500 + ); +-- E_1 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '6CB785D0-CABF-479A-A36D-A6AEC294A4D0', + 'E_1', + 'Enlisted Grade E_1' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '6CB785D0-CABF-479A-A36D-A6AEC294A4DE', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_1' + ), + 5000, + 8000, + 2000, + 500 + ); +-- E_2 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '5F871C82-F259-43CC-9245-A6E18975DDE0', + 'E_2', + 'Enlisted Grade E_2' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '5F871C82-F259-43CC-9245-A6E18975DDE8', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_2' + ), + 5000, + 8000, + 2000, + 500 + ); +-- E_3 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '862EB395-86D1-44AF-AD47-DEC44FBEDA30', + 'E_3', + 'Enlisted Grade E_3' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '862EB395-86D1-44AF-AD47-DEC44FBEDA3F', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_3' + ), + 5000, + 8000, + 2000, + 500 + ); +-- E_4 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + 'BB55F37C-3165-46BA-AD3F-9A477F699990', + 'E_4', + 'Enlisted Grade E_4' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'BB55F37C-3165-46BA-AD3F-9A477F699991', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_4' + ), + 7000, + 8000, + 2000, + 500 + ); +-- E_5 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '3F142461-DCA5-4A77-9295-92EE93371330', + 'E_5', + 'Enlisted Grade E_5' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '3F142461-DCA5-4A77-9295-92EE9337133A', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_5' + ), + 7000, + 9000, + 2000, + 500 + ); +-- E_6 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '541AEC36-BD9F-4AD2-ABB4-D9B63E29DC80', + 'E_6', + 'Enlisted Grade E_6' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '541AEC36-BD9F-4AD2-ABB4-D9B63E29DC8C', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_6' + ), + 8000, + 11000, + 2000, + 500 + ); +-- E_7 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '523D57A1-529C-4DFD-8C33-9CB169FD29A0', + 'E_7', + 'Enlisted Grade E_7' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '523D57A1-529C-4DFD-8C33-9CB169FD29AF', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_7' + ), + 11000, + 13000, + 2000, + 500 + ); +-- E_8 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '1D909DB0-602F-4724-BD43-8F90A6660460', + 'E_8', + 'Enlisted Grade E_8' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '1D909DB0-602F-4724-BD43-8F90A666046E', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_8' + ), + 12000, + 14000, + 2000, + 500 + ); +-- E_9 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + 'A5FC8FD2-6F91-492B-ABE2-2157D03EC990', + 'E_9', + 'Enlisted Grade E_9' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'A5FC8FD2-6F91-492B-ABE2-2157D03EC99B', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_9' + ), + 13000, + 15000, + 2000, + 500 + ); +-- E_9 Special Senior Enlisted +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '911208CC-3D13-49D6-9478-B0A3943435C0', + 'E_9_SPECIAL_SENIOR_ENLISTED', + 'Enlisted Grade E_9 Special Senior Enlisted' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'D219899B-251F-49E9-94B3-C073C22D9D2F', + ( + SELECT id + FROM pay_grades + WHERE grade = 'E_9_SPECIAL_SENIOR_ENLISTED' + ), + 14000, + 17000, + 2000, + 500 + ); +-- O_1 (Academy Graduate) / W_1 uses same as O_1 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + 'B25998F4-4715-4F41-8986-4C5C8E59FC80', + 'O_1_ACADEMY_GRADUATE', + 'Officer Grade O_1 Academy Graduate' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'B25998F4-4715-4F41-8986-4C5C8E59FC84', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_1_ACADEMY_GRADUATE' + ), + 10000, + 12000, + 2000, + 500 + ); +-- O_2 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + 'D1B76A01-D8E4-4BD3-98FF-FA93FF7BC790', + 'O_2', + 'Officer Grade O_2' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'D1B76A01-D8E4-4BD3-98FF-FA93FF7BC79A', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_2' + ), + 12500, + 13500, + 2000, + 500 + ); +-- O_3 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '5658D67B-D510-4226-9E56-714403BA0F10', + 'O_3', + 'Officer Grade O_3' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '5658D67B-D510-4226-9E56-714403BA0F1D', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_3' + ), + 13000, + 14500, + 2000, + 500 + ); +-- O_4 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + 'E83D8F8D-F70B-4DB1-99CC-DD983D2FD250', + 'O_4', + 'Officer Grade O_4' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '0991ABBC-5400-4E6C-8BC4-195F9A602E75', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_4' + ), + 14000, + 17000, + 2000, + 500 + ); +-- O_5 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '3BC4B197-7897-4105-80A1-39A0378D7730', + 'O_5', + 'Officer Grade O_5' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'E83D8F8D-F70B-4DB1-99CC-DD983D2FD25D', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_5' + ), + 16000, + 17500, + 2000, + 500 + ); +-- O_6 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '455A112D-D1E0-4559-81E8-6DF664638F70', + 'O_6', + 'Officer Grade O_6' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '3BC4B197-7897-4105-80A1-39A0378D773E', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_6' + ), + 18000, + 18000, + 2000, + 500 + ); +-- O_7 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + 'CF664124-9BAF-4187-8F28-0908C0F0A5E0', + 'O_7', + 'Officer Grade O_7' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '455A112D-D1E0-4559-81E8-6DF664638F7C', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_7' + ), + 18000, + 18000, + 2000, + 500 + ); +-- O_8 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '6E50B04A-52DC-45C9-91D9-4A7B4FA1AB20', + 'O_8', + 'Officer Grade O_8' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'CF664124-9BAF-4187-8F28-0908C0F0A5E8', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_8' + ), + 18000, + 18000, + 2000, + 500 + ); +-- O_9 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '1D6E34C3-8C6C-4D4F-8B91-F46BED3F5E80', + 'O_9', + 'Officer Grade O_9' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '6E50B04A-52DC-45C9-91D9-4A7B4FA1AB2A', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_9' + ), + 18000, + 18000, + 2000, + 500 + ); +-- O_10 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '7FA938AB-1C34-4666-A878-9B989C916D1A', + 'O_10', + 'Officer Grade O_10' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '1D6E34C3-8C6C-4D4F-8B91-F46BED3F5E85', + ( + SELECT id + FROM pay_grades + WHERE grade = 'O_10' + ), + 18000, + 18000, + 2000, + 500 + ); +-- W_1 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '6BADF8A0-B0EF-4E42-B827-7F63A3987A4B', + 'W_1', + 'Warrant Officer W_1' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '16F0F64F-728A-42A7-98B7-EA9BF289FE1A', + ( + SELECT id + FROM pay_grades + WHERE grade = 'W_1' + ), + 10000, + 12000, + 2000, + 500 + ); +-- W_2 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + 'A687A2E1-488C-4943-B9D9-3D645A2712F4', + 'W_2', + 'Warrant Officer W_2' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'A687A2E1-488C-4943-B9D9-3D645A2712F9', + ( + SELECT id + FROM pay_grades + WHERE grade = 'W_2' + ), + 12500, + 13500, + 2000, + 500 + ); +-- W_3 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '5A65FB1F-4245-4178-B6A7-CC504C9CBB37', + 'W_3', + 'Warrant Officer W_3' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '5A65FB1F-4245-4178-B6A7-CC504C9CBB38', + ( + SELECT id + FROM pay_grades + WHERE grade = 'W_3' + ), + 13000, + 14500, + 2000, + 500 + ); +-- W_4 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '74DB5649-CF66-4AF8-939B-D3D7F1F6B7C6', + 'W_4', + 'Warrant Officer W_4' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '74DB5649-CF66-4AF8-939B-D3D7F1F6B7C7', + ( + SELECT id + FROM pay_grades + WHERE grade = 'W_4' + ), + 14000, + 17000, + 2000, + 500 + ); +-- W_5 +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + 'EA8CB0E9-15FF-43B4-9E41-7168D01E7553', + 'W_5', + 'Warrant Officer W_5' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + 'EA8CB0E9-15FF-43B4-9E41-7168D01E7554', + ( + SELECT id + FROM pay_grades + WHERE grade = 'W_5' + ), + 16000, + 17500, + 2000, + 500 + ); +-- CIVILIAN EMPLOYEE +INSERT INTO pay_grades (id, grade, grade_description) +VALUES ( + '9E2CB9A5-ACE3-4235-9EE7-EBE4CC2A9BC9', + 'CIVILIAN_EMPLOYEE', + 'Civilian Employee' + ); +INSERT INTO hhg_allowances ( + id, + pay_grade_id, + total_weight_self, + total_weight_self_plus_dependents, + pro_gear_weight, + pro_gear_weight_spouse + ) +VALUES ( + '9E2CB9A5-ACE3-4235-9EE7-EBE4CC2A9BC1', + ( + SELECT id + FROM pay_grades + WHERE grade = 'CIVILIAN_EMPLOYEE' + ), + 18000, + 18000, + 2000, + 500 + ); \ No newline at end of file From 49e99b819b16f080f7131db16f1082fd13ba89e6 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 9 Dec 2024 20:34:07 +0000 Subject: [PATCH 002/113] initial models and tests for the entitlement refactor --- pkg/models/hhg_allowance.go | 39 +++++++++++++++++++++++ pkg/models/hhg_allowance_test.go | 53 ++++++++++++++++++++++++++++++++ pkg/models/pay_grade.go | 30 ++++++++++++++++++ pkg/models/pay_grade_test.go | 25 +++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 pkg/models/hhg_allowance.go create mode 100644 pkg/models/hhg_allowance_test.go create mode 100644 pkg/models/pay_grade.go create mode 100644 pkg/models/pay_grade_test.go diff --git a/pkg/models/hhg_allowance.go b/pkg/models/hhg_allowance.go new file mode 100644 index 00000000000..a0a99a5186b --- /dev/null +++ b/pkg/models/hhg_allowance.go @@ -0,0 +1,39 @@ +package models + +import ( + "time" + + "github.com/gobuffalo/pop/v6" + "github.com/gobuffalo/validate/v3" + "github.com/gobuffalo/validate/v3/validators" + "github.com/gofrs/uuid" +) + +// HHGAllowance the allowance in weights for a given pay grade +type HHGAllowance struct { + ID uuid.UUID `json:"id" db:"id"` + PayGradeID uuid.UUID `json:"pay_grade_id" db:"pay_grade_id"` + PayGrade PayGrade `belongs_to:"pay_grades" fk_id:"pay_grade_id"` + TotalWeightSelf int `json:"total_weight_self" db:"total_weight_self"` + TotalWeightSelfPlusDependents int `json:"total_weight_self_plus_dependents" db:"total_weight_self_plus_dependents"` + ProGearWeight int `json:"pro_gear_weight" db:"pro_gear_weight"` + ProGearWeightSpouse int `json:"pro_gear_weight_spouse" db:"pro_gear_weight_spouse"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` +} + +// Validate gets run every time you call a "pop.Validate*" method. +// Adjust validations as needed based on business rules. +func (h HHGAllowance) Validate(_ *pop.Connection) (*validate.Errors, error) { + return validate.Validate( + &validators.UUIDIsPresent{Name: "PayGradeID", Field: h.PayGradeID}, + // Make sure we don't somehow get a negative value + &validators.IntIsGreaterThan{Name: "TotalWeightSelf", Field: h.TotalWeightSelf, Compared: -1}, + &validators.IntIsGreaterThan{Name: "TotalWeightSelfPlusDependents", Field: h.TotalWeightSelfPlusDependents, Compared: -1}, + &validators.IntIsGreaterThan{Name: "ProGearWeight", Field: h.ProGearWeight, Compared: -1}, + &validators.IntIsGreaterThan{Name: "ProGearWeightSpouse", Field: h.ProGearWeightSpouse, Compared: -1}, + ), nil +} + +// HHGAllowances is a slice of HHGAllowance +type HHGAllowances []HHGAllowance diff --git a/pkg/models/hhg_allowance_test.go b/pkg/models/hhg_allowance_test.go new file mode 100644 index 00000000000..f3df8346a95 --- /dev/null +++ b/pkg/models/hhg_allowance_test.go @@ -0,0 +1,53 @@ +package models_test + +import ( + "github.com/gofrs/uuid" + + m "github.com/transcom/mymove/pkg/models" +) + +func (suite *ModelSuite) TestBasicHHGAllowanceInstantiation() { + + newHHGAllowance := &m.HHGAllowance{ + PayGradeID: uuid.Must(uuid.NewV4()), + TotalWeightSelf: 5000, + TotalWeightSelfPlusDependents: 8000, + ProGearWeight: 2000, + ProGearWeightSpouse: 500, + } + + verrs, err := newHHGAllowance.Validate(nil) + + suite.NoError(err) + suite.False(verrs.HasAny(), "Error validating model") +} + +func (suite *ModelSuite) TestEmptyHHGAllowanceInstantiation() { + newHHGAllowance := m.HHGAllowance{} + + expErrors := map[string][]string{ + "pay_grade_id": {"PayGradeID can not be blank."}, + } + + suite.verifyValidationErrors(&newHHGAllowance, expErrors) +} + +// Test validation fields that pass when empty but fail with faulty values +func (suite *ModelSuite) TestFaultyHHGAllowanceInstantiation() { + newHHGAllowance := m.HHGAllowance{ + PayGradeID: uuid.Must(uuid.NewV4()), + TotalWeightSelf: -1, + TotalWeightSelfPlusDependents: -1, + ProGearWeight: -1, + ProGearWeightSpouse: -1, + } + + expErrors := map[string][]string{ + "total_weight_self": {"-1 is not greater than -1."}, + "total_weight_self_plus_dependents": {"-1 is not greater than -1."}, + "pro_gear_weight": {"-1 is not greater than -1."}, + "pro_gear_weight_spouse": {"-1 is not greater than -1."}, + } + + suite.verifyValidationErrors(&newHHGAllowance, expErrors) +} diff --git a/pkg/models/pay_grade.go b/pkg/models/pay_grade.go new file mode 100644 index 00000000000..9802d18c820 --- /dev/null +++ b/pkg/models/pay_grade.go @@ -0,0 +1,30 @@ +package models + +import ( + "time" + + "github.com/gobuffalo/pop/v6" + "github.com/gobuffalo/validate/v3" + "github.com/gobuffalo/validate/v3/validators" + "github.com/gofrs/uuid" +) + +// PayGrade represents a customer's pay grade (Including civilian) +type PayGrade struct { + ID uuid.UUID `json:"id" db:"id"` + Grade string `json:"grade" db:"grade"` + GradeDescription *string `json:"grade_description" db:"grade_description"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` + DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"` +} + +// Validate gets run every time you call a "pop.Validate*" method. +func (pg PayGrade) Validate(_ *pop.Connection) (*validate.Errors, error) { + return validate.Validate( + &validators.StringIsPresent{Name: "Grade", Field: pg.Grade}, + ), nil +} + +// PayGrades is a slice of PayGrade +type PayGrades []PayGrade diff --git a/pkg/models/pay_grade_test.go b/pkg/models/pay_grade_test.go new file mode 100644 index 00000000000..3a5cf233bc2 --- /dev/null +++ b/pkg/models/pay_grade_test.go @@ -0,0 +1,25 @@ +package models_test + +import ( + m "github.com/transcom/mymove/pkg/models" +) + +func (suite *ModelSuite) TestBasicPayGradeInstantiation() { + newPayGrade := &m.PayGrade{ + Grade: "NewGrade", + } + + verrs, err := newPayGrade.Validate(nil) + + suite.NoError(err) + suite.False(verrs.HasAny(), "Error validating model") +} + +func (suite *ModelSuite) TestEmptyPayGradeInstantiation() { + newPayGrade := m.PayGrade{} + + expErrors := map[string][]string{ + "grade": {"Grade can not be blank."}, + } + suite.verifyValidationErrors(&newPayGrade, expErrors) +} From 48c2d0e58a16c480bb7b86a112076379c11162b2 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Wed, 11 Dec 2024 15:46:25 +0000 Subject: [PATCH 003/113] fix migration and models --- .../20241209121924_entitlements_refactor.up.sql | 8 ++++++-- pkg/models/ghc_entitlements.go | 2 ++ pkg/models/hhg_allowance.go | 8 ++++++-- pkg/models/pay_grade.go | 13 ++++++------- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/migrations/app/schema/20241209121924_entitlements_refactor.up.sql b/migrations/app/schema/20241209121924_entitlements_refactor.up.sql index 4535b73af22..3620e24bef0 100644 --- a/migrations/app/schema/20241209121924_entitlements_refactor.up.sql +++ b/migrations/app/schema/20241209121924_entitlements_refactor.up.sql @@ -7,7 +7,9 @@ ADD COLUMN IF NOT EXISTS is_weight_restricted boolean NOT NULL DEFAULT false, CREATE TABLE IF NOT EXISTS pay_grades ( id uuid PRIMARY KEY NOT NULL, grade text NOT NULL UNIQUE, - grade_description text + grade_description text, + created_at timestamp NOT NULL DEFAULT NOW(), + updated_at timestamp NOT NULL DEFAULT NOW() ); -- Create household goods allowances table CREATE TABLE IF NOT EXISTS hhg_allowances ( @@ -16,7 +18,9 @@ CREATE TABLE IF NOT EXISTS hhg_allowances ( total_weight_self int NOT NULL, total_weight_self_plus_dependents int NOT NULL, pro_gear_weight int NOT NULL, - pro_gear_weight_spouse int NOT NULL + pro_gear_weight_spouse int NOT NULL, + created_at timestamp NOT NULL DEFAULT NOW(), + updated_at timestamp NOT NULL DEFAULT NOW() ); -- Insert Max HHG allowance app value -- camel case to match the existing standaloneCrateCap parameter diff --git a/pkg/models/ghc_entitlements.go b/pkg/models/ghc_entitlements.go index 86e1f9d3c18..7708f4ef9ea 100644 --- a/pkg/models/ghc_entitlements.go +++ b/pkg/models/ghc_entitlements.go @@ -27,6 +27,8 @@ type Entitlement struct { OrganizationalClothingAndIndividualEquipment bool `db:"organizational_clothing_and_individual_equipment"` ProGearWeight int `db:"pro_gear_weight"` ProGearWeightSpouse int `db:"pro_gear_weight_spouse"` + IsWeightRestricted bool `db:"is_weight_restricted"` + WeightRestriction *int `db:"weight_restriction"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` } diff --git a/pkg/models/hhg_allowance.go b/pkg/models/hhg_allowance.go index a0a99a5186b..d9c3972637f 100644 --- a/pkg/models/hhg_allowance.go +++ b/pkg/models/hhg_allowance.go @@ -22,8 +22,7 @@ type HHGAllowance struct { UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } -// Validate gets run every time you call a "pop.Validate*" method. -// Adjust validations as needed based on business rules. +// Validate gets run every time you call a "pop.Validate*" method func (h HHGAllowance) Validate(_ *pop.Connection) (*validate.Errors, error) { return validate.Validate( &validators.UUIDIsPresent{Name: "PayGradeID", Field: h.PayGradeID}, @@ -37,3 +36,8 @@ func (h HHGAllowance) Validate(_ *pop.Connection) (*validate.Errors, error) { // HHGAllowances is a slice of HHGAllowance type HHGAllowances []HHGAllowance + +// TableName overrides the table name used by Pop. +func (h HHGAllowance) TableName() string { + return "hhg_allowances" +} diff --git a/pkg/models/pay_grade.go b/pkg/models/pay_grade.go index 9802d18c820..a427e0d7dcf 100644 --- a/pkg/models/pay_grade.go +++ b/pkg/models/pay_grade.go @@ -11,15 +11,14 @@ import ( // PayGrade represents a customer's pay grade (Including civilian) type PayGrade struct { - ID uuid.UUID `json:"id" db:"id"` - Grade string `json:"grade" db:"grade"` - GradeDescription *string `json:"grade_description" db:"grade_description"` - CreatedAt time.Time `json:"created_at" db:"created_at"` - UpdatedAt time.Time `json:"updated_at" db:"updated_at"` - DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"` + ID uuid.UUID `json:"id" db:"id"` + Grade string `json:"grade" db:"grade"` + GradeDescription *string `json:"grade_description" db:"grade_description"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } -// Validate gets run every time you call a "pop.Validate*" method. +// Validate gets run every time you call a "pop.Validate*" method func (pg PayGrade) Validate(_ *pop.Connection) (*validate.Errors, error) { return validate.Validate( &validators.StringIsPresent{Name: "Grade", Field: pg.Grade}, From ea6081ba5aef082077c0bce588600a3a86573905 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Wed, 11 Dec 2024 15:52:07 +0000 Subject: [PATCH 004/113] initial factory and service object setup --- pkg/factory/entitlement_factory.go | 133 ++++++++++++++++++ pkg/factory/entitlement_factory_test.go | 109 ++++++++++++++ pkg/factory/shared.go | 4 + pkg/services/entitlements.go | 23 +++ .../entitlements/entitlements_service_test.go | 27 ++++ .../entitlements/weight_allotment_fetcher.go | 36 +++++ .../weight_allotment_fetcher_test.go | 37 +++++ .../entitlements/weight_restrictor.go | 78 ++++++++++ .../entitlements/weight_restrictor_test.go | 104 ++++++++++++++ 9 files changed, 551 insertions(+) create mode 100644 pkg/services/entitlements.go create mode 100644 pkg/services/entitlements/entitlements_service_test.go create mode 100644 pkg/services/entitlements/weight_allotment_fetcher.go create mode 100644 pkg/services/entitlements/weight_allotment_fetcher_test.go create mode 100644 pkg/services/entitlements/weight_restrictor.go create mode 100644 pkg/services/entitlements/weight_restrictor_test.go diff --git a/pkg/factory/entitlement_factory.go b/pkg/factory/entitlement_factory.go index 2090eb20538..2d656bec89a 100644 --- a/pkg/factory/entitlement_factory.go +++ b/pkg/factory/entitlement_factory.go @@ -73,3 +73,136 @@ func BuildEntitlement(db *pop.Connection, customs []Customization, traits []Trai return entitlement } + +func BuildPayGrade(db *pop.Connection, customs []Customization, traits []Trait) models.PayGrade { + customs = setupCustomizations(customs, traits) + + // Find Pay Grade Customization and extract the custom Pay Grade + var cPayGrade models.PayGrade + if result := findValidCustomization(customs, PayGrade); result != nil { + cPayGrade = result.Model.(models.PayGrade) + if result.LinkOnly { + return cPayGrade + } + } + + // Check if the Grade already exists + var existingPayGrade models.PayGrade + if db != nil { + err := db.Where("grade = ?", cPayGrade.Grade).First(&existingPayGrade) + if err == nil { + return existingPayGrade + } + } + + // Create default Pay Grade + payGrade := models.PayGrade{ + Grade: "E-5", + GradeDescription: models.StringPointer("Enlisted Grade E-5"), + } + + // Overwrite default values with those from custom Pay Grade + testdatagen.MergeModels(&payGrade, cPayGrade) + + if db != nil { + mustCreate(db, &payGrade) + } + + return payGrade +} + +func BuildHHGAllowance(db *pop.Connection, customs []Customization, traits []Trait) models.HHGAllowance { + customs = setupCustomizations(customs, traits) + + // Find HHG Allowance Customization and extract the custom HHG Allowance + var cHHGAllowance models.HHGAllowance + if result := findValidCustomization(customs, HHGAllowance); result != nil { + cHHGAllowance = result.Model.(models.HHGAllowance) + if result.LinkOnly { + return cHHGAllowance + } + } + + // Check if Allowance with this Grade already exists + var existingHHGAllowance models.HHGAllowance + if db != nil { + err := db.Where("pay_grade_id = ?", cHHGAllowance.PayGradeID).First(&existingHHGAllowance) + if err == nil { + return existingHHGAllowance + } + } + + // Create a default HHG Allowance with default pay grade + payGrade := BuildPayGrade(db, customs, traits) + defaultWeightData := getDefaultWeightData(payGrade.Grade) + + hhgAllowance := models.HHGAllowance{ + PayGradeID: payGrade.ID, + PayGrade: payGrade, + TotalWeightSelf: defaultWeightData.TotalWeightSelf, + TotalWeightSelfPlusDependents: defaultWeightData.TotalWeightSelfPlusDependents, + ProGearWeight: defaultWeightData.ProGearWeight, + ProGearWeightSpouse: defaultWeightData.ProGearWeightSpouse, + } + + // Overwrite default values with those from custom HHG Allowance + testdatagen.MergeModels(&hhgAllowance, cHHGAllowance) + + if db != nil { + mustCreate(db, &hhgAllowance) + } + + return hhgAllowance +} + +// Helper function to retrieve default weight data by grade +func getDefaultWeightData(grade string) struct { + TotalWeightSelf int + TotalWeightSelfPlusDependents int + ProGearWeight int + ProGearWeightSpouse int +} { + if data, ok := knownAllowances[grade]; ok { + return data + } + return knownAllowances["EMPTY"] // Default to EMPTY if grade not found. This is just dummy default data +} + +// Default allowances CAO December 2024 +var knownAllowances = map[string]struct { + TotalWeightSelf int + TotalWeightSelfPlusDependents int + ProGearWeight int + ProGearWeightSpouse int +}{ + "EMPTY": {0, 0, 0, 0}, + "ACADEMY_CADET": {350, 350, 0, 0}, + "MIDSHIPMAN": {350, 350, 0, 0}, + "AVIATION_CADET": {7000, 8000, 2000, 500}, + "E-1": {5000, 8000, 2000, 500}, + "E-2": {5000, 8000, 2000, 500}, + "E-3": {5000, 8000, 2000, 500}, + "E-4": {7000, 8000, 2000, 500}, + "E-5": {7000, 9000, 2000, 500}, + "E-6": {8000, 11000, 2000, 500}, + "E-7": {11000, 13000, 2000, 500}, + "E-8": {12000, 14000, 2000, 500}, + "E-9": {13000, 15000, 2000, 500}, + "E-9SPECIALSENIORENLISTED": {14000, 17000, 2000, 500}, + "O-1ACADEMYGRADUATE": {10000, 12000, 2000, 500}, + "O-2": {12500, 13500, 2000, 500}, + "O-3": {13000, 14500, 2000, 500}, + "O-4": {14000, 17000, 2000, 500}, + "O-5": {16000, 17500, 2000, 500}, + "O-6": {18000, 18000, 2000, 500}, + "O-7": {18000, 18000, 2000, 500}, + "O-8": {18000, 18000, 2000, 500}, + "O-9": {18000, 18000, 2000, 500}, + "O-10": {18000, 18000, 2000, 500}, + "W-1": {10000, 12000, 2000, 500}, + "W-2": {12500, 13500, 2000, 500}, + "W-3": {13000, 14500, 2000, 500}, + "W-4": {14000, 17000, 2000, 500}, + "W-5": {16000, 17500, 2000, 500}, + "CIVILIAN_EMPLOYEE": {18000, 18000, 2000, 500}, +} diff --git a/pkg/factory/entitlement_factory_test.go b/pkg/factory/entitlement_factory_test.go index dffdd89ca39..4b4212bfc7c 100644 --- a/pkg/factory/entitlement_factory_test.go +++ b/pkg/factory/entitlement_factory_test.go @@ -149,3 +149,112 @@ func (suite *FactorySuite) TestBuildEntitlement() { }) } + +func (suite *FactorySuite) TestBuildPayGrade() { + suite.Run("Successful creation of PayGrade with default values", func() { + // Default grade should be "E-5" + payGrade := BuildPayGrade(suite.DB(), nil, nil) + + suite.NotNil(payGrade.ID) + suite.Equal("E-5", payGrade.Grade) + suite.Equal("Enlisted Grade E-5", *payGrade.GradeDescription) + + pgCount, err := suite.DB().Count(models.PayGrade{}) + suite.NoError(err) + suite.True(pgCount > 0) + }) + + suite.Run("BuildPayGrade with customization", func() { + customGrade := "X-5" + customDescription := "Custom Grade X-5" + customPayGrade := models.PayGrade{ + Grade: customGrade, + GradeDescription: &customDescription, + } + + payGrade := BuildPayGrade( + suite.DB(), + []Customization{ + {Model: customPayGrade}, + }, + nil, + ) + + suite.Equal(customGrade, payGrade.Grade) + suite.Equal(customDescription, *payGrade.GradeDescription) + }) + + suite.Run("Finds existing record", func() { + + persistedPayGrade := BuildPayGrade(suite.DB(), nil, nil) + + pg := BuildPayGrade(suite.DB(), []Customization{ + { + Model: persistedPayGrade, + LinkOnly: true, + }, + }, nil) + + suite.Equal(persistedPayGrade.ID, pg.ID) + suite.Equal(persistedPayGrade.Grade, pg.Grade) + + }) +} + +func (suite *FactorySuite) TestBuildHHGAllowance() { + suite.Run("Successful creation of HHGAllowance with default values", func() { + // Default allowance and grade of E-5 + hhgAllowance := BuildHHGAllowance(suite.DB(), nil, nil) + suite.NotNil(hhgAllowance.PayGradeID) + suite.NotEmpty(hhgAllowance.PayGrade) + suite.NotEmpty(hhgAllowance.ProGearWeight) + suite.NotEmpty(hhgAllowance.ProGearWeightSpouse) + suite.NotEmpty(hhgAllowance.TotalWeightSelf) + suite.NotEmpty(hhgAllowance.TotalWeightSelfPlusDependents) + }) + + suite.Run("BuildHHGAllowance with customization", func() { + hhgAllowance := BuildHHGAllowance( + suite.DB(), + []Customization{ + {Model: models.HHGAllowance{ + TotalWeightSelf: 8000, + TotalWeightSelfPlusDependents: 12000, + ProGearWeight: 3000, + ProGearWeightSpouse: 600, + }}, + }, + nil, + ) + + // E-5 default allowances + suite.Equal(8000, hhgAllowance.TotalWeightSelf) + suite.Equal(12000, hhgAllowance.TotalWeightSelfPlusDependents) + suite.Equal(3000, hhgAllowance.ProGearWeight) + suite.Equal(600, hhgAllowance.ProGearWeightSpouse) + }) + + suite.Run("Finds existing record", func() { + pg := BuildPayGrade(suite.DB(), nil, nil) + + existingHhg := models.HHGAllowance{ + PayGradeID: pg.ID, + TotalWeightSelf: 8000, + TotalWeightSelfPlusDependents: 12000, + ProGearWeight: 3000, + ProGearWeightSpouse: 600, + } + suite.MustCreate(&existingHhg) + + newHhg := BuildHHGAllowance( + suite.DB(), + []Customization{ + {Model: models.HHGAllowance{PayGradeID: pg.ID}}, + }, + nil, + ) + + suite.Equal(existingHhg.ID, newHhg.ID) + suite.Equal(3000, newHhg.ProGearWeight) + }) +} diff --git a/pkg/factory/shared.go b/pkg/factory/shared.go index 8e1077dd692..c8e2c6d402d 100644 --- a/pkg/factory/shared.go +++ b/pkg/factory/shared.go @@ -53,6 +53,8 @@ var CustomerSupportRemark CustomType = "CustomerSupportRemark" var Document CustomType = "Document" var DutyLocation CustomType = "DutyLocation" var Entitlement CustomType = "Entitlement" +var HHGAllowance CustomType = "HHGAllowance" +var PayGrade CustomType = "PayGrade" var EvaluationReport CustomType = "EvaluationReport" var LineOfAccounting CustomType = "LineOfAccounting" var MobileHome CustomType = "MobileHome" @@ -115,6 +117,8 @@ var defaultTypesMap = map[string]CustomType{ "models.Document": Document, "models.DutyLocation": DutyLocation, "models.Entitlement": Entitlement, + "models.PayGrade": PayGrade, + "models.HHGAllowance": HHGAllowance, "models.EvaluationReport": EvaluationReport, "models.LineOfAccounting": LineOfAccounting, "models.MobileHome": MobileHome, diff --git a/pkg/services/entitlements.go b/pkg/services/entitlements.go new file mode 100644 index 00000000000..08fecea1914 --- /dev/null +++ b/pkg/services/entitlements.go @@ -0,0 +1,23 @@ +package services + +import ( + "github.com/gofrs/uuid" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/models" +) + +// The weight allotment fetcher interface helps identify weight allotments (allowances) for a given grade +// +//go:generate mockery --name WeightAllotmentFetcher +type WeightAllotmentFetcher interface { + GetWeightAllotment(appCtx appcontext.AppContext, grade string) (*models.HHGAllowance, error) +} + +// The weight restrictor interface helps apply weight restrictions to entitlements +// +//go:generate mockery --name WeightRestrictor +type WeightRestrictor interface { + ApplyWeightRestrictionToEntitlement(appCtx appcontext.AppContext, entitlementID uuid.UUID, weightRestriction int) error + RemoveWeightRestrictionFromEntitlement(appCtx appcontext.AppContext, entitlementID uuid.UUID) error +} diff --git a/pkg/services/entitlements/entitlements_service_test.go b/pkg/services/entitlements/entitlements_service_test.go new file mode 100644 index 00000000000..241ebd258cd --- /dev/null +++ b/pkg/services/entitlements/entitlements_service_test.go @@ -0,0 +1,27 @@ +package entitlements + +import ( + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/suite" + + "github.com/transcom/mymove/pkg/testingsuite" +) + +// EntitlementsServiceSuite is a suite for testing entitlement service objects +type EntitlementsServiceSuite struct { + *testingsuite.PopTestSuite + fs *afero.Afero +} + +func TestEntitlementsServiceSuite(t *testing.T) { + var f = afero.NewMemMapFs() + file := &afero.Afero{Fs: f} + ts := &EntitlementsServiceSuite{ + PopTestSuite: testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), testingsuite.WithPerTestTransaction()), + fs: file, + } + suite.Run(t, ts) + ts.PopTestSuite.TearDown() +} diff --git a/pkg/services/entitlements/weight_allotment_fetcher.go b/pkg/services/entitlements/weight_allotment_fetcher.go new file mode 100644 index 00000000000..36ba853ce1e --- /dev/null +++ b/pkg/services/entitlements/weight_allotment_fetcher.go @@ -0,0 +1,36 @@ +package entitlements + +import ( + "fmt" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/apperror" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" +) + +type weightAllotmentFetcher struct { +} + +// NewWeightAllotmentFetcher returns a new weight allotment fetcher +func NewWeightAllotmentFetcher() services.WeightAllotmentFetcher { + return &weightAllotmentFetcher{} +} + +func (waf *weightAllotmentFetcher) GetWeightAllotment(appCtx appcontext.AppContext, grade string) (*models.HHGAllowance, error) { + var hhgAllowance models.HHGAllowance + err := appCtx.DB(). + RawQuery(` + SELECT hhg_allowances.* + FROM hhg_allowances + INNER JOIN pay_grades ON hhg_allowances.pay_grade_id = pay_grades.id + WHERE pay_grades.grade = $1 + LIMIT 1 + `, grade). + First(&hhgAllowance) + if err != nil { + return nil, apperror.NewQueryError("HHGAllowance", err, fmt.Sprintf("Error retrieving HHG allowance for grade: %s", grade)) + } + + return &hhgAllowance, nil +} diff --git a/pkg/services/entitlements/weight_allotment_fetcher_test.go b/pkg/services/entitlements/weight_allotment_fetcher_test.go new file mode 100644 index 00000000000..c9d0c13d7ba --- /dev/null +++ b/pkg/services/entitlements/weight_allotment_fetcher_test.go @@ -0,0 +1,37 @@ +package entitlements + +import ( + "github.com/transcom/mymove/pkg/apperror" + "github.com/transcom/mymove/pkg/factory" +) + +func (suite *EntitlementsServiceSuite) TestGetWeightAllotment() { + suite.Run("If a weight allotment is fetched by grade, it should be returned", func() { + fetcher := NewWeightAllotmentFetcher() + + pg := factory.BuildPayGrade(suite.DB(), nil, nil) + hhgAllowance := factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + + allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), pg.Grade) + + suite.NoError(err) + suite.Equal(hhgAllowance.TotalWeightSelf, allotment.TotalWeightSelf) + suite.Equal(hhgAllowance.TotalWeightSelfPlusDependents, allotment.TotalWeightSelfPlusDependents) + suite.Equal(hhgAllowance.ProGearWeight, allotment.ProGearWeight) + suite.Equal(hhgAllowance.ProGearWeightSpouse, allotment.ProGearWeightSpouse) + }) + + suite.Run("If pay grade does not exist, return an error", func() { + fetcher := NewWeightAllotmentFetcher() + + allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "X-1") + suite.Error(err) + suite.IsType(apperror.QueryError{}, err) + suite.Nil(allotment) + }) +} diff --git a/pkg/services/entitlements/weight_restrictor.go b/pkg/services/entitlements/weight_restrictor.go new file mode 100644 index 00000000000..8aadedfe1e8 --- /dev/null +++ b/pkg/services/entitlements/weight_restrictor.go @@ -0,0 +1,78 @@ +package entitlements + +import ( + "fmt" + + "github.com/gofrs/uuid" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/apperror" + "github.com/transcom/mymove/pkg/services" +) + +type weightRestrictor struct { +} + +func NewWeightRestrictor() services.WeightRestrictor { + return &weightRestrictor{} +} + +func (wr *weightRestrictor) ApplyWeightRestrictionToEntitlement(appCtx appcontext.AppContext, entitlementID uuid.UUID, weightRestriction int) error { + + maxHhgAllowance, err := wr.fetchMaxHhgAllowance(appCtx) + if err != nil { + return err + } + + // Don't allow applying a weight restriction above teh max allowance, that's silly + if weightRestriction > maxHhgAllowance { + return apperror.NewInvalidInputError(entitlementID, fmt.Errorf("weight restriction %d exceeds max HHG allowance %d", weightRestriction, maxHhgAllowance), nil, "error applying weight restriction") + } + + // If we reached this spot we're good to apply the restriction to the entitlement + err = appCtx.DB(). + RawQuery(` + UPDATE entitlements + SET weight_restriction = $1, is_weight_restricted = true + WHERE id = $2 + `, weightRestriction, entitlementID). + Exec() + if err != nil { + return apperror.NewQueryError("Entitlements", err, "error updating weight restriction for entitlement") + } + + return nil +} + +func (wr *weightRestrictor) RemoveWeightRestrictionFromEntitlement(appCtx appcontext.AppContext, entitlementID uuid.UUID) error { + // Remove the restriction by setting weight_restriction = NULL and is_weight_restricted = false + err := appCtx.DB(). + RawQuery(` + UPDATE entitlements + SET weight_restriction = NULL, is_weight_restricted = false + WHERE id = $1 + `, entitlementID). + Exec() + if err != nil { + return apperror.NewQueryError("Entitlements", err, "error removing weight restriction for entitlement") + } + + return nil +} + +func (wr *weightRestrictor) fetchMaxHhgAllowance(appCtx appcontext.AppContext) (int, error) { + var maxHhgAllowance int + err := appCtx.DB(). + RawQuery(` + SELECT parameter_value::int + FROM application_parameters + WHERE parameter_name = $1 + LIMIT 1 + `, "maxHhgAllowance"). + First(&maxHhgAllowance) + + if err != nil { + return maxHhgAllowance, apperror.NewQueryError("ApplicationParameters", err, "error fetching max HHG allowance") + } + return maxHhgAllowance, nil +} diff --git a/pkg/services/entitlements/weight_restrictor_test.go b/pkg/services/entitlements/weight_restrictor_test.go new file mode 100644 index 00000000000..1ed3a3da5a8 --- /dev/null +++ b/pkg/services/entitlements/weight_restrictor_test.go @@ -0,0 +1,104 @@ +package entitlements + +import ( + "github.com/gofrs/uuid" + + "github.com/transcom/mymove/pkg/apperror" + "github.com/transcom/mymove/pkg/models" +) + +func (suite *EntitlementsServiceSuite) TestWeightRestrictor() { + + setupHhgAllowanceParameter := func() { + parameter := models.ApplicationParameters{ + ParameterName: models.StringPointer("maxHhgAllowance"), + ParameterValue: models.StringPointer("18000"), + } + suite.MustCreate(¶meter) + } + + suite.Run("Successfully apply a weight restriction within max allowance", func() { + setupHhgAllowanceParameter() + // Create a blank entitlement db entry, nothing fancy we just want to update columns + entitlement := models.Entitlement{ + ID: uuid.Must(uuid.NewV4()), + } + suite.MustCreate(&entitlement) + + // Set a weight restriction within allowance + restrictor := NewWeightRestrictor() + err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement.ID, 10000) + suite.NoError(err) + + // Fetch updated entitlement + var updatedEntitlement models.Entitlement + err = suite.DB().Find(&updatedEntitlement, entitlement.ID) + suite.NoError(err) + suite.True(updatedEntitlement.IsWeightRestricted) + suite.NotNil(updatedEntitlement.WeightRestriction) + suite.Equal(10000, *updatedEntitlement.WeightRestriction) + }) + + suite.Run("Attempt to apply restriction above max allowance, expect an error", func() { + setupHhgAllowanceParameter() + // Create a blank entitlement db entry, nothing fancy we just want to update columns + entitlement := models.Entitlement{ + ID: uuid.Must(uuid.NewV4()), + } + suite.MustCreate(&entitlement) + + // Set an impossible weight restriction + restrictor := NewWeightRestrictor() + err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement.ID, 20000) + suite.Error(err) + suite.IsType(apperror.InvalidInputError{}, err) + }) + + suite.Run("No maxHhgAllowance parameter found returns error", func() { + // Ensure no parameter with maxHhgAllowance exists + entitlement := models.Entitlement{ + ID: uuid.Must(uuid.NewV4()), + } + suite.MustCreate(&entitlement) + + restrictor := NewWeightRestrictor() + err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement.ID, 10000) + suite.Error(err) + suite.IsType(apperror.QueryError{}, err) + }) + + suite.Run("Successfully remove a weight restriction", func() { + setupHhgAllowanceParameter() + + // Create an entitlement with a restriction already applied + weightRestriction := 5000 + entitlement := models.Entitlement{ + ID: uuid.Must(uuid.NewV4()), + IsWeightRestricted: true, + WeightRestriction: &weightRestriction, + } + suite.MustCreate(&entitlement) + + restrictor := NewWeightRestrictor() + + // Remove the restriction + err := restrictor.RemoveWeightRestrictionFromEntitlement(suite.AppContextForTest(), entitlement.ID) + suite.NoError(err) + + // Fetch updated entitlement + var updatedEntitlement models.Entitlement + err = suite.DB().Find(&updatedEntitlement, entitlement.ID) + suite.NoError(err) + suite.False(updatedEntitlement.IsWeightRestricted) + suite.Nil(updatedEntitlement.WeightRestriction) + }) + + suite.Run("Fails on removing a weight restriction for an entitlement that does not exist", func() { + setupHhgAllowanceParameter() + + restrictor := NewWeightRestrictor() + + err := restrictor.RemoveWeightRestrictionFromEntitlement(suite.AppContextForTest(), uuid.Must(uuid.NewV4())) + suite.Error(err) + }) +} From f21e7d5ebe5e8176c490300e4778a835b66fa3af Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Wed, 11 Dec 2024 17:09:14 +0000 Subject: [PATCH 005/113] weight restrictor revamp --- pkg/services/entitlements.go | 6 +- .../entitlements/weight_restrictor.go | 80 +++++++++++++------ .../entitlements/weight_restrictor_test.go | 36 ++++----- 3 files changed, 73 insertions(+), 49 deletions(-) diff --git a/pkg/services/entitlements.go b/pkg/services/entitlements.go index 08fecea1914..266e871f402 100644 --- a/pkg/services/entitlements.go +++ b/pkg/services/entitlements.go @@ -1,8 +1,6 @@ package services import ( - "github.com/gofrs/uuid" - "github.com/transcom/mymove/pkg/appcontext" "github.com/transcom/mymove/pkg/models" ) @@ -18,6 +16,6 @@ type WeightAllotmentFetcher interface { // //go:generate mockery --name WeightRestrictor type WeightRestrictor interface { - ApplyWeightRestrictionToEntitlement(appCtx appcontext.AppContext, entitlementID uuid.UUID, weightRestriction int) error - RemoveWeightRestrictionFromEntitlement(appCtx appcontext.AppContext, entitlementID uuid.UUID) error + ApplyWeightRestrictionToEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, weightRestriction int, eTag string) (*models.Entitlement, error) + RemoveWeightRestrictionFromEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, eTag string) (*models.Entitlement, error) } diff --git a/pkg/services/entitlements/weight_restrictor.go b/pkg/services/entitlements/weight_restrictor.go index 8aadedfe1e8..8f68f993d92 100644 --- a/pkg/services/entitlements/weight_restrictor.go +++ b/pkg/services/entitlements/weight_restrictor.go @@ -3,10 +3,12 @@ package entitlements import ( "fmt" - "github.com/gofrs/uuid" + "github.com/pkg/errors" "github.com/transcom/mymove/pkg/appcontext" "github.com/transcom/mymove/pkg/apperror" + "github.com/transcom/mymove/pkg/etag" + "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" ) @@ -17,47 +19,75 @@ func NewWeightRestrictor() services.WeightRestrictor { return &weightRestrictor{} } -func (wr *weightRestrictor) ApplyWeightRestrictionToEntitlement(appCtx appcontext.AppContext, entitlementID uuid.UUID, weightRestriction int) error { +func (wr *weightRestrictor) ApplyWeightRestrictionToEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, weightRestriction int, eTag string) (*models.Entitlement, error) { + // First, fetch the latest version of the entitlement for etag check + var originalEntitlement models.Entitlement + err := appCtx.DB().Find(&originalEntitlement, entitlement.ID) + if err != nil { + return nil, apperror.NewQueryError("Entitlements", err, "error fetching entitlement") + } + + // verify ETag + if etag.GenerateEtag(originalEntitlement.UpdatedAt) != eTag { + return nil, apperror.NewPreconditionFailedError(originalEntitlement.ID, nil) + } maxHhgAllowance, err := wr.fetchMaxHhgAllowance(appCtx) if err != nil { - return err + return nil, err } // Don't allow applying a weight restriction above teh max allowance, that's silly if weightRestriction > maxHhgAllowance { - return apperror.NewInvalidInputError(entitlementID, fmt.Errorf("weight restriction %d exceeds max HHG allowance %d", weightRestriction, maxHhgAllowance), nil, "error applying weight restriction") + return nil, apperror.NewInvalidInputError(entitlement.ID, + fmt.Errorf("weight restriction %d exceeds max HHG allowance %d", weightRestriction, maxHhgAllowance), + nil, "error applying weight restriction") } - // If we reached this spot we're good to apply the restriction to the entitlement - err = appCtx.DB(). - RawQuery(` - UPDATE entitlements - SET weight_restriction = $1, is_weight_restricted = true - WHERE id = $2 - `, weightRestriction, entitlementID). - Exec() + // Update the restriction fields + originalEntitlement.IsWeightRestricted = true + originalEntitlement.WeightRestriction = &weightRestriction + + verrs, err := appCtx.DB().ValidateAndUpdate(&originalEntitlement) if err != nil { - return apperror.NewQueryError("Entitlements", err, "error updating weight restriction for entitlement") + return nil, apperror.NewQueryError("Entitlements", err, "error updating weight restriction for entitlement") + } + if verrs != nil && verrs.HasAny() { + return nil, apperror.NewInvalidInputError(originalEntitlement.ID, err, verrs, "invalid input while updating entitlement") } - return nil + return &originalEntitlement, nil } -func (wr *weightRestrictor) RemoveWeightRestrictionFromEntitlement(appCtx appcontext.AppContext, entitlementID uuid.UUID) error { - // Remove the restriction by setting weight_restriction = NULL and is_weight_restricted = false - err := appCtx.DB(). - RawQuery(` - UPDATE entitlements - SET weight_restriction = NULL, is_weight_restricted = false - WHERE id = $1 - `, entitlementID). - Exec() +func (wr *weightRestrictor) RemoveWeightRestrictionFromEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, eTag string) (*models.Entitlement, error) { + // Fetch the latest version of the entitlement for etag check + var originalEntitlement models.Entitlement + err := appCtx.DB().Find(&originalEntitlement, entitlement.ID) + if err != nil { + if errors.Cause(err).Error() == models.RecordNotFoundErrorString { + return nil, apperror.NewNotFoundError(entitlement.ID, "entitlement not found") + } + return nil, apperror.NewQueryError("Entitlements", err, "error fetching entitlement") + } + + // verify ETag + if etag.GenerateEtag(originalEntitlement.UpdatedAt) != eTag { + return nil, apperror.NewPreconditionFailedError(originalEntitlement.ID, nil) + } + + // Update the restriction fields + originalEntitlement.IsWeightRestricted = false + originalEntitlement.WeightRestriction = nil + + verrs, err := appCtx.DB().ValidateAndUpdate(&originalEntitlement) if err != nil { - return apperror.NewQueryError("Entitlements", err, "error removing weight restriction for entitlement") + return nil, apperror.NewQueryError("Entitlements", err, "error removing weight restriction for entitlement") + } + if verrs != nil && verrs.HasAny() { + return nil, apperror.NewInvalidInputError(originalEntitlement.ID, err, verrs, "invalid input while updating entitlement") } - return nil + return &originalEntitlement, nil } func (wr *weightRestrictor) fetchMaxHhgAllowance(appCtx appcontext.AppContext) (int, error) { diff --git a/pkg/services/entitlements/weight_restrictor_test.go b/pkg/services/entitlements/weight_restrictor_test.go index 1ed3a3da5a8..19168e7c55a 100644 --- a/pkg/services/entitlements/weight_restrictor_test.go +++ b/pkg/services/entitlements/weight_restrictor_test.go @@ -4,11 +4,11 @@ import ( "github.com/gofrs/uuid" "github.com/transcom/mymove/pkg/apperror" + "github.com/transcom/mymove/pkg/etag" "github.com/transcom/mymove/pkg/models" ) func (suite *EntitlementsServiceSuite) TestWeightRestrictor() { - setupHhgAllowanceParameter := func() { parameter := models.ApplicationParameters{ ParameterName: models.StringPointer("maxHhgAllowance"), @@ -27,13 +27,9 @@ func (suite *EntitlementsServiceSuite) TestWeightRestrictor() { // Set a weight restriction within allowance restrictor := NewWeightRestrictor() - err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement.ID, 10000) - suite.NoError(err) - - // Fetch updated entitlement - var updatedEntitlement models.Entitlement - err = suite.DB().Find(&updatedEntitlement, entitlement.ID) + updatedEntitlement, err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement, 10000, etag.GenerateEtag(entitlement.UpdatedAt)) suite.NoError(err) + suite.NotNil(updatedEntitlement) suite.True(updatedEntitlement.IsWeightRestricted) suite.NotNil(updatedEntitlement.WeightRestriction) suite.Equal(10000, *updatedEntitlement.WeightRestriction) @@ -49,21 +45,22 @@ func (suite *EntitlementsServiceSuite) TestWeightRestrictor() { // Set an impossible weight restriction restrictor := NewWeightRestrictor() - err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement.ID, 20000) + updatedEntitlement, err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement, 20000, etag.GenerateEtag(entitlement.UpdatedAt)) suite.Error(err) + suite.Nil(updatedEntitlement) suite.IsType(apperror.InvalidInputError{}, err) }) suite.Run("No maxHhgAllowance parameter found returns error", func() { - // Ensure no parameter with maxHhgAllowance exists entitlement := models.Entitlement{ ID: uuid.Must(uuid.NewV4()), } suite.MustCreate(&entitlement) restrictor := NewWeightRestrictor() - err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement.ID, 10000) + updatedEntitlement, err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement, 10000, etag.GenerateEtag(entitlement.UpdatedAt)) suite.Error(err) + suite.Nil(updatedEntitlement) suite.IsType(apperror.QueryError{}, err) }) @@ -80,15 +77,9 @@ func (suite *EntitlementsServiceSuite) TestWeightRestrictor() { suite.MustCreate(&entitlement) restrictor := NewWeightRestrictor() - - // Remove the restriction - err := restrictor.RemoveWeightRestrictionFromEntitlement(suite.AppContextForTest(), entitlement.ID) - suite.NoError(err) - - // Fetch updated entitlement - var updatedEntitlement models.Entitlement - err = suite.DB().Find(&updatedEntitlement, entitlement.ID) + updatedEntitlement, err := restrictor.RemoveWeightRestrictionFromEntitlement(suite.AppContextForTest(), entitlement, etag.GenerateEtag(entitlement.UpdatedAt)) suite.NoError(err) + suite.NotNil(updatedEntitlement) suite.False(updatedEntitlement.IsWeightRestricted) suite.Nil(updatedEntitlement.WeightRestriction) }) @@ -96,9 +87,14 @@ func (suite *EntitlementsServiceSuite) TestWeightRestrictor() { suite.Run("Fails on removing a weight restriction for an entitlement that does not exist", func() { setupHhgAllowanceParameter() - restrictor := NewWeightRestrictor() + entitlement := models.Entitlement{ + ID: uuid.Must(uuid.NewV4()), + } - err := restrictor.RemoveWeightRestrictionFromEntitlement(suite.AppContextForTest(), uuid.Must(uuid.NewV4())) + restrictor := NewWeightRestrictor() + updatedEntitlement, err := restrictor.RemoveWeightRestrictionFromEntitlement(suite.AppContextForTest(), entitlement, etag.GenerateEtag(entitlement.UpdatedAt)) suite.Error(err) + suite.Nil(updatedEntitlement) + suite.IsType(apperror.NotFoundError{}, err) }) } From fdb17d3017fd15d276da4c3b7201e50cf2eadd98 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Wed, 11 Dec 2024 17:26:39 +0000 Subject: [PATCH 006/113] get all weight allotments --- pkg/services/entitlements.go | 1 + .../entitlements/weight_allotment_fetcher.go | 17 +++++++++ .../weight_allotment_fetcher_test.go | 36 +++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/pkg/services/entitlements.go b/pkg/services/entitlements.go index 266e871f402..ddbbc553c83 100644 --- a/pkg/services/entitlements.go +++ b/pkg/services/entitlements.go @@ -10,6 +10,7 @@ import ( //go:generate mockery --name WeightAllotmentFetcher type WeightAllotmentFetcher interface { GetWeightAllotment(appCtx appcontext.AppContext, grade string) (*models.HHGAllowance, error) + GetAllWeightAllotments(appCtx appcontext.AppContext) (models.HHGAllowances, error) } // The weight restrictor interface helps apply weight restrictions to entitlements diff --git a/pkg/services/entitlements/weight_allotment_fetcher.go b/pkg/services/entitlements/weight_allotment_fetcher.go index 36ba853ce1e..7d72c7c9cb8 100644 --- a/pkg/services/entitlements/weight_allotment_fetcher.go +++ b/pkg/services/entitlements/weight_allotment_fetcher.go @@ -34,3 +34,20 @@ func (waf *weightAllotmentFetcher) GetWeightAllotment(appCtx appcontext.AppConte return &hhgAllowance, nil } + +func (waf *weightAllotmentFetcher) GetAllWeightAllotments(appCtx appcontext.AppContext) (models.HHGAllowances, error) { + var hhgAllowances models.HHGAllowances + err := appCtx.DB(). + RawQuery(` + SELECT hhg_allowances.* + FROM hhg_allowances + INNER JOIN pay_grades ON hhg_allowances.pay_grade_id = pay_grades.id + ORDER BY pay_grades.grade ASC + `). + All(&hhgAllowances) + if err != nil { + return nil, apperror.NewQueryError("HHGAllowances", err, "Error retrieving all HHG allowances") + } + + return hhgAllowances, nil +} diff --git a/pkg/services/entitlements/weight_allotment_fetcher_test.go b/pkg/services/entitlements/weight_allotment_fetcher_test.go index c9d0c13d7ba..5bc5f3fc6d3 100644 --- a/pkg/services/entitlements/weight_allotment_fetcher_test.go +++ b/pkg/services/entitlements/weight_allotment_fetcher_test.go @@ -3,6 +3,7 @@ package entitlements import ( "github.com/transcom/mymove/pkg/apperror" "github.com/transcom/mymove/pkg/factory" + "github.com/transcom/mymove/pkg/models" ) func (suite *EntitlementsServiceSuite) TestGetWeightAllotment() { @@ -35,3 +36,38 @@ func (suite *EntitlementsServiceSuite) TestGetWeightAllotment() { suite.Nil(allotment) }) } + +func (suite *EntitlementsServiceSuite) TestGetAllWeightAllotments() { + suite.Run("Successfully fetch all weight allotments", func() { + fetcher := NewWeightAllotmentFetcher() + + // Make the default E-5 + e5 := factory.BuildPayGrade(suite.DB(), nil, nil) + factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: e5, + LinkOnly: true, + }, + }, nil) + + // Make an E-6 + e6 := factory.BuildPayGrade(suite.DB(), []factory.Customization{ + { + Model: models.PayGrade{ + Grade: "E-6", + }, + }, + }, nil) + factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: e6, + LinkOnly: true, + }, + }, nil) + + // Assert both can be fetched + allotments, err := fetcher.GetAllWeightAllotments(suite.AppContextForTest()) + suite.NoError(err) + suite.Len(allotments, 2) + }) +} From 3bd3ff26b621d7edfca83eac4ee66bfb1065849a Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Wed, 11 Dec 2024 17:32:49 +0000 Subject: [PATCH 007/113] fix waf --- .../entitlements/weight_allotment_fetcher.go | 7 +------ .../weight_allotment_fetcher_test.go | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pkg/services/entitlements/weight_allotment_fetcher.go b/pkg/services/entitlements/weight_allotment_fetcher.go index 7d72c7c9cb8..e282947120a 100644 --- a/pkg/services/entitlements/weight_allotment_fetcher.go +++ b/pkg/services/entitlements/weight_allotment_fetcher.go @@ -38,12 +38,7 @@ func (waf *weightAllotmentFetcher) GetWeightAllotment(appCtx appcontext.AppConte func (waf *weightAllotmentFetcher) GetAllWeightAllotments(appCtx appcontext.AppContext) (models.HHGAllowances, error) { var hhgAllowances models.HHGAllowances err := appCtx.DB(). - RawQuery(` - SELECT hhg_allowances.* - FROM hhg_allowances - INNER JOIN pay_grades ON hhg_allowances.pay_grade_id = pay_grades.id - ORDER BY pay_grades.grade ASC - `). + Eager("PayGrade"). All(&hhgAllowances) if err != nil { return nil, apperror.NewQueryError("HHGAllowances", err, "Error retrieving all HHG allowances") diff --git a/pkg/services/entitlements/weight_allotment_fetcher_test.go b/pkg/services/entitlements/weight_allotment_fetcher_test.go index 5bc5f3fc6d3..b8fe65e42e7 100644 --- a/pkg/services/entitlements/weight_allotment_fetcher_test.go +++ b/pkg/services/entitlements/weight_allotment_fetcher_test.go @@ -43,7 +43,7 @@ func (suite *EntitlementsServiceSuite) TestGetAllWeightAllotments() { // Make the default E-5 e5 := factory.BuildPayGrade(suite.DB(), nil, nil) - factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + e5Allowance := factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ { Model: e5, LinkOnly: true, @@ -58,7 +58,7 @@ func (suite *EntitlementsServiceSuite) TestGetAllWeightAllotments() { }, }, }, nil) - factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + e6Allowance := factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ { Model: e6, LinkOnly: true, @@ -69,5 +69,19 @@ func (suite *EntitlementsServiceSuite) TestGetAllWeightAllotments() { allotments, err := fetcher.GetAllWeightAllotments(suite.AppContextForTest()) suite.NoError(err) suite.Len(allotments, 2) + + // Check the first allotment (E-5) + suite.Equal(e5Allowance.TotalWeightSelf, allotments[0].TotalWeightSelf) + suite.Equal(e5Allowance.TotalWeightSelfPlusDependents, allotments[0].TotalWeightSelfPlusDependents) + suite.Equal(e5Allowance.ProGearWeight, allotments[0].ProGearWeight) + suite.Equal(e5Allowance.ProGearWeightSpouse, allotments[0].ProGearWeightSpouse) + suite.Equal(e5.Grade, allotments[0].PayGrade.Grade) + + // Check the second allotment (E-6) + suite.Equal(e6Allowance.TotalWeightSelf, allotments[1].TotalWeightSelf) + suite.Equal(e6Allowance.TotalWeightSelfPlusDependents, allotments[1].TotalWeightSelfPlusDependents) + suite.Equal(e6Allowance.ProGearWeight, allotments[1].ProGearWeight) + suite.Equal(e6Allowance.ProGearWeightSpouse, allotments[1].ProGearWeightSpouse) + suite.Equal(e6.Grade, allotments[1].PayGrade.Grade) }) } From eff3f1c62913415c8a45f799b410852aa4fb422f Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Wed, 11 Dec 2024 17:40:48 +0000 Subject: [PATCH 008/113] one more --- pkg/services/entitlements/weight_allotment_fetcher_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/services/entitlements/weight_allotment_fetcher_test.go b/pkg/services/entitlements/weight_allotment_fetcher_test.go index b8fe65e42e7..a7b876f2dd5 100644 --- a/pkg/services/entitlements/weight_allotment_fetcher_test.go +++ b/pkg/services/entitlements/weight_allotment_fetcher_test.go @@ -25,6 +25,7 @@ func (suite *EntitlementsServiceSuite) TestGetWeightAllotment() { suite.Equal(hhgAllowance.TotalWeightSelfPlusDependents, allotment.TotalWeightSelfPlusDependents) suite.Equal(hhgAllowance.ProGearWeight, allotment.ProGearWeight) suite.Equal(hhgAllowance.ProGearWeightSpouse, allotment.ProGearWeightSpouse) + suite.Equal(hhgAllowance.PayGrade.Grade, pg.Grade) }) suite.Run("If pay grade does not exist, return an error", func() { From fa4e8301c6a567d057663171ec37a3ada7b196bd Mon Sep 17 00:00:00 2001 From: deandreJones Date: Mon, 16 Dec 2024 09:32:58 -0600 Subject: [PATCH 009/113] add test --- .../AddOrdersForm/AddOrdersForm.test.jsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx b/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx index f6c4f04e22f..611331d78a9 100644 --- a/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx +++ b/src/components/Office/AddOrdersForm/AddOrdersForm.test.jsx @@ -194,3 +194,23 @@ describe('AddOrdersForm - OCONUS and Accompanied Tour Test', () => { }); }); }); +describe('AddOrdersForm - Edge Cases and Additional Scenarios', () => { + it('disables orders type when safety move is selected', async () => { + render( + + + , + ); + + expect(screen.getByLabelText('Orders type')).toBeDisabled(); + }); + + it('disables orders type when bluebark move is selected', async () => { + render( + + + , + ); + expect(screen.getByLabelText('Orders type')).toBeDisabled(); + }); +}); From 0b862c8518c7d51c66530b61baa2bde51ddbeec0 Mon Sep 17 00:00:00 2001 From: loganwc Date: Mon, 16 Dec 2024 17:08:23 +0000 Subject: [PATCH 010/113] mobile home and boat shipments now have 2nd and 3rd address on create for prime sim --- .../Shipment/PrimeUIShipmentCreate.jsx | 70 +++++++++++++++++++ .../Shipment/PrimeUIShipmentCreate.test.jsx | 41 +++++++++++ 2 files changed, 111 insertions(+) diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx index 36139c32e23..c265b67e98c 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx @@ -177,6 +177,10 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { destinationAddress, diversion, divertedFromShipmentId, + hasSecondaryPickupAddress, + hasSecondaryDestinationAddress, + hasTertiaryPickupAddress, + hasTertiaryDestinationAddress, boatShipment: { year, make, @@ -192,6 +196,23 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { }, } = values; + let { tertiaryPickupAddress, tertiaryDestinationAddress, secondaryPickupAddress, secondaryDestinationAddress } = + values; + if (hasSecondaryPickupAddress !== 'true') { + secondaryPickupAddress = {}; + tertiaryPickupAddress = {}; + } + if (hasTertiaryPickupAddress !== 'true') { + tertiaryPickupAddress = {}; + } + if (hasSecondaryDestinationAddress !== 'true') { + secondaryDestinationAddress = {}; + tertiaryDestinationAddress = {}; + } + if (hasTertiaryDestinationAddress !== 'true') { + tertiaryDestinationAddress = {}; + } + // Sum the feet and inches fields into only inches for backend/db const totalLengthInInches = parseInt(lengthInFeet, 10) * 12 + parseInt(lengthInInches, 10); const totalWidthInInches = parseInt(widthInFeet, 10) * 12 + parseInt(widthInInches, 10); @@ -219,6 +240,20 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { destinationAddress: isEmpty(destinationAddress) ? null : formatAddressForPrimeAPI(destinationAddress), diversion: diversion || null, divertedFromShipmentId: divertedFromShipmentId || null, + hasSecondaryPickupAddress: hasSecondaryPickupAddress === 'true', + hasSecondaryDestinationAddress: hasSecondaryDestinationAddress === 'true', + hasTertiaryPickupAddress: hasTertiaryPickupAddress === 'true', + hasTertiaryDestinationAddress: hasTertiaryDestinationAddress === 'true', + secondaryPickupAddress: isEmpty(secondaryPickupAddress) + ? null + : formatAddressForPrimeAPI(secondaryPickupAddress), + secondaryDestinationAddress: isEmpty(secondaryDestinationAddress) + ? null + : formatAddressForPrimeAPI(secondaryDestinationAddress), + tertiaryPickupAddress: isEmpty(tertiaryPickupAddress) ? null : formatAddressForPrimeAPI(tertiaryPickupAddress), + tertiaryDestinationAddress: isEmpty(tertiaryDestinationAddress) + ? null + : formatAddressForPrimeAPI(tertiaryDestinationAddress), }; } else if (isMobileHome) { const { @@ -229,6 +264,10 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { destinationAddress, diversion, divertedFromShipmentId, + hasSecondaryPickupAddress, + hasSecondaryDestinationAddress, + hasTertiaryPickupAddress, + hasTertiaryDestinationAddress, mobileHomeShipment: { year, make, @@ -242,6 +281,23 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { }, } = values; + let { tertiaryPickupAddress, tertiaryDestinationAddress, secondaryPickupAddress, secondaryDestinationAddress } = + values; + if (hasSecondaryPickupAddress !== 'true') { + secondaryPickupAddress = {}; + tertiaryPickupAddress = {}; + } + if (hasTertiaryPickupAddress !== 'true') { + tertiaryPickupAddress = {}; + } + if (hasSecondaryDestinationAddress !== 'true') { + secondaryDestinationAddress = {}; + tertiaryDestinationAddress = {}; + } + if (hasTertiaryDestinationAddress !== 'true') { + tertiaryDestinationAddress = {}; + } + // Sum the feet and inches fields into only inches for backend/db const totalLengthInInches = parseInt(lengthInFeet, 10) * 12 + parseInt(lengthInInches, 10); const totalWidthInInches = parseInt(widthInFeet, 10) * 12 + parseInt(widthInInches, 10); @@ -265,6 +321,20 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { destinationAddress: isEmpty(destinationAddress) ? null : formatAddressForPrimeAPI(destinationAddress), diversion: diversion || null, divertedFromShipmentId: divertedFromShipmentId || null, + hasSecondaryPickupAddress: hasSecondaryPickupAddress === 'true', + hasSecondaryDestinationAddress: hasSecondaryDestinationAddress === 'true', + hasTertiaryPickupAddress: hasTertiaryPickupAddress === 'true', + hasTertiaryDestinationAddress: hasTertiaryDestinationAddress === 'true', + secondaryPickupAddress: isEmpty(secondaryPickupAddress) + ? null + : formatAddressForPrimeAPI(secondaryPickupAddress), + secondaryDestinationAddress: isEmpty(secondaryDestinationAddress) + ? null + : formatAddressForPrimeAPI(secondaryDestinationAddress), + tertiaryPickupAddress: isEmpty(tertiaryPickupAddress) ? null : formatAddressForPrimeAPI(tertiaryPickupAddress), + tertiaryDestinationAddress: isEmpty(tertiaryDestinationAddress) + ? null + : formatAddressForPrimeAPI(tertiaryDestinationAddress), }; } else { const { diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx index eec912b1793..ffa73be8202 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx @@ -171,3 +171,44 @@ describe('Create PPM', () => { }); }); }); + +describe('Create Mobile Home', () => { + it('test with 2nd addresses', async () => { + createPrimeMTOShipmentV3.mockReturnValue({}); + + render(mockedComponent); + + waitFor(async () => { + await userEvent.selectOptions(screen.getByLabelText('Shipment type'), 'MOBILE_HOME'); + + // Start controlled test case to verify everything is working. + let input = await document.querySelector('input[name="pickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup + await userEvent.type(input, '123 Street'); + + const secondAddressToggle = document.querySelector('[data-testid="has-secondary-pickup"]'); + expect(secondAddressToggle).toBeInTheDocument(); + await userEvent.click(secondAddressToggle); + + input = await document.querySelector('input[name="pickupAddress.streetAddress2"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Street'); + + input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter something + await userEvent.type(input, '123 Street'); + + const saveButton = await screen.getByRole('button', { name: 'Save' }); + + expect(saveButton).not.toBeDisabled(); + await userEvent.click(saveButton); + + await waitFor(() => { + expect(mockNavigate).toHaveBeenCalledWith(moveDetailsURL); + }); + }); + }); +}); From 6fd4cf00ec3760c0699326c369db94106ed774c2 Mon Sep 17 00:00:00 2001 From: loganwc Date: Wed, 18 Dec 2024 16:55:54 +0000 Subject: [PATCH 011/113] added 3rd address and fixed 2nd address test --- .../Shipment/PrimeUIShipmentCreate.test.jsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx index ffa73be8202..815c90fc14b 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx @@ -173,7 +173,7 @@ describe('Create PPM', () => { }); describe('Create Mobile Home', () => { - it('test with 2nd addresses', async () => { + it('test with 2nd and 3rd addresses', async () => { createPrimeMTOShipmentV3.mockReturnValue({}); render(mockedComponent); @@ -191,10 +191,19 @@ describe('Create Mobile Home', () => { expect(secondAddressToggle).toBeInTheDocument(); await userEvent.click(secondAddressToggle); - input = await document.querySelector('input[name="pickupAddress.streetAddress2"]'); + input = await document.querySelector('input[name="secondaryPickupAddress.streetAddress1"]'); expect(input).toBeInTheDocument(); // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Street'); + await userEvent.type(input, '123 Street 2'); + + const thirdAddressToggle = document.querySelector('[data-testid="has-tertiary-pickup"]'); + expect(thirdAddressToggle).toBeInTheDocument(); + await userEvent.click(thirdAddressToggle); + + input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Street 2'); input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); expect(input).toBeInTheDocument(); From 0c3f4ff41df6322fd73b362893f1a479a095b715 Mon Sep 17 00:00:00 2001 From: loganwc Date: Wed, 18 Dec 2024 16:59:22 +0000 Subject: [PATCH 012/113] change typo --- src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx index 815c90fc14b..7d3d732c89c 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx @@ -203,7 +203,7 @@ describe('Create Mobile Home', () => { input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); expect(input).toBeInTheDocument(); // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Street 2'); + await userEvent.type(input, '123 Street 3'); input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); expect(input).toBeInTheDocument(); From ae862c0d915133e9a581508fe4ec94fce0f9c13a Mon Sep 17 00:00:00 2001 From: loganwc Date: Wed, 18 Dec 2024 20:17:14 +0000 Subject: [PATCH 013/113] added more test coverage to prime ui shipment create --- .../Shipment/PrimeUIShipmentCreate.test.jsx | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx index 7d3d732c89c..fd49eff9b16 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx @@ -221,3 +221,66 @@ describe('Create Mobile Home', () => { }); }); }); + +describe('When creating shipment', () => { + it('disabling 2nd/3rd address clears out the data', async () => { + createPrimeMTOShipmentV3.mockReturnValue({}); + + render(mockedComponent); + + waitFor(async () => { + await userEvent.selectOptions(screen.getByLabelText('Shipment type'), 'HHG'); + + // Start controlled test case to verify everything is working. + let input = await document.querySelector('input[name="pickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup + await userEvent.type(input, '123 Street'); + + const secondAddressToggle = document.querySelector('[data-testid="has-secondary-pickup"]'); + expect(secondAddressToggle).toBeInTheDocument(); + await userEvent.click(secondAddressToggle); + + input = await document.querySelector('input[name="secondaryPickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Street 2'); + + const thirdAddressToggle = document.querySelector('[data-testid="has-tertiary-pickup"]'); + expect(thirdAddressToggle).toBeInTheDocument(); + await userEvent.click(thirdAddressToggle); + + input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Street 3'); + + const disable3rdAddressToggle = document.querySelector('[data-testid="no-tertiary-pickup"]'); + expect(disable3rdAddressToggle).toBeInTheDocument(); + await userEvent.click(disable3rdAddressToggle); + + const disable2ndAddressToggle = document.querySelector('[data-testid="no-secondary-pickup"]'); + expect(disable2ndAddressToggle).toBeInTheDocument(); + await userEvent.click(disable2ndAddressToggle); + + input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter something + await userEvent.type(input, '123 Street'); + + const saveButton = await screen.getByRole('button', { name: 'Save' }); + + expect(saveButton).not.toBeDisabled(); + await userEvent.click(saveButton); + + await waitFor(() => { + expect(createPrimeMTOShipmentV3).toHaveBeenCalledWith( + expect.objectContaining({ + secondaryPickupAddress: {}, + tertiaryPickupAddress: {}, + }), + ); + }); + }); + }); +}); From 26ba792683c0dc9337eed78b1711f58f394f6bfa Mon Sep 17 00:00:00 2001 From: loganwc Date: Mon, 23 Dec 2024 16:15:46 +0000 Subject: [PATCH 014/113] moved test from create to form --- .../Shipment/PrimeUIShipmentCreate.test.jsx | 63 ---------------- .../PrimeUIShipmentCreateForm.test.jsx | 75 ++++++++++++++++++- 2 files changed, 74 insertions(+), 64 deletions(-) diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx index fd49eff9b16..7d3d732c89c 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx @@ -221,66 +221,3 @@ describe('Create Mobile Home', () => { }); }); }); - -describe('When creating shipment', () => { - it('disabling 2nd/3rd address clears out the data', async () => { - createPrimeMTOShipmentV3.mockReturnValue({}); - - render(mockedComponent); - - waitFor(async () => { - await userEvent.selectOptions(screen.getByLabelText('Shipment type'), 'HHG'); - - // Start controlled test case to verify everything is working. - let input = await document.querySelector('input[name="pickupAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter required street 1 for pickup - await userEvent.type(input, '123 Street'); - - const secondAddressToggle = document.querySelector('[data-testid="has-secondary-pickup"]'); - expect(secondAddressToggle).toBeInTheDocument(); - await userEvent.click(secondAddressToggle); - - input = await document.querySelector('input[name="secondaryPickupAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Street 2'); - - const thirdAddressToggle = document.querySelector('[data-testid="has-tertiary-pickup"]'); - expect(thirdAddressToggle).toBeInTheDocument(); - await userEvent.click(thirdAddressToggle); - - input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Street 3'); - - const disable3rdAddressToggle = document.querySelector('[data-testid="no-tertiary-pickup"]'); - expect(disable3rdAddressToggle).toBeInTheDocument(); - await userEvent.click(disable3rdAddressToggle); - - const disable2ndAddressToggle = document.querySelector('[data-testid="no-secondary-pickup"]'); - expect(disable2ndAddressToggle).toBeInTheDocument(); - await userEvent.click(disable2ndAddressToggle); - - input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter something - await userEvent.type(input, '123 Street'); - - const saveButton = await screen.getByRole('button', { name: 'Save' }); - - expect(saveButton).not.toBeDisabled(); - await userEvent.click(saveButton); - - await waitFor(() => { - expect(createPrimeMTOShipmentV3).toHaveBeenCalledWith( - expect.objectContaining({ - secondaryPickupAddress: {}, - tertiaryPickupAddress: {}, - }), - ); - }); - }); - }); -}); diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx index 80ea98a6f8a..833caf7e17a 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx @@ -181,7 +181,7 @@ function renderShipmentCreateForm(props) { jest.mock('utils/featureFlags', () => ({ ...jest.requireActual('utils/featureFlags'), - isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve(false)), + isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve()), })); const mockNavigate = jest.fn(); @@ -493,4 +493,77 @@ describe('PrimeUIShipmentCreateForm', () => { // now the text input should be invisible expect(await screen.queryByTestId('divertedFromShipmentIdInput')).toBeNull(); }); + + it.each( + ['BOAT_HAUL_AWAY', 'BOAT_TOW_AWAY', 'MOBILE_HOME'], + 'when creating a %s clears 2nd/3rd address fields when secondary/tertiary checkbox is unchecked', + async (shipmentType) => { + // isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(false)); // Allow for testing of boats and mobile homes + isBooleanFlagEnabled.mockResolvedValue(true); + + const shipmentTypeInput = await screen.findByLabelText('Shipment type'); + expect(shipmentTypeInput).toBeInTheDocument(); + + // Select the boat or mobile home shipment type + await userEvent.selectOptions(shipmentTypeInput, [shipmentType]); + + // Make sure that a PPM-specific field is not visible. + expect(await screen.queryByLabelText('Expected Departure Date')).not.toBeInTheDocument(); + + // Check for usual HHG fields + expect(await screen.findByRole('heading', { name: 'Diversion', level: 2 })).toBeInTheDocument(); + expect(await screen.findByLabelText('Diversion')).not.toBeChecked(); + + // Checking to make sure the text box isn't shown prior to clicking the box + expect(screen.queryByTestId('divertedFromShipmentIdInput')).toBeNull(); + + // Check the diversion box + const diversionCheckbox = await screen.findByLabelText('Diversion'); + await userEvent.click(diversionCheckbox); + + // now the text input should be visible + expect(await screen.findByTestId('divertedFromShipmentIdInput')).toBeInTheDocument(); + + // Now check for a boat and mobile home shipment specific field + expect(await screen.findByLabelText('Length (Feet)')).toBeVisible(); + + let input = await document.querySelector('input[name="pickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup + await userEvent.type(input, '123 Pickup Street'); + + const secondAddressToggle = document.querySelector('[data-testid="has-secondary-pickup"]'); + expect(secondAddressToggle).toBeInTheDocument(); + await userEvent.click(secondAddressToggle); + + input = await document.querySelector('input[name="secondaryPickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Pickup Street 2'); + + const thirdAddressToggle = document.querySelector('[data-testid="has-tertiary-pickup"]'); + expect(thirdAddressToggle).toBeInTheDocument(); + await userEvent.click(thirdAddressToggle); + + input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Pickup Street 3'); + + const disable3rdAddressToggle = document.querySelector('[data-testid="no-tertiary-pickup"]'); + expect(disable3rdAddressToggle).toBeInTheDocument(); + await userEvent.click(disable3rdAddressToggle); + + const disable2ndAddressToggle = document.querySelector('[data-testid="no-secondary-pickup"]'); + expect(disable2ndAddressToggle).toBeInTheDocument(); + await userEvent.click(disable2ndAddressToggle); + + expect(input).not.toBeInTheDocument(); + + input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter something + await userEvent.type(input, '123 destination Street'); + }, + ); }); From 2bd0fd268eaef53939a04f341f41bb59f84b4579 Mon Sep 17 00:00:00 2001 From: loganwc Date: Mon, 23 Dec 2024 16:20:41 +0000 Subject: [PATCH 015/113] removed comment --- src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx index 833caf7e17a..8d82913f8c8 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx @@ -498,7 +498,6 @@ describe('PrimeUIShipmentCreateForm', () => { ['BOAT_HAUL_AWAY', 'BOAT_TOW_AWAY', 'MOBILE_HOME'], 'when creating a %s clears 2nd/3rd address fields when secondary/tertiary checkbox is unchecked', async (shipmentType) => { - // isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(false)); // Allow for testing of boats and mobile homes isBooleanFlagEnabled.mockResolvedValue(true); const shipmentTypeInput = await screen.findByLabelText('Shipment type'); From ad255d99ef504a7db5979c4220f5e0c9d4f28cbe Mon Sep 17 00:00:00 2001 From: loganwc Date: Thu, 26 Dec 2024 16:46:19 +0000 Subject: [PATCH 016/113] do the create tests for mobile home and boats --- .../Shipment/PrimeUIShipmentCreate.test.jsx | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx index 7d3d732c89c..0198675ae53 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx @@ -173,51 +173,54 @@ describe('Create PPM', () => { }); describe('Create Mobile Home', () => { - it('test with 2nd and 3rd addresses', async () => { - createPrimeMTOShipmentV3.mockReturnValue({}); + it.each(['MOBILE_HOME', 'BOAT_TOW_AWAY', 'BOAT_HAUL_AWAY'])( + 'test with 2nd and 3rd addresses', + async (shipmentType) => { + createPrimeMTOShipmentV3.mockReturnValue({}); - render(mockedComponent); + render(mockedComponent); - waitFor(async () => { - await userEvent.selectOptions(screen.getByLabelText('Shipment type'), 'MOBILE_HOME'); + waitFor(async () => { + await userEvent.selectOptions(screen.getByLabelText('Shipment type'), shipmentType); - // Start controlled test case to verify everything is working. - let input = await document.querySelector('input[name="pickupAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter required street 1 for pickup - await userEvent.type(input, '123 Street'); + // Start controlled test case to verify everything is working. + let input = await document.querySelector('input[name="pickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup + await userEvent.type(input, '123 Street'); - const secondAddressToggle = document.querySelector('[data-testid="has-secondary-pickup"]'); - expect(secondAddressToggle).toBeInTheDocument(); - await userEvent.click(secondAddressToggle); + const secondAddressToggle = document.querySelector('[data-testid="has-secondary-pickup"]'); + expect(secondAddressToggle).toBeInTheDocument(); + await userEvent.click(secondAddressToggle); - input = await document.querySelector('input[name="secondaryPickupAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Street 2'); + input = await document.querySelector('input[name="secondaryPickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Street 2'); - const thirdAddressToggle = document.querySelector('[data-testid="has-tertiary-pickup"]'); - expect(thirdAddressToggle).toBeInTheDocument(); - await userEvent.click(thirdAddressToggle); + const thirdAddressToggle = document.querySelector('[data-testid="has-tertiary-pickup"]'); + expect(thirdAddressToggle).toBeInTheDocument(); + await userEvent.click(thirdAddressToggle); - input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Street 3'); + input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Street 3'); - input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter something - await userEvent.type(input, '123 Street'); + input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter something + await userEvent.type(input, '123 Street'); - const saveButton = await screen.getByRole('button', { name: 'Save' }); + const saveButton = await screen.getByRole('button', { name: 'Save' }); - expect(saveButton).not.toBeDisabled(); - await userEvent.click(saveButton); + expect(saveButton).not.toBeDisabled(); + await userEvent.click(saveButton); - await waitFor(() => { - expect(mockNavigate).toHaveBeenCalledWith(moveDetailsURL); + await waitFor(() => { + expect(mockNavigate).toHaveBeenCalledWith(moveDetailsURL); + }); }); - }); - }); + }, + ); }); From 67500e13417d3e5601159ff12675d6c4dd791e44 Mon Sep 17 00:00:00 2001 From: loganwc Date: Thu, 26 Dec 2024 20:34:07 +0000 Subject: [PATCH 017/113] moved ff check out of form --- .../Shipment/PrimeUIShipmentCreate.jsx | 18 +- .../Shipment/PrimeUIShipmentCreate.test.jsx | 245 ++++++++++++++++-- .../Shipment/PrimeUIShipmentCreateForm.jsx | 18 +- .../PrimeUIShipmentCreateForm.test.jsx | 74 +----- 4 files changed, 247 insertions(+), 108 deletions(-) diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx index c265b67e98c..9dae219dfbc 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Formik } from 'formik'; import * as Yup from 'yup'; import { useNavigate, useParams, generatePath } from 'react-router-dom'; @@ -21,13 +21,23 @@ import { setFlashMessage as setFlashMessageAction } from 'store/flash/actions'; import { requiredAddressSchema, partialRequiredAddressSchema } from 'utils/validation'; import PrimeUIShipmentCreateForm from 'pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm'; import { OptionalAddressSchema } from 'components/Customer/MtoShipmentForm/validationSchemas'; -import { SHIPMENT_OPTIONS, SHIPMENT_TYPES } from 'shared/constants'; +import { FEATURE_FLAG_KEYS, SHIPMENT_OPTIONS, SHIPMENT_TYPES } from 'shared/constants'; +import { isBooleanFlagEnabled } from 'utils/featureFlags'; const PrimeUIShipmentCreate = ({ setFlashMessage }) => { const [errorMessage, setErrorMessage] = useState(); const { moveCodeOrID } = useParams(); const navigate = useNavigate(); - + const [enableBoat, setEnableBoat] = useState(false); + const [enableMobileHome, setEnableMobileHome] = useState(false); + + useEffect(() => { + const fetchData = async () => { + setEnableBoat(await isBooleanFlagEnabled(FEATURE_FLAG_KEYS.BOAT)); + setEnableMobileHome(await isBooleanFlagEnabled(FEATURE_FLAG_KEYS.MOBILE_HOME)); + }; + fetchData(); + }, []); const handleClose = () => { navigate(generatePath(primeSimulatorRoutes.VIEW_MOVE_PATH, { moveCodeOrID })); }; @@ -676,7 +686,7 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { {({ isValid, isSubmitting, handleSubmit }) => { return (
- +
({ jest.mock('utils/featureFlags', () => ({ ...jest.requireActual('utils/featureFlags'), - isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve(false)), + isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve(true)), })); const moveDetailsURL = generatePath(primeSimulatorRoutes.VIEW_MOVE_PATH, { moveCodeOrID: moveId }); +const initialValues = { + shipmentType: '', + + // PPM + counselorRemarks: '', + ppmShipment: { + expectedDepartureDate: '', + sitExpected: false, + sitLocation: '', + sitEstimatedWeight: '', + sitEstimatedEntryDate: '', + sitEstimatedDepartureDate: '', + estimatedWeight: '', + hasProGear: false, + proGearWeight: '', + spouseProGearWeight: '', + pickupAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + destinationAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + secondaryDeliveryAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + secondaryPickupAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + tertiaryDeliveryAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + tertiaryPickupAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + hasSecondaryPickupAddress: 'false', + hasSecondaryDestinationAddress: 'false', + hasTertiaryPickupAddress: 'false', + hasTertiaryDestinationAddress: 'false', + }, + + // Boat Shipment + boatShipment: { + make: 'make', + model: 'model', + year: 1999, + hasTrailer: true, + isRoadworthy: true, + lengthInFeet: 16, + lengthInInches: 0, + widthInFeet: 1, + widthInInches: 1, + heightInFeet: 1, + heightInInches: 1, + }, + + // Mobile Home Shipment + mobileHomeShipment: { + make: 'mobile make', + model: 'mobile model', + year: 1999, + lengthInFeet: 16, + lengthInInches: 0, + widthInFeet: 1, + widthInInches: 1, + heightInFeet: 1, + heightInInches: 1, + }, + + // Other shipment types + requestedPickupDate: '', + estimatedWeight: '', + pickupAddress: {}, + destinationAddress: {}, + secondaryDeliveryAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + secondaryPickupAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + tertiaryDeliveryAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + tertiaryPickupAddress: { + city: '', + postalCode: '', + state: '', + streetAddress1: '', + }, + hasSecondaryPickupAddress: 'false', + hasSecondaryDestinationAddress: 'false', + hasTertiaryPickupAddress: 'false', + hasTertiaryDestinationAddress: 'false', + diversion: '', + divertedFromShipmentId: '', +}; + const mockedComponent = ( - + + + + + ); @@ -145,7 +274,7 @@ describe('Create PPM', () => { await userEvent.selectOptions(screen.getByLabelText('Shipment type'), 'PPM'); // Start controlled test case to verify everything is working. - let input = await document.querySelector('input[name="ppmShipment.pickupAddress.streetAddress1"]'); + let input = await document.querySelector('input[name=ppmShipment.pickupAddress.streetAddress1]'); expect(input).toBeInTheDocument(); // enter required street 1 for pickup await userEvent.type(input, '123 Street'); @@ -159,7 +288,7 @@ describe('Create PPM', () => { await userEvent.type(input, '123 Street'); // Verify destination address street 1 is OPTIONAL. - input = await document.querySelector('input[name="ppmShipment.destinationAddress.streetAddress1"]'); + input = await document.querySelector('input[name=ppmShipment.destinationAddress.streetAddress1]'); expect(input).toBeInTheDocument(); // enter something await userEvent.type(input, '123 Street'); @@ -174,21 +303,51 @@ describe('Create PPM', () => { describe('Create Mobile Home', () => { it.each(['MOBILE_HOME', 'BOAT_TOW_AWAY', 'BOAT_HAUL_AWAY'])( - 'test with 2nd and 3rd addresses', + 'resets secondary and tertiary addresses when flags are not true for shipment type %s', async (shipmentType) => { + isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); createPrimeMTOShipmentV3.mockReturnValue({}); + // Render the component render(mockedComponent); + // Wait for the component to load waitFor(async () => { + expect(screen.getByLabelText('Shipment type')).toBeInTheDocument(); + + // Select shipment type await userEvent.selectOptions(screen.getByLabelText('Shipment type'), shipmentType); - // Start controlled test case to verify everything is working. - let input = await document.querySelector('input[name="pickupAddress.streetAddress1"]'); + await userEvent.type(screen.getByLabelText('Requested pickup'), '01 Nov 2020'); + + // Fill in required pickup and destination addresses + let input = document.querySelector('input[name=pickupAddress.streetAddress1]'); expect(input).toBeInTheDocument(); - // enter required street 1 for pickup await userEvent.type(input, '123 Street'); + input = document.querySelector('input[name=pickupAddress.city]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, 'Folsom'); + input = document.querySelector('input[name=pickupAddress.state]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, 'CA'); + input = document.querySelector('input[name=pickupAddress.postalCode]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, '95630'); + + input = document.querySelector('input[name=destinationAddress.streetAddress1]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, '456 Destination St'); + input = document.querySelector('input[name=destinationAddress.city]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, 'Bevy Hills'); + input = document.querySelector('input[name=destinationAddress.state]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, 'CA'); + input = document.querySelector('input[name=destinationAddress.postalCode]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, '90210'); + // Enable and disable secondary and tertiary toggles const secondAddressToggle = document.querySelector('[data-testid="has-secondary-pickup"]'); expect(secondAddressToggle).toBeInTheDocument(); await userEvent.click(secondAddressToggle); @@ -196,7 +355,7 @@ describe('Create Mobile Home', () => { input = await document.querySelector('input[name="secondaryPickupAddress.streetAddress1"]'); expect(input).toBeInTheDocument(); // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Street 2'); + await userEvent.type(input, '123 Pickup Street 2'); const thirdAddressToggle = document.querySelector('[data-testid="has-tertiary-pickup"]'); expect(thirdAddressToggle).toBeInTheDocument(); @@ -205,20 +364,74 @@ describe('Create Mobile Home', () => { input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); expect(input).toBeInTheDocument(); // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Street 3'); + await userEvent.type(input, '123 Pickup Street 3'); - input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); + const disable2ndAddressToggle = document.querySelector('[data-testid="no-secondary-pickup"]'); + expect(disable2ndAddressToggle).toBeInTheDocument(); + await userEvent.click(disable2ndAddressToggle); + + // input boat/mobile home model info + input = document.createElement('input[label="Year"]'); expect(input).toBeInTheDocument(); - // enter something - await userEvent.type(input, '123 Street'); + await userEvent.type(input, '2023'); - const saveButton = await screen.getByRole('button', { name: 'Save' }); + input = document.createElement('input[label="Make"]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, 'Genesis'); + + input = document.createElement('input[label="Model"]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, 'G70'); + + // input boat/mobile home dimensions + input = document.createElement('input[label="Length (Feet)"]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, '10'); + + input = document.createElement('input[label="Length (Inches)"]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, '10'); + + input = document.createElement('input[label="Width (Feet)"]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, '10'); + + input = document.createElement('input[label="Width (Inches)"]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, '10'); + + input = document.createElement('input[label="Height (Feet)"]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, '10'); + + input = document.createElement('input[label="Height (Inches)"]'); + expect(input).toBeInTheDocument(); + await userEvent.type(input, '10'); + // Submit the form + const saveButton = screen.getByRole('button', { name: 'Save' }); expect(saveButton).not.toBeDisabled(); await userEvent.click(saveButton); - await waitFor(() => { - expect(mockNavigate).toHaveBeenCalledWith(moveDetailsURL); + // Verify that API call resets addresses when flags are not 'true' + expect(createPrimeMTOShipmentV3).toHaveBeenCalledWith({ + body: expect.objectContaining({ + destinationAddress: null, + diversion: null, + divertedFromShipmentId: null, + hasSecondaryDestinationAddress: false, + hasSecondaryPickupAddress: false, + hasTertiaryDestinationAddress: false, + hasTertiaryPickupAddress: false, + secondaryDestinationAddress: {}, + secondaryPickupAddress: {}, + tertiaryDestinationAddress: {}, + tertiaryPickupAddress: {}, + moveTaskOrderID: '9c7b255c-2981-4bf8-839f-61c7458e2b4d', + pickupAddress: null, + primeEstimatedWeight: null, + requestedPickupDate: null, + }), }); }); }, diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.jsx index 3f70fb3fb53..22fd5f366df 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.jsx @@ -1,10 +1,8 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import { Radio, FormGroup, Label, Textarea } from '@trussworks/react-uswds'; import { Field, useField, useFormikContext } from 'formik'; -import { isBooleanFlagEnabled } from '../../../utils/featureFlags'; - -import { SHIPMENT_OPTIONS, SHIPMENT_TYPES, FEATURE_FLAG_KEYS } from 'shared/constants'; +import { SHIPMENT_OPTIONS, SHIPMENT_TYPES } from 'shared/constants'; import { CheckboxField, DatePickerInput, DropdownInput } from 'components/form/fields'; import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; import styles from 'components/Office/CustomerContactInfoForm/CustomerContactInfoForm.module.scss'; @@ -17,7 +15,7 @@ import { LOCATION_TYPES } from 'types/sitStatusShape'; const sitLocationOptions = dropdownInputOptions(LOCATION_TYPES); -const PrimeUIShipmentCreateForm = () => { +const PrimeUIShipmentCreateForm = ({ enableBoat, enableMobileHome }) => { const { values } = useFormikContext(); const { shipmentType } = values; const { sitExpected, hasProGear } = values.ppmShipment; @@ -25,8 +23,6 @@ const PrimeUIShipmentCreateForm = () => { const [, , checkBoxHelperProps] = useField('diversion'); const [, , divertedFromIdHelperProps] = useField('divertedFromShipmentId'); const [isChecked, setIsChecked] = useState(false); - const [enableBoat, setEnableBoat] = useState(false); - const [enableMobileHome, setEnableMobileHome] = useState(false); const hasShipmentType = !!shipmentType; const isPPM = shipmentType === SHIPMENT_OPTIONS.PPM; @@ -80,14 +76,6 @@ const PrimeUIShipmentCreateForm = () => { return undefined; }; - useEffect(() => { - const fetchData = async () => { - setEnableBoat(await isBooleanFlagEnabled(FEATURE_FLAG_KEYS.BOAT)); - setEnableMobileHome(await isBooleanFlagEnabled(FEATURE_FLAG_KEYS.MOBILE_HOME)); - }; - fetchData(); - }, []); - let shipmentTypeOptions = Object.values(SHIPMENT_TYPES).map((value) => ({ key: value, value })); if (!enableBoat) { // Disallow the Prime from choosing Boat shipments if the feature flag is not enabled diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx index 8d82913f8c8..80ea98a6f8a 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.test.jsx @@ -181,7 +181,7 @@ function renderShipmentCreateForm(props) { jest.mock('utils/featureFlags', () => ({ ...jest.requireActual('utils/featureFlags'), - isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve()), + isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve(false)), })); const mockNavigate = jest.fn(); @@ -493,76 +493,4 @@ describe('PrimeUIShipmentCreateForm', () => { // now the text input should be invisible expect(await screen.queryByTestId('divertedFromShipmentIdInput')).toBeNull(); }); - - it.each( - ['BOAT_HAUL_AWAY', 'BOAT_TOW_AWAY', 'MOBILE_HOME'], - 'when creating a %s clears 2nd/3rd address fields when secondary/tertiary checkbox is unchecked', - async (shipmentType) => { - isBooleanFlagEnabled.mockResolvedValue(true); - - const shipmentTypeInput = await screen.findByLabelText('Shipment type'); - expect(shipmentTypeInput).toBeInTheDocument(); - - // Select the boat or mobile home shipment type - await userEvent.selectOptions(shipmentTypeInput, [shipmentType]); - - // Make sure that a PPM-specific field is not visible. - expect(await screen.queryByLabelText('Expected Departure Date')).not.toBeInTheDocument(); - - // Check for usual HHG fields - expect(await screen.findByRole('heading', { name: 'Diversion', level: 2 })).toBeInTheDocument(); - expect(await screen.findByLabelText('Diversion')).not.toBeChecked(); - - // Checking to make sure the text box isn't shown prior to clicking the box - expect(screen.queryByTestId('divertedFromShipmentIdInput')).toBeNull(); - - // Check the diversion box - const diversionCheckbox = await screen.findByLabelText('Diversion'); - await userEvent.click(diversionCheckbox); - - // now the text input should be visible - expect(await screen.findByTestId('divertedFromShipmentIdInput')).toBeInTheDocument(); - - // Now check for a boat and mobile home shipment specific field - expect(await screen.findByLabelText('Length (Feet)')).toBeVisible(); - - let input = await document.querySelector('input[name="pickupAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter required street 1 for pickup - await userEvent.type(input, '123 Pickup Street'); - - const secondAddressToggle = document.querySelector('[data-testid="has-secondary-pickup"]'); - expect(secondAddressToggle).toBeInTheDocument(); - await userEvent.click(secondAddressToggle); - - input = await document.querySelector('input[name="secondaryPickupAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Pickup Street 2'); - - const thirdAddressToggle = document.querySelector('[data-testid="has-tertiary-pickup"]'); - expect(thirdAddressToggle).toBeInTheDocument(); - await userEvent.click(thirdAddressToggle); - - input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter required street 1 for pickup 2 - await userEvent.type(input, '123 Pickup Street 3'); - - const disable3rdAddressToggle = document.querySelector('[data-testid="no-tertiary-pickup"]'); - expect(disable3rdAddressToggle).toBeInTheDocument(); - await userEvent.click(disable3rdAddressToggle); - - const disable2ndAddressToggle = document.querySelector('[data-testid="no-secondary-pickup"]'); - expect(disable2ndAddressToggle).toBeInTheDocument(); - await userEvent.click(disable2ndAddressToggle); - - expect(input).not.toBeInTheDocument(); - - input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); - expect(input).toBeInTheDocument(); - // enter something - await userEvent.type(input, '123 destination Street'); - }, - ); }); From 27b76a6bfbaead45643409f1b6dd7cf73117211b Mon Sep 17 00:00:00 2001 From: loganwc Date: Fri, 27 Dec 2024 17:04:23 +0000 Subject: [PATCH 018/113] big test --- .../Shipment/PrimeUIShipmentCreate.test.jsx | 91 +++++++++++++++---- 1 file changed, 71 insertions(+), 20 deletions(-) diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx index 3af240f1842..0c10c40d7bd 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx @@ -301,7 +301,57 @@ describe('Create PPM', () => { }); }); -describe('Create Mobile Home', () => { +describe('Create PPM Home', () => { + it('test with 2nd and 3rd addresses', async () => { + createPrimeMTOShipmentV3.mockReturnValue({}); + + render(mockedComponent); + + waitFor(async () => { + await userEvent.selectOptions(screen.getByLabelText('Shipment type'), 'PPM'); + + // Start controlled test case to verify everything is working. + let input = await document.querySelector('input[name="pickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup + await userEvent.type(input, '123 Street'); + + const secondAddressToggle = document.querySelector('[data-testid="has-secondary-pickup"]'); + expect(secondAddressToggle).toBeInTheDocument(); + await userEvent.click(secondAddressToggle); + + input = await document.querySelector('input[name="secondaryPickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Street 2'); + + const thirdAddressToggle = document.querySelector('[data-testid="has-tertiary-pickup"]'); + expect(thirdAddressToggle).toBeInTheDocument(); + await userEvent.click(thirdAddressToggle); + + input = await document.querySelector('input[name="tertiaryPickupAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter required street 1 for pickup 2 + await userEvent.type(input, '123 Street 3'); + + input = await document.querySelector('input[name="destinationAddress.streetAddress1"]'); + expect(input).toBeInTheDocument(); + // enter something + await userEvent.type(input, '123 Street'); + + const saveButton = await screen.getByRole('button', { name: 'Save' }); + + expect(saveButton).not.toBeDisabled(); + await userEvent.click(saveButton); + + await waitFor(() => { + expect(mockNavigate).toHaveBeenCalledWith(moveDetailsURL); + }); + }); + }); +}); + +describe('Create Mobile Home and Boat', () => { it.each(['MOBILE_HOME', 'BOAT_TOW_AWAY', 'BOAT_HAUL_AWAY'])( 'resets secondary and tertiary addresses when flags are not true for shipment type %s', async (shipmentType) => { @@ -414,25 +464,26 @@ describe('Create Mobile Home', () => { await userEvent.click(saveButton); // Verify that API call resets addresses when flags are not 'true' - expect(createPrimeMTOShipmentV3).toHaveBeenCalledWith({ - body: expect.objectContaining({ - destinationAddress: null, - diversion: null, - divertedFromShipmentId: null, - hasSecondaryDestinationAddress: false, - hasSecondaryPickupAddress: false, - hasTertiaryDestinationAddress: false, - hasTertiaryPickupAddress: false, - secondaryDestinationAddress: {}, - secondaryPickupAddress: {}, - tertiaryDestinationAddress: {}, - tertiaryPickupAddress: {}, - moveTaskOrderID: '9c7b255c-2981-4bf8-839f-61c7458e2b4d', - pickupAddress: null, - primeEstimatedWeight: null, - requestedPickupDate: null, - }), - }); + }); + + expect(createPrimeMTOShipmentV3).toHaveBeenCalledWith({ + body: expect.objectContaining({ + destinationAddress: null, + diversion: null, + divertedFromShipmentId: null, + hasSecondaryDestinationAddress: false, + hasSecondaryPickupAddress: false, + hasTertiaryDestinationAddress: false, + hasTertiaryPickupAddress: false, + secondaryDestinationAddress: null, + secondaryPickupAddress: null, + tertiaryDestinationAddress: null, + tertiaryPickupAddress: null, + moveTaskOrderID: '9c7b255c-2981-4bf8-839f-61c7458e2b4d', + pickupAddress: null, + primeEstimatedWeight: null, + requestedPickupDate: null, + }), }); }, ); From 3d0f4bcb4fbcbf27e448e42824b5e6c7f0a198b1 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Wed, 11 Dec 2024 22:17:27 +0000 Subject: [PATCH 019/113] hide emplid --- src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx index d51c45122e3..d8351e49f49 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx @@ -281,7 +281,7 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage, setCanAddO const handleBluebarkChange = (e) => { if (e.target.value === 'true') { setIsBluebarkMove(true); - setShowEmplid(true); + setShowEmplid(false); setValues({ ...values, affiliation: e.target.value, From 85fa79d0a40bff75507d564bf437625d207a9a91 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Fri, 13 Dec 2024 11:21:34 -0600 Subject: [PATCH 020/113] updates --- .../CustomerOnboarding/CreateCustomerForm.jsx | 1 + .../CreateCustomerForm.test.jsx | 152 +++++++++++++++++- 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx index d8351e49f49..ed6e6c024da 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx @@ -281,6 +281,7 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage, setCanAddO const handleBluebarkChange = (e) => { if (e.target.value === 'true') { setIsBluebarkMove(true); + setIsSafetyMove(false); setShowEmplid(false); setValues({ ...values, diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx index 99e398a005c..adc7c505cb3 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx @@ -211,6 +211,11 @@ const safetyPayload = { is_bluebark: 'false', }; +const bluebarkPayload = { + is_safety_move: 'false', + is_bluebark: 'true', +}; + const mockUserPrivileges = [ { createdAt: '0001-01-01T00:00:00.000Z', @@ -615,7 +620,7 @@ describe('CreateCustomerForm', () => { safetyPayload.residential_address.streetAddress1, ); - const locationBox = screen.getAllByRole('combobox'); + const locationBox = screen.getAllByLabelText('Location lookup'); await act(async () => { await userEvent.type(locationBox[1], 'BEVERLY HILLS'); @@ -655,5 +660,148 @@ describe('CreateCustomerForm', () => { }, }); }); - }, 20000); + }, 10000); + + it('submits the form and tests for unsupported state validation', async () => { + createCustomerWithOktaOption.mockImplementation(() => Promise.resolve(fakeResponse)); + + const { getByLabelText, getByTestId, getByRole } = render( + + + , + ); + + const user = userEvent.setup(); + + const saveBtn = await screen.findByRole('button', { name: 'Save' }); + expect(saveBtn).toBeInTheDocument(); + + await user.selectOptions(getByLabelText('Branch of service'), [fakePayload.affiliation]); + await userEvent.type(getByTestId('edipiInput'), fakePayload.edipi); + + await user.type(getByLabelText('First name'), fakePayload.first_name); + await user.type(getByLabelText('Last name'), fakePayload.last_name); + + await user.type(getByLabelText('Best contact phone'), fakePayload.telephone); + await user.type(getByLabelText('Personal email'), fakePayload.personal_email); + + await userEvent.type(getByTestId('backup-add-street1'), fakePayload.backup_mailing_address.streetAddress1); + await userEvent.type(getByTestId('backup-add-city'), fakePayload.backup_mailing_address.city); + await userEvent.selectOptions(getByTestId('backup-add-state'), [fakePayload.backup_mailing_address.state]); + await userEvent.type(getByTestId('backup-add-zip'), fakePayload.backup_mailing_address.postalCode); + + await userEvent.type(getByLabelText('Name'), fakePayload.backup_contact.name); + await userEvent.type(getByRole('textbox', { name: 'Email' }), fakePayload.backup_contact.email); + await userEvent.type(getByRole('textbox', { name: 'Phone' }), fakePayload.backup_contact.telephone); + + await userEvent.type(getByTestId('create-okta-account-yes'), fakePayload.create_okta_account); + + await userEvent.type(getByTestId('cac-user-no'), fakePayload.cac_user); + + await waitFor(() => { + expect(saveBtn).toBeEnabled(); + }); + + await userEvent.selectOptions(getByTestId('backup-add-state'), 'HI'); + await userEvent.tab(); + + const msg = screen.getByText('Moves to this state are not supported at this time.'); + expect(msg).toBeVisible(); + + await userEvent.selectOptions(getByTestId('backup-add-state'), [fakePayload.residential_address.state]); + await userEvent.tab(); + expect(msg).not.toBeVisible(); + + await waitFor(() => { + expect(saveBtn).toBeEnabled(); + }); + + await userEvent.click(saveBtn); + + await waitFor(() => { + expect(createCustomerWithOktaOption).toHaveBeenCalled(); + expect(mockNavigate).toHaveBeenCalledWith(ordersPath, { + state: { + isSafetyMoveSelected: false, + isBluebarkMoveSelected: false, + }, + }); + }); + }, 10000); + + it('disables okta and non cac user inputs when bluebark move is selected', async () => { + createCustomerWithOktaOption.mockImplementation(() => Promise.resolve(fakeResponse)); + isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); + searchLocationByZipCityState.mockImplementation(mockSearchPickupLocation); + + const { getByLabelText, getByTestId, getByRole } = render( + + + , + ); + + const user = userEvent.setup(); + + const safetyMove = await screen.findByTestId('is-safety-move-no'); + expect(safetyMove).toBeChecked(); + + // check the safety move box + await userEvent.type(getByTestId('is-safety-move-no'), bluebarkPayload.is_safety_move); + await userEvent.type(getByTestId('is-bluebark-yes'), bluebarkPayload.is_bluebark); + + await userEvent.selectOptions(getByLabelText('Branch of service'), ['ARMY']); + await user.type(getByTestId('edipiInput'), safetyPayload.edipi); + + await user.type(getByLabelText('First name'), safetyPayload.first_name); + await user.type(getByLabelText('Last name'), safetyPayload.last_name); + + await user.type(getByLabelText('Best contact phone'), safetyPayload.telephone); + await user.type(getByLabelText('Personal email'), safetyPayload.personal_email); + + await userEvent.type( + getByTestId('residential_address.streetAddress1'), + safetyPayload.residential_address.streetAddress1, + ); + + const locationBox = screen.getAllByRole('combobox'); + + await act(async () => { + await userEvent.type(locationBox[1], 'BEVERLY HILLS'); + const selectedResidentialLocation = await screen.findByText(/90210/); + await userEvent.click(selectedResidentialLocation); + }); + + await userEvent.type( + getByTestId('backup_mailing_address.streetAddress1'), + safetyPayload.backup_mailing_address.streetAddress1, + ); + + await act(async () => { + await userEvent.type(locationBox[1], 'DRYDEN'); + const selectedBackupLocation = await screen.findByText(/04225/); + await userEvent.click(selectedBackupLocation); + }); + + await userEvent.type(getByLabelText('Name'), safetyPayload.backup_contact.name); + await userEvent.type(getByRole('textbox', { name: 'Email' }), safetyPayload.backup_contact.email); + await userEvent.type(getByRole('textbox', { name: 'Phone' }), safetyPayload.backup_contact.telephone); + + const saveBtn = await screen.findByRole('button', { name: 'Save' }); + expect(saveBtn).toBeInTheDocument(); + + await waitFor(() => { + expect(saveBtn).toBeEnabled(); + }); + await userEvent.click(saveBtn); + + await waitFor(() => { + expect(createCustomerWithOktaOption).toHaveBeenCalled(); + expect(mockNavigate).toHaveBeenCalledWith(ordersPath, { + state: { + isSafetyMoveSelected: false, + isBluebarkMoveSelected: true, + }, + }); + }); + }, 10000); }); From 03958adb0b083e0ef26d6348e93689fa412b5b83 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Fri, 13 Dec 2024 11:31:06 -0600 Subject: [PATCH 021/113] fix test --- .../CustomerOnboarding/CreateCustomerForm.test.jsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx index adc7c505cb3..27947aacee7 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx @@ -620,7 +620,7 @@ describe('CreateCustomerForm', () => { safetyPayload.residential_address.streetAddress1, ); - const locationBox = screen.getAllByLabelText('Location lookup'); + const locationBox = screen.getAllByRole('combobox'); await act(async () => { await userEvent.type(locationBox[1], 'BEVERLY HILLS'); @@ -735,15 +735,15 @@ describe('CreateCustomerForm', () => { searchLocationByZipCityState.mockImplementation(mockSearchPickupLocation); const { getByLabelText, getByTestId, getByRole } = render( - + , ); const user = userEvent.setup(); - const safetyMove = await screen.findByTestId('is-safety-move-no'); - expect(safetyMove).toBeChecked(); + const saveBtn = await screen.findByRole('button', { name: 'Save' }); + expect(saveBtn).toBeInTheDocument(); // check the safety move box await userEvent.type(getByTestId('is-safety-move-no'), bluebarkPayload.is_safety_move); @@ -777,7 +777,7 @@ describe('CreateCustomerForm', () => { ); await act(async () => { - await userEvent.type(locationBox[1], 'DRYDEN'); + await userEvent.type(locationBox[2], 'DRYDEN'); const selectedBackupLocation = await screen.findByText(/04225/); await userEvent.click(selectedBackupLocation); }); @@ -786,7 +786,6 @@ describe('CreateCustomerForm', () => { await userEvent.type(getByRole('textbox', { name: 'Email' }), safetyPayload.backup_contact.email); await userEvent.type(getByRole('textbox', { name: 'Phone' }), safetyPayload.backup_contact.telephone); - const saveBtn = await screen.findByRole('button', { name: 'Save' }); expect(saveBtn).toBeInTheDocument(); await waitFor(() => { From f03251b2a3170a4ba7d191893f11661188073658 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Fri, 13 Dec 2024 11:39:17 -0600 Subject: [PATCH 022/113] remove what was removed --- .../CreateCustomerForm.test.jsx | 67 ------------------- 1 file changed, 67 deletions(-) diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx index 27947aacee7..efa59c88f0b 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx @@ -662,73 +662,6 @@ describe('CreateCustomerForm', () => { }); }, 10000); - it('submits the form and tests for unsupported state validation', async () => { - createCustomerWithOktaOption.mockImplementation(() => Promise.resolve(fakeResponse)); - - const { getByLabelText, getByTestId, getByRole } = render( - - - , - ); - - const user = userEvent.setup(); - - const saveBtn = await screen.findByRole('button', { name: 'Save' }); - expect(saveBtn).toBeInTheDocument(); - - await user.selectOptions(getByLabelText('Branch of service'), [fakePayload.affiliation]); - await userEvent.type(getByTestId('edipiInput'), fakePayload.edipi); - - await user.type(getByLabelText('First name'), fakePayload.first_name); - await user.type(getByLabelText('Last name'), fakePayload.last_name); - - await user.type(getByLabelText('Best contact phone'), fakePayload.telephone); - await user.type(getByLabelText('Personal email'), fakePayload.personal_email); - - await userEvent.type(getByTestId('backup-add-street1'), fakePayload.backup_mailing_address.streetAddress1); - await userEvent.type(getByTestId('backup-add-city'), fakePayload.backup_mailing_address.city); - await userEvent.selectOptions(getByTestId('backup-add-state'), [fakePayload.backup_mailing_address.state]); - await userEvent.type(getByTestId('backup-add-zip'), fakePayload.backup_mailing_address.postalCode); - - await userEvent.type(getByLabelText('Name'), fakePayload.backup_contact.name); - await userEvent.type(getByRole('textbox', { name: 'Email' }), fakePayload.backup_contact.email); - await userEvent.type(getByRole('textbox', { name: 'Phone' }), fakePayload.backup_contact.telephone); - - await userEvent.type(getByTestId('create-okta-account-yes'), fakePayload.create_okta_account); - - await userEvent.type(getByTestId('cac-user-no'), fakePayload.cac_user); - - await waitFor(() => { - expect(saveBtn).toBeEnabled(); - }); - - await userEvent.selectOptions(getByTestId('backup-add-state'), 'HI'); - await userEvent.tab(); - - const msg = screen.getByText('Moves to this state are not supported at this time.'); - expect(msg).toBeVisible(); - - await userEvent.selectOptions(getByTestId('backup-add-state'), [fakePayload.residential_address.state]); - await userEvent.tab(); - expect(msg).not.toBeVisible(); - - await waitFor(() => { - expect(saveBtn).toBeEnabled(); - }); - - await userEvent.click(saveBtn); - - await waitFor(() => { - expect(createCustomerWithOktaOption).toHaveBeenCalled(); - expect(mockNavigate).toHaveBeenCalledWith(ordersPath, { - state: { - isSafetyMoveSelected: false, - isBluebarkMoveSelected: false, - }, - }); - }); - }, 10000); - it('disables okta and non cac user inputs when bluebark move is selected', async () => { createCustomerWithOktaOption.mockImplementation(() => Promise.resolve(fakeResponse)); isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); From a7529c1476a412fa16367db7d5620549a09efe87 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Mon, 16 Dec 2024 14:42:08 -0600 Subject: [PATCH 023/113] add timeout --- src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx index efa59c88f0b..3636608f467 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx @@ -31,6 +31,7 @@ const mockPickupLocation = [ const mockSearchPickupLocation = () => Promise.resolve(mockPickupLocation); +jest.setTimeout(60000); const mockNavigate = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), From e0847f17b0216bd336321dcaf3c949603dde0bd5 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Mon, 16 Dec 2024 15:38:07 -0600 Subject: [PATCH 024/113] move to lower --- src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx index 3636608f467..876cb97962a 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx @@ -31,7 +31,6 @@ const mockPickupLocation = [ const mockSearchPickupLocation = () => Promise.resolve(mockPickupLocation); -jest.setTimeout(60000); const mockNavigate = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -59,6 +58,7 @@ jest.mock('store/general/actions', () => ({ beforeEach(() => { jest.clearAllMocks(); + jest.setTimeout(60000); }); const serviceCounselorState = { From d069b1cf66c8064a80697dc6710b0ca9b70e4c97 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Mon, 16 Dec 2024 15:44:35 -0600 Subject: [PATCH 025/113] dont be a flake --- .../Office/CustomerOnboarding/CreateCustomerForm.test.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx index 876cb97962a..b49f6aea696 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.test.jsx @@ -58,7 +58,6 @@ jest.mock('store/general/actions', () => ({ beforeEach(() => { jest.clearAllMocks(); - jest.setTimeout(60000); }); const serviceCounselorState = { @@ -661,7 +660,7 @@ describe('CreateCustomerForm', () => { }, }); }); - }, 10000); + }, 50000); it('disables okta and non cac user inputs when bluebark move is selected', async () => { createCustomerWithOktaOption.mockImplementation(() => Promise.resolve(fakeResponse)); @@ -736,5 +735,5 @@ describe('CreateCustomerForm', () => { }, }); }); - }, 10000); + }, 50000); }); From beaf6f43e684cc32dce247b7ac25b6ca12538bda Mon Sep 17 00:00:00 2001 From: deandreJones Date: Mon, 16 Dec 2024 16:10:09 -0600 Subject: [PATCH 026/113] add safety type test --- pkg/models/order_test.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/models/order_test.go b/pkg/models/order_test.go index 4171045ee74..6ef09fe7598 100644 --- a/pkg/models/order_test.go +++ b/pkg/models/order_test.go @@ -500,7 +500,7 @@ func (suite *ModelSuite) TestOrderCanSendEmailWithOrdersType() { suite.True(canSendEmail) }) - suite.Run("Safety and BB orders cannot send email", func() { + suite.Run(" BB orders cannot send email", func() { order := factory.BuildOrder(suite.DB(), []factory.Customization{ { Model: m.Order{ @@ -512,4 +512,17 @@ func (suite *ModelSuite) TestOrderCanSendEmailWithOrdersType() { canSendEmail := order.CanSendEmailWithOrdersType() suite.False(canSendEmail) }) + + suite.Run("Safety orders cannot send email", func() { + order := factory.BuildOrder(suite.DB(), []factory.Customization{ + { + Model: m.Order{ + OrdersType: internalmessages.OrdersTypeSAFETY, + }, + }, + }, nil) + + canSendEmail := order.CanSendEmailWithOrdersType() + suite.False(canSendEmail) + }) } From 28c8181691791b42968e8be0ea7c9d5a25d1e24d Mon Sep 17 00:00:00 2001 From: deandreJones Date: Tue, 17 Dec 2024 09:13:09 -0600 Subject: [PATCH 027/113] special moves section --- src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx index ed6e6c024da..fbe210ce4e1 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx @@ -305,7 +305,7 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage, setCanAddO

Create Customer Profile

-

Customer Affiliation

+

Special Moves

{isSafetyPrivileged && (
Is this a Safety move? @@ -358,6 +358,9 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage, setCanAddO />
+ + +

Customer Affiliation

Date: Tue, 17 Dec 2024 09:19:39 -0600 Subject: [PATCH 028/113] lowercase --- src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx index fbe210ce4e1..6b4bd9830cb 100644 --- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx +++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx @@ -334,7 +334,7 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage, setCanAddO )}
- Is this a Bluebark Move? + Is this a Bluebark move?
Date: Sat, 21 Dec 2024 02:21:59 +0000 Subject: [PATCH 029/113] fix capturing the order type in adding orders detail info --- .../Office/OrdersDetailForm/OrdersDetailForm.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx index b24ff3ae670..1c6897583b2 100644 --- a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx +++ b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx @@ -82,12 +82,16 @@ const OrdersDetailForm = ({ { setFormOrdersType(e.target.value); setFieldValue('ordersType', e.target.value); }} - isDisabled={formIsDisabled || formOrdersType === 'SAFETY'} + isDisabled={formIsDisabled || formOrdersType === 'SAFETY' || formOrdersType === 'BLUEBARK'} /> {showOrdersTypeDetail && ( Date: Thu, 26 Dec 2024 15:57:53 +0000 Subject: [PATCH 030/113] use contant values --- .../Office/OrdersDetailForm/OrdersDetailForm.jsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx index 1c6897583b2..046c4ae9169 100644 --- a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx +++ b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx @@ -8,6 +8,7 @@ import { CheckboxField, DropdownInput, DatePickerInput, DutyLocationInput } from import TextField from 'components/form/fields/TextField/TextField'; import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; import { DropdownArrayOf } from 'types/form'; +import { SPECIAL_ORDERS_TYPES } from 'constants/orders'; const OrdersDetailForm = ({ deptIndicatorOptions, @@ -83,7 +84,7 @@ const OrdersDetailForm = ({ name="ordersType" label="Orders type" options={ - formOrdersType === 'SAFETY' || formOrdersType === 'BLUEBARK' + formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY || formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK ? dropdownInputOptions({ SAFETY: 'Safety', BLUEBARK: 'Bluebark' }) : ordersTypeOptions } @@ -91,7 +92,11 @@ const OrdersDetailForm = ({ setFormOrdersType(e.target.value); setFieldValue('ordersType', e.target.value); }} - isDisabled={formIsDisabled || formOrdersType === 'SAFETY' || formOrdersType === 'BLUEBARK'} + isDisabled={ + formIsDisabled || + formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY || + formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK + } /> {showOrdersTypeDetail && ( Date: Thu, 26 Dec 2024 16:29:51 +0000 Subject: [PATCH 031/113] fix test --- .../OrdersDetailForm/OrdersDetailForm.test.jsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/components/Office/OrdersDetailForm/OrdersDetailForm.test.jsx b/src/components/Office/OrdersDetailForm/OrdersDetailForm.test.jsx index 37ec69d7645..37861c8b881 100644 --- a/src/components/Office/OrdersDetailForm/OrdersDetailForm.test.jsx +++ b/src/components/Office/OrdersDetailForm/OrdersDetailForm.test.jsx @@ -196,7 +196,23 @@ describe('OrdersDetailForm', () => { showNTSTac: false, showNTSSac: false, showOrdersAcknowledgement: false, - ordersType: 'SAFETY', + ordersType: 'Safety', + }); + + // correct labels are visible + expect(await screen.findByLabelText('Orders type')).toBeDisabled(); + }); + it('has orders type dropdown disabled if bluebark move', async () => { + renderOrdersDetailForm({ + showDepartmentIndicator: false, + showOrdersNumber: false, + showOrdersTypeDetail: false, + showHHGTac: false, + showHHGSac: false, + showNTSTac: false, + showNTSSac: false, + showOrdersAcknowledgement: false, + ordersType: 'BLUEBARK', }); // correct labels are visible From 3231a83e002036b3a87a076133a6f337eced4a43 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 30 Dec 2024 19:10:21 +0000 Subject: [PATCH 032/113] weight allotment service overhaul with model and api updates --- migrations/app/migrations_manifest.txt | 1 + ...4_student_travel_weight_limit_param.up.sql | 16 +++ pkg/gen/internalapi/embedded_spec.go | 6 + .../index_entitlements_responses.go | 25 ++++ pkg/handlers/internalapi/api.go | 4 +- pkg/handlers/internalapi/entitlements.go | 7 +- pkg/handlers/internalapi/entitlements_test.go | 4 +- pkg/models/application_parameters.go | 14 +- pkg/models/entitlements.go | 9 -- pkg/models/ghc_entitlements.go | 9 ++ pkg/services/entitlements.go | 6 +- .../entitlements/weight_allotment_fetcher.go | 84 ++++++++++- .../weight_allotment_fetcher_test.go | 131 +++++++++++++++--- swagger-def/internal.yaml | 2 + swagger/internal.yaml | 2 + 15 files changed, 275 insertions(+), 45 deletions(-) create mode 100644 migrations/app/schema/20241230150644_student_travel_weight_limit_param.up.sql diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index a614c844996..7fad9d4cd73 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1053,3 +1053,4 @@ 20241204210208_retroactive_update_of_ppm_max_and_estimated_incentives_prd.up.sql 20241209121924_entitlements_refactor.up.sql 20241227153723_remove_empty_string_emplid_values.up.sql +20241230150644_student_travel_weight_limit_param.up.sql diff --git a/migrations/app/schema/20241230150644_student_travel_weight_limit_param.up.sql b/migrations/app/schema/20241230150644_student_travel_weight_limit_param.up.sql new file mode 100644 index 00000000000..5edefb66caa --- /dev/null +++ b/migrations/app/schema/20241230150644_student_travel_weight_limit_param.up.sql @@ -0,0 +1,16 @@ +-- Prep app param table for json storage +ALTER TABLE application_parameters +ADD COLUMN IF NOT EXISTS parameter_json JSONB; + +-- Insert one-off student travel app param value for weight limits +INSERT INTO application_parameters (id, parameter_name, parameter_json) +VALUES ( + '4BEEAE29-C074-4CB6-B4AE-F222F755733C', + 'studentTravelHhgAllowance', + '{ + "TotalWeightSelf": 350, + "TotalWeightSelfPlusDependents": 350, + "ProGearWeight": 0, + "ProGearWeightSpouse": 0 + }'::jsonb + ); \ No newline at end of file diff --git a/pkg/gen/internalapi/embedded_spec.go b/pkg/gen/internalapi/embedded_spec.go index c1351734062..f720c86951b 100644 --- a/pkg/gen/internalapi/embedded_spec.go +++ b/pkg/gen/internalapi/embedded_spec.go @@ -555,6 +555,9 @@ func init() { "schema": { "$ref": "#/definitions/IndexEntitlements" } + }, + "500": { + "description": "internal server error" } } } @@ -9242,6 +9245,9 @@ func init() { "schema": { "$ref": "#/definitions/IndexEntitlements" } + }, + "500": { + "description": "internal server error" } } } diff --git a/pkg/gen/internalapi/internaloperations/entitlements/index_entitlements_responses.go b/pkg/gen/internalapi/internaloperations/entitlements/index_entitlements_responses.go index 972c901ef1d..a9358ff2691 100644 --- a/pkg/gen/internalapi/internaloperations/entitlements/index_entitlements_responses.go +++ b/pkg/gen/internalapi/internaloperations/entitlements/index_entitlements_responses.go @@ -60,3 +60,28 @@ func (o *IndexEntitlementsOK) WriteResponse(rw http.ResponseWriter, producer run panic(err) // let the recovery middleware deal with this } } + +// IndexEntitlementsInternalServerErrorCode is the HTTP code returned for type IndexEntitlementsInternalServerError +const IndexEntitlementsInternalServerErrorCode int = 500 + +/* +IndexEntitlementsInternalServerError internal server error + +swagger:response indexEntitlementsInternalServerError +*/ +type IndexEntitlementsInternalServerError struct { +} + +// NewIndexEntitlementsInternalServerError creates IndexEntitlementsInternalServerError with default headers values +func NewIndexEntitlementsInternalServerError() *IndexEntitlementsInternalServerError { + + return &IndexEntitlementsInternalServerError{} +} + +// WriteResponse to the client +func (o *IndexEntitlementsInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(500) +} diff --git a/pkg/handlers/internalapi/api.go b/pkg/handlers/internalapi/api.go index 8aff3ee28e4..500e9474ded 100644 --- a/pkg/handlers/internalapi/api.go +++ b/pkg/handlers/internalapi/api.go @@ -17,6 +17,7 @@ import ( "github.com/transcom/mymove/pkg/services/address" boatshipment "github.com/transcom/mymove/pkg/services/boat_shipment" dateservice "github.com/transcom/mymove/pkg/services/calendar" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" mobilehomeshipment "github.com/transcom/mymove/pkg/services/mobile_home_shipment" @@ -57,6 +58,7 @@ func NewInternalAPI(handlerConfig handlers.HandlerConfig) *internalops.MymoveAPI builder := query.NewQueryBuilder() fetcher := fetch.NewFetcher(builder) moveRouter := move.NewMoveRouter() + waf := entitlements.NewWeightAllotmentFetcher() uploadCreator := upload.NewUploadCreator(handlerConfig.FileStorer()) ppmEstimator := ppmshipment.NewEstimatePPM(handlerConfig.DTODPlanner(), &paymentrequesthelper.RequestPaymentHelper{}) ppmCloseoutFetcher := ppmcloseout.NewPPMCloseoutFetcher(handlerConfig.DTODPlanner(), &paymentrequesthelper.RequestPaymentHelper{}, ppmEstimator) @@ -179,7 +181,7 @@ func NewInternalAPI(handlerConfig handlers.HandlerConfig) *internalops.MymoveAPI internalAPI.OfficeApproveReimbursementHandler = ApproveReimbursementHandler{handlerConfig} internalAPI.OfficeCancelMoveHandler = CancelMoveHandler{handlerConfig, moveRouter} - internalAPI.EntitlementsIndexEntitlementsHandler = IndexEntitlementsHandler{handlerConfig} + internalAPI.EntitlementsIndexEntitlementsHandler = IndexEntitlementsHandler{handlerConfig, waf} internalAPI.CalendarShowAvailableMoveDatesHandler = ShowAvailableMoveDatesHandler{handlerConfig} diff --git a/pkg/handlers/internalapi/entitlements.go b/pkg/handlers/internalapi/entitlements.go index 3ae75c18eb4..ed50edbb132 100644 --- a/pkg/handlers/internalapi/entitlements.go +++ b/pkg/handlers/internalapi/entitlements.go @@ -8,6 +8,7 @@ import ( "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" ) func payloadForEntitlementModel(e models.WeightAllotment) internalmessages.WeightAllotment { @@ -28,13 +29,17 @@ func payloadForEntitlementModel(e models.WeightAllotment) internalmessages.Weigh // IndexEntitlementsHandler indexes entitlements type IndexEntitlementsHandler struct { handlers.HandlerConfig + services.WeightAllotmentFetcher } // Handle is the handler func (h IndexEntitlementsHandler) Handle(params entitlementop.IndexEntitlementsParams) middleware.Responder { return h.AuditableAppContextFromRequestWithErrors(params.HTTPRequest, func(appCtx appcontext.AppContext) (middleware.Responder, error) { - entitlements := models.AllWeightAllotments() + entitlements, err := h.WeightAllotmentFetcher.GetAllWeightAllotments(appCtx) + if err != nil { + return entitlementop.NewIndexEntitlementsInternalServerError(), nil + } payload := make(map[string]internalmessages.WeightAllotment) for k, v := range entitlements { grade := string(k) diff --git a/pkg/handlers/internalapi/entitlements_test.go b/pkg/handlers/internalapi/entitlements_test.go index ec6829443c2..0b896da13b9 100644 --- a/pkg/handlers/internalapi/entitlements_test.go +++ b/pkg/handlers/internalapi/entitlements_test.go @@ -5,11 +5,13 @@ import ( "github.com/transcom/mymove/pkg/factory" entitlementop "github.com/transcom/mymove/pkg/gen/internalapi/internaloperations/entitlements" + "github.com/transcom/mymove/pkg/services/entitlements" ) func (suite *HandlerSuite) TestIndexEntitlementsHandlerReturns200() { // Given: a set of orders, a move, user, servicemember and a PPM + waf := entitlements.NewWeightAllotmentFetcher() ppm := factory.BuildMinimalPPMShipment(suite.DB(), nil, nil) move := factory.BuildMove(suite.DB(), nil, nil) mtoShipment := factory.BuildMTOShipmentWithMove(&move, suite.DB(), nil, nil) @@ -24,7 +26,7 @@ func (suite *HandlerSuite) TestIndexEntitlementsHandlerReturns200() { } // And: index entitlements endpoint is hit - handler := IndexEntitlementsHandler{suite.HandlerConfig()} + handler := IndexEntitlementsHandler{suite.HandlerConfig(), waf} response := handler.Handle(params) // Then: expect a 200 status code diff --git a/pkg/models/application_parameters.go b/pkg/models/application_parameters.go index dccf21fd10f..083605c9383 100644 --- a/pkg/models/application_parameters.go +++ b/pkg/models/application_parameters.go @@ -1,6 +1,7 @@ package models import ( + "encoding/json" "time" "github.com/gobuffalo/pop/v6" @@ -10,12 +11,13 @@ import ( // ApplicationParameters is a model representing application parameters and holds parameter values and parameter names stored in the database type ApplicationParameters struct { - ID uuid.UUID `json:"id" db:"id"` - ValidationCode *string `json:"validation_code" db:"validation_code"` - ParameterName *string `json:"parameter_name" db:"parameter_name"` - ParameterValue *string `json:"parameter_value" db:"parameter_value"` - CreatedAt time.Time `json:"created_at" db:"created_at"` - UpdatedAt time.Time `json:"updated_at" db:"updated_at"` + ID uuid.UUID `json:"id" db:"id"` + ValidationCode *string `json:"validation_code" db:"validation_code"` + ParameterName *string `json:"parameter_name" db:"parameter_name"` + ParameterValue *string `json:"parameter_value" db:"parameter_value"` + ParameterJson *json.RawMessage `json:"parameter_json" db:"parameter_json"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } func (a ApplicationParameters) TableName() string { diff --git a/pkg/models/entitlements.go b/pkg/models/entitlements.go index 94fa25f6ef2..121198d0721 100644 --- a/pkg/models/entitlements.go +++ b/pkg/models/entitlements.go @@ -9,15 +9,6 @@ import ( "github.com/transcom/mymove/pkg/gen/internalmessages" ) -// WeightAllotment represents the weights allotted for a rank -type WeightAllotment struct { - TotalWeightSelf int - TotalWeightSelfPlusDependents int - ProGearWeight int - ProGearWeightSpouse int - UnaccompaniedBaggageAllowance int -} - // the midshipman entitlement is shared with service academy cadet var midshipman = WeightAllotment{ TotalWeightSelf: 350, diff --git a/pkg/models/ghc_entitlements.go b/pkg/models/ghc_entitlements.go index cc47134f63c..8bd2136874d 100644 --- a/pkg/models/ghc_entitlements.go +++ b/pkg/models/ghc_entitlements.go @@ -131,3 +131,12 @@ func (e *Entitlement) UBWeightAllowance() *int { return nil } } + +// WeightAllotment represents the weights allotted for a rank +type WeightAllotment struct { + TotalWeightSelf int + TotalWeightSelfPlusDependents int + ProGearWeight int + ProGearWeightSpouse int + UnaccompaniedBaggageAllowance int +} diff --git a/pkg/services/entitlements.go b/pkg/services/entitlements.go index ddbbc553c83..80e02f2d92e 100644 --- a/pkg/services/entitlements.go +++ b/pkg/services/entitlements.go @@ -2,6 +2,7 @@ package services import ( "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" ) @@ -9,8 +10,9 @@ import ( // //go:generate mockery --name WeightAllotmentFetcher type WeightAllotmentFetcher interface { - GetWeightAllotment(appCtx appcontext.AppContext, grade string) (*models.HHGAllowance, error) - GetAllWeightAllotments(appCtx appcontext.AppContext) (models.HHGAllowances, error) + GetWeightAllotment(appCtx appcontext.AppContext, grade string, ordersType internalmessages.OrdersType) (models.WeightAllotment, error) + GetAllWeightAllotments(appCtx appcontext.AppContext) (map[internalmessages.OrderPayGrade]models.WeightAllotment, error) + GetWeightAllotmentByOrdersType(appCtx appcontext.AppContext, ordersType internalmessages.OrdersType) (models.WeightAllotment, error) } // The weight restrictor interface helps apply weight restrictions to entitlements diff --git a/pkg/services/entitlements/weight_allotment_fetcher.go b/pkg/services/entitlements/weight_allotment_fetcher.go index e282947120a..718a0e30169 100644 --- a/pkg/services/entitlements/weight_allotment_fetcher.go +++ b/pkg/services/entitlements/weight_allotment_fetcher.go @@ -1,10 +1,13 @@ package entitlements import ( + "bytes" + "encoding/json" "fmt" "github.com/transcom/mymove/pkg/appcontext" "github.com/transcom/mymove/pkg/apperror" + "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" ) @@ -17,7 +20,17 @@ func NewWeightAllotmentFetcher() services.WeightAllotmentFetcher { return &weightAllotmentFetcher{} } -func (waf *weightAllotmentFetcher) GetWeightAllotment(appCtx appcontext.AppContext, grade string) (*models.HHGAllowance, error) { +func (waf *weightAllotmentFetcher) GetWeightAllotment(appCtx appcontext.AppContext, grade string, ordersType internalmessages.OrdersType) (models.WeightAllotment, error) { + // Check order allotment first + if ordersType == internalmessages.OrdersTypeSTUDENTTRAVEL { // currently only applies to student travel order that limits overall authorized weight + entitlement, err := waf.GetWeightAllotmentByOrdersType(appCtx, ordersType) + if err != nil { + return models.WeightAllotment{}, err + } + return entitlement, nil + } + + // Continue if the orders type is not student travel var hhgAllowance models.HHGAllowance err := appCtx.DB(). RawQuery(` @@ -29,13 +42,60 @@ func (waf *weightAllotmentFetcher) GetWeightAllotment(appCtx appcontext.AppConte `, grade). First(&hhgAllowance) if err != nil { - return nil, apperror.NewQueryError("HHGAllowance", err, fmt.Sprintf("Error retrieving HHG allowance for grade: %s", grade)) + return models.WeightAllotment{}, apperror.NewQueryError("HHGAllowance", err, fmt.Sprintf("Error retrieving HHG allowance for grade: %s", grade)) } - return &hhgAllowance, nil + // Convert HHGAllowance to WeightAllotment + weightAllotment := models.WeightAllotment{ + TotalWeightSelf: hhgAllowance.TotalWeightSelf, + TotalWeightSelfPlusDependents: hhgAllowance.TotalWeightSelfPlusDependents, + ProGearWeight: hhgAllowance.ProGearWeight, + ProGearWeightSpouse: hhgAllowance.ProGearWeightSpouse, + } + + return weightAllotment, nil } -func (waf *weightAllotmentFetcher) GetAllWeightAllotments(appCtx appcontext.AppContext) (models.HHGAllowances, error) { +var ordersTypeToAllotmentAppParamName = map[internalmessages.OrdersType]string{ + internalmessages.OrdersTypeSTUDENTTRAVEL: "studentTravelHhgAllowance", +} + +// Helper func to enforce strict unmarshal of application param values into a given interface +func strictUnmarshal(data []byte, v interface{}) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + // Fail on unknown fields + decoder.DisallowUnknownFields() + return decoder.Decode(v) +} + +func (waf *weightAllotmentFetcher) GetWeightAllotmentByOrdersType(appCtx appcontext.AppContext, ordersType internalmessages.OrdersType) (models.WeightAllotment, error) { + if paramName, ok := ordersTypeToAllotmentAppParamName[ordersType]; ok { + // We currently store orders allotment overrides as an application parameter + // as it is a current one-off use case introduced by E-06189 + var jsonData json.RawMessage + err := appCtx.DB().RawQuery(` + SELECT parameter_json + FROM application_parameters + WHERE parameter_name = $1 + `, paramName).First(&jsonData) + + if err != nil { + return models.WeightAllotment{}, fmt.Errorf("failed to fetch weight allotment for orders type %s: %w", ordersType, err) + } + + // Convert the JSON data to the WeightAllotment struct + var weightAllotment models.WeightAllotment + err = strictUnmarshal(jsonData, &weightAllotment) + if err != nil { + return models.WeightAllotment{}, fmt.Errorf("failed to parse weight allotment JSON for orders type %s: %w", ordersType, err) + } + + return weightAllotment, nil + } + return models.WeightAllotment{}, fmt.Errorf("no entitlement found for orders type %s", ordersType) +} + +func (waf *weightAllotmentFetcher) GetAllWeightAllotments(appCtx appcontext.AppContext) (map[internalmessages.OrderPayGrade]models.WeightAllotment, error) { var hhgAllowances models.HHGAllowances err := appCtx.DB(). Eager("PayGrade"). @@ -44,5 +104,19 @@ func (waf *weightAllotmentFetcher) GetAllWeightAllotments(appCtx appcontext.AppC return nil, apperror.NewQueryError("HHGAllowances", err, "Error retrieving all HHG allowances") } - return hhgAllowances, nil + weightAllotments := make(map[internalmessages.OrderPayGrade]models.WeightAllotment) + + for _, hhgAllowance := range hhgAllowances { + // Convert HHGAllowance to WeightAllotment + weightAllotment := models.WeightAllotment{ + TotalWeightSelf: hhgAllowance.TotalWeightSelf, + TotalWeightSelfPlusDependents: hhgAllowance.TotalWeightSelfPlusDependents, + ProGearWeight: hhgAllowance.ProGearWeight, + ProGearWeightSpouse: hhgAllowance.ProGearWeightSpouse, + } + + grade := internalmessages.OrderPayGrade(hhgAllowance.PayGrade.Grade) + weightAllotments[grade] = weightAllotment + } + return weightAllotments, nil } diff --git a/pkg/services/entitlements/weight_allotment_fetcher_test.go b/pkg/services/entitlements/weight_allotment_fetcher_test.go index a7b876f2dd5..dd6bc994693 100644 --- a/pkg/services/entitlements/weight_allotment_fetcher_test.go +++ b/pkg/services/entitlements/weight_allotment_fetcher_test.go @@ -1,8 +1,11 @@ package entitlements import ( + "encoding/json" + "github.com/transcom/mymove/pkg/apperror" "github.com/transcom/mymove/pkg/factory" + "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" ) @@ -18,7 +21,7 @@ func (suite *EntitlementsServiceSuite) TestGetWeightAllotment() { }, }, nil) - allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), pg.Grade) + allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), pg.Grade, internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) suite.NoError(err) suite.Equal(hhgAllowance.TotalWeightSelf, allotment.TotalWeightSelf) @@ -31,10 +34,10 @@ func (suite *EntitlementsServiceSuite) TestGetWeightAllotment() { suite.Run("If pay grade does not exist, return an error", func() { fetcher := NewWeightAllotmentFetcher() - allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "X-1") + allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "X-1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) suite.Error(err) suite.IsType(apperror.QueryError{}, err) - suite.Nil(allotment) + suite.Empty(allotment) }) } @@ -42,16 +45,16 @@ func (suite *EntitlementsServiceSuite) TestGetAllWeightAllotments() { suite.Run("Successfully fetch all weight allotments", func() { fetcher := NewWeightAllotmentFetcher() - // Make the default E-5 + // Build E-5 e5 := factory.BuildPayGrade(suite.DB(), nil, nil) e5Allowance := factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ { - Model: e5, + Model: e5, // Link the pay grade LinkOnly: true, }, }, nil) - // Make an E-6 + // Build E-6 e6 := factory.BuildPayGrade(suite.DB(), []factory.Customization{ { Model: models.PayGrade{ @@ -66,23 +69,111 @@ func (suite *EntitlementsServiceSuite) TestGetAllWeightAllotments() { }, }, nil) - // Assert both can be fetched allotments, err := fetcher.GetAllWeightAllotments(suite.AppContextForTest()) suite.NoError(err) suite.Len(allotments, 2) - // Check the first allotment (E-5) - suite.Equal(e5Allowance.TotalWeightSelf, allotments[0].TotalWeightSelf) - suite.Equal(e5Allowance.TotalWeightSelfPlusDependents, allotments[0].TotalWeightSelfPlusDependents) - suite.Equal(e5Allowance.ProGearWeight, allotments[0].ProGearWeight) - suite.Equal(e5Allowance.ProGearWeightSpouse, allotments[0].ProGearWeightSpouse) - suite.Equal(e5.Grade, allotments[0].PayGrade.Grade) - - // Check the second allotment (E-6) - suite.Equal(e6Allowance.TotalWeightSelf, allotments[1].TotalWeightSelf) - suite.Equal(e6Allowance.TotalWeightSelfPlusDependents, allotments[1].TotalWeightSelfPlusDependents) - suite.Equal(e6Allowance.ProGearWeight, allotments[1].ProGearWeight) - suite.Equal(e6Allowance.ProGearWeightSpouse, allotments[1].ProGearWeightSpouse) - suite.Equal(e6.Grade, allotments[1].PayGrade.Grade) + // Check E-5 allotment by its map key + e5Key := internalmessages.OrderPayGrade(e5.Grade) + suite.Equal(e5Allowance.TotalWeightSelf, allotments[e5Key].TotalWeightSelf) + suite.Equal(e5Allowance.TotalWeightSelfPlusDependents, allotments[e5Key].TotalWeightSelfPlusDependents) + suite.Equal(e5Allowance.ProGearWeight, allotments[e5Key].ProGearWeight) + suite.Equal(e5Allowance.ProGearWeightSpouse, allotments[e5Key].ProGearWeightSpouse) + + // Check E-6 allotment by its map key + e6Key := internalmessages.OrderPayGrade(e6.Grade) + suite.Equal(e6Allowance.TotalWeightSelf, allotments[e6Key].TotalWeightSelf) + suite.Equal(e6Allowance.TotalWeightSelfPlusDependents, allotments[e6Key].TotalWeightSelfPlusDependents) + suite.Equal(e6Allowance.ProGearWeight, allotments[e6Key].ProGearWeight) + suite.Equal(e6Allowance.ProGearWeightSpouse, allotments[e6Key].ProGearWeightSpouse) + }) +} + +func (suite *EntitlementsServiceSuite) TestGetWeightAllotmentByOrdersType() { + setupHhgStudentAllowanceParameter := func() { + paramJSON := `{ + "TotalWeightSelf": 350, + "TotalWeightSelfPlusDependents": 350, + "ProGearWeight": 0, + "ProGearWeightSpouse": 0, + "UnaccompaniedBaggageAllowance": 100 + }` + rawMessage := json.RawMessage(paramJSON) + + parameter := models.ApplicationParameters{ + ParameterName: models.StringPointer("studentTravelHhgAllowance"), + ParameterJson: &rawMessage, + } + suite.MustCreate(¶meter) + } + + suite.Run("Successfully fetch student travel allotment from application_parameters", func() { + setupHhgStudentAllowanceParameter() + fetcher := NewWeightAllotmentFetcher() + + allotment, err := fetcher.GetWeightAllotment( + suite.AppContextForTest(), + "E-1", + internalmessages.OrdersTypeSTUDENTTRAVEL, + ) + + suite.NoError(err) + suite.Equal(350, allotment.TotalWeightSelf) + suite.Equal(350, allotment.TotalWeightSelfPlusDependents) + suite.Equal(0, allotment.ProGearWeight) + suite.Equal(0, allotment.ProGearWeightSpouse) + suite.Equal(100, allotment.UnaccompaniedBaggageAllowance) + }) + + suite.Run("Returns an error if json does not match allotment from db", func() { + // Proper JSON but not proper target struct + faultyParamJSON := `{ + "TotalWeight": 350, + "TotalWeightPlusDependents": 350, + "ProGear": 0, + "ProGearSpouse": 0, + "Allowance": 100 + }` + rawMessage := json.RawMessage(faultyParamJSON) + + parameter := models.ApplicationParameters{ + ParameterName: models.StringPointer("studentTravelHhgAllowance"), + ParameterJson: &rawMessage, + } + suite.MustCreate(¶meter) + + fetcher := NewWeightAllotmentFetcher() + _, err := fetcher.GetWeightAllotmentByOrdersType( + suite.AppContextForTest(), + internalmessages.OrdersTypeSTUDENTTRAVEL, + ) + + suite.Error(err) + suite.Contains(err.Error(), "failed to parse weight allotment JSON for orders type") + + }) + + suite.Run("Returns an error if no application_parameters entry exists for student travel", func() { + // Don’t create the parameter this time for the student travel + fetcher := NewWeightAllotmentFetcher() + _, err := fetcher.GetWeightAllotment( + suite.AppContextForTest(), + "E-1", + internalmessages.OrdersTypeSTUDENTTRAVEL, + ) + + suite.Error(err) + suite.Contains(err.Error(), "failed to fetch weight allotment for orders type STUDENT_TRAVEL: sql: no rows in result set") + }) + + suite.Run("Returns an error if the orders type is not in the ordersTypeToAllotmentAppParamName map", func() { + fetcher := NewWeightAllotmentFetcher() + _, err := fetcher.GetWeightAllotmentByOrdersType( + suite.AppContextForTest(), + internalmessages.OrdersTypeSEPARATION, + ) + + suite.Error(err) + suite.Contains(err.Error(), "no entitlement found for orders type SEPARATION") }) } diff --git a/swagger-def/internal.yaml b/swagger-def/internal.yaml index a8c8dfb732c..dc151ada9b7 100644 --- a/swagger-def/internal.yaml +++ b/swagger-def/internal.yaml @@ -3826,6 +3826,8 @@ paths: description: List of weights allotted entitlement schema: $ref: '#/definitions/IndexEntitlements' + '500': + description: internal server error /calendar/available_move_dates: get: summary: Returns available dates for the move calendar diff --git a/swagger/internal.yaml b/swagger/internal.yaml index 84097cd100a..4a79321746f 100644 --- a/swagger/internal.yaml +++ b/swagger/internal.yaml @@ -5741,6 +5741,8 @@ paths: description: List of weights allotted entitlement schema: $ref: '#/definitions/IndexEntitlements' + '500': + description: internal server error /calendar/available_move_dates: get: summary: Returns available dates for the move calendar From ee2ee9ba79e6605def0521290a4d8198da2d9e20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 19:49:45 +0000 Subject: [PATCH 033/113] Bump golang.org/x/crypto from 0.27.0 to 0.31.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.27.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.27.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 12 ++++++------ go.sum | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index e528f684f9d..264e97343fa 100644 --- a/go.mod +++ b/go.mod @@ -95,10 +95,10 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.28.0 go.opentelemetry.io/otel/trace v1.31.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.27.0 + golang.org/x/crypto v0.31.0 golang.org/x/net v0.29.0 golang.org/x/oauth2 v0.23.0 - golang.org/x/text v0.18.0 + golang.org/x/text v0.21.0 golang.org/x/tools v0.24.0 google.golang.org/grpc v1.68.0 gopkg.in/dnaeon/go-vcr.v3 v3.2.0 @@ -261,10 +261,10 @@ require ( golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/image v0.18.0 // indirect golang.org/x/mod v0.20.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/protobuf v1.34.2 // indirect diff --git a/go.sum b/go.sum index edfdd1c49f0..8a725675df2 100644 --- a/go.sum +++ b/go.sum @@ -723,8 +723,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= @@ -790,8 +790,8 @@ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -831,8 +831,8 @@ golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -849,8 +849,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -863,8 +863,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= From df5f8fc073e13933cc095127e4b20ba3df0efa5a Mon Sep 17 00:00:00 2001 From: deandreJones Date: Thu, 2 Jan 2025 13:08:22 -0600 Subject: [PATCH 034/113] caps --- .../Office/OrdersDetailForm/OrdersDetailForm.jsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx index 046c4ae9169..e83bf2afcfb 100644 --- a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx +++ b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx @@ -84,7 +84,7 @@ const OrdersDetailForm = ({ name="ordersType" label="Orders type" options={ - formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY || formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK + formOrdersType === 'SAFETY' || formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK ? dropdownInputOptions({ SAFETY: 'Safety', BLUEBARK: 'Bluebark' }) : ordersTypeOptions } @@ -92,11 +92,7 @@ const OrdersDetailForm = ({ setFormOrdersType(e.target.value); setFieldValue('ordersType', e.target.value); }} - isDisabled={ - formIsDisabled || - formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY || - formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK - } + isDisabled={formIsDisabled || formOrdersType === 'SAFETY' || formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK} /> {showOrdersTypeDetail && ( Date: Thu, 2 Jan 2025 13:38:52 -0600 Subject: [PATCH 035/113] put back --- src/constants/orders.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/orders.js b/src/constants/orders.js index 93e20b804c8..eb74a318b38 100644 --- a/src/constants/orders.js +++ b/src/constants/orders.js @@ -24,6 +24,7 @@ export const ORDERS_TYPE_OPTIONS = { RETIREMENT: 'Retirement', SEPARATION: 'Separation', WOUNDED_WARRIOR: 'Wounded Warrior', + BLUEBARK: 'BLUEBARK', TEMPORARY_DUTY: 'Temporary Duty (TDY)', EARLY_RETURN_OF_DEPENDENTS: 'Early Return of Dependents', STUDENT_TRAVEL: 'Student Travel', From cea9ade6f31d8f145ee301d34724754341850ea0 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Thu, 2 Jan 2025 13:55:30 -0600 Subject: [PATCH 036/113] put test order type back to caps --- .../Office/OrdersDetailForm/OrdersDetailForm.test.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Office/OrdersDetailForm/OrdersDetailForm.test.jsx b/src/components/Office/OrdersDetailForm/OrdersDetailForm.test.jsx index 37861c8b881..59c860e300f 100644 --- a/src/components/Office/OrdersDetailForm/OrdersDetailForm.test.jsx +++ b/src/components/Office/OrdersDetailForm/OrdersDetailForm.test.jsx @@ -196,7 +196,7 @@ describe('OrdersDetailForm', () => { showNTSTac: false, showNTSSac: false, showOrdersAcknowledgement: false, - ordersType: 'Safety', + ordersType: 'SAFETY', }); // correct labels are visible From 541c093125d211ff006e9bb2407eb1ac828130e5 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Thu, 2 Jan 2025 20:15:30 +0000 Subject: [PATCH 037/113] continued large entitlement refactoring --- pkg/factory/entitlement_factory.go | 63 ++-- pkg/factory/entitlement_factory_test.go | 34 +- pkg/factory/order_factory.go | 33 ++ pkg/handlers/ghcapi/api.go | 3 + .../internal/payloads/model_to_payload.go | 3 - pkg/handlers/ghcapi/orders.go | 8 +- pkg/handlers/ghcapi/orders_test.go | 8 +- pkg/handlers/internalapi/api.go | 1 - pkg/handlers/internalapi/move_queue_items.go | 125 ------ .../internalapi/move_queue_items_test.go | 64 ---- pkg/handlers/internalapi/orders.go | 23 +- pkg/handlers/internalapi/orders_test.go | 9 +- pkg/handlers/internalapi/weight_allotment.go | 16 - .../primeapi/payloads/model_to_payload.go | 3 - .../payloads/model_to_payload_test.go | 11 +- .../primeapiv2/payloads/model_to_payload.go | 3 - .../payloads/model_to_payload_test.go | 10 +- .../primeapiv3/payloads/model_to_payload.go | 3 - .../payloads/model_to_payload_test.go | 11 +- .../internal/payloads/model_to_payload.go | 3 - .../payloads/model_to_payload_test.go | 128 ++++--- pkg/models/entitlements.go | 356 ------------------ pkg/models/entitlements_test.go | 278 -------------- pkg/models/ghc_entitlements.go | 116 +++++- pkg/models/ghc_entitlements_test.go | 18 - pkg/models/order.go | 21 ++ pkg/models/queue.go | 188 --------- pkg/models/queue_test.go | 55 --- pkg/notifications/move_submitted.go | 17 +- pkg/services/entitlements.go | 6 + pkg/services/move/move_weights.go | 13 +- pkg/services/order/order_updater.go | 63 +++- .../pptas_report/pptas_report_list_fetcher.go | 7 +- .../shipment_summary_worksheet.go | 19 +- .../shipment_summary_worksheet_test.go | 31 +- pkg/testdatagen/make_entitlement.go | 64 +++- src/scenes/Office/api.js | 11 - 37 files changed, 540 insertions(+), 1285 deletions(-) delete mode 100644 pkg/handlers/internalapi/move_queue_items.go delete mode 100644 pkg/handlers/internalapi/move_queue_items_test.go delete mode 100644 pkg/handlers/internalapi/weight_allotment.go delete mode 100644 pkg/models/entitlements.go delete mode 100644 pkg/models/entitlements_test.go delete mode 100644 pkg/models/queue.go delete mode 100644 pkg/models/queue_test.go delete mode 100644 src/scenes/Office/api.js diff --git a/pkg/factory/entitlement_factory.go b/pkg/factory/entitlement_factory.go index c63f1518008..48cd3294a2f 100644 --- a/pkg/factory/entitlement_factory.go +++ b/pkg/factory/entitlement_factory.go @@ -46,7 +46,6 @@ func BuildEntitlement(db *pop.Connection, customs []Customization, traits []Trai ocie := true proGearWeight := 2000 proGearWeightSpouse := 500 - ordersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION // Create default Entitlement entitlement := models.Entitlement{ @@ -61,7 +60,14 @@ func BuildEntitlement(db *pop.Connection, customs []Customization, traits []Trai OrganizationalClothingAndIndividualEquipment: ocie, } // Set default calculated values - entitlement.SetWeightAllotment(string(*grade), ordersType) + weightData := getDefaultWeightData(string(*grade)) + allotment := models.WeightAllotment{ + TotalWeightSelf: weightData.TotalWeightSelf, + TotalWeightSelfPlusDependents: weightData.TotalWeightSelfPlusDependents, + ProGearWeight: weightData.ProGearWeight, + ProGearWeightSpouse: weightData.ProGearWeightSpouse, + } + entitlement.WeightAllotted = &allotment entitlement.DBAuthorizedWeight = entitlement.AuthorizedWeight() // Overwrite default values with those from custom Entitlement @@ -98,7 +104,7 @@ func BuildPayGrade(db *pop.Connection, customs []Customization, traits []Trait) // Create default Pay Grade payGrade := models.PayGrade{ - Grade: "E-5", + Grade: "E_5", GradeDescription: models.StringPointer("Enlisted Grade E-5"), } @@ -170,6 +176,7 @@ func getDefaultWeightData(grade string) struct { } // Default allowances CAO December 2024 +// Note that the testdatagen package has its own default allowance var knownAllowances = map[string]struct { TotalWeightSelf int TotalWeightSelfPlusDependents int @@ -180,30 +187,30 @@ var knownAllowances = map[string]struct { "ACADEMY_CADET": {350, 350, 0, 0}, "MIDSHIPMAN": {350, 350, 0, 0}, "AVIATION_CADET": {7000, 8000, 2000, 500}, - "E-1": {5000, 8000, 2000, 500}, - "E-2": {5000, 8000, 2000, 500}, - "E-3": {5000, 8000, 2000, 500}, - "E-4": {7000, 8000, 2000, 500}, - "E-5": {7000, 9000, 2000, 500}, - "E-6": {8000, 11000, 2000, 500}, - "E-7": {11000, 13000, 2000, 500}, - "E-8": {12000, 14000, 2000, 500}, - "E-9": {13000, 15000, 2000, 500}, - "E-9SPECIALSENIORENLISTED": {14000, 17000, 2000, 500}, - "O-1ACADEMYGRADUATE": {10000, 12000, 2000, 500}, - "O-2": {12500, 13500, 2000, 500}, - "O-3": {13000, 14500, 2000, 500}, - "O-4": {14000, 17000, 2000, 500}, - "O-5": {16000, 17500, 2000, 500}, - "O-6": {18000, 18000, 2000, 500}, - "O-7": {18000, 18000, 2000, 500}, - "O-8": {18000, 18000, 2000, 500}, - "O-9": {18000, 18000, 2000, 500}, - "O-10": {18000, 18000, 2000, 500}, - "W-1": {10000, 12000, 2000, 500}, - "W-2": {12500, 13500, 2000, 500}, - "W-3": {13000, 14500, 2000, 500}, - "W-4": {14000, 17000, 2000, 500}, - "W-5": {16000, 17500, 2000, 500}, + "E_1": {5000, 8000, 2000, 500}, + "E_2": {5000, 8000, 2000, 500}, + "E_3": {5000, 8000, 2000, 500}, + "E_4": {7000, 8000, 2000, 500}, + "E_5": {7000, 9000, 2000, 500}, + "E_6": {8000, 11000, 2000, 500}, + "E_7": {11000, 13000, 2000, 500}, + "E_8": {12000, 14000, 2000, 500}, + "E_9": {13000, 15000, 2000, 500}, + "E_9SPECIALSENIORENLISTED": {14000, 17000, 2000, 500}, + "O_1ACADEMYGRADUATE": {10000, 12000, 2000, 500}, + "O_2": {12500, 13500, 2000, 500}, + "O_3": {13000, 14500, 2000, 500}, + "O_4": {14000, 17000, 2000, 500}, + "O_5": {16000, 17500, 2000, 500}, + "O_6": {18000, 18000, 2000, 500}, + "O_7": {18000, 18000, 2000, 500}, + "O_8": {18000, 18000, 2000, 500}, + "O_9": {18000, 18000, 2000, 500}, + "O_10": {18000, 18000, 2000, 500}, + "W_1": {10000, 12000, 2000, 500}, + "W_2": {12500, 13500, 2000, 500}, + "W_3": {13000, 14500, 2000, 500}, + "W_4": {14000, 17000, 2000, 500}, + "W_5": {16000, 17500, 2000, 500}, "CIVILIAN_EMPLOYEE": {18000, 18000, 2000, 500}, } diff --git a/pkg/factory/entitlement_factory_test.go b/pkg/factory/entitlement_factory_test.go index 85f552c1780..2599af99a50 100644 --- a/pkg/factory/entitlement_factory_test.go +++ b/pkg/factory/entitlement_factory_test.go @@ -5,9 +5,27 @@ import ( "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services/entitlements" ) func (suite *FactorySuite) TestBuildEntitlement() { + fetcher := entitlements.NewWeightAllotmentFetcher() + + setupE1Allotment := func() { + pg := BuildPayGrade(suite.DB(), []Customization{ + { + Model: models.PayGrade{ + Grade: "E_1", + }, + }, + }, nil) + BuildHHGAllowance(suite.DB(), []Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + } suite.Run("Successful creation of default entitlement", func() { // Under test: BuildEntitlement // Mocked: None @@ -16,6 +34,7 @@ func (suite *FactorySuite) TestBuildEntitlement() { // SETUP // Create a default entitlement to compare values + setupE1Allotment() defEnt := models.Entitlement{ DependentsAuthorized: models.BoolPointer(true), TotalDependents: models.IntPointer(0), @@ -27,7 +46,10 @@ func (suite *FactorySuite) TestBuildEntitlement() { RequiredMedicalEquipmentWeight: 1000, OrganizationalClothingAndIndividualEquipment: true, } - defEnt.SetWeightAllotment("E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + defEnt.WeightAllotted = &allotment + defEnt.DBAuthorizedWeight = defEnt.AuthorizedWeight() // FUNCTION UNDER TEST @@ -55,6 +77,7 @@ func (suite *FactorySuite) TestBuildEntitlement() { // SETUP // Create a default entitlement to compare values + setupE1Allotment() custEnt := models.Entitlement{ DependentsAuthorized: models.BoolPointer(false), TotalDependents: models.IntPointer(0), @@ -84,7 +107,9 @@ func (suite *FactorySuite) TestBuildEntitlement() { suite.Equal(custEnt.OrganizationalClothingAndIndividualEquipment, entitlement.OrganizationalClothingAndIndividualEquipment) // Set the weight allotment on the custom object so as to compare - custEnt.SetWeightAllotment("E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + *custEnt.WeightAllotted = allotment custEnt.DBAuthorizedWeight = custEnt.AuthorizedWeight() // Check that the created object had the correct allotments set @@ -126,10 +151,13 @@ func (suite *FactorySuite) TestBuildEntitlement() { // SETUP // Create a default stubbed entitlement to compare values + setupE1Allotment() testEnt := BuildEntitlement(nil, nil, nil) // Set the weight allotment on the custom object to O_9 testEnt.DBAuthorizedWeight = nil // clear original value - testEnt.SetWeightAllotment("O_9", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + *testEnt.WeightAllotted = allotment testEnt.DBAuthorizedWeight = testEnt.AuthorizedWeight() // Now DBAuthorizedWeight should be appropriate for O_9 grade diff --git a/pkg/factory/order_factory.go b/pkg/factory/order_factory.go index d6f2165e095..c2359e602a4 100644 --- a/pkg/factory/order_factory.go +++ b/pkg/factory/order_factory.go @@ -290,6 +290,39 @@ func buildOrderWithBuildType(db *pop.Connection, customs []Customization, traits // Overwrite values with those from assertions testdatagen.MergeModels(&order, cOrder) + // Check if PayGrade already exists + var existingPayGrade models.PayGrade + if order.Grade != nil { + err := db.Where("grade = ?", string(*order.Grade)).First(&existingPayGrade) + if err == nil { + // PayGrade exists + grade := internalmessages.OrderPayGrade(existingPayGrade.Grade) + order.Grade = internalmessages.NewOrderPayGrade(grade) + } else { + // Create a new PayGrade + existingPayGrade = BuildPayGrade(db, []Customization{ + { + Model: models.PayGrade{ + Grade: string(*order.Grade), + }, + }, + }, nil) + } + } + + // Check if HHGAllowance already exists for this PayGrade + var existingHHGAllowance models.HHGAllowance + err := db.Where("pay_grade_id = ?", existingPayGrade.ID).First(&existingHHGAllowance) + if err != nil { + // Create a new HHGAllowance + BuildHHGAllowance(db, []Customization{ + { + Model: existingPayGrade, + LinkOnly: true, + }, + }, nil) + } + // If db is false, it's a stub. No need to create in database if db != nil { mustCreate(db, &order) diff --git a/pkg/handlers/ghcapi/api.go b/pkg/handlers/ghcapi/api.go index 1e6d7fcb11c..03b9f0c22f1 100644 --- a/pkg/handlers/ghcapi/api.go +++ b/pkg/handlers/ghcapi/api.go @@ -14,6 +14,7 @@ import ( boatshipment "github.com/transcom/mymove/pkg/services/boat_shipment" dateservice "github.com/transcom/mymove/pkg/services/calendar" customerserviceremarks "github.com/transcom/mymove/pkg/services/customer_support_remarks" + "github.com/transcom/mymove/pkg/services/entitlements" evaluationreport "github.com/transcom/mymove/pkg/services/evaluation_report" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" @@ -59,6 +60,7 @@ import ( // NewGhcAPIHandler returns a handler for the GHC API func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { + waf := entitlements.NewWeightAllotmentFetcher() ghcSpec, err := loads.Analyzed(ghcapi.SwaggerJSON, "") if err != nil { log.Fatalln(err) @@ -310,6 +312,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { } ghcAPI.OrderCreateOrderHandler = CreateOrderHandler{ handlerConfig, + waf, } ghcAPI.OrderUpdateOrderHandler = UpdateOrderHandler{ diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index caa4aef11d5..193540cd530 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -615,9 +615,6 @@ func Order(order *models.Order) *ghcmessages.Order { destinationDutyLocation := DutyLocation(&order.NewDutyLocation) originDutyLocation := DutyLocation(order.OriginDutyLocation) - if order.Grade != nil && order.Entitlement != nil { - order.Entitlement.SetWeightAllotment(string(*order.Grade), order.OrdersType) - } entitlements := Entitlement(order.Entitlement) var deptIndicator ghcmessages.DeptIndicator diff --git a/pkg/handlers/ghcapi/orders.go b/pkg/handlers/ghcapi/orders.go index ff1c47a9569..06610849409 100644 --- a/pkg/handlers/ghcapi/orders.go +++ b/pkg/handlers/ghcapi/orders.go @@ -164,6 +164,7 @@ func (h CounselingUpdateOrderHandler) Handle( // CounselingUpdateOrderHandler create an order via POST /orders type CreateOrderHandler struct { handlers.HandlerConfig + services.WeightAllotmentFetcher } // Handle ... creates an order as requested by a services counselor @@ -237,7 +238,12 @@ func (h CreateOrderHandler) Handle(params orderop.CreateOrderParams) middleware. grade := (internalmessages.OrderPayGrade)(*payload.Grade) ordersType := (internalmessages.OrdersType)(*payload.OrdersType) - weightAllotment := models.GetWeightAllotment(grade, ordersType) + weightAllotment, err := h.WeightAllotmentFetcher.GetWeightAllotment(appCtx, string(grade), ordersType) + if err != nil { + err = apperror.NewBadDataError("Weight allotment cannot be verified") + appCtx.Logger().Error(err.Error()) + return orderop.NewCreateOrderUnprocessableEntity(), err + } weight := weightAllotment.TotalWeightSelf if *payload.HasDependents { weight = weightAllotment.TotalWeightSelfPlusDependents diff --git a/pkg/handlers/ghcapi/orders_test.go b/pkg/handlers/ghcapi/orders_test.go index 93fdc901600..928a5e0db95 100644 --- a/pkg/handlers/ghcapi/orders_test.go +++ b/pkg/handlers/ghcapi/orders_test.go @@ -22,6 +22,7 @@ import ( "github.com/transcom/mymove/pkg/models/roles" routemocks "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/ghcrateengine" "github.com/transcom/mymove/pkg/services/mocks" moverouter "github.com/transcom/mymove/pkg/services/move" @@ -36,6 +37,7 @@ import ( ) func (suite *HandlerSuite) TestCreateOrder() { + waf := entitlements.NewWeightAllotmentFetcher() sm := factory.BuildExtendedServiceMember(suite.AppContextForTest().DB(), nil, nil) officeUser := factory.BuildOfficeUserWithRoles(suite.AppContextForTest().DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) @@ -83,7 +85,7 @@ func (suite *HandlerSuite) TestCreateOrder() { fakeS3 := storageTest.NewFakeS3Storage(true) handlerConfig := suite.HandlerConfig() handlerConfig.SetFileStorer(fakeS3) - createHandler := CreateOrderHandler{handlerConfig} + createHandler := CreateOrderHandler{handlerConfig, waf} response := createHandler.Handle(params) @@ -106,6 +108,8 @@ func (suite *HandlerSuite) TestCreateOrder() { } func (suite *HandlerSuite) TestCreateOrderWithOCONUSValues() { + waf := entitlements.NewWeightAllotmentFetcher() + sm := factory.BuildExtendedServiceMember(suite.AppContextForTest().DB(), nil, nil) officeUser := factory.BuildOfficeUserWithRoles(suite.AppContextForTest().DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) @@ -159,7 +163,7 @@ func (suite *HandlerSuite) TestCreateOrderWithOCONUSValues() { fakeS3 := storageTest.NewFakeS3Storage(true) handlerConfig := suite.HandlerConfig() handlerConfig.SetFileStorer(fakeS3) - createHandler := CreateOrderHandler{handlerConfig} + createHandler := CreateOrderHandler{handlerConfig, waf} response := createHandler.Handle(params) diff --git a/pkg/handlers/internalapi/api.go b/pkg/handlers/internalapi/api.go index 500e9474ded..e746987ede7 100644 --- a/pkg/handlers/internalapi/api.go +++ b/pkg/handlers/internalapi/api.go @@ -176,7 +176,6 @@ func NewInternalAPI(handlerConfig handlers.HandlerConfig) *internalops.MymoveAPI internalAPI.UploadsDeleteUploadHandler = DeleteUploadHandler{handlerConfig, upload.NewUploadInformationFetcher()} internalAPI.UploadsDeleteUploadsHandler = DeleteUploadsHandler{handlerConfig} - internalAPI.QueuesShowQueueHandler = ShowQueueHandler{handlerConfig} internalAPI.OfficeApproveMoveHandler = ApproveMoveHandler{handlerConfig, moveRouter} internalAPI.OfficeApproveReimbursementHandler = ApproveReimbursementHandler{handlerConfig} internalAPI.OfficeCancelMoveHandler = CancelMoveHandler{handlerConfig, moveRouter} diff --git a/pkg/handlers/internalapi/move_queue_items.go b/pkg/handlers/internalapi/move_queue_items.go deleted file mode 100644 index 071ae2fba79..00000000000 --- a/pkg/handlers/internalapi/move_queue_items.go +++ /dev/null @@ -1,125 +0,0 @@ -package internalapi - -import ( - "sort" - "strings" - "time" - - "github.com/go-openapi/runtime/middleware" - "github.com/gofrs/uuid" - "go.uber.org/zap" - - "github.com/transcom/mymove/pkg/appcontext" - "github.com/transcom/mymove/pkg/apperror" - queueop "github.com/transcom/mymove/pkg/gen/internalapi/internaloperations/queues" - "github.com/transcom/mymove/pkg/gen/internalmessages" - "github.com/transcom/mymove/pkg/handlers" - "github.com/transcom/mymove/pkg/models" -) - -func payloadForMoveQueueItem(MoveQueueItem models.MoveQueueItem) *internalmessages.MoveQueueItem { - MoveQueueItemPayload := internalmessages.MoveQueueItem{ - ID: handlers.FmtUUID(MoveQueueItem.ID), - CreatedAt: handlers.FmtDateTime(MoveQueueItem.CreatedAt), - Edipi: models.StringPointer(MoveQueueItem.Edipi), - Grade: MoveQueueItem.Grade, - CustomerName: models.StringPointer(MoveQueueItem.CustomerName), - Locator: models.StringPointer(MoveQueueItem.Locator), - Status: models.StringPointer(MoveQueueItem.Status), - PpmStatus: handlers.FmtStringPtr(MoveQueueItem.PpmStatus), - OrdersType: MoveQueueItem.OrdersType, - MoveDate: handlers.FmtDatePtr(MoveQueueItem.MoveDate), - SubmittedDate: handlers.FmtDateTimePtr(MoveQueueItem.SubmittedDate), - LastModifiedDate: handlers.FmtDateTime(MoveQueueItem.LastModifiedDate), - OriginDutyLocationName: models.StringPointer(MoveQueueItem.OriginDutyLocationName), - DestinationDutyLocationName: models.StringPointer(MoveQueueItem.DestinationDutyLocationName), - PmSurveyConductedDate: handlers.FmtDateTimePtr(MoveQueueItem.PmSurveyConductedDate), - OriginGbloc: handlers.FmtStringPtr(MoveQueueItem.OriginGBLOC), - DestinationGbloc: handlers.FmtStringPtr(MoveQueueItem.DestinationGBLOC), - DeliveredDate: handlers.FmtDateTimePtr(MoveQueueItem.DeliveredDate), - InvoiceApprovedDate: handlers.FmtDateTimePtr(MoveQueueItem.InvoiceApprovedDate), - WeightAllotment: payloadForWeightAllotmentModel(models.GetWeightAllotment(*MoveQueueItem.Grade, *MoveQueueItem.OrdersType)), - BranchOfService: handlers.FmtString(MoveQueueItem.BranchOfService), - ActualMoveDate: handlers.FmtDatePtr(MoveQueueItem.ActualMoveDate), - OriginalMoveDate: handlers.FmtDatePtr(MoveQueueItem.OriginalMoveDate), - } - return &MoveQueueItemPayload -} - -// ShowQueueHandler returns a list of all MoveQueueItems in the moves queue -type ShowQueueHandler struct { - handlers.HandlerConfig -} - -// JSONDate is a time type -type JSONDate time.Time - -// UnmarshalJSON Dates without timestamps need custom unmarshalling -func (j *JSONDate) UnmarshalJSON(b []byte) error { - s := strings.Trim(string(b), "\"") - if s == "null" { - return nil - } - t, err := time.Parse("2006-01-02", s) - if err != nil { - return err - } - *j = JSONDate(t) - return nil -} - -// QueueSitData is SIT data in a queue -type QueueSitData struct { - ID uuid.UUID `json:"id"` - Status string `json:"status"` - ActualStartDate JSONDate `json:"actual_start_date"` - OutDate JSONDate `json:"out_date"` - Location string `json:"location"` -} - -// MoveQueueItems is a set of move queue items -// Implementation of a type and methods in order to use sort.Interface directly. -// This allows us to call sortQueueItemsByLastModifiedDate in the ShowQueueHandler which will -// sort the slice by the LastModfiedDate. Doing it this way allows us to avoid having reflect called -// which should act to speed the sort up. -type MoveQueueItems []models.MoveQueueItem - -func (mqi MoveQueueItems) Less(i, j int) bool { - return mqi[i].LastModifiedDate.Before(mqi[j].LastModifiedDate) -} -func (mqi MoveQueueItems) Len() int { return len(mqi) } -func (mqi MoveQueueItems) Swap(i, j int) { mqi[i], mqi[j] = mqi[j], mqi[i] } - -func sortQueueItemsByLastModifiedDate(moveQueueItems []models.MoveQueueItem) { - sort.Sort(MoveQueueItems(moveQueueItems)) -} - -// Handle retrieves a list of all MoveQueueItems in the system in the moves queue -func (h ShowQueueHandler) Handle(params queueop.ShowQueueParams) middleware.Responder { - return h.AuditableAppContextFromRequestWithErrors(params.HTTPRequest, - func(appCtx appcontext.AppContext) (middleware.Responder, error) { - - if !appCtx.Session().IsOfficeUser() { - badUserErr := apperror.NewSessionError("User is not an Office user") - return queueop.NewShowQueueForbidden(), badUserErr - } - - lifecycleState := params.QueueType - - MoveQueueItems, err := models.GetMoveQueueItems(appCtx.DB(), lifecycleState) - if err != nil { - appCtx.Logger().Error("Loading Queue", zap.String("State", lifecycleState), zap.Error(err)) - return handlers.ResponseForError(appCtx.Logger(), err), err - } - - // Sorting the slice by LastModifiedDate so that the API results follow suit. - sortQueueItemsByLastModifiedDate(MoveQueueItems) - - MoveQueueItemPayloads := make([]*internalmessages.MoveQueueItem, len(MoveQueueItems)) - for i, MoveQueueItem := range MoveQueueItems { - MoveQueueItemPayload := payloadForMoveQueueItem(MoveQueueItem) - MoveQueueItemPayloads[i] = MoveQueueItemPayload - } - return queueop.NewShowQueueOK().WithPayload(MoveQueueItemPayloads), nil - }) -} diff --git a/pkg/handlers/internalapi/move_queue_items_test.go b/pkg/handlers/internalapi/move_queue_items_test.go deleted file mode 100644 index bc95161d5d0..00000000000 --- a/pkg/handlers/internalapi/move_queue_items_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package internalapi - -import ( - "net/http/httptest" - - "github.com/transcom/mymove/pkg/factory" - queueop "github.com/transcom/mymove/pkg/gen/internalapi/internaloperations/queues" - "github.com/transcom/mymove/pkg/models/roles" -) - -var statusToQueueMap = map[string]string{ - "SUBMITTED": "new", - "APPROVED": "ppm_approved", - "PAYMENT_REQUESTED": "ppm_payment_requested", - "COMPLETED": "ppm_completed", -} - -func (suite *HandlerSuite) TestShowQueueHandlerForbidden() { - for _, queueType := range statusToQueueMap { - - // Given: A non-office user - user := factory.BuildServiceMember(suite.DB(), nil, nil) - - // And: the context contains the auth values - path := "/queues/" + queueType - req := httptest.NewRequest("GET", path, nil) - req = suite.AuthenticateRequest(req, user) - - params := queueop.ShowQueueParams{ - HTTPRequest: req, - QueueType: queueType, - } - - // And: show Queue is queried - showHandler := ShowQueueHandler{suite.HandlerConfig()} - showResponse := showHandler.Handle(params) - - // Then: Expect a 403 status code - suite.Assertions.IsType(&queueop.ShowQueueForbidden{}, showResponse) - } -} - -func (suite *HandlerSuite) TestShowQueueHandlerNotFound() { - - // Given: An office user - officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) - - // And: the context contains the auth values - queueType := "queue_not_found" - path := "/queues/" + queueType - req := httptest.NewRequest("GET", path, nil) - req = suite.AuthenticateOfficeRequest(req, officeUser) - - params := queueop.ShowQueueParams{ - HTTPRequest: req, - QueueType: queueType, - } - // And: show Queue is queried - showHandler := ShowQueueHandler{suite.HandlerConfig()} - showResponse := showHandler.Handle(params) - - // Then: Expect a 404 status code - suite.CheckResponseNotFound(showResponse) -} diff --git a/pkg/handlers/internalapi/orders.go b/pkg/handlers/internalapi/orders.go index c9b5125c827..da3d9c118bc 100644 --- a/pkg/handlers/internalapi/orders.go +++ b/pkg/handlers/internalapi/orders.go @@ -19,6 +19,7 @@ import ( "github.com/transcom/mymove/pkg/handlers/internalapi/internal/payloads" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/storage" "github.com/transcom/mymove/pkg/uploader" ) @@ -195,9 +196,18 @@ func (h CreateOrdersHandler) Handle(params ordersop.CreateOrdersParams) middlewa grade := payload.Grade + if payload.OrdersType == nil { + errMsg := "missing required field: OrdersType" + return handlers.ResponseForError(appCtx.Logger(), errors.New(errMsg)), apperror.NewBadDataError("missing required field: OrdersType") + } + // Calculate the entitlement for the order ordersType := payload.OrdersType - weightAllotment := models.GetWeightAllotment(*grade, *ordersType) + waf := entitlements.NewWeightAllotmentFetcher() + weightAllotment, err := waf.GetWeightAllotment(appCtx, string(*grade), *ordersType) + if err != nil { + return handlers.ResponseForError(appCtx.Logger(), err), err + } weight := weightAllotment.TotalWeightSelf if *payload.HasDependents { weight = weightAllotment.TotalWeightSelfPlusDependents @@ -247,11 +257,6 @@ func (h CreateOrdersHandler) Handle(params ordersop.CreateOrdersParams) middlewa deptIndicator = &converted } - if payload.OrdersType == nil { - errMsg := "missing required field: OrdersType" - return handlers.ResponseForError(appCtx.Logger(), errors.New(errMsg)), apperror.NewBadDataError("missing required field: OrdersType") - } - contractor, err := models.FetchGHCPrimeContractor(appCtx.DB()) if err != nil { return handlers.ResponseForError(appCtx.Logger(), err), err @@ -437,7 +442,11 @@ func (h UpdateOrdersHandler) Handle(params ordersop.UpdateOrdersParams) middlewa // Check if the grade or dependents are receiving an update if hasEntitlementChanged(order, payload.OrdersType, payload.Grade, payload.DependentsUnderTwelve, payload.DependentsTwelveAndOver, payload.AccompaniedTour) { - weightAllotment := models.GetWeightAllotment(*payload.Grade, *payload.OrdersType) + waf := entitlements.NewWeightAllotmentFetcher() + weightAllotment, err := waf.GetWeightAllotment(appCtx, string(*payload.Grade), *payload.OrdersType) + if err != nil { + return handlers.ResponseForError(appCtx.Logger(), err), err + } weight := weightAllotment.TotalWeightSelf if *payload.HasDependents { weight = weightAllotment.TotalWeightSelfPlusDependents diff --git a/pkg/handlers/internalapi/orders_test.go b/pkg/handlers/internalapi/orders_test.go index b2ad7897f8a..f6ab9b03906 100644 --- a/pkg/handlers/internalapi/orders_test.go +++ b/pkg/handlers/internalapi/orders_test.go @@ -17,6 +17,7 @@ import ( "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/mocks" "github.com/transcom/mymove/pkg/services/move" orderservice "github.com/transcom/mymove/pkg/services/order" @@ -584,7 +585,7 @@ func (suite *HandlerSuite) TestUploadAmendedOrdersHandlerIntegration() { } func (suite *HandlerSuite) TestUpdateOrdersHandler() { - + waf := entitlements.NewWeightAllotmentFetcher() suite.Run("Can update CONUS and OCONUS orders", func() { testCases := []struct { isOconus bool @@ -689,13 +690,15 @@ func (suite *HandlerSuite) TestUpdateOrdersHandler() { suite.NoError(err) suite.Equal(payload.Grade, updatedOrder.Grade) suite.Equal(*okResponse.Payload.AuthorizedWeight, int64(7000)) // E4 authorized weight is 7000, make sure we return that in the response - expectedUpdatedOrderWeightAllotment := models.GetWeightAllotment(*updatedOrder.Grade, updatedOrder.OrdersType) + expectedUpdatedOrderWeightAllotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(*updatedOrder.Grade), updatedOrder.OrdersType) + suite.NoError(err) expectedUpdatedOrderAuthorizedWeight := expectedUpdatedOrderWeightAllotment.TotalWeightSelf if *payload.HasDependents { expectedUpdatedOrderAuthorizedWeight = expectedUpdatedOrderWeightAllotment.TotalWeightSelfPlusDependents } - expectedOriginalOrderWeightAllotment := models.GetWeightAllotment(*order.Grade, updatedOrder.OrdersType) + expectedOriginalOrderWeightAllotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(*order.Grade), updatedOrder.OrdersType) + suite.NoError(err) expectedOriginalOrderAuthorizedWeight := expectedOriginalOrderWeightAllotment.TotalWeightSelf if *payload.HasDependents { expectedUpdatedOrderAuthorizedWeight = expectedOriginalOrderWeightAllotment.TotalWeightSelfPlusDependents diff --git a/pkg/handlers/internalapi/weight_allotment.go b/pkg/handlers/internalapi/weight_allotment.go deleted file mode 100644 index 90e2058c53f..00000000000 --- a/pkg/handlers/internalapi/weight_allotment.go +++ /dev/null @@ -1,16 +0,0 @@ -package internalapi - -import ( - "github.com/transcom/mymove/pkg/gen/internalmessages" - "github.com/transcom/mymove/pkg/handlers" - "github.com/transcom/mymove/pkg/models" -) - -func payloadForWeightAllotmentModel(allotment models.WeightAllotment) *internalmessages.WeightAllotment { - return &internalmessages.WeightAllotment{ - ProGearWeight: handlers.FmtInt64(int64(allotment.ProGearWeight)), - ProGearWeightSpouse: handlers.FmtInt64(int64(allotment.ProGearWeightSpouse)), - TotalWeightSelf: handlers.FmtInt64(int64(allotment.TotalWeightSelf)), - TotalWeightSelfPlusDependents: handlers.FmtInt64(int64(allotment.TotalWeightSelfPlusDependents)), - } -} diff --git a/pkg/handlers/primeapi/payloads/model_to_payload.go b/pkg/handlers/primeapi/payloads/model_to_payload.go index d5c76723fa4..2802cd99717 100644 --- a/pkg/handlers/primeapi/payloads/model_to_payload.go +++ b/pkg/handlers/primeapi/payloads/model_to_payload.go @@ -151,9 +151,6 @@ func Order(order *models.Order) *primemessages.Order { } destinationDutyLocation := DutyLocation(&order.NewDutyLocation) originDutyLocation := DutyLocation(order.OriginDutyLocation) - if order.Grade != nil && order.Entitlement != nil { - order.Entitlement.SetWeightAllotment(string(*order.Grade), order.OrdersType) - } var grade string if order.Grade != nil { diff --git a/pkg/handlers/primeapi/payloads/model_to_payload_test.go b/pkg/handlers/primeapi/payloads/model_to_payload_test.go index 73342af2430..b71615cf6a4 100644 --- a/pkg/handlers/primeapi/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapi/payloads/model_to_payload_test.go @@ -13,6 +13,7 @@ import ( "github.com/transcom/mymove/pkg/gen/primemessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/storage/test" "github.com/transcom/mymove/pkg/unit" ) @@ -269,6 +270,7 @@ func (suite *PayloadsSuite) TestSitExtension() { } func (suite *PayloadsSuite) TestEntitlement() { + waf := entitlements.NewWeightAllotmentFetcher() suite.Run("Success - Returns the entitlement payload with only required fields", func() { entitlement := models.Entitlement{ @@ -330,8 +332,9 @@ func (suite *PayloadsSuite) TestEntitlement() { // TotalWeight needs to read from the internal weightAllotment, in this case 7000 lbs w/o dependents and // 9000 lbs with dependents - entitlement.SetWeightAllotment(string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) - + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + entitlement.WeightAllotted = &allotment payload := Entitlement(&entitlement) suite.Equal(strfmt.UUID(entitlement.ID.String()), payload.ID) @@ -371,7 +374,9 @@ func (suite *PayloadsSuite) TestEntitlement() { // TotalWeight needs to read from the internal weightAllotment, in this case 7000 lbs w/o dependents and // 9000 lbs with dependents - entitlement.SetWeightAllotment(string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + entitlement.WeightAllotted = &allotment payload := Entitlement(&entitlement) diff --git a/pkg/handlers/primeapiv2/payloads/model_to_payload.go b/pkg/handlers/primeapiv2/payloads/model_to_payload.go index 56ad6caabcb..6828c5f839c 100644 --- a/pkg/handlers/primeapiv2/payloads/model_to_payload.go +++ b/pkg/handlers/primeapiv2/payloads/model_to_payload.go @@ -93,9 +93,6 @@ func Order(order *models.Order) *primev2messages.Order { } destinationDutyLocation := DutyLocation(&order.NewDutyLocation) originDutyLocation := DutyLocation(order.OriginDutyLocation) - if order.Grade != nil && order.Entitlement != nil { - order.Entitlement.SetWeightAllotment(string(*order.Grade), order.OrdersType) - } var grade string if order.Grade != nil { diff --git a/pkg/handlers/primeapiv2/payloads/model_to_payload_test.go b/pkg/handlers/primeapiv2/payloads/model_to_payload_test.go index 13e3bd35e3c..9594d29e683 100644 --- a/pkg/handlers/primeapiv2/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapiv2/payloads/model_to_payload_test.go @@ -13,6 +13,7 @@ import ( "github.com/transcom/mymove/pkg/gen/primev2messages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/unit" ) @@ -215,6 +216,7 @@ func (suite *PayloadsSuite) TestSitExtension() { } func (suite *PayloadsSuite) TestEntitlement() { + waf := entitlements.NewWeightAllotmentFetcher() suite.Run("Success - Returns the entitlement payload with only required fields", func() { entitlement := models.Entitlement{ @@ -276,7 +278,9 @@ func (suite *PayloadsSuite) TestEntitlement() { // TotalWeight needs to read from the internal weightAllotment, in this case 7000 lbs w/o dependents and // 9000 lbs with dependents - entitlement.SetWeightAllotment(string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + entitlement.WeightAllotted = &allotment payload := Entitlement(&entitlement) @@ -317,7 +321,9 @@ func (suite *PayloadsSuite) TestEntitlement() { // TotalWeight needs to read from the internal weightAllotment, in this case 7000 lbs w/o dependents and // 9000 lbs with dependents - entitlement.SetWeightAllotment(string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + entitlement.WeightAllotted = &allotment payload := Entitlement(&entitlement) diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload.go b/pkg/handlers/primeapiv3/payloads/model_to_payload.go index f4cd6ab2609..1cb5ea97933 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload.go @@ -94,9 +94,6 @@ func Order(order *models.Order) *primev3messages.Order { } destinationDutyLocation := DutyLocation(&order.NewDutyLocation) originDutyLocation := DutyLocation(order.OriginDutyLocation) - if order.Grade != nil && order.Entitlement != nil { - order.Entitlement.SetWeightAllotment(string(*order.Grade), order.OrdersType) - } var grade string if order.Grade != nil { diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go b/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go index 5f7e78e43ed..c42ba63dbed 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go @@ -13,6 +13,7 @@ import ( "github.com/transcom/mymove/pkg/gen/primev3messages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/unit" ) @@ -231,7 +232,7 @@ func (suite *PayloadsSuite) TestSitExtension() { } func (suite *PayloadsSuite) TestEntitlement() { - + waf := entitlements.NewWeightAllotmentFetcher() suite.Run("Success - Returns the entitlement payload with only required fields", func() { entitlement := models.Entitlement{ ID: uuid.Must(uuid.NewV4()), @@ -292,7 +293,9 @@ func (suite *PayloadsSuite) TestEntitlement() { // TotalWeight needs to read from the internal weightAllotment, in this case 7000 lbs w/o dependents and // 9000 lbs with dependents - entitlement.SetWeightAllotment(string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + entitlement.WeightAllotted = &allotment payload := Entitlement(&entitlement) @@ -334,7 +337,9 @@ func (suite *PayloadsSuite) TestEntitlement() { // TotalWeight needs to read from the internal weightAllotment, in this case 7000 lbs w/o dependents and // 9000 lbs with dependents - entitlement.SetWeightAllotment(string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + entitlement.WeightAllotted = &allotment payload := Entitlement(&entitlement) diff --git a/pkg/handlers/supportapi/internal/payloads/model_to_payload.go b/pkg/handlers/supportapi/internal/payloads/model_to_payload.go index 5e43cd2070b..12ebfd4b8a1 100644 --- a/pkg/handlers/supportapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/supportapi/internal/payloads/model_to_payload.go @@ -85,9 +85,6 @@ func Order(order *models.Order) *supportmessages.Order { destinationDutyLocation := DutyLocation(&order.NewDutyLocation) originDutyLocation := DutyLocation(order.OriginDutyLocation) uploadedOrders := Document(&order.UploadedOrders) - if order.Grade != nil && order.Entitlement != nil { - order.Entitlement.SetWeightAllotment(string(*order.Grade), order.OrdersType) - } reportByDate := strfmt.Date(order.ReportByDate) issueDate := strfmt.Date(order.IssueDate) diff --git a/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go index 05852f8b087..eeb93bc0394 100644 --- a/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go @@ -6,22 +6,36 @@ import ( "github.com/go-openapi/strfmt" "github.com/gofrs/uuid" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" "github.com/transcom/mymove/pkg/etag" "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/notifications" + "github.com/transcom/mymove/pkg/services/entitlements" + "github.com/transcom/mymove/pkg/testingsuite" ) -func TestOrder(_ *testing.T) { - order := &models.Order{} - Order(order) +// HandlerSuite is an abstraction of our original suite +type PayloadsSuite struct { + handlers.BaseHandlerTestSuite } -func TestEntitlement(t *testing.T) { +// TestHandlerSuite creates our test suite +func TestHandlerSuite(t *testing.T) { + hs := &PayloadsSuite{ + BaseHandlerTestSuite: handlers.NewBaseHandlerTestSuite(notifications.NewStubNotificationSender("milmovelocal"), testingsuite.CurrentPackage(), + testingsuite.WithPerTestTransaction()), + } - t.Run("Success - Returns the entitlement payload with only required fields", func(t *testing.T) { + suite.Run(t, hs) + hs.PopTestSuite.TearDown() +} + +func (suite *PayloadsSuite) TestEntitlement() { + waf := entitlements.NewWeightAllotmentFetcher() + suite.Run("Success - Returns the entitlement payload with only required fields", func() { entitlement := models.Entitlement{ ID: uuid.Must(uuid.NewV4()), DependentsAuthorized: nil, @@ -41,27 +55,27 @@ func TestEntitlement(t *testing.T) { payload := Entitlement(&entitlement) - assert.Equal(t, strfmt.UUID(entitlement.ID.String()), payload.ID) - assert.Equal(t, int64(0), payload.RequiredMedicalEquipmentWeight) - assert.Equal(t, false, payload.OrganizationalClothingAndIndividualEquipment) - assert.Equal(t, int64(0), payload.ProGearWeight) - assert.Equal(t, int64(0), payload.ProGearWeightSpouse) - assert.NotEmpty(t, payload.ETag) - assert.Equal(t, etag.GenerateEtag(entitlement.UpdatedAt), payload.ETag) + suite.Equal(strfmt.UUID(entitlement.ID.String()), payload.ID) + suite.Equal(int64(0), payload.RequiredMedicalEquipmentWeight) + suite.Equal(false, payload.OrganizationalClothingAndIndividualEquipment) + suite.Equal(int64(0), payload.ProGearWeight) + suite.Equal(int64(0), payload.ProGearWeightSpouse) + suite.NotEmpty(payload.ETag) + suite.Equal(etag.GenerateEtag(entitlement.UpdatedAt), payload.ETag) - assert.Nil(t, payload.AuthorizedWeight) - assert.Nil(t, payload.DependentsAuthorized) - assert.Nil(t, payload.NonTemporaryStorage) - assert.Nil(t, payload.PrivatelyOwnedVehicle) + suite.Nil(payload.AuthorizedWeight) + suite.Nil(payload.DependentsAuthorized) + suite.Nil(payload.NonTemporaryStorage) + suite.Nil(payload.PrivatelyOwnedVehicle) /* These fields are defaulting to zero if they are nil in the model */ - assert.Equal(t, int64(0), payload.StorageInTransit) - assert.Equal(t, int64(0), payload.TotalDependents) - assert.Equal(t, int64(0), payload.TotalWeight) - assert.Equal(t, int64(0), *payload.UnaccompaniedBaggageAllowance) + suite.Equal(int64(0), payload.StorageInTransit) + suite.Equal(int64(0), payload.TotalDependents) + suite.Equal(int64(0), payload.TotalWeight) + suite.Equal(int64(0), *payload.UnaccompaniedBaggageAllowance) }) - t.Run("Success - Returns the entitlement payload with all optional fields populated", func(t *testing.T) { + suite.Run("Success - Returns the entitlement payload with all optional fields populated", func() { entitlement := models.Entitlement{ ID: uuid.Must(uuid.NewV4()), DependentsAuthorized: handlers.FmtBool(true), @@ -81,28 +95,30 @@ func TestEntitlement(t *testing.T) { // TotalWeight needs to read from the internal weightAllotment, in this case 7000 lbs w/o dependents and // 9000 lbs with dependents - entitlement.SetWeightAllotment(string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + entitlement.WeightAllotted = &allotment payload := Entitlement(&entitlement) - assert.Equal(t, strfmt.UUID(entitlement.ID.String()), payload.ID) - assert.True(t, *payload.DependentsAuthorized) - assert.Equal(t, int64(2), payload.TotalDependents) - assert.True(t, *payload.NonTemporaryStorage) - assert.True(t, *payload.PrivatelyOwnedVehicle) - assert.Equal(t, int64(10000), *payload.AuthorizedWeight) - assert.Equal(t, int64(400), *payload.UnaccompaniedBaggageAllowance) - assert.Equal(t, int64(9000), payload.TotalWeight) - assert.Equal(t, int64(45), payload.StorageInTransit) - assert.Equal(t, int64(500), payload.RequiredMedicalEquipmentWeight) - assert.Equal(t, true, payload.OrganizationalClothingAndIndividualEquipment) - assert.Equal(t, int64(1000), payload.ProGearWeight) - assert.Equal(t, int64(750), payload.ProGearWeightSpouse) - assert.NotEmpty(t, payload.ETag) - assert.Equal(t, etag.GenerateEtag(entitlement.UpdatedAt), payload.ETag) + suite.Equal(strfmt.UUID(entitlement.ID.String()), payload.ID) + suite.True(*payload.DependentsAuthorized) + suite.Equal(int64(2), payload.TotalDependents) + suite.True(*payload.NonTemporaryStorage) + suite.True(*payload.PrivatelyOwnedVehicle) + suite.Equal(int64(10000), *payload.AuthorizedWeight) + suite.Equal(int64(400), *payload.UnaccompaniedBaggageAllowance) + suite.Equal(int64(9000), payload.TotalWeight) + suite.Equal(int64(45), payload.StorageInTransit) + suite.Equal(int64(500), payload.RequiredMedicalEquipmentWeight) + suite.Equal(true, payload.OrganizationalClothingAndIndividualEquipment) + suite.Equal(int64(1000), payload.ProGearWeight) + suite.Equal(int64(750), payload.ProGearWeightSpouse) + suite.NotEmpty(payload.ETag) + suite.Equal(etag.GenerateEtag(entitlement.UpdatedAt), payload.ETag) }) - t.Run("Success - Returns the entitlement payload with total weight self when dependents are not authorized", func(t *testing.T) { + suite.Run("Success - Returns the entitlement payload with total weight self when dependents are not authorized", func() { entitlement := models.Entitlement{ ID: uuid.Must(uuid.NewV4()), DependentsAuthorized: handlers.FmtBool(false), @@ -122,24 +138,26 @@ func TestEntitlement(t *testing.T) { // TotalWeight needs to read from the internal weightAllotment, in this case 7000 lbs w/o dependents and // 9000 lbs with dependents - entitlement.SetWeightAllotment(string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE5), internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + suite.NoError(err) + entitlement.WeightAllotted = &allotment payload := Entitlement(&entitlement) - assert.Equal(t, strfmt.UUID(entitlement.ID.String()), payload.ID) - assert.False(t, *payload.DependentsAuthorized) - assert.Equal(t, int64(2), payload.TotalDependents) - assert.True(t, *payload.NonTemporaryStorage) - assert.True(t, *payload.PrivatelyOwnedVehicle) - assert.Equal(t, int64(10000), *payload.AuthorizedWeight) - assert.Equal(t, int64(400), *payload.UnaccompaniedBaggageAllowance) - assert.Equal(t, int64(7000), payload.TotalWeight) - assert.Equal(t, int64(45), payload.StorageInTransit) - assert.Equal(t, int64(500), payload.RequiredMedicalEquipmentWeight) - assert.Equal(t, true, payload.OrganizationalClothingAndIndividualEquipment) - assert.Equal(t, int64(1000), payload.ProGearWeight) - assert.Equal(t, int64(750), payload.ProGearWeightSpouse) - assert.NotEmpty(t, payload.ETag) - assert.Equal(t, etag.GenerateEtag(entitlement.UpdatedAt), payload.ETag) + suite.Equal(strfmt.UUID(entitlement.ID.String()), payload.ID) + suite.False(*payload.DependentsAuthorized) + suite.Equal(int64(2), payload.TotalDependents) + suite.True(*payload.NonTemporaryStorage) + suite.True(*payload.PrivatelyOwnedVehicle) + suite.Equal(int64(10000), *payload.AuthorizedWeight) + suite.Equal(int64(400), *payload.UnaccompaniedBaggageAllowance) + suite.Equal(int64(7000), payload.TotalWeight) + suite.Equal(int64(45), payload.StorageInTransit) + suite.Equal(int64(500), payload.RequiredMedicalEquipmentWeight) + suite.Equal(true, payload.OrganizationalClothingAndIndividualEquipment) + suite.Equal(int64(1000), payload.ProGearWeight) + suite.Equal(int64(750), payload.ProGearWeightSpouse) + suite.NotEmpty(payload.ETag) + suite.Equal(etag.GenerateEtag(entitlement.UpdatedAt), payload.ETag) }) } diff --git a/pkg/models/entitlements.go b/pkg/models/entitlements.go deleted file mode 100644 index 121198d0721..00000000000 --- a/pkg/models/entitlements.go +++ /dev/null @@ -1,356 +0,0 @@ -package models - -import ( - "fmt" - - "github.com/pkg/errors" - - "github.com/transcom/mymove/pkg/appcontext" - "github.com/transcom/mymove/pkg/gen/internalmessages" -) - -// the midshipman entitlement is shared with service academy cadet -var midshipman = WeightAllotment{ - TotalWeightSelf: 350, - TotalWeightSelfPlusDependents: 350, - ProGearWeight: 0, - ProGearWeightSpouse: 0, -} - -var aviationCadet = WeightAllotment{ - TotalWeightSelf: 7000, - TotalWeightSelfPlusDependents: 8000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e1 = WeightAllotment{ - TotalWeightSelf: 5000, - TotalWeightSelfPlusDependents: 8000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e2 = WeightAllotment{ - TotalWeightSelf: 5000, - TotalWeightSelfPlusDependents: 8000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e3 = WeightAllotment{ - TotalWeightSelf: 5000, - TotalWeightSelfPlusDependents: 8000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e4 = WeightAllotment{ - TotalWeightSelf: 7000, - TotalWeightSelfPlusDependents: 8000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e5 = WeightAllotment{ - TotalWeightSelf: 7000, - TotalWeightSelfPlusDependents: 9000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e6 = WeightAllotment{ - TotalWeightSelf: 8000, - TotalWeightSelfPlusDependents: 11000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e7 = WeightAllotment{ - TotalWeightSelf: 11000, - TotalWeightSelfPlusDependents: 13000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e8 = WeightAllotment{ - TotalWeightSelf: 12000, - TotalWeightSelfPlusDependents: 14000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e9 = WeightAllotment{ - TotalWeightSelf: 13000, - TotalWeightSelfPlusDependents: 15000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var e9SpecialSeniorEnlisted = WeightAllotment{ - TotalWeightSelf: 14000, - TotalWeightSelfPlusDependents: 17000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -// O-1 through O-5 share their entitlements with W-1 through W-5 -var o1W1AcademyGraduate = WeightAllotment{ - TotalWeightSelf: 10000, - TotalWeightSelfPlusDependents: 12000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var o2W2 = WeightAllotment{ - TotalWeightSelf: 12500, - TotalWeightSelfPlusDependents: 13500, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var o3W3 = WeightAllotment{ - TotalWeightSelf: 13000, - TotalWeightSelfPlusDependents: 14500, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var o4W4 = WeightAllotment{ - TotalWeightSelf: 14000, - TotalWeightSelfPlusDependents: 17000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var o5W5 = WeightAllotment{ - TotalWeightSelf: 16000, - TotalWeightSelfPlusDependents: 17500, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var o6 = WeightAllotment{ - TotalWeightSelf: 18000, - TotalWeightSelfPlusDependents: 18000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var o7 = WeightAllotment{ - TotalWeightSelf: 18000, - TotalWeightSelfPlusDependents: 18000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var o8 = WeightAllotment{ - TotalWeightSelf: 18000, - TotalWeightSelfPlusDependents: 18000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var o9 = WeightAllotment{ - TotalWeightSelf: 18000, - TotalWeightSelfPlusDependents: 18000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var o10 = WeightAllotment{ - TotalWeightSelf: 18000, - TotalWeightSelfPlusDependents: 18000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -var civilianEmployee = WeightAllotment{ - TotalWeightSelf: 18000, - TotalWeightSelfPlusDependents: 18000, - ProGearWeight: 2000, - ProGearWeightSpouse: 500, -} - -// allotment by orders type -var studentTravel = WeightAllotment{ - TotalWeightSelf: 350, - TotalWeightSelfPlusDependents: 350, - ProGearWeight: 0, - ProGearWeightSpouse: 0, -} - -var entitlements = map[internalmessages.OrderPayGrade]WeightAllotment{ - ServiceMemberGradeACADEMYCADET: midshipman, - ServiceMemberGradeAVIATIONCADET: aviationCadet, - ServiceMemberGradeE1: e1, - ServiceMemberGradeE2: e2, - ServiceMemberGradeE3: e3, - ServiceMemberGradeE4: e4, - ServiceMemberGradeE5: e5, - ServiceMemberGradeE6: e6, - ServiceMemberGradeE7: e7, - ServiceMemberGradeE8: e8, - ServiceMemberGradeE9: e9, - ServiceMemberGradeE9SPECIALSENIORENLISTED: e9SpecialSeniorEnlisted, - ServiceMemberGradeMIDSHIPMAN: midshipman, - ServiceMemberGradeO1ACADEMYGRADUATE: o1W1AcademyGraduate, - ServiceMemberGradeO2: o2W2, - ServiceMemberGradeO3: o3W3, - ServiceMemberGradeO4: o4W4, - ServiceMemberGradeO5: o5W5, - ServiceMemberGradeO6: o6, - ServiceMemberGradeO7: o7, - ServiceMemberGradeO8: o8, - ServiceMemberGradeO9: o9, - ServiceMemberGradeO10: o10, - ServiceMemberGradeW1: o1W1AcademyGraduate, - ServiceMemberGradeW2: o2W2, - ServiceMemberGradeW3: o3W3, - ServiceMemberGradeW4: o4W4, - ServiceMemberGradeW5: o5W5, - ServiceMemberGradeCIVILIANEMPLOYEE: civilianEmployee, -} - -var entitlementsByOrdersType = map[internalmessages.OrdersType]WeightAllotment{ - internalmessages.OrdersTypeSTUDENTTRAVEL: studentTravel, -} - -func getEntitlement(grade internalmessages.OrderPayGrade) (WeightAllotment, error) { - if entitlement, ok := entitlements[grade]; ok { - return entitlement, nil - } - return WeightAllotment{}, fmt.Errorf("no entitlement found for pay grade %s", grade) -} - -func getEntitlementByOrdersType(ordersType internalmessages.OrdersType) (WeightAllotment, error) { - if entitlement, ok := entitlementsByOrdersType[ordersType]; ok { - return entitlement, nil - } - return WeightAllotment{}, fmt.Errorf("no entitlement found for orders type %s", ordersType) -} - -// AllWeightAllotments returns all the weight allotments for each rank. -func AllWeightAllotments() map[internalmessages.OrderPayGrade]WeightAllotment { - return entitlements -} - -// GetWeightAllotment returns the weight allotments for a given pay grade or an orders type. -func GetWeightAllotment(grade internalmessages.OrderPayGrade, ordersType internalmessages.OrdersType) WeightAllotment { - var entitlement WeightAllotment - var err error - - if ordersType == internalmessages.OrdersTypeSTUDENTTRAVEL { // currently only applies to student travel order that limits overall authorized weight - entitlement, err = getEntitlementByOrdersType(ordersType) - } else { - entitlement, err = getEntitlement(grade) - } - if err != nil { - return WeightAllotment{} - } - return entitlement -} - -// GetUBWeightAllowance returns the UB weight allowance for a UB shipment, part of the overall entitlements for an order -func GetUBWeightAllowance(appCtx appcontext.AppContext, originDutyLocationIsOconus *bool, newDutyLocationIsOconus *bool, branch *ServiceMemberAffiliation, grade *internalmessages.OrderPayGrade, orderType *internalmessages.OrdersType, dependentsAuthorized *bool, isAccompaniedTour *bool, dependentsUnderTwelve *int, dependentsTwelveAndOver *int) (int, error) { - originDutyLocationIsOconusValue := false - if originDutyLocationIsOconus != nil { - originDutyLocationIsOconusValue = *originDutyLocationIsOconus - } - newDutyLocationIsOconusValue := false - if newDutyLocationIsOconus != nil { - newDutyLocationIsOconusValue = *newDutyLocationIsOconus - } - branchOfService := "" - if branch != nil { - branchOfService = string(*branch) - } - orderPayGrade := "" - if grade != nil { - orderPayGrade = string(*grade) - } - typeOfOrder := "" - if orderType != nil { - typeOfOrder = string(*orderType) - } - dependentsAreAuthorized := false - if dependentsAuthorized != nil { - dependentsAreAuthorized = *dependentsAuthorized - } - isAnAccompaniedTour := false - if isAccompaniedTour != nil { - isAnAccompaniedTour = *isAccompaniedTour - } - underTwelveDependents := 0 - if dependentsUnderTwelve != nil { - underTwelveDependents = *dependentsUnderTwelve - } - twelveAndOverDependents := 0 - if dependentsTwelveAndOver != nil { - twelveAndOverDependents = *dependentsTwelveAndOver - } - - // only calculate UB allowance if either origin or new duty locations are OCONUS - if originDutyLocationIsOconusValue || newDutyLocationIsOconusValue { - - const civilianBaseUBAllowance = 350 - const dependents12AndOverUBAllowance = 350 - const depedentsUnder12UBAllowance = 175 - const maxWholeFamilyCivilianUBAllowance = 2000 - const studentTravelMaxAllowance = 350 - ubAllowance := 0 - - if typeOfOrder == string(internalmessages.OrdersTypeSTUDENTTRAVEL) { - ubAllowance = studentTravelMaxAllowance - } else if orderPayGrade == string(internalmessages.OrderPayGradeCIVILIANEMPLOYEE) && dependentsAreAuthorized && underTwelveDependents == 0 && twelveAndOverDependents == 0 { - ubAllowance = civilianBaseUBAllowance - } else if orderPayGrade == string(internalmessages.OrderPayGradeCIVILIANEMPLOYEE) && dependentsAreAuthorized && (underTwelveDependents > 0 || twelveAndOverDependents > 0) { - ubAllowance = civilianBaseUBAllowance - // for each dependent 12 and older, add an additional 350 lbs to the civilian's baggage allowance - ubAllowance += twelveAndOverDependents * dependents12AndOverUBAllowance - // for each dependent under 12, add an additional 175 lbs to the civilian's baggage allowance - ubAllowance += underTwelveDependents * depedentsUnder12UBAllowance - // max allowance of 2,000 lbs for entire family - if ubAllowance > maxWholeFamilyCivilianUBAllowance { - ubAllowance = maxWholeFamilyCivilianUBAllowance - } - } else { - if typeOfOrder == string(internalmessages.OrdersTypeLOCALMOVE) { - // no UB allowance for local moves - return 0, nil - } else if typeOfOrder != string(internalmessages.OrdersTypeTEMPORARYDUTY) { - // all order types other than temporary duty are treated as permanent change of station types for the lookup - typeOfOrder = string(internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) - } - // space force members entitled to the same allowance as air force members - if branchOfService == AffiliationSPACEFORCE.String() { - branchOfService = AffiliationAIRFORCE.String() - } - // e9 special senior enlisted members entitled to the same allowance as e9 members - if orderPayGrade == string(ServiceMemberGradeE9SPECIALSENIORENLISTED) { - orderPayGrade = string(ServiceMemberGradeE9) - } - - var baseUBAllowance UBAllowances - err := appCtx.DB().Where("branch = ? AND grade = ? AND orders_type = ? AND dependents_authorized = ? AND accompanied_tour = ?", branchOfService, orderPayGrade, typeOfOrder, dependentsAreAuthorized, isAnAccompaniedTour).First(&baseUBAllowance) - if err != nil { - if errors.Cause(err).Error() == RecordNotFoundErrorString { - message := fmt.Sprintf("No UB allowance entry found in ub_allowances table for branch: %s, grade: %s, orders_type: %s, dependents_authorized: %t, accompanied_tour: %t.", branchOfService, orderPayGrade, typeOfOrder, dependentsAreAuthorized, isAnAccompaniedTour) - appCtx.Logger().Info(message) - return 0, nil - } - return 0, err - } - if baseUBAllowance.UBAllowance != nil { - ubAllowance = *baseUBAllowance.UBAllowance - return ubAllowance, nil - } else { - return 0, nil - } - } - return ubAllowance, nil - } else { - appCtx.Logger().Info("No OCONUS duty location found for orders, no UB allowance calculated as part of order entitlement.") - return 0, nil - } -} diff --git a/pkg/models/entitlements_test.go b/pkg/models/entitlements_test.go deleted file mode 100644 index 6ab3e576bd8..00000000000 --- a/pkg/models/entitlements_test.go +++ /dev/null @@ -1,278 +0,0 @@ -package models_test - -import ( - "github.com/transcom/mymove/pkg/gen/internalmessages" - "github.com/transcom/mymove/pkg/models" -) - -const civilianBaseUBAllowanceTestConstant = 350 -const dependents12AndOverUBAllowanceTestConstant = 350 -const depedentsUnder12UBAllowanceTestConstant = 175 -const maxWholeFamilyCivilianUBAllowanceTestConstant = 2000 - -func (suite *ModelSuite) TestGetEntitlementWithValidValues() { - E1 := models.ServiceMemberGradeE1 - ordersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - - suite.Run("E1 with dependents", func() { - E1FullLoad := models.GetWeightAllotment(E1, ordersType) - suite.Assertions.Equal(8000, E1FullLoad.TotalWeightSelfPlusDependents) - }) - - suite.Run("E1 without dependents", func() { - E1Solo := models.GetWeightAllotment(E1, ordersType) - suite.Assertions.Equal(5000, E1Solo.TotalWeightSelf) - }) - - suite.Run("E1 Pro Gear", func() { - E1ProGear := models.GetWeightAllotment(E1, ordersType) - suite.Assertions.Equal(2000, E1ProGear.ProGearWeight) - }) - - suite.Run("E1 Pro Gear Spouse", func() { - E1ProGearSpouse := models.GetWeightAllotment(E1, ordersType) - suite.Assertions.Equal(500, E1ProGearSpouse.ProGearWeightSpouse) - }) -} - -func (suite *ModelSuite) TestGetUBWeightAllowanceIsZero() { - appCtx := suite.AppContextForTest() - branch := models.AffiliationMARINES - originDutyLocationIsOconus := false - newDutyLocationIsOconus := false - grade := models.ServiceMemberGradeE1 - orderType := internalmessages.OrdersTypeLOCALMOVE - dependentsAuthorized := true - isAccompaniedTour := true - dependentsUnderTwelve := 2 - dependentsTwelveAndOver := 1 - - suite.Run("UB allowance is zero when origin and new duty location are both CONUS", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(0, ubAllowance) - }) - - suite.Run("UB allowance is zero for orders type OrdersTypeLOCALMOVE", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(0, ubAllowance) - }) - - originDutyLocationIsOconus = true - orderType = internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - dependentsAuthorized = false - suite.Run("UB allowance is zero for nonexistent combination of branch, grade, orders_type, dependents_authorized, and accompanied_tour", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(0, ubAllowance) - }) -} - -func (suite *ModelSuite) TestGetUBWeightAllowanceCivilians() { - appCtx := suite.AppContextForTest() - branch := models.AffiliationCOASTGUARD - originDutyLocationIsOconus := true - newDutyLocationIsOconus := false - grade := models.ServiceMemberGradeCIVILIANEMPLOYEE - orderType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - dependentsAuthorized := true - isAccompaniedTour := true - - dependentsUnderTwelve := 0 - dependentsTwelveAndOver := 0 - suite.Run("UB allowance is calculated for Civilian Employee pay grade with no dependents", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(civilianBaseUBAllowanceTestConstant, ubAllowance) - }) - - dependentsUnderTwelve = 0 - dependentsTwelveAndOver = 2 - suite.Run("UB allowance is calculated for Civilian Employee pay grade when dependentsUnderTwelve is 0 and dependentsTwelveAndOver is > 0", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - civilianPlusDependentsTotalBaggageAllowance := civilianBaseUBAllowanceTestConstant + (dependentsUnderTwelve * depedentsUnder12UBAllowanceTestConstant) + (dependentsTwelveAndOver * dependents12AndOverUBAllowanceTestConstant) - suite.Assertions.Equal(1050, civilianPlusDependentsTotalBaggageAllowance) - suite.Assertions.Equal(civilianPlusDependentsTotalBaggageAllowance, ubAllowance) - }) - - dependentsUnderTwelve = 3 - dependentsTwelveAndOver = 0 - suite.Run("UB allowance is calculated for Civilian Employee pay grade when dependentsUnderTwelve is > 0 and dependentsTwelveAndOver is 0", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - civilianPlusDependentsTotalBaggageAllowance := civilianBaseUBAllowanceTestConstant + (dependentsUnderTwelve * depedentsUnder12UBAllowanceTestConstant) + (dependentsTwelveAndOver * dependents12AndOverUBAllowanceTestConstant) - suite.Assertions.Equal(875, civilianPlusDependentsTotalBaggageAllowance) - suite.Assertions.Equal(civilianPlusDependentsTotalBaggageAllowance, ubAllowance) - }) - - dependentsUnderTwelve = 2 - dependentsTwelveAndOver = 1 - suite.Run("UB allowance is calculated for Civilian Employee pay grade", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - civilianPlusDependentsTotalBaggageAllowance := civilianBaseUBAllowanceTestConstant + (dependentsUnderTwelve * depedentsUnder12UBAllowanceTestConstant) + (dependentsTwelveAndOver * dependents12AndOverUBAllowanceTestConstant) - suite.Assertions.Equal(1050, civilianPlusDependentsTotalBaggageAllowance) - suite.Assertions.Equal(civilianPlusDependentsTotalBaggageAllowance, ubAllowance) - }) - - dependentsUnderTwelve = 3 - dependentsTwelveAndOver = 4 - // this combination of depdendents would tally up to 2275 pounds normally - // however, we limit the max ub allowance for a family to 2000 - suite.Run("UB allowance is set to 2000 for the max weight for a family", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - civilianPlusDependentsTotalBaggageAllowance := civilianBaseUBAllowanceTestConstant + (dependentsUnderTwelve * depedentsUnder12UBAllowanceTestConstant) + (dependentsTwelveAndOver * dependents12AndOverUBAllowanceTestConstant) - suite.Assertions.Equal(2275, civilianPlusDependentsTotalBaggageAllowance) - suite.Assertions.NotEqual(civilianPlusDependentsTotalBaggageAllowance, ubAllowance) - suite.Assertions.Equal(maxWholeFamilyCivilianUBAllowanceTestConstant, ubAllowance) - }) - - orderType = internalmessages.OrdersTypeSTUDENTTRAVEL - // This should limit the ub allowance to 350 lbs because it is a Student Travel order type - suite.Run("UB allowance is set to 350 for Student Travel orders", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(350, ubAllowance) - }) -} - -func (suite *ModelSuite) TestGetUBWeightAllowanceEdgeCases() { - - appCtx := suite.AppContextForTest() - branch := models.AffiliationAIRFORCE - originDutyLocationIsOconus := true - newDutyLocationIsOconus := false - grade := models.ServiceMemberGradeE1 - orderType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - dependentsAuthorized := true - isAccompaniedTour := true - dependentsUnderTwelve := 0 - dependentsTwelveAndOver := 0 - - suite.Run("Air Force gets a UB allowance", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(2000, ubAllowance) - }) - - branch = models.AffiliationSPACEFORCE - suite.Run("Space Force gets the same UB allowance as Air Force", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(2000, ubAllowance) - }) - - branch = models.AffiliationNAVY - grade = models.ServiceMemberGradeE9 - suite.Run("Pay grade E9 gets a UB allowance", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(2000, ubAllowance) - }) - - grade = models.ServiceMemberGradeE9SPECIALSENIORENLISTED - suite.Run("Pay grade E9 Special Senior Enlisted and pay grade E9 get the same UB allowance", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(2000, ubAllowance) - }) -} - -func (suite *ModelSuite) TestGetUBWeightAllowanceWithValidValues() { - appCtx := suite.AppContextForTest() - branch := models.AffiliationMARINES - originDutyLocationIsOconus := true - newDutyLocationIsOconus := false - grade := models.ServiceMemberGradeE1 - orderType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - dependentsAuthorized := true - isAccompaniedTour := true - dependentsUnderTwelve := 2 - dependentsTwelveAndOver := 4 - - suite.Run("UB allowance is calculated when origin duty location is OCONUS", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(2000, ubAllowance) - }) - - originDutyLocationIsOconus = false - newDutyLocationIsOconus = true - suite.Run("UB allowance is calculated when new duty location is OCONUS", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(2000, ubAllowance) - }) - - suite.Run("OCONUS, Marines, E1, PCS, dependents are authorized, is accompanied = 2000 lbs", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(2000, ubAllowance) - }) - - suite.Run("OCONUS, Marines, E1, PCS, dependents are authorized, is accompanied = 2000 lbs", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(2000, ubAllowance) - }) - - branch = models.AffiliationAIRFORCE - suite.Run("OCONUS, Air Force, E1, PCS, dependents are authorized, is accompanied = 2000 lbs", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(2000, ubAllowance) - }) - - orderType = internalmessages.OrdersTypeTEMPORARYDUTY - dependentsAuthorized = false - isAccompaniedTour = false - suite.Run("OCONUS, Air Force, E1, Temporary Duty, dependents are NOT authorized, is NOT accompanied = 400 lbs", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(400, ubAllowance) - }) - - grade = models.ServiceMemberGradeW2 - retirementOrderType := internalmessages.OrdersTypeRETIREMENT - orderType = internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - suite.Run("Orders type of Retirement returns same entitlement value as the PCS orders type in the database", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &retirementOrderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(600, ubAllowance) - }) - - orderType = internalmessages.OrdersTypeTEMPORARYDUTY - suite.Run("OCONUS, Air Force, W1, Temporary Duty, dependents are NOT authorized, is NOT accompanied = 600", func() { - ubAllowance, err := models.GetUBWeightAllowance(appCtx, &originDutyLocationIsOconus, &newDutyLocationIsOconus, &branch, &grade, &orderType, &dependentsAuthorized, &isAccompaniedTour, &dependentsUnderTwelve, &dependentsTwelveAndOver) - suite.NoError(err) - suite.Assertions.Equal(600, ubAllowance) - }) -} - -func (suite *ModelSuite) TestGetEntitlementByOrdersTypeWithValidValues() { - E1 := models.ServiceMemberGradeE1 - ordersType := internalmessages.OrdersTypeSTUDENTTRAVEL - - suite.Run("Student Travel with dependents", func() { - STFullLoad := models.GetWeightAllotment(E1, ordersType) - suite.Assertions.Equal(350, STFullLoad.TotalWeightSelfPlusDependents) - }) - - suite.Run("Student Travel without dependents", func() { - STSolo := models.GetWeightAllotment(E1, ordersType) - suite.Assertions.Equal(350, STSolo.TotalWeightSelf) - }) - - suite.Run("Student Travel Pro Gear", func() { - STProGear := models.GetWeightAllotment(E1, ordersType) - suite.Assertions.Equal(0, STProGear.ProGearWeight) - }) - - suite.Run("Student Travel Pro Gear Spouse", func() { - STProGearSpouse := models.GetWeightAllotment(E1, ordersType) - suite.Assertions.Equal(0, STProGearSpouse.ProGearWeightSpouse) - }) -} diff --git a/pkg/models/ghc_entitlements.go b/pkg/models/ghc_entitlements.go index 8bd2136874d..eade163db45 100644 --- a/pkg/models/ghc_entitlements.go +++ b/pkg/models/ghc_entitlements.go @@ -1,13 +1,16 @@ package models import ( + "fmt" "time" "github.com/gobuffalo/pop/v6" "github.com/gobuffalo/validate/v3" "github.com/gobuffalo/validate/v3/validators" "github.com/gofrs/uuid" + "github.com/pkg/errors" + "github.com/transcom/mymove/pkg/appcontext" "github.com/transcom/mymove/pkg/gen/internalmessages" ) @@ -68,15 +71,6 @@ func (e *Entitlement) Validate(*pop.Connection) (*validate.Errors, error) { return validate.Validate(vs...), nil } -// SetWeightAllotment sets the weight allotment -// TODO probably want to reconsider keeping grade a string rather than enum -// TODO and possibly consider creating ghc specific GetWeightAllotment should the two -// TODO diverge in the future -func (e *Entitlement) SetWeightAllotment(grade string, ordersType internalmessages.OrdersType) { - wa := GetWeightAllotment(internalmessages.OrderPayGrade(grade), ordersType) - e.WeightAllotted = &wa -} - // WeightAllotment returns the weight allotment func (e *Entitlement) WeightAllotment() *WeightAllotment { return e.WeightAllotted @@ -132,6 +126,110 @@ func (e *Entitlement) UBWeightAllowance() *int { } } +// GetUBWeightAllowance returns the UB weight allowance for a UB shipment, part of the overall entitlements for an order +func GetUBWeightAllowance(appCtx appcontext.AppContext, originDutyLocationIsOconus *bool, newDutyLocationIsOconus *bool, branch *ServiceMemberAffiliation, grade *internalmessages.OrderPayGrade, orderType *internalmessages.OrdersType, dependentsAuthorized *bool, isAccompaniedTour *bool, dependentsUnderTwelve *int, dependentsTwelveAndOver *int) (int, error) { + originDutyLocationIsOconusValue := false + if originDutyLocationIsOconus != nil { + originDutyLocationIsOconusValue = *originDutyLocationIsOconus + } + newDutyLocationIsOconusValue := false + if newDutyLocationIsOconus != nil { + newDutyLocationIsOconusValue = *newDutyLocationIsOconus + } + branchOfService := "" + if branch != nil { + branchOfService = string(*branch) + } + orderPayGrade := "" + if grade != nil { + orderPayGrade = string(*grade) + } + typeOfOrder := "" + if orderType != nil { + typeOfOrder = string(*orderType) + } + dependentsAreAuthorized := false + if dependentsAuthorized != nil { + dependentsAreAuthorized = *dependentsAuthorized + } + isAnAccompaniedTour := false + if isAccompaniedTour != nil { + isAnAccompaniedTour = *isAccompaniedTour + } + underTwelveDependents := 0 + if dependentsUnderTwelve != nil { + underTwelveDependents = *dependentsUnderTwelve + } + twelveAndOverDependents := 0 + if dependentsTwelveAndOver != nil { + twelveAndOverDependents = *dependentsTwelveAndOver + } + + // only calculate UB allowance if either origin or new duty locations are OCONUS + if originDutyLocationIsOconusValue || newDutyLocationIsOconusValue { + + const civilianBaseUBAllowance = 350 + const dependents12AndOverUBAllowance = 350 + const depedentsUnder12UBAllowance = 175 + const maxWholeFamilyCivilianUBAllowance = 2000 + const studentTravelMaxAllowance = 350 + ubAllowance := 0 + + if typeOfOrder == string(internalmessages.OrdersTypeSTUDENTTRAVEL) { + ubAllowance = studentTravelMaxAllowance + } else if orderPayGrade == string(internalmessages.OrderPayGradeCIVILIANEMPLOYEE) && dependentsAreAuthorized && underTwelveDependents == 0 && twelveAndOverDependents == 0 { + ubAllowance = civilianBaseUBAllowance + } else if orderPayGrade == string(internalmessages.OrderPayGradeCIVILIANEMPLOYEE) && dependentsAreAuthorized && (underTwelveDependents > 0 || twelveAndOverDependents > 0) { + ubAllowance = civilianBaseUBAllowance + // for each dependent 12 and older, add an additional 350 lbs to the civilian's baggage allowance + ubAllowance += twelveAndOverDependents * dependents12AndOverUBAllowance + // for each dependent under 12, add an additional 175 lbs to the civilian's baggage allowance + ubAllowance += underTwelveDependents * depedentsUnder12UBAllowance + // max allowance of 2,000 lbs for entire family + if ubAllowance > maxWholeFamilyCivilianUBAllowance { + ubAllowance = maxWholeFamilyCivilianUBAllowance + } + } else { + if typeOfOrder == string(internalmessages.OrdersTypeLOCALMOVE) { + // no UB allowance for local moves + return 0, nil + } else if typeOfOrder != string(internalmessages.OrdersTypeTEMPORARYDUTY) { + // all order types other than temporary duty are treated as permanent change of station types for the lookup + typeOfOrder = string(internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + } + // space force members entitled to the same allowance as air force members + if branchOfService == AffiliationSPACEFORCE.String() { + branchOfService = AffiliationAIRFORCE.String() + } + // e9 special senior enlisted members entitled to the same allowance as e9 members + if orderPayGrade == string(ServiceMemberGradeE9SPECIALSENIORENLISTED) { + orderPayGrade = string(ServiceMemberGradeE9) + } + + var baseUBAllowance UBAllowances + err := appCtx.DB().Where("branch = ? AND grade = ? AND orders_type = ? AND dependents_authorized = ? AND accompanied_tour = ?", branchOfService, orderPayGrade, typeOfOrder, dependentsAreAuthorized, isAnAccompaniedTour).First(&baseUBAllowance) + if err != nil { + if errors.Cause(err).Error() == RecordNotFoundErrorString { + message := fmt.Sprintf("No UB allowance entry found in ub_allowances table for branch: %s, grade: %s, orders_type: %s, dependents_authorized: %t, accompanied_tour: %t.", branchOfService, orderPayGrade, typeOfOrder, dependentsAreAuthorized, isAnAccompaniedTour) + appCtx.Logger().Info(message) + return 0, nil + } + return 0, err + } + if baseUBAllowance.UBAllowance != nil { + ubAllowance = *baseUBAllowance.UBAllowance + return ubAllowance, nil + } else { + return 0, nil + } + } + return ubAllowance, nil + } else { + appCtx.Logger().Info("No OCONUS duty location found for orders, no UB allowance calculated as part of order entitlement.") + return 0, nil + } +} + // WeightAllotment represents the weights allotted for a rank type WeightAllotment struct { TotalWeightSelf int diff --git a/pkg/models/ghc_entitlements_test.go b/pkg/models/ghc_entitlements_test.go index d5e56ca96b7..f3cd227f2f1 100644 --- a/pkg/models/ghc_entitlements_test.go +++ b/pkg/models/ghc_entitlements_test.go @@ -1,7 +1,6 @@ package models_test import ( - "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" ) @@ -14,23 +13,6 @@ func (suite *ModelSuite) TestAuthorizedWeightWhenExistsInDB() { suite.Equal(entitlement.DBAuthorizedWeight, entitlement.AuthorizedWeight()) } -func (suite *ModelSuite) TestAuthorizedWeightWhenNotInDBAndHaveWeightAllotment() { - suite.Run("with no dependents authorized, TotalWeightSelf is AuthorizedWeight", func() { - entitlement := models.Entitlement{} - entitlement.SetWeightAllotment("E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) - - suite.Equal(entitlement.WeightAllotment().TotalWeightSelf, *entitlement.AuthorizedWeight()) - }) - - suite.Run("with dependents authorized, TotalWeightSelfPlusDependents is AuthorizedWeight", func() { - dependentsAuthorized := true - entitlement := models.Entitlement{DependentsAuthorized: &dependentsAuthorized} - entitlement.SetWeightAllotment("E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) - - suite.Equal(entitlement.WeightAllotment().TotalWeightSelfPlusDependents, *entitlement.AuthorizedWeight()) - }) -} - func (suite *ModelSuite) TestProGearAndProGearSpouseWeight() { suite.Run("no validation errors for ProGearWeight and ProGearSpouseWeight", func() { entitlement := models.Entitlement{ diff --git a/pkg/models/order.go b/pkg/models/order.go index add1e705938..8302ad634e0 100644 --- a/pkg/models/order.go +++ b/pkg/models/order.go @@ -208,6 +208,27 @@ func FetchOrderForUser(db *pop.Connection, session *auth.Session, id uuid.UUID) return Order{}, err } + // Conduct allotment lookup + // TODO: OCONUS case orders type + if order.Entitlement != nil && order.Grade != nil { + var hhgAllowance HHGAllowance + err = db.RawQuery(` + SELECT hhg_allowances.* + FROM hhg_allowances + INNER JOIN pay_grades ON hhg_allowances.pay_grade_id = pay_grades.id + WHERE pay_grades.grade = $1 + LIMIT 1 + `, order.Grade).First(&hhgAllowance) + if err == nil { + order.Entitlement.WeightAllotted = &WeightAllotment{ + TotalWeightSelf: hhgAllowance.TotalWeightSelf, + TotalWeightSelfPlusDependents: hhgAllowance.TotalWeightSelfPlusDependents, + ProGearWeight: hhgAllowance.ProGearWeight, + ProGearWeightSpouse: hhgAllowance.ProGearWeightSpouse, + } + } + } + // TODO: Handle case where more than one user is authorized to modify orders if session.IsMilApp() && order.ServiceMember.ID != session.ServiceMemberID { return Order{}, ErrFetchForbidden diff --git a/pkg/models/queue.go b/pkg/models/queue.go deleted file mode 100644 index d616eb8d94a..00000000000 --- a/pkg/models/queue.go +++ /dev/null @@ -1,188 +0,0 @@ -package models - -import ( - "time" - - "github.com/gobuffalo/pop/v6" - "github.com/gofrs/uuid" - - "github.com/transcom/mymove/pkg/gen/internalmessages" -) - -// MoveQueueItem represents a single move queue item within a queue. -type MoveQueueItem struct { - ID uuid.UUID `json:"id" db:"id"` - CreatedAt time.Time `json:"created_at" db:"created_at"` - Edipi string `json:"edipi" db:"edipi"` - Grade *internalmessages.OrderPayGrade `json:"grade" db:"grade"` - CustomerName string `json:"customer_name" db:"customer_name"` - Locator string `json:"locator" db:"locator"` - Status string `json:"status" db:"status"` - PpmStatus *string `json:"ppm_status" db:"ppm_status"` - OrdersType *internalmessages.OrdersType `json:"orders_type" db:"orders_type"` - MoveDate *time.Time `json:"move_date" db:"move_date"` - SubmittedDate *time.Time `json:"submitted_date" db:"submitted_date"` - LastModifiedDate time.Time `json:"last_modified_date" db:"last_modified_date"` - OriginDutyLocationName string `json:"origin_duty_location_name" db:"origin_duty_location_name"` - DestinationDutyLocationName string `json:"destination_duty_location_name" db:"destination_duty_location_name"` - PmSurveyConductedDate *time.Time `json:"pm_survey_conducted_date" db:"pm_survey_conducted_date"` - OriginGBLOC *string `json:"origin_gbloc" db:"origin_gbloc"` - DestinationGBLOC *string `json:"destination_gbloc" db:"destination_gbloc"` - DeliveredDate *time.Time `json:"delivered_date" db:"delivered_date"` - InvoiceApprovedDate *time.Time `json:"invoice_approved_date" db:"invoice_approved_date"` - BranchOfService string `json:"branch_of_service" db:"branch_of_service"` - ActualMoveDate *time.Time `json:"actual_move_date" db:"actual_move_date"` - OriginalMoveDate *time.Time `json:"original_move_date" db:"original_move_date"` -} - -// GetMoveQueueItems gets all moveQueueItems for a specific lifecycleState -func GetMoveQueueItems(db *pop.Connection, lifecycleState string) ([]MoveQueueItem, error) { - var moveQueueItems []MoveQueueItem - var query string - - if lifecycleState == "new" { - query = ` - SELECT moves.ID, - COALESCE(sm.edipi, '*missing*') as edipi, - CONCAT(COALESCE(sm.last_name, '*missing*'), ', ', COALESCE(sm.first_name, '*missing*')) AS customer_name, - moves.locator as locator, - sm.affiliation as branch_of_service, - ord.orders_type as orders_type, - ord.grade as grade, - COALESCE( - ppm.actual_move_date, - ppm.original_move_date - ) as move_date, - COALESCE( - ppm.submit_date - ) as submitted_date, - moves.created_at as created_at, - moves.updated_at as last_modified_date, - moves.status as status, - ppm.status as ppm_status, - origin_duty_location.name as origin_duty_location_name, - ppm.actual_move_date, - ppm.original_move_date - FROM moves - JOIN orders as ord ON moves.orders_id = ord.id - JOIN service_members AS sm ON ord.service_member_id = sm.id - JOIN duty_locations as origin_duty_location ON ord.origin_duty_location_id = origin_duty_location.id - JOIN mto_shipments AS mto ON moves.id = mto.move_id - JOIN ppm_shipments AS ppm ON mto.id = ppm.shipment_id WHERE (moves.status = 'SUBMITTED' - OR (ppm.status = 'SUBMITTED' - AND (NOT moves.status in ('CANCELED', 'DRAFT')))) - AND moves.show is true - GROUP BY moves.ID, grade, customer_name, edipi, locator, orders_type, move_date, moves.created_at, last_modified_date, moves.status, ppm.submit_date, ppm_status, origin_duty_location.name, sm.affiliation, ppm.actual_move_date, ppm.original_move_date - ` - } else if lifecycleState == "ppm_payment_requested" { - query = ` - SELECT moves.ID, - COALESCE(sm.edipi, '*missing*') as edipi, - CONCAT(COALESCE(sm.last_name, '*missing*'), ', ', COALESCE(sm.first_name, '*missing*')) AS customer_name, - moves.locator as locator, - sm.affiliation as branch_of_service, - ord.orders_type as orders_type, - ord.grade as grade, - COALESCE(ppm.actual_move_date, ppm.original_move_date) as move_date, - moves.created_at as created_at, - ppm.updated_at as last_modified_date, - moves.status as status, - ppm.status as ppm_status, - origin_duty_location.name as origin_duty_location_name, - destination_duty_location.name as destination_duty_location_name, - ppm.actual_move_date, - ppm.original_move_date - FROM moves - JOIN orders as ord ON moves.orders_id = ord.id - JOIN service_members AS sm ON ord.service_member_id = sm.id - JOIN mto_shipments AS mto ON moves.id = mto.move_id - JOIN ppm_shipments AS ppm ON mto.id = ppm.shipment_id JOIN duty_locations as origin_duty_location ON ord.origin_duty_location_id = origin_duty_location.id - JOIN duty_locations as destination_duty_location ON ord.new_duty_location_id = destination_duty_location.id - WHERE moves.show is true - and ppm.status = 'PAYMENT_REQUESTED' - ` - } else if lifecycleState == "ppm_completed" { - query = ` - SELECT moves.ID, - COALESCE(sm.edipi, '*missing*') as edipi, - CONCAT(COALESCE(sm.last_name, '*missing*'), ', ', COALESCE(sm.first_name, '*missing*')) AS customer_name, - moves.locator as locator, - sm.affiliation as branch_of_service, - ord.orders_type as orders_type, - ord.grade as grade, - COALESCE(ppm.actual_move_date, ppm.original_move_date) as move_date, - moves.created_at as created_at, - ppm.updated_at as last_modified_date, - moves.status as status, - ppm.status as ppm_status, - origin_duty_location.name as origin_duty_location_name, - destination_duty_location.name as destination_duty_location_name, - ppm.actual_move_date, - ppm.original_move_date - FROM moves - JOIN orders as ord ON moves.orders_id = ord.id - JOIN service_members AS sm ON ord.service_member_id = sm.id - JOIN mto_shipments AS mto ON moves.id = mto.move_id - JOIN ppm_shipments AS ppm ON mto.id = ppm.shipment_id JOIN duty_locations as origin_duty_location ON ord.origin_duty_location_id = origin_duty_location.id - JOIN duty_locations as destination_duty_location ON ord.new_duty_location_id = destination_duty_location.id - WHERE moves.show is true - and ppm.status = 'COMPLETED' - ` - } else if lifecycleState == "ppm_approved" { - query = ` - SELECT moves.ID, - COALESCE(sm.edipi, '*missing*') as edipi, - CONCAT(COALESCE(sm.last_name, '*missing*'), ', ', COALESCE(sm.first_name, '*missing*')) AS customer_name, - moves.locator as locator, - sm.affiliation as branch_of_service, - ord.orders_type as orders_type, - ord.grade as grade, - COALESCE(ppm.actual_move_date, ppm.original_move_date) as move_date, - moves.created_at as created_at, - ppm.updated_at as last_modified_date, - moves.status as status, - ppm.status as ppm_status, - origin_duty_location.name as origin_duty_location_name, - destination_duty_location.name as destination_duty_location_name, - ppm.actual_move_date, - ppm.original_move_date - FROM moves - JOIN orders as ord ON moves.orders_id = ord.id - JOIN service_members AS sm ON ord.service_member_id = sm.id - JOIN mto_shipments AS mto ON moves.id = mto.move_id - JOIN ppm_shipments AS ppm ON mto.id = ppm.shipment_id JOIN duty_locations as origin_duty_location ON ord.origin_duty_location_id = origin_duty_location.id - JOIN duty_locations as destination_duty_location ON ord.new_duty_location_id = destination_duty_location.id - WHERE moves.show is true - and ppm.status = 'APPROVED' - ` - } else if lifecycleState == "all" { - query = ` - SELECT moves.ID, - COALESCE(sm.edipi, '*missing*') as edipi, - CONCAT(COALESCE(sm.last_name, '*missing*'), ', ', COALESCE(sm.first_name, '*missing*')) AS customer_name, - moves.locator as locator, - sm.affiliation as branch_of_service, - ord.orders_type as orders_type, - moves.created_at as created_at, - moves.updated_at as last_modified_date, - moves.status as status, - ppm.status as ppm_status, - origin_duty_location.name as origin_duty_location_name, - destination_duty_location.name as destination_duty_location_name, - ppm.actual_move_date - FROM moves - JOIN orders as ord ON moves.orders_id = ord.id - JOIN service_members AS sm ON ord.service_member_id = sm.id - JOIN mto_shipments AS mto ON moves.id = mto.move_id - JOIN ppm_shipments AS ppm ON mto.id = ppm.shipment_id - JOIN duty_locations as origin_duty_location ON ord.origin_duty_location_id = origin_duty_location.id - JOIN duty_locations as destination_duty_location ON ord.new_duty_location_id = destination_duty_location.id - WHERE moves.show is true - ` - } else { - return moveQueueItems, ErrFetchNotFound - } - - err := db.RawQuery(query).All(&moveQueueItems) - return moveQueueItems, err -} diff --git a/pkg/models/queue_test.go b/pkg/models/queue_test.go deleted file mode 100644 index 18d2e3b90c0..00000000000 --- a/pkg/models/queue_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package models_test - -import ( - "github.com/transcom/mymove/pkg/factory" - m "github.com/transcom/mymove/pkg/models" -) - -func (suite *ModelSuite) TestCreateMoveWithPPMShow() { - - factory.BuildMoveWithPPMShipment(suite.DB(), nil, nil) - - moves, moveErrs := m.GetMoveQueueItems(suite.DB(), "all") - suite.Nil(moveErrs) - suite.Len(moves, 1) -} - -func (suite *ModelSuite) TestCreateMoveWithPPMNoShow() { - moveTemplate := m.Move{ - Show: m.BoolPointer(false), - } - factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ - { - Model: moveTemplate, - }, - }, nil) - - moves, moveErrs := m.GetMoveQueueItems(suite.DB(), "all") - suite.Nil(moveErrs) - suite.Empty(moves) - -} - -func (suite *ModelSuite) TestCreateNewMoveWithNoPPMShow() { - orders := factory.BuildOrder(suite.DB(), nil, nil) - factory.FetchOrBuildDefaultContractor(suite.DB(), nil, nil) - office := factory.BuildTransportationOffice(suite.DB(), nil, nil) - - moveOptions := m.MoveOptions{ - Show: m.BoolPointer(true), - CounselingOfficeID: &office.ID, - } - _, verrs, err := orders.CreateNewMove(suite.DB(), moveOptions) - suite.NoError(err) - suite.False(verrs.HasAny(), "failed to validate move") - - moves, moveErrs := m.GetMoveQueueItems(suite.DB(), "all") - suite.Nil(moveErrs) - suite.Empty(moves) -} - -func (suite *ModelSuite) TestQueueNotFound() { - moves, moveErrs := m.GetMoveQueueItems(suite.DB(), "queue_not_found") - suite.Equal(m.ErrFetchNotFound, moveErrs, "Expected not to find move queue items") - suite.Empty(moves) -} diff --git a/pkg/notifications/move_submitted.go b/pkg/notifications/move_submitted.go index d9cc1a26431..e553ce2e480 100644 --- a/pkg/notifications/move_submitted.go +++ b/pkg/notifications/move_submitted.go @@ -2,6 +2,7 @@ package notifications import ( "bytes" + "errors" "fmt" html "html/template" text "text/template" @@ -53,6 +54,15 @@ func (m MoveSubmitted) emails(appCtx appcontext.AppContext) ([]emailContent, err return emails, err } + // Nil check here. Previously weight allotments were hard coded and a lookup was placed here. + // Since allotments are now stored in the database, to avoid an import circle we enhance the + // "FetchOrderForUser" to return the allotment, preventing any need for addtional lookup or db querying. + if orders.Entitlement == nil || orders.Entitlement.WeightAllotted == nil { + errMsg := "WeightAllotted is nil during move email generation. Ensure orders fetch includes an entitlement join and the correct pay grade exists" + appCtx.Logger().Error(errMsg, zap.String("orderID", orders.ID.String())) + return nil, errors.New(errMsg) + } + destinationAddress := orders.NewDutyLocation.Name isSeparateeRetiree := orders.OrdersType == internalmessages.OrdersTypeRETIREMENT || orders.OrdersType == internalmessages.OrdersTypeSEPARATION if isSeparateeRetiree && len(move.MTOShipments) > 0 { @@ -90,15 +100,14 @@ func (m MoveSubmitted) emails(appCtx appcontext.AppContext) ([]emailContent, err originDutyLocationName = &originDutyLocation.Name } - totalEntitlement := models.GetWeightAllotment(*orders.Grade, orders.OrdersType) unaccompaniedBaggageAllowance, err := models.GetUBWeightAllowance(appCtx, originDutyLocation.Address.IsOconus, orders.NewDutyLocation.Address.IsOconus, orders.ServiceMember.Affiliation, orders.Grade, &orders.OrdersType, orders.Entitlement.DependentsAuthorized, orders.Entitlement.AccompaniedTour, orders.Entitlement.DependentsUnderTwelve, orders.Entitlement.DependentsTwelveAndOver) if err == nil { - totalEntitlement.UnaccompaniedBaggageAllowance = unaccompaniedBaggageAllowance + orders.Entitlement.WeightAllotted.UnaccompaniedBaggageAllowance = unaccompaniedBaggageAllowance } - weight := totalEntitlement.TotalWeightSelf + weight := orders.Entitlement.WeightAllotted.TotalWeightSelf if orders.HasDependents { - weight = totalEntitlement.TotalWeightSelfPlusDependents + weight = orders.Entitlement.WeightAllotted.TotalWeightSelfPlusDependents } if serviceMember.PersonalEmail == nil { diff --git a/pkg/services/entitlements.go b/pkg/services/entitlements.go index 80e02f2d92e..58b8ccc09fd 100644 --- a/pkg/services/entitlements.go +++ b/pkg/services/entitlements.go @@ -22,3 +22,9 @@ type WeightRestrictor interface { ApplyWeightRestrictionToEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, weightRestriction int, eTag string) (*models.Entitlement, error) RemoveWeightRestrictionFromEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, eTag string) (*models.Entitlement, error) } + +// The weight allotment saver helps apply weight restrictions to entitlements +// +//go:generate mockery --name WeightRestrictor +type WeightAllotmentSaver interface { +} diff --git a/pkg/services/move/move_weights.go b/pkg/services/move/move_weights.go index cf75d604652..3d9adc5d12a 100644 --- a/pkg/services/move/move_weights.go +++ b/pkg/services/move/move_weights.go @@ -23,7 +23,8 @@ const RiskOfExcessThreshold = .9 const AutoReweighRequestThreshold = .9 type moveWeights struct { - ReweighRequestor services.ShipmentReweighRequester + ReweighRequestor services.ShipmentReweighRequester + WeightAllotmentFetcher services.WeightAllotmentFetcher } // NewMoveWeights creates a new moveWeights service @@ -98,7 +99,10 @@ func (w moveWeights) CheckExcessWeight(appCtx appcontext.AppContext, moveID uuid return nil, nil, errors.New("could not determine excess weight entitlement without dependents authorization value") } - totalWeightAllowance := models.GetWeightAllotment(*move.Orders.Grade, move.Orders.OrdersType) + totalWeightAllowance, err := w.WeightAllotmentFetcher.GetWeightAllotment(appCtx, string(*move.Orders.Grade), move.Orders.OrdersType) + if err != nil { + return nil, nil, err + } weight := totalWeightAllowance.TotalWeightSelf if *move.Orders.Entitlement.DependentsAuthorized { @@ -175,7 +179,10 @@ func (w moveWeights) CheckAutoReweigh(appCtx appcontext.AppContext, moveID uuid. return nil, errors.New("could not determine excess weight entitlement without dependents authorization value") } - totalWeightAllowance := models.GetWeightAllotment(*move.Orders.Grade, move.Orders.OrdersType) + totalWeightAllowance, err := w.WeightAllotmentFetcher.GetWeightAllotment(appCtx, string(*move.Orders.Grade), move.Orders.OrdersType) + if err != nil { + return nil, err + } weight := totalWeightAllowance.TotalWeightSelf if *move.Orders.Entitlement.DependentsAuthorized { diff --git a/pkg/services/order/order_updater.go b/pkg/services/order/order_updater.go index 38c991a112a..336f057a5b0 100644 --- a/pkg/services/order/order_updater.go +++ b/pkg/services/order/order_updater.go @@ -17,6 +17,7 @@ import ( "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/query" "github.com/transcom/mymove/pkg/storage" "github.com/transcom/mymove/pkg/uploader" @@ -48,7 +49,10 @@ func (f *orderUpdater) UpdateOrderAsTOO(appCtx appcontext.AppContext, orderID uu return &models.Order{}, uuid.Nil, apperror.NewInvalidInputError(orderID, nil, nil, "SAC cannot be more than 80 characters") } - orderToUpdate := orderFromTOOPayload(appCtx, *order, payload) + orderToUpdate, err := orderFromTOOPayload(appCtx, *order, payload) + if err != nil { + return nil, uuid.Nil, err + } return f.updateOrderAsTOO(appCtx, orderToUpdate, CheckRequiredFields()) } @@ -70,7 +74,10 @@ func (f *orderUpdater) UpdateOrderAsCounselor(appCtx appcontext.AppContext, orde return &models.Order{}, uuid.Nil, apperror.NewInvalidInputError(orderID, nil, nil, "SAC cannot be more than 80 characters") } - orderToUpdate := orderFromCounselingPayload(*order, payload) + orderToUpdate, err := orderFromCounselingPayload(appCtx, *order, payload) + if err != nil { + return &models.Order{}, uuid.Nil, nil + } return f.updateOrder(appCtx, orderToUpdate, CheckRequiredFields()) } @@ -87,7 +94,10 @@ func (f *orderUpdater) UpdateAllowanceAsTOO(appCtx appcontext.AppContext, orderI return &models.Order{}, uuid.Nil, apperror.NewPreconditionFailedError(orderID, query.StaleIdentifierError{StaleIdentifier: eTag}) } - orderToUpdate := allowanceFromTOOPayload(appCtx, *order, payload) + orderToUpdate, err := allowanceFromTOOPayload(appCtx, *order, payload) + if err != nil { + return &models.Order{}, uuid.Nil, err + } return f.updateOrder(appCtx, orderToUpdate) } @@ -104,7 +114,10 @@ func (f *orderUpdater) UpdateAllowanceAsCounselor(appCtx appcontext.AppContext, return &models.Order{}, uuid.Nil, apperror.NewPreconditionFailedError(orderID, query.StaleIdentifierError{StaleIdentifier: eTag}) } - orderToUpdate := allowanceFromCounselingPayload(appCtx, *order, payload) + orderToUpdate, err := allowanceFromCounselingPayload(appCtx, *order, payload) + if err != nil { + return &models.Order{}, uuid.Nil, err + } return f.updateOrder(appCtx, orderToUpdate) } @@ -177,8 +190,9 @@ func (f *orderUpdater) findOrderWithAmendedOrders(appCtx appcontext.AppContext, return &order, nil } -func orderFromTOOPayload(appCtx appcontext.AppContext, existingOrder models.Order, payload ghcmessages.UpdateOrderPayload) models.Order { +func orderFromTOOPayload(appCtx appcontext.AppContext, existingOrder models.Order, payload ghcmessages.UpdateOrderPayload) (models.Order, error) { order := existingOrder + waf := entitlements.NewWeightAllotmentFetcher() // update order origin duty location if payload.OriginDutyLocationID != nil { @@ -257,7 +271,10 @@ func orderFromTOOPayload(appCtx appcontext.AppContext, existingOrder models.Orde if payload.Grade != nil { order.Grade = (*internalmessages.OrderPayGrade)(payload.Grade) // Calculate new DBWeightAuthorized based on the new grade - weightAllotment := models.GetWeightAllotment(*order.Grade, order.OrdersType) + weightAllotment, err := waf.GetWeightAllotment(appCtx, string(*order.Grade), order.OrdersType) + if err != nil { + return models.Order{}, err + } weight := weightAllotment.TotalWeightSelf // Payload does not have this information, retrieve dependents from the existing order if existingOrder.HasDependents && *order.Entitlement.DependentsAuthorized { @@ -267,7 +284,7 @@ func orderFromTOOPayload(appCtx appcontext.AppContext, existingOrder models.Orde order.Entitlement.DBAuthorizedWeight = &weight } - return order + return order, nil } func (f *orderUpdater) amendedOrder(appCtx appcontext.AppContext, userID uuid.UUID, order models.Order, file io.ReadCloser, filename string, storer storage.FileStorer, uploadType models.UploadType) (models.UserUpload, string, *validate.Errors, error) { @@ -317,8 +334,9 @@ func (f *orderUpdater) amendedOrder(appCtx appcontext.AppContext, userID uuid.UU return *userUpload, url, nil, nil } -func orderFromCounselingPayload(existingOrder models.Order, payload ghcmessages.CounselingUpdateOrderPayload) models.Order { +func orderFromCounselingPayload(appCtx appcontext.AppContext, existingOrder models.Order, payload ghcmessages.CounselingUpdateOrderPayload) (models.Order, error) { order := existingOrder + waf := entitlements.NewWeightAllotmentFetcher() // update order origin duty location if payload.OriginDutyLocationID != nil { @@ -390,7 +408,10 @@ func orderFromCounselingPayload(existingOrder models.Order, payload ghcmessages. if payload.Grade != nil { order.Grade = (*internalmessages.OrderPayGrade)(payload.Grade) // Calculate new DBWeightAuthorized based on the new grade - weightAllotment := models.GetWeightAllotment(*order.Grade, order.OrdersType) + weightAllotment, err := waf.GetWeightAllotment(appCtx, string(*order.Grade), order.OrdersType) + if err != nil { + return models.Order{}, err + } weight := weightAllotment.TotalWeightSelf // Payload does not have this information, retrieve dependents from the existing order if existingOrder.HasDependents && *order.Entitlement.DependentsAuthorized { @@ -400,11 +421,12 @@ func orderFromCounselingPayload(existingOrder models.Order, payload ghcmessages. order.Entitlement.DBAuthorizedWeight = &weight } - return order + return order, nil } -func allowanceFromTOOPayload(appCtx appcontext.AppContext, existingOrder models.Order, payload ghcmessages.UpdateAllowancePayload) models.Order { +func allowanceFromTOOPayload(appCtx appcontext.AppContext, existingOrder models.Order, payload ghcmessages.UpdateAllowancePayload) (models.Order, error) { order := existingOrder + waf := entitlements.NewWeightAllotmentFetcher() if payload.ProGearWeight != nil { order.Entitlement.ProGearWeight = int(*payload.ProGearWeight) @@ -430,7 +452,10 @@ func allowanceFromTOOPayload(appCtx appcontext.AppContext, existingOrder models. } // Calculate new DBWeightAuthorized based on the new grade - weightAllotment := models.GetWeightAllotment(*order.Grade, order.OrdersType) + weightAllotment, err := waf.GetWeightAllotment(appCtx, string(*order.Grade), order.OrdersType) + if err != nil { + return models.Order{}, err + } weight := weightAllotment.TotalWeightSelf // Payload does not have this information, retrieve dependents from the existing order if existingOrder.HasDependents && *payload.DependentsAuthorized { @@ -496,15 +521,16 @@ func allowanceFromTOOPayload(appCtx appcontext.AppContext, existingOrder models. if order.Entitlement != nil { unaccompaniedBaggageAllowance, err := models.GetUBWeightAllowance(appCtx, order.OriginDutyLocation.Address.IsOconus, order.NewDutyLocation.Address.IsOconus, order.ServiceMember.Affiliation, order.Grade, &order.OrdersType, &hasDepedents, order.Entitlement.AccompaniedTour, order.Entitlement.DependentsUnderTwelve, order.Entitlement.DependentsTwelveAndOver) if err == nil { - weightAllotment.UnaccompaniedBaggageAllowance = unaccompaniedBaggageAllowance + order.Entitlement.WeightAllotted.UnaccompaniedBaggageAllowance = unaccompaniedBaggageAllowance } } - return order + return order, nil } -func allowanceFromCounselingPayload(appCtx appcontext.AppContext, existingOrder models.Order, payload ghcmessages.CounselingUpdateAllowancePayload) models.Order { +func allowanceFromCounselingPayload(appCtx appcontext.AppContext, existingOrder models.Order, payload ghcmessages.CounselingUpdateAllowancePayload) (models.Order, error) { order := existingOrder + waf := entitlements.NewWeightAllotmentFetcher() if payload.ProGearWeight != nil { order.Entitlement.ProGearWeight = int(*payload.ProGearWeight) @@ -530,7 +556,10 @@ func allowanceFromCounselingPayload(appCtx appcontext.AppContext, existingOrder } // Calculate new DBWeightAuthorized based on the new grade - weightAllotment := models.GetWeightAllotment(*order.Grade, order.OrdersType) + weightAllotment, err := waf.GetWeightAllotment(appCtx, string(*order.Grade), order.OrdersType) + if err != nil { + return models.Order{}, nil + } weight := weightAllotment.TotalWeightSelf // Payload does not have this information, retrieve dependents from the existing order if existingOrder.HasDependents && *payload.DependentsAuthorized { @@ -594,7 +623,7 @@ func allowanceFromCounselingPayload(appCtx appcontext.AppContext, existingOrder } } - return order + return order, nil } func (f *orderUpdater) saveDocumentForAmendedOrder(appCtx appcontext.AppContext, doc *models.Document) (*models.Document, error) { diff --git a/pkg/services/pptas_report/pptas_report_list_fetcher.go b/pkg/services/pptas_report/pptas_report_list_fetcher.go index b0ce2d24015..85fc0c45f6c 100644 --- a/pkg/services/pptas_report/pptas_report_list_fetcher.go +++ b/pkg/services/pptas_report/pptas_report_list_fetcher.go @@ -19,6 +19,7 @@ type pptasReportListFetcher struct { moveFetcher services.MoveFetcher tacFetcher services.TransportationAccountingCodeFetcher loaFetcher services.LineOfAccountingFetcher + waf services.WeightAllotmentFetcher } func NewPPTASReportListFetcher(estimator services.PPMEstimator, moveFetcher services.MoveFetcher, tacFetcher services.TransportationAccountingCodeFetcher, loaFetcher services.LineOfAccountingFetcher) services.PPTASReportListFetcher { @@ -91,7 +92,11 @@ func (f *pptasReportListFetcher) BuildPPTASReportsFromMoves(appCtx appcontext.Ap report.Address = orders.ServiceMember.ResidentialAddress if orders.Grade != nil && orders.Entitlement != nil { - orders.Entitlement.SetWeightAllotment(string(*orders.Grade), orders.OrdersType) + entitlement, err := f.waf.GetWeightAllotment(appCtx, string(*orders.Grade), orders.OrdersType) + if err != nil { + return nil, err + } + orders.Entitlement.WeightAllotted = &entitlement } weightAllotment := orders.Entitlement.WeightAllotment() diff --git a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet.go b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet.go index faa4bf6e3b3..b3458d8f13e 100644 --- a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet.go +++ b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet.go @@ -24,6 +24,7 @@ import ( "github.com/transcom/mymove/pkg/paperwork" "github.com/transcom/mymove/pkg/route" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/unit" ) @@ -194,17 +195,22 @@ func (wa *SSWMaxWeightEntitlement) addLineItem(field string, value int) { // SSWGetEntitlement calculates the entitlement for the shipment summary worksheet based on the parameters of // a move (hasDependents, spouseHasProGear) -func SSWGetEntitlement(grade internalmessages.OrderPayGrade, hasDependents bool, spouseHasProGear bool, ordersType internalmessages.OrdersType) models.SSWMaxWeightEntitlement { +func SSWGetEntitlement(appCtx appcontext.AppContext, grade internalmessages.OrderPayGrade, hasDependents bool, spouseHasProGear bool, ordersType internalmessages.OrdersType) (models.SSWMaxWeightEntitlement, error) { sswEntitlements := SSWMaxWeightEntitlement{} - entitlements := models.GetWeightAllotment(grade, ordersType) + waf := entitlements.NewWeightAllotmentFetcher() + entitlements, err := waf.GetWeightAllotment(appCtx, string(grade), ordersType) + if err != nil { + return models.SSWMaxWeightEntitlement{}, nil + } + //entitlements := models.GetWeightAllotment(grade, ordersType) sswEntitlements.addLineItem("ProGear", entitlements.ProGearWeight) sswEntitlements.addLineItem("SpouseProGear", entitlements.ProGearWeightSpouse) if !hasDependents { sswEntitlements.addLineItem("Entitlement", entitlements.TotalWeightSelf) - return models.SSWMaxWeightEntitlement(sswEntitlements) + return models.SSWMaxWeightEntitlement(sswEntitlements), nil } sswEntitlements.addLineItem("Entitlement", entitlements.TotalWeightSelfPlusDependents) - return models.SSWMaxWeightEntitlement(sswEntitlements) + return models.SSWMaxWeightEntitlement(sswEntitlements), nil } // Calculates cost for the Remaining PPM Incentive (pre-tax) field on page 2 of SSW form. @@ -1074,7 +1080,10 @@ func (SSWPPMComputer *SSWPPMComputer) FetchDataShipmentSummaryWorksheetFormData( return nil, errors.New("order for requested shipment summary worksheet data does not have a pay grade attached") } - weightAllotment := SSWGetEntitlement(*ppmShipment.Shipment.MoveTaskOrder.Orders.Grade, ppmShipment.Shipment.MoveTaskOrder.Orders.HasDependents, ppmShipment.Shipment.MoveTaskOrder.Orders.SpouseHasProGear, ppmShipment.Shipment.MoveTaskOrder.Orders.OrdersType) + weightAllotment, err := SSWGetEntitlement(appCtx, *ppmShipment.Shipment.MoveTaskOrder.Orders.Grade, ppmShipment.Shipment.MoveTaskOrder.Orders.HasDependents, ppmShipment.Shipment.MoveTaskOrder.Orders.SpouseHasProGear, ppmShipment.Shipment.MoveTaskOrder.Orders.OrdersType) + if err != nil { + return nil, err + } maxSit, err := CalculateShipmentSITAllowance(appCtx, ppmShipment.Shipment) if err != nil { diff --git a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_test.go b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_test.go index 664c0765cc5..b7aa7d19510 100644 --- a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_test.go +++ b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_test.go @@ -20,6 +20,7 @@ import ( "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" paperworkgenerator "github.com/transcom/mymove/pkg/paperwork" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/mocks" storageTest "github.com/transcom/mymove/pkg/storage/test" "github.com/transcom/mymove/pkg/unit" @@ -27,9 +28,9 @@ import ( ) func (suite *ShipmentSummaryWorksheetServiceSuite) TestFetchDataShipmentSummaryWorksheet() { - //advanceID, _ := uuid.NewV4() ordersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION yuma := factory.FetchOrBuildCurrentDutyLocation(suite.DB()) + waf := entitlements.NewWeightAllotmentFetcher() fortGordon := factory.FetchOrBuildOrdersDutyLocation(suite.DB()) grade := models.ServiceMemberGradeE9 mockPPMCloseoutFetcher := &mocks.PPMCloseoutFetcher{} @@ -91,12 +92,14 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFetchDataShipmentSummaryW suite.Equal(yuma.Address.ID, ssd.CurrentDutyLocation.Address.ID) suite.Equal(fortGordon.ID, ssd.NewDutyLocation.ID) suite.Equal(fortGordon.Address.ID, ssd.NewDutyLocation.Address.ID) - gradeWtgAllotment := models.GetWeightAllotment(grade, ordersType) + gradeWtgAllotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(grade), ordersType) + suite.NoError(err) suite.Equal(unit.Pound(gradeWtgAllotment.TotalWeightSelf), ssd.WeightAllotment.Entitlement) suite.Equal(unit.Pound(gradeWtgAllotment.ProGearWeight), ssd.WeightAllotment.ProGear) suite.Equal(unit.Pound(500), ssd.WeightAllotment.SpouseProGear) suite.Require().NotNil(ssd.Order.Grade) - weightAllotment := models.GetWeightAllotment(*ssd.Order.Grade, ssd.Order.OrdersType) + weightAllotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(*ssd.Order.Grade), ssd.Order.OrdersType) + suite.NoError(err) // E_9 rank, no dependents, with spouse pro-gear totalWeight := weightAllotment.TotalWeightSelf + weightAllotment.ProGearWeight + weightAllotment.ProGearWeightSpouse suite.Require().Nil(err) @@ -219,6 +222,7 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFetchDataShipmentSummaryW yuma := factory.FetchOrBuildCurrentDutyLocation(suite.DB()) fortGordon := factory.FetchOrBuildOrdersDutyLocation(suite.DB()) grade := models.ServiceMemberGradeE9 + waf := entitlements.NewWeightAllotmentFetcher() mockPPMCloseoutFetcher := &mocks.PPMCloseoutFetcher{} SSWPPMComputer := NewSSWPPMComputer(mockPPMCloseoutFetcher) @@ -263,12 +267,14 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFetchDataShipmentSummaryW suite.Equal(yuma.Address.ID, ssd.CurrentDutyLocation.Address.ID) suite.Equal(fortGordon.ID, ssd.NewDutyLocation.ID) suite.Equal(fortGordon.Address.ID, ssd.NewDutyLocation.Address.ID) - gradeWtgAllotment := models.GetWeightAllotment(grade, ordersType) + gradeWtgAllotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(grade), ordersType) + suite.NoError(err) suite.Equal(unit.Pound(gradeWtgAllotment.TotalWeightSelf), ssd.WeightAllotment.Entitlement) suite.Equal(unit.Pound(gradeWtgAllotment.ProGearWeight), ssd.WeightAllotment.ProGear) suite.Equal(unit.Pound(500), ssd.WeightAllotment.SpouseProGear) suite.Require().NotNil(ssd.Order.Grade) - weightAllotment := models.GetWeightAllotment(*ssd.Order.Grade, ssd.Order.OrdersType) + weightAllotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(*ssd.Order.Grade), ssd.Order.OrdersType) + suite.NoError(err) // E_9 rank, no dependents, with spouse pro-gear totalWeight := weightAllotment.TotalWeightSelf + weightAllotment.ProGearWeight + weightAllotment.ProGearWeightSpouse suite.Equal(unit.Pound(totalWeight), ssd.WeightAllotment.TotalWeight) @@ -885,10 +891,12 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFormatSSWGetEntitlement() spouseHasProGear := true hasDependants := true ordersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - allotment := models.GetWeightAllotment(models.ServiceMemberGradeE1, ordersType) + waf := entitlements.NewWeightAllotmentFetcher() + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE1), ordersType) + suite.NoError(err) expectedTotalWeight := allotment.TotalWeightSelfPlusDependents + allotment.ProGearWeight + allotment.ProGearWeightSpouse - sswEntitlement := SSWGetEntitlement(models.ServiceMemberGradeE1, hasDependants, spouseHasProGear, ordersType) - + sswEntitlement, err := SSWGetEntitlement(suite.AppContextForTest(), models.ServiceMemberGradeE1, hasDependants, spouseHasProGear, ordersType) + suite.NoError(err) suite.Equal(unit.Pound(expectedTotalWeight), sswEntitlement.TotalWeight) suite.Equal(unit.Pound(allotment.TotalWeightSelfPlusDependents), sswEntitlement.Entitlement) suite.Equal(unit.Pound(allotment.ProGearWeightSpouse), sswEntitlement.SpouseProGear) @@ -898,10 +906,13 @@ func (suite *ShipmentSummaryWorksheetServiceSuite) TestFormatSSWGetEntitlement() func (suite *ShipmentSummaryWorksheetServiceSuite) TestFormatSSWGetEntitlementNoDependants() { spouseHasProGear := false hasDependants := false + waf := entitlements.NewWeightAllotmentFetcher() ordersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - allotment := models.GetWeightAllotment(models.ServiceMemberGradeE1, ordersType) + allotment, err := waf.GetWeightAllotment(suite.AppContextForTest(), string(models.ServiceMemberGradeE1), ordersType) + suite.NoError(err) expectedTotalWeight := allotment.TotalWeightSelf + allotment.ProGearWeight + allotment.ProGearWeightSpouse - sswEntitlement := SSWGetEntitlement(models.ServiceMemberGradeE1, hasDependants, spouseHasProGear, ordersType) + sswEntitlement, err := SSWGetEntitlement(suite.AppContextForTest(), models.ServiceMemberGradeE1, hasDependants, spouseHasProGear, ordersType) + suite.NoError(err) suite.Equal(unit.Pound(expectedTotalWeight), sswEntitlement.TotalWeight) suite.Equal(unit.Pound(allotment.TotalWeightSelf), sswEntitlement.Entitlement) diff --git a/pkg/testdatagen/make_entitlement.go b/pkg/testdatagen/make_entitlement.go index e71bf02cec1..dc49f7cd65d 100644 --- a/pkg/testdatagen/make_entitlement.go +++ b/pkg/testdatagen/make_entitlement.go @@ -14,7 +14,6 @@ func makeEntitlement(db *pop.Connection, assertions Assertions) models.Entitleme rmeWeight := 1000 ocie := true grade := assertions.Order.Grade - ordersType := assertions.Order.OrdersType proGearWeight := 2000 proGearWeightSpouse := 500 @@ -33,7 +32,15 @@ func makeEntitlement(db *pop.Connection, assertions Assertions) models.Entitleme RequiredMedicalEquipmentWeight: rmeWeight, OrganizationalClothingAndIndividualEquipment: ocie, } - entitlement.SetWeightAllotment(string(*grade), ordersType) + + weightData := getDefaultWeightData(string(*grade)) + allotment := models.WeightAllotment{ + TotalWeightSelf: weightData.TotalWeightSelf, + TotalWeightSelfPlusDependents: weightData.TotalWeightSelfPlusDependents, + ProGearWeight: weightData.ProGearWeight, + ProGearWeightSpouse: weightData.ProGearWeightSpouse, + } + *entitlement.WeightAllotted = allotment dBAuthorizedWeight := entitlement.AuthorizedWeight() entitlement.DBAuthorizedWeight = dBAuthorizedWeight @@ -45,6 +52,59 @@ func makeEntitlement(db *pop.Connection, assertions Assertions) models.Entitleme return entitlement } +// Helper function to retrieve default weight data by grade +func getDefaultWeightData(grade string) struct { + TotalWeightSelf int + TotalWeightSelfPlusDependents int + ProGearWeight int + ProGearWeightSpouse int +} { + if data, ok := knownAllowances[grade]; ok { + return data + } + return knownAllowances["EMPTY"] // Default to EMPTY if grade not found. This is just dummy default data +} + +// Default allowances CAO December 2024 +// Note that the testdatagen package has its own default allowance +var knownAllowances = map[string]struct { + TotalWeightSelf int + TotalWeightSelfPlusDependents int + ProGearWeight int + ProGearWeightSpouse int +}{ + "EMPTY": {0, 0, 0, 0}, + "ACADEMY_CADET": {350, 350, 0, 0}, + "MIDSHIPMAN": {350, 350, 0, 0}, + "AVIATION_CADET": {7000, 8000, 2000, 500}, + "E-1": {5000, 8000, 2000, 500}, + "E-2": {5000, 8000, 2000, 500}, + "E-3": {5000, 8000, 2000, 500}, + "E-4": {7000, 8000, 2000, 500}, + "E-5": {7000, 9000, 2000, 500}, + "E-6": {8000, 11000, 2000, 500}, + "E-7": {11000, 13000, 2000, 500}, + "E-8": {12000, 14000, 2000, 500}, + "E-9": {13000, 15000, 2000, 500}, + "E-9SPECIALSENIORENLISTED": {14000, 17000, 2000, 500}, + "O-1ACADEMYGRADUATE": {10000, 12000, 2000, 500}, + "O-2": {12500, 13500, 2000, 500}, + "O-3": {13000, 14500, 2000, 500}, + "O-4": {14000, 17000, 2000, 500}, + "O-5": {16000, 17500, 2000, 500}, + "O-6": {18000, 18000, 2000, 500}, + "O-7": {18000, 18000, 2000, 500}, + "O-8": {18000, 18000, 2000, 500}, + "O-9": {18000, 18000, 2000, 500}, + "O-10": {18000, 18000, 2000, 500}, + "W-1": {10000, 12000, 2000, 500}, + "W-2": {12500, 13500, 2000, 500}, + "W-3": {13000, 14500, 2000, 500}, + "W-4": {14000, 17000, 2000, 500}, + "W-5": {16000, 17500, 2000, 500}, + "CIVILIAN_EMPLOYEE": {18000, 18000, 2000, 500}, +} + func setDependentsAuthorized(assertionDependentsAuthorized *bool) *bool { dependentsAuthorized := models.BoolPointer(true) if assertionDependentsAuthorized != nil { diff --git a/src/scenes/Office/api.js b/src/scenes/Office/api.js deleted file mode 100644 index e6ab7e50622..00000000000 --- a/src/scenes/Office/api.js +++ /dev/null @@ -1,11 +0,0 @@ -import { getClient, checkResponse } from 'shared/Swagger/api'; - -// MOVE QUEUE -export async function RetrieveMovesForOffice(queueType) { - const client = await getClient(); - const response = await client.apis.queues.showQueue({ - queueType, - }); - checkResponse(response, 'failed to retrieve moves due to server error'); - return response.body; -} From b57afab09bbb175d3c269d280d7214075cfa5519 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Thu, 2 Jan 2025 14:43:53 -0600 Subject: [PATCH 038/113] caps on the const --- .../Office/OrdersDetailForm/OrdersDetailForm.jsx | 8 ++++++-- src/constants/orders.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx index e83bf2afcfb..046c4ae9169 100644 --- a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx +++ b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx @@ -84,7 +84,7 @@ const OrdersDetailForm = ({ name="ordersType" label="Orders type" options={ - formOrdersType === 'SAFETY' || formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK + formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY || formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK ? dropdownInputOptions({ SAFETY: 'Safety', BLUEBARK: 'Bluebark' }) : ordersTypeOptions } @@ -92,7 +92,11 @@ const OrdersDetailForm = ({ setFormOrdersType(e.target.value); setFieldValue('ordersType', e.target.value); }} - isDisabled={formIsDisabled || formOrdersType === 'SAFETY' || formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK} + isDisabled={ + formIsDisabled || + formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY || + formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK + } /> {showOrdersTypeDetail && ( { From cc69631197162e8ac58f11ca83122a5ffb27007e Mon Sep 17 00:00:00 2001 From: deandreJones Date: Thu, 2 Jan 2025 14:55:00 -0600 Subject: [PATCH 039/113] no cap on the move details header --- .../Office/OrdersDetailForm/OrdersDetailForm.jsx | 9 +++++---- src/constants/orders.js | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx index 046c4ae9169..df282179f60 100644 --- a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx +++ b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx @@ -8,7 +8,7 @@ import { CheckboxField, DropdownInput, DatePickerInput, DutyLocationInput } from import TextField from 'components/form/fields/TextField/TextField'; import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; import { DropdownArrayOf } from 'types/form'; -import { SPECIAL_ORDERS_TYPES } from 'constants/orders'; +import { SPECIAL_ORDERS_TYPES_NON_LABEL } from 'constants/orders'; const OrdersDetailForm = ({ deptIndicatorOptions, @@ -84,7 +84,8 @@ const OrdersDetailForm = ({ name="ordersType" label="Orders type" options={ - formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY || formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK + formOrdersType === SPECIAL_ORDERS_TYPES_NON_LABEL.SAFETY || + formOrdersType === SPECIAL_ORDERS_TYPES_NON_LABEL.BLUEBARK ? dropdownInputOptions({ SAFETY: 'Safety', BLUEBARK: 'Bluebark' }) : ordersTypeOptions } @@ -94,8 +95,8 @@ const OrdersDetailForm = ({ }} isDisabled={ formIsDisabled || - formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY || - formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK + formOrdersType === SPECIAL_ORDERS_TYPES_NON_LABEL.SAFETY || + formOrdersType === SPECIAL_ORDERS_TYPES_NON_LABEL.BLUEBARK } /> {showOrdersTypeDetail && ( diff --git a/src/constants/orders.js b/src/constants/orders.js index 56781f96b86..4f7b9dccd13 100644 --- a/src/constants/orders.js +++ b/src/constants/orders.js @@ -9,6 +9,12 @@ export const ORDERS_TYPE = { }; export const SPECIAL_ORDERS_TYPES = { + WOUNDED_WARRIOR: 'Wounded Warrior', + BLUEBARK: 'Bluebark', + SAFETY: 'Safety', +}; + +export const SPECIAL_ORDERS_TYPES_NON_LABEL = { WOUNDED_WARRIOR: 'Wounded Warrior', BLUEBARK: 'BLUEBARK', SAFETY: 'SAFETY', From f23c0d112831c781760e67291fc9a935a54baf9c Mon Sep 17 00:00:00 2001 From: deandreJones Date: Thu, 2 Jan 2025 15:03:38 -0600 Subject: [PATCH 040/113] trash basket --- .../Office/OrdersDetailForm/OrdersDetailForm.jsx | 9 ++++----- src/constants/orders.js | 9 ++------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx index df282179f60..26028b4ea69 100644 --- a/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx +++ b/src/components/Office/OrdersDetailForm/OrdersDetailForm.jsx @@ -8,7 +8,7 @@ import { CheckboxField, DropdownInput, DatePickerInput, DutyLocationInput } from import TextField from 'components/form/fields/TextField/TextField'; import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; import { DropdownArrayOf } from 'types/form'; -import { SPECIAL_ORDERS_TYPES_NON_LABEL } from 'constants/orders'; +import { SPECIAL_ORDERS_TYPES } from 'constants/orders'; const OrdersDetailForm = ({ deptIndicatorOptions, @@ -84,8 +84,7 @@ const OrdersDetailForm = ({ name="ordersType" label="Orders type" options={ - formOrdersType === SPECIAL_ORDERS_TYPES_NON_LABEL.SAFETY || - formOrdersType === SPECIAL_ORDERS_TYPES_NON_LABEL.BLUEBARK + formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY_NON_LABEL || formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK ? dropdownInputOptions({ SAFETY: 'Safety', BLUEBARK: 'Bluebark' }) : ordersTypeOptions } @@ -95,8 +94,8 @@ const OrdersDetailForm = ({ }} isDisabled={ formIsDisabled || - formOrdersType === SPECIAL_ORDERS_TYPES_NON_LABEL.SAFETY || - formOrdersType === SPECIAL_ORDERS_TYPES_NON_LABEL.BLUEBARK + formOrdersType === SPECIAL_ORDERS_TYPES.SAFETY_NON_LABEL || + formOrdersType === SPECIAL_ORDERS_TYPES.BLUEBARK } /> {showOrdersTypeDetail && ( diff --git a/src/constants/orders.js b/src/constants/orders.js index 4f7b9dccd13..7eeb4ab9e73 100644 --- a/src/constants/orders.js +++ b/src/constants/orders.js @@ -9,15 +9,10 @@ export const ORDERS_TYPE = { }; export const SPECIAL_ORDERS_TYPES = { - WOUNDED_WARRIOR: 'Wounded Warrior', - BLUEBARK: 'Bluebark', - SAFETY: 'Safety', -}; - -export const SPECIAL_ORDERS_TYPES_NON_LABEL = { WOUNDED_WARRIOR: 'Wounded Warrior', BLUEBARK: 'BLUEBARK', - SAFETY: 'SAFETY', + SAFETY: 'Safety', + SAFETY_NON_LABEL: 'SAFETY', }; export const CHECK_SPECIAL_ORDERS_TYPES = (ordersType) => { From cae89c8263bd898f2f9b42b25f076659fb37fe2f Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Fri, 3 Jan 2025 18:05:15 +0000 Subject: [PATCH 041/113] fix entitlement factory test --- pkg/factory/entitlement_factory_test.go | 32 ++++++++++++++++++------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/pkg/factory/entitlement_factory_test.go b/pkg/factory/entitlement_factory_test.go index 2599af99a50..e0dd57c6bde 100644 --- a/pkg/factory/entitlement_factory_test.go +++ b/pkg/factory/entitlement_factory_test.go @@ -26,6 +26,21 @@ func (suite *FactorySuite) TestBuildEntitlement() { }, }, nil) } + setupO9Allotment := func() { + pg := BuildPayGrade(suite.DB(), []Customization{ + { + Model: models.PayGrade{ + Grade: "O_9", + }, + }, + }, nil) + BuildHHGAllowance(suite.DB(), []Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + } suite.Run("Successful creation of default entitlement", func() { // Under test: BuildEntitlement // Mocked: None @@ -109,7 +124,7 @@ func (suite *FactorySuite) TestBuildEntitlement() { // Set the weight allotment on the custom object so as to compare allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) suite.NoError(err) - *custEnt.WeightAllotted = allotment + custEnt.WeightAllotted = &allotment custEnt.DBAuthorizedWeight = custEnt.AuthorizedWeight() // Check that the created object had the correct allotments set @@ -151,15 +166,14 @@ func (suite *FactorySuite) TestBuildEntitlement() { // SETUP // Create a default stubbed entitlement to compare values - setupE1Allotment() + setupO9Allotment() testEnt := BuildEntitlement(nil, nil, nil) // Set the weight allotment on the custom object to O_9 testEnt.DBAuthorizedWeight = nil // clear original value - allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "O_9", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) suite.NoError(err) - *testEnt.WeightAllotted = allotment + testEnt.WeightAllotted = &allotment testEnt.DBAuthorizedWeight = testEnt.AuthorizedWeight() - // Now DBAuthorizedWeight should be appropriate for O_9 grade // FUNCTION UNDER TEST grade := internalmessages.OrderPayGrade(models.ServiceMemberGradeO9) @@ -180,11 +194,11 @@ func (suite *FactorySuite) TestBuildEntitlement() { func (suite *FactorySuite) TestBuildPayGrade() { suite.Run("Successful creation of PayGrade with default values", func() { - // Default grade should be "E-5" + // Default grade should be "E_5" payGrade := BuildPayGrade(suite.DB(), nil, nil) suite.NotNil(payGrade.ID) - suite.Equal("E-5", payGrade.Grade) + suite.Equal("E_5", payGrade.Grade) suite.Equal("Enlisted Grade E-5", *payGrade.GradeDescription) pgCount, err := suite.DB().Count(models.PayGrade{}) @@ -231,7 +245,7 @@ func (suite *FactorySuite) TestBuildPayGrade() { func (suite *FactorySuite) TestBuildHHGAllowance() { suite.Run("Successful creation of HHGAllowance with default values", func() { - // Default allowance and grade of E-5 + // Default allowance and grade of E_5 hhgAllowance := BuildHHGAllowance(suite.DB(), nil, nil) suite.NotNil(hhgAllowance.PayGradeID) suite.NotEmpty(hhgAllowance.PayGrade) @@ -255,7 +269,7 @@ func (suite *FactorySuite) TestBuildHHGAllowance() { nil, ) - // E-5 default allowances + // E_5 default allowances suite.Equal(8000, hhgAllowance.TotalWeightSelf) suite.Equal(12000, hhgAllowance.TotalWeightSelfPlusDependents) suite.Equal(3000, hhgAllowance.ProGearWeight) From 88f304a2b798785c3b53ab07b85194116378c328 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Fri, 3 Jan 2025 18:05:25 +0000 Subject: [PATCH 042/113] fix factory tests --- pkg/factory/order_factory.go | 60 ++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/pkg/factory/order_factory.go b/pkg/factory/order_factory.go index c2359e602a4..771dd5b2f3f 100644 --- a/pkg/factory/order_factory.go +++ b/pkg/factory/order_factory.go @@ -290,41 +290,41 @@ func buildOrderWithBuildType(db *pop.Connection, customs []Customization, traits // Overwrite values with those from assertions testdatagen.MergeModels(&order, cOrder) - // Check if PayGrade already exists - var existingPayGrade models.PayGrade - if order.Grade != nil { - err := db.Where("grade = ?", string(*order.Grade)).First(&existingPayGrade) - if err == nil { - // PayGrade exists - grade := internalmessages.OrderPayGrade(existingPayGrade.Grade) - order.Grade = internalmessages.NewOrderPayGrade(grade) - } else { - // Create a new PayGrade - existingPayGrade = BuildPayGrade(db, []Customization{ - { - Model: models.PayGrade{ - Grade: string(*order.Grade), + // If db is false, it's a stub. No need to create in database + if db != nil { + // Check if PayGrade already exists + var existingPayGrade models.PayGrade + if order.Grade != nil { + err := db.Where("grade = ?", string(*order.Grade)).First(&existingPayGrade) + if err == nil { + // PayGrade exists + grade := internalmessages.OrderPayGrade(existingPayGrade.Grade) + order.Grade = internalmessages.NewOrderPayGrade(grade) + } else { + // Create a new PayGrade + existingPayGrade = BuildPayGrade(db, []Customization{ + { + Model: models.PayGrade{ + Grade: string(*order.Grade), + }, }, + }, nil) + } + } + + // Check if HHGAllowance already exists for this PayGrade + var existingHHGAllowance models.HHGAllowance + err := db.Where("pay_grade_id = ?", existingPayGrade.ID).First(&existingHHGAllowance) + if err != nil { + // Create a new HHGAllowance + BuildHHGAllowance(db, []Customization{ + { + Model: existingPayGrade, + LinkOnly: true, }, }, nil) } - } - - // Check if HHGAllowance already exists for this PayGrade - var existingHHGAllowance models.HHGAllowance - err := db.Where("pay_grade_id = ?", existingPayGrade.ID).First(&existingHHGAllowance) - if err != nil { - // Create a new HHGAllowance - BuildHHGAllowance(db, []Customization{ - { - Model: existingPayGrade, - LinkOnly: true, - }, - }, nil) - } - // If db is false, it's a stub. No need to create in database - if db != nil { mustCreate(db, &order) } From 0ad9a2a568b89be0153dc1d6fe82a8d35c2af0fa Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Fri, 3 Jan 2025 20:04:27 +0000 Subject: [PATCH 043/113] fix ghcapi and datagen --- pkg/handlers/ghcapi/orders_test.go | 68 +++++++++++++++++++++++++++-- pkg/services/order/order_updater.go | 16 ++++--- pkg/testdatagen/make_entitlement.go | 2 +- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/pkg/handlers/ghcapi/orders_test.go b/pkg/handlers/ghcapi/orders_test.go index 928a5e0db95..38db697cec7 100644 --- a/pkg/handlers/ghcapi/orders_test.go +++ b/pkg/handlers/ghcapi/orders_test.go @@ -40,7 +40,22 @@ func (suite *HandlerSuite) TestCreateOrder() { waf := entitlements.NewWeightAllotmentFetcher() sm := factory.BuildExtendedServiceMember(suite.AppContextForTest().DB(), nil, nil) officeUser := factory.BuildOfficeUserWithRoles(suite.AppContextForTest().DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) - + setupE1Allotment := func() { + pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ + { + Model: models.PayGrade{ + Grade: "E_1", + }, + }, + }, nil) + factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + } + setupE1Allotment() originDutyLocation := factory.BuildDutyLocation(suite.AppContextForTest().DB(), []factory.Customization{ { Model: models.DutyLocation{ @@ -109,7 +124,22 @@ func (suite *HandlerSuite) TestCreateOrder() { func (suite *HandlerSuite) TestCreateOrderWithOCONUSValues() { waf := entitlements.NewWeightAllotmentFetcher() - + setupE1Allotment := func() { + pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ + { + Model: models.PayGrade{ + Grade: "E_1", + }, + }, + }, nil) + factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + } + setupE1Allotment() sm := factory.BuildExtendedServiceMember(suite.AppContextForTest().DB(), nil, nil) officeUser := factory.BuildOfficeUserWithRoles(suite.AppContextForTest().DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) @@ -1252,12 +1282,28 @@ func (suite *HandlerSuite) makeUpdateBillableWeightHandlerSubtestData() (subtest func (suite *HandlerSuite) TestUpdateAllowanceHandler() { request := httptest.NewRequest("PATCH", "/orders/{orderID}/allowances", nil) - + setupGrade := func(grade string) { + pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ + { + Model: models.PayGrade{ + Grade: grade, + }, + }, + }, nil) + factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + } suite.Run("Returns 200 when all validations pass", func() { handlerConfig := suite.HandlerConfig() subtestData := suite.makeUpdateAllowanceHandlerSubtestData() order := subtestData.order body := subtestData.body + // We're going to update from E_1 to O_5. Make sure O_5 has an allowance + setupGrade(string(*subtestData.body.Grade)) requestUser := factory.BuildOfficeUserWithRoles(nil, nil, []roles.RoleType{roles.RoleTypeTOO, roles.RoleTypeTIO, roles.RoleTypeServicesCounselor}) request = suite.AuthenticateOfficeRequest(request, requestUser) @@ -1463,6 +1509,21 @@ func (suite *HandlerSuite) TestUpdateAllowanceEventTrigger() { func (suite *HandlerSuite) TestCounselingUpdateAllowanceHandler() { grade := ghcmessages.GradeO5 + setupGrade := func(grade string) { + pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ + { + Model: models.PayGrade{ + Grade: grade, + }, + }, + }, nil) + factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + } affiliation := ghcmessages.AffiliationAIRFORCE ocie := false proGearWeight := models.Int64Pointer(100) @@ -1483,6 +1544,7 @@ func (suite *HandlerSuite) TestCounselingUpdateAllowanceHandler() { request := httptest.NewRequest("PATCH", "/counseling/orders/{orderID}/allowances", nil) suite.Run("Returns 200 when all validations pass", func() { + setupGrade(string(grade)) handlerConfig := suite.HandlerConfig() move := factory.BuildNeedsServiceCounselingMove(suite.DB(), nil, nil) order := move.Orders diff --git a/pkg/services/order/order_updater.go b/pkg/services/order/order_updater.go index 336f057a5b0..2d15739271b 100644 --- a/pkg/services/order/order_updater.go +++ b/pkg/services/order/order_updater.go @@ -521,7 +521,7 @@ func allowanceFromTOOPayload(appCtx appcontext.AppContext, existingOrder models. if order.Entitlement != nil { unaccompaniedBaggageAllowance, err := models.GetUBWeightAllowance(appCtx, order.OriginDutyLocation.Address.IsOconus, order.NewDutyLocation.Address.IsOconus, order.ServiceMember.Affiliation, order.Grade, &order.OrdersType, &hasDepedents, order.Entitlement.AccompaniedTour, order.Entitlement.DependentsUnderTwelve, order.Entitlement.DependentsTwelveAndOver) if err == nil { - order.Entitlement.WeightAllotted.UnaccompaniedBaggageAllowance = unaccompaniedBaggageAllowance + order.Entitlement.UBAllowance = &unaccompaniedBaggageAllowance } } @@ -558,7 +558,7 @@ func allowanceFromCounselingPayload(appCtx appcontext.AppContext, existingOrder // Calculate new DBWeightAuthorized based on the new grade weightAllotment, err := waf.GetWeightAllotment(appCtx, string(*order.Grade), order.OrdersType) if err != nil { - return models.Order{}, nil + return models.Order{}, err } weight := weightAllotment.TotalWeightSelf // Payload does not have this information, retrieve dependents from the existing order @@ -609,18 +609,20 @@ func allowanceFromCounselingPayload(appCtx appcontext.AppContext, existingOrder if order.NewDutyLocationID != uuid.Nil { var newDutyLocation models.DutyLocation newDutyLocation, err := models.FetchDutyLocation(appCtx.DB(), order.NewDutyLocationID) - if err == nil { - order.NewDutyLocationID = newDutyLocation.ID - order.NewDutyLocation = newDutyLocation + if err != nil { + return models.Order{}, err } + order.NewDutyLocationID = newDutyLocation.ID + order.NewDutyLocation = newDutyLocation } // Recalculate UB allowance of order entitlement if order.Entitlement != nil { unaccompaniedBaggageAllowance, err := models.GetUBWeightAllowance(appCtx, order.OriginDutyLocation.Address.IsOconus, order.NewDutyLocation.Address.IsOconus, order.ServiceMember.Affiliation, order.Grade, &order.OrdersType, payload.DependentsAuthorized, order.Entitlement.AccompaniedTour, order.Entitlement.DependentsUnderTwelve, order.Entitlement.DependentsTwelveAndOver) - if err == nil { - weightAllotment.UnaccompaniedBaggageAllowance = unaccompaniedBaggageAllowance + if err != nil { + return models.Order{}, err } + order.Entitlement.UBAllowance = &unaccompaniedBaggageAllowance } return order, nil diff --git a/pkg/testdatagen/make_entitlement.go b/pkg/testdatagen/make_entitlement.go index dc49f7cd65d..2f502f59e3e 100644 --- a/pkg/testdatagen/make_entitlement.go +++ b/pkg/testdatagen/make_entitlement.go @@ -40,7 +40,7 @@ func makeEntitlement(db *pop.Connection, assertions Assertions) models.Entitleme ProGearWeight: weightData.ProGearWeight, ProGearWeightSpouse: weightData.ProGearWeightSpouse, } - *entitlement.WeightAllotted = allotment + entitlement.WeightAllotted = &allotment dBAuthorizedWeight := entitlement.AuthorizedWeight() entitlement.DBAuthorizedWeight = dBAuthorizedWeight From 402ad2a042b8643426c1f79a5e759a17f321aa39 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Fri, 3 Jan 2025 20:19:14 +0000 Subject: [PATCH 044/113] fix internalapi --- pkg/handlers/internalapi/orders_test.go | 50 +++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/pkg/handlers/internalapi/orders_test.go b/pkg/handlers/internalapi/orders_test.go index f6ab9b03906..05aa6dcf7b4 100644 --- a/pkg/handlers/internalapi/orders_test.go +++ b/pkg/handlers/internalapi/orders_test.go @@ -26,6 +26,22 @@ import ( ) func (suite *HandlerSuite) TestCreateOrder() { + setupGrade := func(grade string) { + pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ + { + Model: models.PayGrade{ + Grade: grade, + }, + }, + }, nil) + factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + } + suite.PreloadData(func() { factory.FetchOrBuildCountry(suite.DB(), []factory.Customization{ { @@ -38,6 +54,7 @@ func (suite *HandlerSuite) TestCreateOrder() { }) sm := factory.BuildExtendedServiceMember(suite.DB(), nil, nil) suite.Run("can create conus and oconus orders", func() { + setupGrade("E_1") testCases := []struct { test string isOconus bool @@ -94,6 +111,7 @@ func (suite *HandlerSuite) TestCreateOrder() { DepartmentIndicator: internalmessages.NewDeptIndicator(deptIndicator), Grade: models.ServiceMemberGradeE1.Pointer(), } + if tc.isOconus { payload.AccompaniedTour = models.BoolPointer(true) payload.DependentsTwelveAndOver = models.Int64Pointer(5) @@ -586,7 +604,23 @@ func (suite *HandlerSuite) TestUploadAmendedOrdersHandlerIntegration() { func (suite *HandlerSuite) TestUpdateOrdersHandler() { waf := entitlements.NewWeightAllotmentFetcher() + setupGrade := func(grade string) { + pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ + { + Model: models.PayGrade{ + Grade: grade, + }, + }, + }, nil) + factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + } suite.Run("Can update CONUS and OCONUS orders", func() { + setupGrade("E_4") testCases := []struct { isOconus bool }{ @@ -785,6 +819,22 @@ func (suite *HandlerSuite) TestEntitlementHelperFunc() { } func (suite *HandlerSuite) TestUpdateOrdersHandlerWithCounselingOffice() { + setupGrade := func(grade string) { + pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ + { + Model: models.PayGrade{ + Grade: grade, + }, + }, + }, nil) + factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ + { + Model: pg, + LinkOnly: true, + }, + }, nil) + } + setupGrade("E_4") originDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ { Model: models.DutyLocation{ From 02e8b741b111e9ca9eacf228dac7ee57e5bd8e16 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 6 Jan 2025 15:27:13 +0000 Subject: [PATCH 045/113] fix prime api and flaky payment request test --- pkg/factory/entitlement_factory.go | 33 +++++++++++++++++++ pkg/factory/entitlement_factory_test.go | 27 +++++++++++++++ pkg/handlers/primeapi/api_test.go | 7 ++++ pkg/handlers/primeapi/move_task_order_test.go | 14 ++++++-- .../move_task_order_fetcher.go | 17 +++++++++- .../move_task_order_updater.go | 8 ++++- 6 files changed, 102 insertions(+), 4 deletions(-) diff --git a/pkg/factory/entitlement_factory.go b/pkg/factory/entitlement_factory.go index 48cd3294a2f..80a27ba5e61 100644 --- a/pkg/factory/entitlement_factory.go +++ b/pkg/factory/entitlement_factory.go @@ -175,6 +175,39 @@ func getDefaultWeightData(grade string) struct { return knownAllowances["EMPTY"] // Default to EMPTY if grade not found. This is just dummy default data } +// Make the life easier for test suites by creating all +// known allotments and their expected outcomes +func SetupDefaultAllotments(db *pop.Connection) { + // Wrap in case of stub + if db != nil { + // Iterate over all known allowances + for grade, allowance := range knownAllowances { + // Build the pay grade and HHG allowance + pg := BuildPayGrade(db, []Customization{ + { + Model: models.PayGrade{ + Grade: grade, + }, + }, + }, nil) + BuildHHGAllowance(db, []Customization{ + { + Model: pg, + LinkOnly: true, + }, + { + Model: models.HHGAllowance{ + TotalWeightSelf: allowance.TotalWeightSelf, + TotalWeightSelfPlusDependents: allowance.TotalWeightSelfPlusDependents, + ProGearWeight: allowance.ProGearWeight, + ProGearWeightSpouse: allowance.ProGearWeightSpouse, + }, + }, + }, nil) + } + } +} + // Default allowances CAO December 2024 // Note that the testdatagen package has its own default allowance var knownAllowances = map[string]struct { diff --git a/pkg/factory/entitlement_factory_test.go b/pkg/factory/entitlement_factory_test.go index e0dd57c6bde..01b65b7cbb8 100644 --- a/pkg/factory/entitlement_factory_test.go +++ b/pkg/factory/entitlement_factory_test.go @@ -300,3 +300,30 @@ func (suite *FactorySuite) TestBuildHHGAllowance() { suite.Equal(3000, newHhg.ProGearWeight) }) } + +func (suite *FactorySuite) TestSetupAllAllotments() { + suite.Run("Successful creation of allotments for all known grades", func() { + err := suite.DB().TruncateAll() + suite.NoError(err) + + SetupDefaultAllotments(suite.DB()) + + // Validate the allotments + for grade, allowance := range knownAllowances { + // Ensure pay grade exists + pg := &models.PayGrade{} + err := suite.DB().Where("grade = ?", grade).First(pg) + suite.NoError(err, grade) + suite.NotNil(pg.ID, grade) + + // Ensure HHGAllowance was created and matches the expected values + hhgAllowance := &models.HHGAllowance{} + err = suite.DB().Where("pay_grade_id = ?", pg.ID).First(hhgAllowance) + suite.NoError(err, grade) + suite.Equal(allowance.TotalWeightSelf, hhgAllowance.TotalWeightSelf, grade) + suite.Equal(allowance.TotalWeightSelfPlusDependents, hhgAllowance.TotalWeightSelfPlusDependents, grade) + suite.Equal(allowance.ProGearWeight, hhgAllowance.ProGearWeight, grade) + suite.Equal(allowance.ProGearWeightSpouse, hhgAllowance.ProGearWeightSpouse, grade) + } + }) +} diff --git a/pkg/handlers/primeapi/api_test.go b/pkg/handlers/primeapi/api_test.go index 971fa44c0f2..4ab9b83e4e5 100644 --- a/pkg/handlers/primeapi/api_test.go +++ b/pkg/handlers/primeapi/api_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/gen/primemessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" @@ -17,6 +18,12 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } +func (suite *HandlerSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + // AfterTest completes tests by trying to close open files func (suite *HandlerSuite) AfterTest() { for _, file := range suite.TestFilesToClose() { diff --git a/pkg/handlers/primeapi/move_task_order_test.go b/pkg/handlers/primeapi/move_task_order_test.go index 2bfa5abfbcc..db6d45244aa 100644 --- a/pkg/handlers/primeapi/move_task_order_test.go +++ b/pkg/handlers/primeapi/move_task_order_test.go @@ -794,7 +794,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.NotNil(ordersPayload.OriginDutyLocation.ETag) }) - suite.Run("Success - return all PaymentRequests fields assoicated with the getMoveTaskOrder", func() { + suite.Run("Success - return all PaymentRequests fields associated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), movetaskorder.NewMoveTaskOrderFetcher(), @@ -938,10 +938,20 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.NoError(movePayload.Validate(strfmt.Default)) suite.Len(movePayload.PaymentRequests, 2) - paymentRequestPayload := movePayload.PaymentRequests[0] + var paymentRequestPayload *primemessages.PaymentRequest + // Correctly grab the payment request by id + for _, pr := range movePayload.PaymentRequests { + if pr.ID.String() == paymentRequest.ID.String() { + paymentRequestPayload = pr + break + } + } + suite.NotNil(paymentRequestPayload) suite.Equal(paymentRequest.ID.String(), paymentRequestPayload.ID.String()) suite.Equal(successMove.ID.String(), paymentRequestPayload.MoveTaskOrderID.String()) suite.Equal(paymentRequest.IsFinal, *paymentRequestPayload.IsFinal) + suite.NotNil(paymentRequest.RejectionReason) + suite.NotNil(paymentRequestPayload.RejectionReason) suite.Equal(*paymentRequest.RejectionReason, *paymentRequestPayload.RejectionReason) suite.Equal(paymentRequest.Status.String(), string(paymentRequestPayload.Status)) suite.Equal(paymentRequest.PaymentRequestNumber, paymentRequestPayload.PaymentRequestNumber) diff --git a/pkg/services/move_task_order/move_task_order_fetcher.go b/pkg/services/move_task_order/move_task_order_fetcher.go index b63f5960290..63ff6585891 100644 --- a/pkg/services/move_task_order/move_task_order_fetcher.go +++ b/pkg/services/move_task_order/move_task_order_fetcher.go @@ -16,15 +16,20 @@ import ( "github.com/transcom/mymove/pkg/cli" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/featureflag" ) type moveTaskOrderFetcher struct { + waf services.WeightAllotmentFetcher } // NewMoveTaskOrderFetcher creates a new struct with the service dependencies func NewMoveTaskOrderFetcher() services.MoveTaskOrderFetcher { - return &moveTaskOrderFetcher{} + waf := entitlements.NewWeightAllotmentFetcher() + return &moveTaskOrderFetcher{ + waf: waf, + } } // ListAllMoveTaskOrders retrieves all Move Task Orders that may or may not be available to prime, and may or may not be enabled. @@ -196,6 +201,16 @@ func (f moveTaskOrderFetcher) FetchMoveTaskOrder(appCtx appcontext.AppContext, s } } + // Now that we have the move and order, construct the allotment (hhg allowance) + // Only fetch if grade us not nil + if mto.Orders.Grade != nil { + allotment, err := f.waf.GetWeightAllotment(appCtx, string(*mto.Orders.Grade), mto.Orders.OrdersType) + if err != nil { + return nil, err + } + mto.Orders.Entitlement.WeightAllotted = &allotment + } + // Due to a bug in Pop for EagerPreload the New Address of the DeliveryAddressUpdate and the PortLocation (City, Country, UsPostRegionCity.UsPostRegion.State") must be loaded manually. // The bug occurs in EagerPreload when there are two or more eager paths with 3+ levels // where the first 2 levels match. For example: diff --git a/pkg/services/move_task_order/move_task_order_updater.go b/pkg/services/move_task_order/move_task_order_updater.go index 71389da1a3a..b53af456412 100644 --- a/pkg/services/move_task_order/move_task_order_updater.go +++ b/pkg/services/move_task_order/move_task_order_updater.go @@ -15,6 +15,7 @@ import ( "github.com/transcom/mymove/pkg/etag" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/order" "github.com/transcom/mymove/pkg/services/query" ) @@ -31,7 +32,12 @@ type moveTaskOrderUpdater struct { // NewMoveTaskOrderUpdater creates a new struct with the service dependencies func NewMoveTaskOrderUpdater(builder UpdateMoveTaskOrderQueryBuilder, serviceItemCreator services.MTOServiceItemCreator, moveRouter services.MoveRouter, signedCertificationCreator services.SignedCertificationCreator, signedCertificationUpdater services.SignedCertificationUpdater, estimator services.PPMEstimator) services.MoveTaskOrderUpdater { - return &moveTaskOrderUpdater{moveTaskOrderFetcher{}, builder, serviceItemCreator, moveRouter, signedCertificationCreator, signedCertificationUpdater, estimator} + // Fetcher dependency + waf := entitlements.NewWeightAllotmentFetcher() + + return &moveTaskOrderUpdater{moveTaskOrderFetcher{ + waf: waf, + }, builder, serviceItemCreator, moveRouter, signedCertificationCreator, signedCertificationUpdater, estimator} } // UpdateStatusServiceCounselingCompleted updates the status on the move (move task order) to service counseling completed From ce2b733504a7dd61edb7f019f294b23312c3e5ab Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 6 Jan 2025 16:49:45 +0000 Subject: [PATCH 046/113] backend preload data --- pkg/handlers/internalapi/api_test.go | 16 +++++ .../internal/payloads/payloads_test.go | 7 +++ pkg/handlers/internalapi/orders_test.go | 59 ------------------- pkg/handlers/internalapi/ppm_shipment_test.go | 8 ++- .../primeapi/payloads/payloads_test.go | 7 +++ pkg/handlers/primeapiv2/api_test.go | 7 +++ .../primeapiv2/payloads/payloads_test.go | 7 +++ pkg/handlers/primeapiv3/api_test.go | 7 +++ .../primeapiv3/payloads/payloads_test.go | 7 +++ pkg/services/move/move_service_test.go | 7 +++ .../mto_shipment/mto_shipment_service_test.go | 7 +++ pkg/services/order/order_service_test.go | 7 +++ .../pptas_report/pptas_report_service_test.go | 7 +++ ...shipment_summary_worksheet_service_test.go | 7 +++ scripts/run-server-test | 6 +- 15 files changed, 102 insertions(+), 64 deletions(-) diff --git a/pkg/handlers/internalapi/api_test.go b/pkg/handlers/internalapi/api_test.go index ef45c139cd0..0f56159a405 100644 --- a/pkg/handlers/internalapi/api_test.go +++ b/pkg/handlers/internalapi/api_test.go @@ -5,7 +5,9 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" + "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/notifications" storageTest "github.com/transcom/mymove/pkg/storage/test" "github.com/transcom/mymove/pkg/testingsuite" @@ -25,6 +27,20 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } +func (suite *HandlerSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + factory.FetchOrBuildCountry(suite.DB(), []factory.Customization{ + { + Model: models.Country{ + Country: "US", + CountryName: "UNITED STATES", + }, + }, + }, nil) + }) +} + // AfterTest completes tests by trying to close open files func (suite *HandlerSuite) AfterTest() { for _, file := range suite.TestFilesToClose() { diff --git a/pkg/handlers/internalapi/internal/payloads/payloads_test.go b/pkg/handlers/internalapi/internal/payloads/payloads_test.go index c17b8a85256..6913c01afb7 100644 --- a/pkg/handlers/internalapi/internal/payloads/payloads_test.go +++ b/pkg/handlers/internalapi/internal/payloads/payloads_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/storage" @@ -18,6 +19,12 @@ type PayloadsSuite struct { storer storage.FileStorer } +func (suite *PayloadsSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/handlers/internalapi/orders_test.go b/pkg/handlers/internalapi/orders_test.go index 05aa6dcf7b4..02f3ba0d183 100644 --- a/pkg/handlers/internalapi/orders_test.go +++ b/pkg/handlers/internalapi/orders_test.go @@ -26,35 +26,8 @@ import ( ) func (suite *HandlerSuite) TestCreateOrder() { - setupGrade := func(grade string) { - pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ - { - Model: models.PayGrade{ - Grade: grade, - }, - }, - }, nil) - factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - } - - suite.PreloadData(func() { - factory.FetchOrBuildCountry(suite.DB(), []factory.Customization{ - { - Model: models.Country{ - Country: "US", - CountryName: "UNITED STATES", - }, - }, - }, nil) - }) sm := factory.BuildExtendedServiceMember(suite.DB(), nil, nil) suite.Run("can create conus and oconus orders", func() { - setupGrade("E_1") testCases := []struct { test string isOconus bool @@ -604,23 +577,7 @@ func (suite *HandlerSuite) TestUploadAmendedOrdersHandlerIntegration() { func (suite *HandlerSuite) TestUpdateOrdersHandler() { waf := entitlements.NewWeightAllotmentFetcher() - setupGrade := func(grade string) { - pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ - { - Model: models.PayGrade{ - Grade: grade, - }, - }, - }, nil) - factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - } suite.Run("Can update CONUS and OCONUS orders", func() { - setupGrade("E_4") testCases := []struct { isOconus bool }{ @@ -819,22 +776,6 @@ func (suite *HandlerSuite) TestEntitlementHelperFunc() { } func (suite *HandlerSuite) TestUpdateOrdersHandlerWithCounselingOffice() { - setupGrade := func(grade string) { - pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ - { - Model: models.PayGrade{ - Grade: grade, - }, - }, - }, nil) - factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - } - setupGrade("E_4") originDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ { Model: models.DutyLocation{ diff --git a/pkg/handlers/internalapi/ppm_shipment_test.go b/pkg/handlers/internalapi/ppm_shipment_test.go index d01813b5ef8..b9e20809025 100644 --- a/pkg/handlers/internalapi/ppm_shipment_test.go +++ b/pkg/handlers/internalapi/ppm_shipment_test.go @@ -798,7 +798,7 @@ func (suite *HandlerSuite) TestResubmitPPMShipmentDocumentationHandlerIntegratio var shipmentNeedsCloseout models.PPMShipment var needsCloseoutSM models.ServiceMember - suite.PreloadData(func() { + setupPPMData := func() { shipmentNeedsResubmitted = factory.BuildPPMShipmentThatNeedsToBeResubmitted(suite.DB(), userUploader, nil) shipmentNeedsResubmitted.SubmittedAt = &submissionTime suite.NoError(suite.DB().Save(&shipmentNeedsResubmitted)) @@ -806,7 +806,7 @@ func (suite *HandlerSuite) TestResubmitPPMShipmentDocumentationHandlerIntegratio shipmentNeedsCloseout = factory.BuildPPMShipmentThatNeedsCloseout(suite.DB(), nil, nil) needsCloseoutSM = shipmentNeedsCloseout.Shipment.MoveTaskOrder.Orders.ServiceMember - }) + } setUpParamsAndHandler := func(ppmShipment models.PPMShipment, serviceMember models.ServiceMember, payload *internalmessages.SavePPMShipmentSignedCertification) (ppmops.ResubmitPPMShipmentDocumentationParams, ResubmitPPMShipmentDocumentationHandler) { endpoint := fmt.Sprintf( @@ -840,6 +840,7 @@ func (suite *HandlerSuite) TestResubmitPPMShipmentDocumentationHandlerIntegratio } suite.Run("Returns an error if the PPM shipment is not found", func() { + setupPPMData() shipmentWithUnknownID := models.PPMShipment{ ID: uuid.Must(uuid.NewV4()), SignedCertification: &models.SignedCertification{ @@ -863,6 +864,7 @@ func (suite *HandlerSuite) TestResubmitPPMShipmentDocumentationHandlerIntegratio }) suite.Run("Returns an error if the signed certification is not found", func() { + setupPPMData() shipmentWithUnknownSignedCert := models.PPMShipment{ ID: shipmentNeedsResubmitted.ID, SignedCertification: &models.SignedCertification{ @@ -886,6 +888,7 @@ func (suite *HandlerSuite) TestResubmitPPMShipmentDocumentationHandlerIntegratio }) suite.Run("Returns an error if the PPM shipment is not in the right status", func() { + setupPPMData() params, handler := setUpParamsAndHandler(shipmentNeedsCloseout, needsCloseoutSM, &internalmessages.SavePPMShipmentSignedCertification{ CertificationText: handlers.FmtString("certification text"), Signature: handlers.FmtString("signature"), @@ -909,6 +912,7 @@ func (suite *HandlerSuite) TestResubmitPPMShipmentDocumentationHandlerIntegratio }) suite.Run("Can successfully resubmit a PPM shipment for close out", func() { + setupPPMData() newCertText := "new certification text" newSignature := "new signature" newSignDate := time.Now().AddDate(0, 0, 1) diff --git a/pkg/handlers/primeapi/payloads/payloads_test.go b/pkg/handlers/primeapi/payloads/payloads_test.go index 3ec8f8386dd..8f9d7410aa3 100644 --- a/pkg/handlers/primeapi/payloads/payloads_test.go +++ b/pkg/handlers/primeapi/payloads/payloads_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -15,6 +16,12 @@ type PayloadsSuite struct { handlers.BaseHandlerTestSuite } +func (suite *PayloadsSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/handlers/primeapiv2/api_test.go b/pkg/handlers/primeapiv2/api_test.go index f1697c293f9..c9ffc043d29 100644 --- a/pkg/handlers/primeapiv2/api_test.go +++ b/pkg/handlers/primeapiv2/api_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -15,6 +16,12 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } +func (suite *HandlerSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + // AfterTest completes tests by trying to close open files func (suite *HandlerSuite) AfterTest() { for _, file := range suite.TestFilesToClose() { diff --git a/pkg/handlers/primeapiv2/payloads/payloads_test.go b/pkg/handlers/primeapiv2/payloads/payloads_test.go index 3ec8f8386dd..8f9d7410aa3 100644 --- a/pkg/handlers/primeapiv2/payloads/payloads_test.go +++ b/pkg/handlers/primeapiv2/payloads/payloads_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -15,6 +16,12 @@ type PayloadsSuite struct { handlers.BaseHandlerTestSuite } +func (suite *PayloadsSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/handlers/primeapiv3/api_test.go b/pkg/handlers/primeapiv3/api_test.go index 19aa406fdae..0c425c90948 100644 --- a/pkg/handlers/primeapiv3/api_test.go +++ b/pkg/handlers/primeapiv3/api_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -22,6 +23,12 @@ func (suite *HandlerSuite) AfterTest() { } } +func (suite *HandlerSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &HandlerSuite{ diff --git a/pkg/handlers/primeapiv3/payloads/payloads_test.go b/pkg/handlers/primeapiv3/payloads/payloads_test.go index 3ec8f8386dd..8f9d7410aa3 100644 --- a/pkg/handlers/primeapiv3/payloads/payloads_test.go +++ b/pkg/handlers/primeapiv3/payloads/payloads_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -15,6 +16,12 @@ type PayloadsSuite struct { handlers.BaseHandlerTestSuite } +func (suite *PayloadsSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/services/move/move_service_test.go b/pkg/services/move/move_service_test.go index 1a16c967e81..197c48f4c93 100644 --- a/pkg/services/move/move_service_test.go +++ b/pkg/services/move/move_service_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -12,6 +13,12 @@ type MoveServiceSuite struct { *testingsuite.PopTestSuite } +func (suite *MoveServiceSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + func TestMoveServiceSuite(t *testing.T) { hs := &MoveServiceSuite{ diff --git a/pkg/services/mto_shipment/mto_shipment_service_test.go b/pkg/services/mto_shipment/mto_shipment_service_test.go index e5696befda7..3cba0276e4e 100644 --- a/pkg/services/mto_shipment/mto_shipment_service_test.go +++ b/pkg/services/mto_shipment/mto_shipment_service_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -12,6 +13,12 @@ type MTOShipmentServiceSuite struct { *testingsuite.PopTestSuite } +func (suite *MTOShipmentServiceSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + func TestMTOShipmentServiceSuite(t *testing.T) { ts := &MTOShipmentServiceSuite{ diff --git a/pkg/services/order/order_service_test.go b/pkg/services/order/order_service_test.go index 92313ed9635..f2c2a57c8e1 100644 --- a/pkg/services/order/order_service_test.go +++ b/pkg/services/order/order_service_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -12,6 +13,12 @@ type OrderServiceSuite struct { *testingsuite.PopTestSuite } +func (suite *OrderServiceSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + func TestOrderServiceSuite(t *testing.T) { ts := &OrderServiceSuite{ PopTestSuite: testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), testingsuite.WithPerTestTransaction()), diff --git a/pkg/services/pptas_report/pptas_report_service_test.go b/pkg/services/pptas_report/pptas_report_service_test.go index 11a857a646e..8b8aa416896 100644 --- a/pkg/services/pptas_report/pptas_report_service_test.go +++ b/pkg/services/pptas_report/pptas_report_service_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -12,6 +13,12 @@ type ReportServiceSuite struct { *testingsuite.PopTestSuite } +func (suite *ReportServiceSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + func TestReportServiceSuite(t *testing.T) { ts := &ReportServiceSuite{ PopTestSuite: testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), diff --git a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_service_test.go b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_service_test.go index 86e6cd7f457..a6ae372873a 100644 --- a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_service_test.go +++ b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_service_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -12,6 +13,12 @@ type ShipmentSummaryWorksheetServiceSuite struct { *testingsuite.PopTestSuite } +func (suite *ShipmentSummaryWorksheetServiceSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + func TestShipmentSummaryWorksheetServiceSuite(t *testing.T) { ts := &ShipmentSummaryWorksheetServiceSuite{ testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), testingsuite.WithPerTestTransaction()), 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 997ac0d87f4fc1e28c8e7b4d2067db96bd35aa74 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 6 Jan 2025 17:29:12 +0000 Subject: [PATCH 047/113] fixing e2e tests --- pkg/handlers/ghcapi/api.go | 12 ++--- pkg/handlers/ghcapi/move_task_order_test.go | 5 ++- pkg/handlers/ghcapi/mto_service_items_test.go | 9 ++-- pkg/handlers/ghcapi/mto_shipment_test.go | 31 ++++++++----- pkg/handlers/ghcapi/queues_test.go | 4 +- pkg/handlers/internalapi/api.go | 2 +- pkg/handlers/internalapi/mto_shipment_test.go | 4 +- pkg/handlers/primeapi/api.go | 9 ++-- pkg/handlers/primeapi/move_task_order_test.go | 44 ++++++++++--------- pkg/handlers/primeapi/mto_shipment_test.go | 5 ++- pkg/handlers/primeapiv2/api.go | 6 ++- .../primeapiv2/move_task_order_test.go | 38 ++++++++-------- pkg/handlers/primeapiv3/api.go | 6 ++- .../primeapiv3/move_task_order_test.go | 38 ++++++++-------- pkg/handlers/primeapiv3/mto_shipment_test.go | 4 +- pkg/handlers/supportapi/api.go | 6 ++- .../supportapi/move_task_order_test.go | 7 ++- pkg/services/move/move_weights.go | 4 +- pkg/services/move/move_weights_test.go | 16 ++++--- .../move_task_order_fetcher.go | 6 +-- .../move_task_order_fetcher_test.go | 20 ++++++--- .../mto_shipment/mto_shipment_updater_test.go | 16 +++++-- .../mto_shipment/shipment_approver_test.go | 4 +- .../sit_extension_creator_test.go | 4 +- 24 files changed, 182 insertions(+), 118 deletions(-) diff --git a/pkg/handlers/ghcapi/api.go b/pkg/handlers/ghcapi/api.go index 03b9f0c22f1..4da669a9db9 100644 --- a/pkg/handlers/ghcapi/api.go +++ b/pkg/handlers/ghcapi/api.go @@ -229,7 +229,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { fetch.NewFetcher(queryBuilder), handlerConfig.HHGPlanner(), moveRouter, - move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()), + move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf), handlerConfig.NotificationSender(), paymentRequestShipmentRecalculator, addressUpdater, @@ -284,7 +284,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { ghcAPI.MoveTaskOrderGetMoveTaskOrderHandler = GetMoveTaskOrderHandler{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } ghcAPI.MoveSetFinancialReviewFlagHandler = SetFinancialReviewFlagHandler{ handlerConfig, @@ -411,7 +411,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { mtoshipment.NewShipmentRouter(), mtoserviceitem.NewMTOServiceItemCreator(handlerConfig.HHGPlanner(), queryBuilder, moveRouter, ghcrateengine.NewDomesticUnpackPricer(), ghcrateengine.NewDomesticPackPricer(), ghcrateengine.NewDomesticLinehaulPricer(), ghcrateengine.NewDomesticShorthaulPricer(), ghcrateengine.NewDomesticOriginPricer(), ghcrateengine.NewDomesticDestinationPricer(), ghcrateengine.NewFuelSurchargePricer()), handlerConfig.HHGPlanner(), - move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()), + move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf), ), shipmentSITStatus, } @@ -458,7 +458,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { fetch.NewFetcher(queryBuilder), handlerConfig.HHGPlanner(), moveRouter, - move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()), + move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf), handlerConfig.NotificationSender(), paymentRequestShipmentRecalculator, addressUpdater, @@ -470,7 +470,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { fetch.NewFetcher(queryBuilder), handlerConfig.HHGPlanner(), moveRouter, - move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()), + move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf), handlerConfig.NotificationSender(), paymentRequestShipmentRecalculator, addressUpdater, @@ -542,7 +542,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { ghcAPI.QueuesListPrimeMovesHandler = ListPrimeMovesHandler{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } ghcAPI.QueuesGetPaymentRequestsQueueHandler = GetPaymentRequestsQueueHandler{ diff --git a/pkg/handlers/ghcapi/move_task_order_test.go b/pkg/handlers/ghcapi/move_task_order_test.go index 10e9630fc44..dfac0e3d47d 100644 --- a/pkg/handlers/ghcapi/move_task_order_test.go +++ b/pkg/handlers/ghcapi/move_task_order_test.go @@ -30,6 +30,7 @@ import ( "github.com/transcom/mymove/pkg/notifications" routemocks "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/ghcrateengine" "github.com/transcom/mymove/pkg/services/mocks" moverouter "github.com/transcom/mymove/pkg/services/move" @@ -44,7 +45,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrderHandlerIntegration() { moveTaskOrder := factory.BuildMove(suite.DB(), nil, nil) factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeMS) factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeCS) - + waf := entitlements.NewWeightAllotmentFetcher() request := httptest.NewRequest("GET", "/move-task-orders/{moveTaskOrderID}", nil) params := movetaskorderops.GetMoveTaskOrderParams{ HTTPRequest: request, @@ -53,7 +54,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrderHandlerIntegration() { handlerConfig := suite.HandlerConfig() handler := GetMoveTaskOrderHandler{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } // Validate incoming payload: no body to validate diff --git a/pkg/handlers/ghcapi/mto_service_items_test.go b/pkg/handlers/ghcapi/mto_service_items_test.go index b1ff7e9c405..0a7d5494275 100644 --- a/pkg/handlers/ghcapi/mto_service_items_test.go +++ b/pkg/handlers/ghcapi/mto_service_items_test.go @@ -20,6 +20,7 @@ import ( routemocks "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services/address" boatshipment "github.com/transcom/mymove/pkg/services/boat_shipment" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" mobilehomeshipment "github.com/transcom/mymove/pkg/services/mobile_home_shipment" @@ -304,7 +305,7 @@ func (suite *HandlerSuite) createServiceItem() (models.MTOServiceItem, models.Mo } func (suite *HandlerSuite) TestUpdateMTOServiceItemStatusHandler() { - + waf := entitlements.NewWeightAllotmentFetcher() builder := query.NewQueryBuilder() fetcher := fetch.NewFetcher(builder) planner := &routemocks.Planner{} @@ -313,7 +314,7 @@ func (suite *HandlerSuite) TestUpdateMTOServiceItemStatusHandler() { mock.Anything, mock.Anything, ).Return(400, nil) - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -730,6 +731,8 @@ func (suite *HandlerSuite) TestGetMTOServiceItemHandler() { func (suite *HandlerSuite) TestUpdateServiceItemSitEntryDateHandler() { serviceItemID := uuid.Must(uuid.FromString("f7b4b9e2-04e8-4c34-827a-df917e69caf4")) + waf := entitlements.NewWeightAllotmentFetcher() + var requestUser models.User newSitEntryDate := time.Date(2023, time.October, 10, 10, 10, 0, 0, time.UTC) @@ -760,7 +763,7 @@ func (suite *HandlerSuite) TestUpdateServiceItemSitEntryDateHandler() { mock.Anything, mock.Anything, ).Return(400, nil) - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) diff --git a/pkg/handlers/ghcapi/mto_shipment_test.go b/pkg/handlers/ghcapi/mto_shipment_test.go index dd60fdf7eee..cf9e0b1e5b0 100644 --- a/pkg/handlers/ghcapi/mto_shipment_test.go +++ b/pkg/handlers/ghcapi/mto_shipment_test.go @@ -24,6 +24,7 @@ import ( "github.com/transcom/mymove/pkg/services" "github.com/transcom/mymove/pkg/services/address" boatshipment "github.com/transcom/mymove/pkg/services/boat_shipment" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" mobilehomeshipment "github.com/transcom/mymove/pkg/services/mobile_home_shipment" @@ -579,6 +580,8 @@ func (suite *HandlerSuite) TestGetShipmentHandler() { } func (suite *HandlerSuite) TestApproveShipmentHandler() { + waf := entitlements.NewWeightAllotmentFetcher() + suite.Run("Returns 200 when all validations pass", func() { move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ @@ -598,7 +601,7 @@ func (suite *HandlerSuite) TestApproveShipmentHandler() { builder := query.NewQueryBuilder() moveRouter := moveservices.NewMoveRouter() planner := &routemocks.Planner{} - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) planner.On("ZipTransitDistance", mock.AnythingOfType("*appcontext.appContext"), mock.Anything, @@ -2091,6 +2094,7 @@ func (suite *HandlerSuite) TestRequestShipmentCancellationHandler() { func (suite *HandlerSuite) TestRequestShipmentReweighHandler() { addressUpdater := address.NewAddressUpdater() addressCreator := address.NewAddressCreator() + waf := entitlements.NewWeightAllotmentFetcher() suite.Run("Returns 200 when all validations pass", func() { move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -2125,7 +2129,7 @@ func (suite *HandlerSuite) TestRequestShipmentReweighHandler() { mock.Anything, ).Return(400, nil) moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -2183,7 +2187,7 @@ func (suite *HandlerSuite) TestRequestShipmentReweighHandler() { mock.Anything, ).Return(400, nil) moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -2238,7 +2242,7 @@ func (suite *HandlerSuite) TestRequestShipmentReweighHandler() { mock.Anything, ).Return(400, nil) moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -2294,7 +2298,7 @@ func (suite *HandlerSuite) TestRequestShipmentReweighHandler() { mock.Anything, ).Return(400, nil) moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -2351,7 +2355,7 @@ func (suite *HandlerSuite) TestRequestShipmentReweighHandler() { mock.Anything, ).Return(400, nil) moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -2407,7 +2411,7 @@ func (suite *HandlerSuite) TestRequestShipmentReweighHandler() { mock.Anything, ).Return(400, nil) moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -2675,6 +2679,8 @@ func (suite *HandlerSuite) TestReviewShipmentAddressUpdateHandler() { } func (suite *HandlerSuite) TestApproveSITExtensionHandler() { + waf := entitlements.NewWeightAllotmentFetcher() + suite.Run("Returns 200 and updates SIT days allowance when validations pass", func() { sitDaysAllowance := 20 move := factory.BuildApprovalsRequestedMove(suite.DB(), []factory.Customization{ @@ -2744,7 +2750,7 @@ func (suite *HandlerSuite) TestApproveSITExtensionHandler() { mock.Anything, mock.Anything, ).Return(400, nil) - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -2866,6 +2872,8 @@ func (suite *HandlerSuite) TestDenySITExtensionHandler() { } func (suite *HandlerSuite) CreateApprovedSITDurationUpdate() { + waf := entitlements.NewWeightAllotmentFetcher() + suite.Run("Returns 200, creates new SIT extension, and updates SIT days allowance on shipment without an allowance when validations pass", func() { mtoShipment := factory.BuildMTOShipment(suite.DB(), nil, nil) @@ -2884,7 +2892,7 @@ func (suite *HandlerSuite) CreateApprovedSITDurationUpdate() { mock.Anything, mock.Anything, ).Return(400, nil) - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -2967,7 +2975,7 @@ func (suite *HandlerSuite) CreateApprovedSITDurationUpdate() { mock.Anything, mock.Anything, ).Return(400, nil) - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) @@ -4022,6 +4030,7 @@ func (suite *HandlerSuite) getUpdateShipmentParams(originalShipment models.MTOSh func (suite *HandlerSuite) TestUpdateShipmentHandler() { addressUpdater := address.NewAddressUpdater() addressCreator := address.NewAddressCreator() + waf := entitlements.NewWeightAllotmentFetcher() planner := &routemocks.Planner{} planner.On("ZipTransitDistance", @@ -4030,7 +4039,7 @@ func (suite *HandlerSuite) TestUpdateShipmentHandler() { mock.Anything, ).Return(400, nil) moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) diff --git a/pkg/handlers/ghcapi/queues_test.go b/pkg/handlers/ghcapi/queues_test.go index e2c0fcb00df..e71499f1c50 100644 --- a/pkg/handlers/ghcapi/queues_test.go +++ b/pkg/handlers/ghcapi/queues_test.go @@ -15,6 +15,7 @@ import ( "github.com/transcom/mymove/pkg/gen/ghcmessages" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/models/roles" + "github.com/transcom/mymove/pkg/services/entitlements" movelocker "github.com/transcom/mymove/pkg/services/lock_move" "github.com/transcom/mymove/pkg/services/mocks" movetaskorder "github.com/transcom/mymove/pkg/services/move_task_order" @@ -110,6 +111,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandler() { func (suite *HandlerSuite) TestListPrimeMovesHandler() { // Default Origin Duty Location GBLOC is KKFA move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) + waf := entitlements.NewWeightAllotmentFetcher() request := httptest.NewRequest("GET", "/queues/listPrimeMoves", nil) params := queues.ListPrimeMovesParams{ @@ -118,7 +120,7 @@ func (suite *HandlerSuite) TestListPrimeMovesHandler() { handlerConfig := suite.HandlerConfig() handler := ListPrimeMovesHandler{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } // Validate incoming payload: no body to validate diff --git a/pkg/handlers/internalapi/api.go b/pkg/handlers/internalapi/api.go index e746987ede7..cbdba27433a 100644 --- a/pkg/handlers/internalapi/api.go +++ b/pkg/handlers/internalapi/api.go @@ -224,7 +224,7 @@ func NewInternalAPI(handlerConfig handlers.HandlerConfig) *internalops.MymoveAPI fetcher, handlerConfig.DTODPlanner(), moveRouter, - move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()), + move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf), handlerConfig.NotificationSender(), paymentRequestShipmentRecalculator, addressUpdater, diff --git a/pkg/handlers/internalapi/mto_shipment_test.go b/pkg/handlers/internalapi/mto_shipment_test.go index 4d2b557286f..5a59d9cbc86 100644 --- a/pkg/handlers/internalapi/mto_shipment_test.go +++ b/pkg/handlers/internalapi/mto_shipment_test.go @@ -22,6 +22,7 @@ import ( "github.com/transcom/mymove/pkg/services" "github.com/transcom/mymove/pkg/services/address" boatshipment "github.com/transcom/mymove/pkg/services/boat_shipment" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" mobilehomeshipment "github.com/transcom/mymove/pkg/services/mobile_home_shipment" @@ -734,6 +735,7 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentHandler() { testMTOShipmentObjects := suite.setUpMTOShipmentObjects() planner := &routemocks.Planner{} + waf := entitlements.NewWeightAllotmentFetcher() planner.On("TransitDistance", mock.AnythingOfType("*appcontext.appContext"), @@ -741,7 +743,7 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentHandler() { mock.Anything, ).Return(400, nil) - moveWeights := moverouter.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moverouter.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) diff --git a/pkg/handlers/primeapi/api.go b/pkg/handlers/primeapi/api.go index c3bb0327668..74f6427b9b6 100644 --- a/pkg/handlers/primeapi/api.go +++ b/pkg/handlers/primeapi/api.go @@ -11,6 +11,7 @@ import ( paperwork "github.com/transcom/mymove/pkg/paperwork" paymentrequesthelper "github.com/transcom/mymove/pkg/payment_request" "github.com/transcom/mymove/pkg/services/address" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" "github.com/transcom/mymove/pkg/services/move" @@ -41,13 +42,15 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primeoperations.MymoveAP if err != nil { log.Fatalln(err) } + waf := entitlements.NewWeightAllotmentFetcher() + primeAPI := primeoperations.NewMymoveAPI(primeSpec) queryBuilder := query.NewQueryBuilder() moveRouter := move.NewMoveRouter() addressCreator := address.NewAddressCreator() portLocationFetcher := portlocation.NewPortLocationFetcher() shipmentFetcher := mtoshipment.NewMTOShipmentFetcher() - moveWeights := move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) uploadCreator := upload.NewUploadCreator(handlerConfig.FileStorer()) ppmEstimator := ppmshipment.NewEstimatePPM(handlerConfig.DTODPlanner(), &paymentrequesthelper.RequestPaymentHelper{}) serviceItemUpdater := mtoserviceitem.NewMTOServiceItemUpdater(handlerConfig.HHGPlanner(), queryBuilder, moveRouter, shipmentFetcher, addressCreator, portLocationFetcher) @@ -79,12 +82,12 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primeoperations.MymoveAP primeAPI.MoveTaskOrderListMovesHandler = ListMovesHandler{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } primeAPI.MoveTaskOrderGetMoveTaskOrderHandler = GetMoveTaskOrderHandler{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } primeAPI.MoveTaskOrderCreateExcessWeightRecordHandler = CreateExcessWeightRecordHandler{ diff --git a/pkg/handlers/primeapi/move_task_order_test.go b/pkg/handlers/primeapi/move_task_order_test.go index db6d45244aa..ad5f82247fd 100644 --- a/pkg/handlers/primeapi/move_task_order_test.go +++ b/pkg/handlers/primeapi/move_task_order_test.go @@ -21,6 +21,7 @@ import ( "github.com/transcom/mymove/pkg/models" routemocks "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" "github.com/transcom/mymove/pkg/services/mocks" @@ -36,6 +37,8 @@ import ( ) func (suite *HandlerSuite) TestListMovesHandler() { + waf := entitlements.NewWeightAllotmentFetcher() + suite.Run("Test returns updated with no amendments count", func() { now := time.Now() lastFetch := now.Add(-time.Second) @@ -59,7 +62,7 @@ func (suite *HandlerSuite) TestListMovesHandler() { // Validate incoming payload: no body to validate // make the request - handler := ListMovesHandler{HandlerConfig: handlerConfig, MoveTaskOrderFetcher: movetaskorder.NewMoveTaskOrderFetcher()} + handler := ListMovesHandler{HandlerConfig: handlerConfig, MoveTaskOrderFetcher: movetaskorder.NewMoveTaskOrderFetcher(waf)} response := handler.Handle(params) suite.IsNotErrResponse(response) @@ -131,7 +134,7 @@ func (suite *HandlerSuite) TestListMovesHandler() { // Validate incoming payload: no body to validate // make the request - handler := ListMovesHandler{HandlerConfig: handlerConfig, MoveTaskOrderFetcher: movetaskorder.NewMoveTaskOrderFetcher()} + handler := ListMovesHandler{HandlerConfig: handlerConfig, MoveTaskOrderFetcher: movetaskorder.NewMoveTaskOrderFetcher(waf)} response := handler.Handle(params) suite.IsNotErrResponse(response) @@ -150,6 +153,7 @@ func (suite *HandlerSuite) TestListMovesHandler() { func (suite *HandlerSuite) TestGetMoveTaskOrder() { request := httptest.NewRequest("GET", "/move-task-orders/{moveTaskOrderID}", nil) + waf := entitlements.NewWeightAllotmentFetcher() verifyAddressFields := func(address *models.Address, payload *primemessages.Address) { suite.Equal(address.ID.String(), payload.ID.String()) @@ -169,7 +173,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success with Prime-available move by ID", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -200,7 +204,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success with Prime-available move by Locator", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -230,7 +234,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success returns reweighs on shipments if they exist", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -284,7 +288,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns sit extensions on shipments if they exist", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -342,7 +346,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - filters shipments handled by an external vendor", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -397,7 +401,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns shipment with attached PpmShipment", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) ppmShipment := factory.BuildPPMShipment(suite.DB(), []factory.Customization{ @@ -433,7 +437,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { // This tests fields that aren't other structs and Addresses handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) destinationAddress := factory.BuildAddress(suite.DB(), nil, nil) @@ -548,7 +552,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns all the fields associated with StorageFacility within MtoShipments", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -604,7 +608,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns all the fields associated with Agents within MtoShipments", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -655,7 +659,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all base fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } now := time.Now() aWeekAgo := now.AddDate(0, 0, -7) @@ -707,7 +711,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all Order fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } currentAddress := factory.BuildAddress(suite.DB(), nil, nil) successMove := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ @@ -797,7 +801,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all PaymentRequests fields associated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -999,7 +1003,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemBasic fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1078,7 +1082,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemOriginSIT fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1192,7 +1196,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemDestSIT fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1317,7 +1321,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemShuttle fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1403,7 +1407,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemDomesticCrating fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1531,7 +1535,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Failure 'Not Found' for non-available move", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } failureMove := factory.BuildMove(suite.DB(), nil, nil) // default is not available to Prime params := movetaskorderops.GetMoveTaskOrderParams{ diff --git a/pkg/handlers/primeapi/mto_shipment_test.go b/pkg/handlers/primeapi/mto_shipment_test.go index 5b602de7873..304719639dd 100644 --- a/pkg/handlers/primeapi/mto_shipment_test.go +++ b/pkg/handlers/primeapi/mto_shipment_test.go @@ -22,6 +22,7 @@ import ( routemocks "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services" "github.com/transcom/mymove/pkg/services/address" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" "github.com/transcom/mymove/pkg/services/mocks" @@ -199,6 +200,8 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentStatusHandler() { builder := query.NewQueryBuilder() fetcher := fetch.NewFetcher(builder) planner := &routemocks.Planner{} + waf := entitlements.NewWeightAllotmentFetcher() + planner.On("ZipTransitDistance", mock.AnythingOfType("*appcontext.appContext"), mock.Anything, @@ -207,7 +210,7 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentStatusHandler() { moveRouter := moveservices.NewMoveRouter() addressUpdater := address.NewAddressUpdater() addressCreator := address.NewAddressCreator() - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) // Get shipment payment request recalculator service creator := paymentrequest.NewPaymentRequestCreator(planner, ghcrateengine.NewServiceItemPricer()) statusUpdater := paymentrequest.NewPaymentRequestStatusUpdater(query.NewQueryBuilder()) diff --git a/pkg/handlers/primeapiv2/api.go b/pkg/handlers/primeapiv2/api.go index dedf3e218ac..2d22387e986 100644 --- a/pkg/handlers/primeapiv2/api.go +++ b/pkg/handlers/primeapiv2/api.go @@ -11,6 +11,7 @@ import ( paymentrequesthelper "github.com/transcom/mymove/pkg/payment_request" "github.com/transcom/mymove/pkg/services/address" boatshipment "github.com/transcom/mymove/pkg/services/boat_shipment" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" mobilehomeshipment "github.com/transcom/mymove/pkg/services/mobile_home_shipment" @@ -31,6 +32,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primev2operations.Mymove fetcher := fetch.NewFetcher(builder) queryBuilder := query.NewQueryBuilder() moveRouter := move.NewMoveRouter() + waf := entitlements.NewWeightAllotmentFetcher() primeSpec, err := loads.Analyzed(primev2api.SwaggerJSON, "") if err != nil { @@ -44,7 +46,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primev2operations.Mymove primeAPIV2.MoveTaskOrderGetMoveTaskOrderHandler = GetMoveTaskOrderHandler{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } signedCertificationCreator := signedcertification.NewSignedCertificationCreator() @@ -78,7 +80,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primev2operations.Mymove paymentrequest.NewPaymentRequestStatusUpdater(queryBuilder), ) paymentRequestShipmentRecalculator := paymentrequest.NewPaymentRequestShipmentRecalculator(paymentRequestRecalculator) - moveWeights := move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) addressUpdater := address.NewAddressUpdater() mtoShipmentUpdater := mtoshipment.NewPrimeMTOShipmentUpdater( builder, diff --git a/pkg/handlers/primeapiv2/move_task_order_test.go b/pkg/handlers/primeapiv2/move_task_order_test.go index 9636b898359..9d736c5b8d5 100644 --- a/pkg/handlers/primeapiv2/move_task_order_test.go +++ b/pkg/handlers/primeapiv2/move_task_order_test.go @@ -13,6 +13,7 @@ import ( "github.com/transcom/mymove/pkg/gen/primev2messages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services/entitlements" movetaskorder "github.com/transcom/mymove/pkg/services/move_task_order" "github.com/transcom/mymove/pkg/testdatagen" "github.com/transcom/mymove/pkg/unit" @@ -20,6 +21,7 @@ import ( func (suite *HandlerSuite) TestGetMoveTaskOrder() { request := httptest.NewRequest("GET", "/move-task-orders/{moveTaskOrderID}", nil) + waf := entitlements.NewWeightAllotmentFetcher() verifyAddressFields := func(address *models.Address, payload *primev2messages.Address) { suite.Equal(address.ID.String(), payload.ID.String()) @@ -39,7 +41,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success with Prime-available move by ID", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -70,7 +72,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success with Prime-available move by Locator", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -100,7 +102,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success returns reweighs on shipments if they exist", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -154,7 +156,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns sit extensions on shipments if they exist", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -212,7 +214,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - filters shipments handled by an external vendor", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -267,7 +269,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns shipment with attached PpmShipment", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) ppmShipment := factory.BuildPPMShipment(suite.DB(), []factory.Customization{ @@ -303,7 +305,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { // This tests fields that aren't other structs and Addresses handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) destinationAddress := factory.BuildAddress(suite.DB(), nil, nil) @@ -418,7 +420,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns all the fields associated with StorageFacility within MtoShipments", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -474,7 +476,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns all the fields associated with Agents within MtoShipments", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -525,7 +527,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all base fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } now := time.Now() aWeekAgo := now.AddDate(0, 0, -7) @@ -578,7 +580,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all Order fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } currentAddress := factory.BuildAddress(suite.DB(), nil, nil) successMove := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ @@ -673,7 +675,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all PaymentRequests fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -862,7 +864,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemBasic fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -941,7 +943,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemOriginSIT fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1055,7 +1057,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemDestSIT fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1180,7 +1182,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemShuttle fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1266,7 +1268,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemDomesticCrating fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1394,7 +1396,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Failure 'Not Found' for non-available move", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } failureMove := factory.BuildMove(suite.DB(), nil, nil) // default is not available to Prime params := movetaskorderops.GetMoveTaskOrderParams{ diff --git a/pkg/handlers/primeapiv3/api.go b/pkg/handlers/primeapiv3/api.go index e9c522f8d42..f1548e30d52 100644 --- a/pkg/handlers/primeapiv3/api.go +++ b/pkg/handlers/primeapiv3/api.go @@ -11,6 +11,7 @@ import ( paymentrequesthelper "github.com/transcom/mymove/pkg/payment_request" "github.com/transcom/mymove/pkg/services/address" boatshipment "github.com/transcom/mymove/pkg/services/boat_shipment" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" mobilehomeshipment "github.com/transcom/mymove/pkg/services/mobile_home_shipment" @@ -31,6 +32,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primev3operations.Mymove fetcher := fetch.NewFetcher(builder) queryBuilder := query.NewQueryBuilder() moveRouter := move.NewMoveRouter() + waf := entitlements.NewWeightAllotmentFetcher() primeSpec, err := loads.Analyzed(primev3api.SwaggerJSON, "") if err != nil { @@ -45,7 +47,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primev3operations.Mymove primeAPIV3.MoveTaskOrderGetMoveTaskOrderHandler = GetMoveTaskOrderHandler{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } signedCertificationCreator := signedcertification.NewSignedCertificationCreator() @@ -79,7 +81,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primev3operations.Mymove paymentrequest.NewPaymentRequestStatusUpdater(queryBuilder), ) paymentRequestShipmentRecalculator := paymentrequest.NewPaymentRequestShipmentRecalculator(paymentRequestRecalculator) - moveWeights := move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := move.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) mtoShipmentUpdater := mtoshipment.NewPrimeMTOShipmentUpdater( builder, fetcher, diff --git a/pkg/handlers/primeapiv3/move_task_order_test.go b/pkg/handlers/primeapiv3/move_task_order_test.go index cb76222e7ad..77649212c4a 100644 --- a/pkg/handlers/primeapiv3/move_task_order_test.go +++ b/pkg/handlers/primeapiv3/move_task_order_test.go @@ -13,6 +13,7 @@ import ( "github.com/transcom/mymove/pkg/gen/primev3messages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services/entitlements" movetaskorder "github.com/transcom/mymove/pkg/services/move_task_order" "github.com/transcom/mymove/pkg/testdatagen" "github.com/transcom/mymove/pkg/unit" @@ -20,6 +21,7 @@ import ( func (suite *HandlerSuite) TestGetMoveTaskOrder() { request := httptest.NewRequest("GET", "/move-task-orders/{moveTaskOrderID}", nil) + waf := entitlements.NewWeightAllotmentFetcher() verifyAddressFields := func(address *models.Address, payload *primev3messages.Address) { suite.Equal(address.ID.String(), payload.ID.String()) @@ -39,7 +41,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success with Prime-available move by ID", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -68,7 +70,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success with Prime-available move by Locator", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -96,7 +98,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success returns reweighs on shipments if they exist", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -148,7 +150,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns sit extensions on shipments if they exist", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -206,7 +208,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - filters shipments handled by an external vendor", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -261,7 +263,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns shipment with attached PpmShipment", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) ppmShipment := factory.BuildPPMShipment(suite.DB(), []factory.Customization{ @@ -297,7 +299,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { // This tests fields that aren't other structs and Addresses handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) destinationAddress := factory.BuildAddress(suite.DB(), nil, nil) @@ -424,7 +426,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns all the fields associated with StorageFacility within MtoShipments", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -480,7 +482,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - returns all the fields associated with Agents within MtoShipments", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) params := movetaskorderops.GetMoveTaskOrderParams{ @@ -531,7 +533,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all base fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } now := time.Now() aWeekAgo := now.AddDate(0, 0, -7) @@ -583,7 +585,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all Order fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } currentAddress := factory.BuildAddress(suite.DB(), nil, nil) successMove := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ @@ -678,7 +680,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all PaymentRequests fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -867,7 +869,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemBasic fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -946,7 +948,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemOriginSIT fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1060,7 +1062,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemDestSIT fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1185,7 +1187,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemShuttle fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1271,7 +1273,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Success - return all MTOServiceItemDomesticCrating fields assoicated with the getMoveTaskOrder", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } successMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -1399,7 +1401,7 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { suite.Run("Failure 'Not Found' for non-available move", func() { handler := GetMoveTaskOrderHandler{ suite.HandlerConfig(), - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } failureMove := factory.BuildMove(suite.DB(), nil, nil) // default is not available to Prime params := movetaskorderops.GetMoveTaskOrderParams{ diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index c0443c77eb2..29e5b4e6589 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -24,6 +24,7 @@ import ( "github.com/transcom/mymove/pkg/services" "github.com/transcom/mymove/pkg/services/address" boatshipment "github.com/transcom/mymove/pkg/services/boat_shipment" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" mobilehomeshipment "github.com/transcom/mymove/pkg/services/mobile_home_shipment" @@ -92,6 +93,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { ) shipmentCreator := shipmentorchestrator.NewShipmentCreator(mtoShipmentCreator, ppmShipmentCreator, boatShipmentCreator, mobileHomeShipmentCreator, shipmentRouter, moveTaskOrderUpdater) mockCreator := mocks.ShipmentCreator{} + waf := entitlements.NewWeightAllotmentFetcher() var pickupAddress primev3messages.Address var secondaryPickupAddress primev3messages.Address @@ -104,7 +106,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { statusUpdater := paymentrequest.NewPaymentRequestStatusUpdater(query.NewQueryBuilder()) recalculator := paymentrequest.NewPaymentRequestRecalculator(creator, statusUpdater) paymentRequestShipmentRecalculator := paymentrequest.NewPaymentRequestShipmentRecalculator(recalculator) - moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) ppmShipmentUpdater := ppmshipment.NewPPMShipmentUpdater(&ppmEstimator, addressCreator, addressUpdater) boatShipmentUpdater := boatshipment.NewBoatShipmentUpdater() mobileHomeShipmentUpdater := mobilehomeshipment.NewMobileHomeShipmentUpdater() diff --git a/pkg/handlers/supportapi/api.go b/pkg/handlers/supportapi/api.go index 54a06b50e92..f9970af0553 100644 --- a/pkg/handlers/supportapi/api.go +++ b/pkg/handlers/supportapi/api.go @@ -12,6 +12,7 @@ import ( "github.com/transcom/mymove/pkg/handlers" paymentrequesthelper "github.com/transcom/mymove/pkg/payment_request" "github.com/transcom/mymove/pkg/services/address" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" "github.com/transcom/mymove/pkg/services/invoice" @@ -40,6 +41,7 @@ func NewSupportAPIHandler(handlerConfig handlers.HandlerConfig) http.Handler { if err != nil { log.Fatalln(err) } + waf := entitlements.NewWeightAllotmentFetcher() supportAPI := supportops.NewMymoveAPI(supportSpec) @@ -47,7 +49,7 @@ func NewSupportAPIHandler(handlerConfig handlers.HandlerConfig) http.Handler { supportAPI.MoveTaskOrderListMTOsHandler = ListMTOsHandler{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } signedCertificationCreator := signedcertification.NewSignedCertificationCreator() @@ -69,7 +71,7 @@ func NewSupportAPIHandler(handlerConfig handlers.HandlerConfig) http.Handler { supportAPI.MoveTaskOrderGetMoveTaskOrderHandler = GetMoveTaskOrderHandlerFunc{ handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher()} + movetaskorder.NewMoveTaskOrderFetcher(waf)} supportAPI.MoveTaskOrderCreateMoveTaskOrderHandler = CreateMoveTaskOrderHandler{ handlerConfig, diff --git a/pkg/handlers/supportapi/move_task_order_test.go b/pkg/handlers/supportapi/move_task_order_test.go index f10fde1438e..56e40a5f430 100644 --- a/pkg/handlers/supportapi/move_task_order_test.go +++ b/pkg/handlers/supportapi/move_task_order_test.go @@ -18,6 +18,7 @@ import ( "github.com/transcom/mymove/pkg/models" routemocks "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/ghcrateengine" "github.com/transcom/mymove/pkg/services/mocks" moverouter "github.com/transcom/mymove/pkg/services/move" @@ -31,6 +32,7 @@ import ( func (suite *HandlerSuite) TestListMTOsHandler() { // unavailable MTO factory.BuildMove(suite.DB(), nil, nil) + waf := entitlements.NewWeightAllotmentFetcher() moveTaskOrder := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -55,7 +57,7 @@ func (suite *HandlerSuite) TestListMTOsHandler() { handler := ListMTOsHandler{ HandlerConfig: handlerConfig, - MoveTaskOrderFetcher: movetaskorder.NewMoveTaskOrderFetcher(), + MoveTaskOrderFetcher: movetaskorder.NewMoveTaskOrderFetcher(waf), } response := handler.Handle(params) @@ -225,10 +227,11 @@ func (suite *HandlerSuite) TestGetMoveTaskOrder() { HTTPRequest: request, MoveTaskOrderID: move.ID.String(), } + waf := entitlements.NewWeightAllotmentFetcher() handlerConfig := suite.HandlerConfig() handler := GetMoveTaskOrderHandlerFunc{handlerConfig, - movetaskorder.NewMoveTaskOrderFetcher(), + movetaskorder.NewMoveTaskOrderFetcher(waf), } response := handler.Handle(params) suite.IsNotErrResponse(response) diff --git a/pkg/services/move/move_weights.go b/pkg/services/move/move_weights.go index 3d9adc5d12a..3155c0989d7 100644 --- a/pkg/services/move/move_weights.go +++ b/pkg/services/move/move_weights.go @@ -28,8 +28,8 @@ type moveWeights struct { } // NewMoveWeights creates a new moveWeights service -func NewMoveWeights(reweighRequestor services.ShipmentReweighRequester) services.MoveWeights { - return &moveWeights{ReweighRequestor: reweighRequestor} +func NewMoveWeights(reweighRequestor services.ShipmentReweighRequester, weightAllotmentFetcher services.WeightAllotmentFetcher) services.MoveWeights { + return &moveWeights{ReweighRequestor: reweighRequestor, WeightAllotmentFetcher: weightAllotmentFetcher} } func validateAndSave(appCtx appcontext.AppContext, move *models.Move) (*validate.Errors, error) { diff --git a/pkg/services/move/move_weights_test.go b/pkg/services/move/move_weights_test.go index 6ae571c088c..864e025749e 100644 --- a/pkg/services/move/move_weights_test.go +++ b/pkg/services/move/move_weights_test.go @@ -8,6 +8,7 @@ import ( "github.com/transcom/mymove/pkg/auth" "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/mocks" mtoshipment "github.com/transcom/mymove/pkg/services/mto_shipment" "github.com/transcom/mymove/pkg/testdatagen" @@ -15,7 +16,9 @@ import ( ) func (suite *MoveServiceSuite) TestExcessWeight() { - moveWeights := NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + waf := entitlements.NewWeightAllotmentFetcher() + + moveWeights := NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) suite.Run("qualifies move for excess weight when an approved shipment estimated weight is updated within threshold", func() { // The default weight allotment for this move is 8000 and the threshold is 90% of that @@ -363,9 +366,10 @@ func (suite *MoveServiceSuite) TestExcessWeight() { } func (suite *MoveServiceSuite) TestAutoReweigh() { - moveWeights := NewMoveWeights(mtoshipment.NewShipmentReweighRequester()) + waf := entitlements.NewWeightAllotmentFetcher() + moveWeights := NewMoveWeights(mtoshipment.NewShipmentReweighRequester(), waf) - suite.Run("requests reweigh on shipment if the acutal weight is 90% of the weight allowance", func() { + suite.Run("requests reweigh on shipment if the actual weight is 90% of the weight allowance", func() { // The default weight allotment for this move is 8000 and the threshold is 90% of that approvedMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) now := time.Now() @@ -403,7 +407,7 @@ func (suite *MoveServiceSuite) TestAutoReweigh() { suite.Run("does not request reweigh on shipments when below 90% of weight allowance threshold", func() { mockedReweighRequestor := mocks.ShipmentReweighRequester{} - mockedWeightService := NewMoveWeights(&mockedReweighRequestor) + mockedWeightService := NewMoveWeights(&mockedReweighRequestor, waf) approvedMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -494,7 +498,7 @@ func (suite *MoveServiceSuite) TestAutoReweigh() { suite.Run("does not request reweigh when shipments aren't in approved statuses", func() { mockedReweighRequestor := mocks.ShipmentReweighRequester{} - mockedWeightService := NewMoveWeights(&mockedReweighRequestor) + mockedWeightService := NewMoveWeights(&mockedReweighRequestor, waf) approvedMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) now := time.Now() @@ -543,7 +547,7 @@ func (suite *MoveServiceSuite) TestAutoReweigh() { suite.Run("uses lower reweigh weight on shipments that already have reweighs", func() { mockedReweighRequestor := mocks.ShipmentReweighRequester{} - mockedWeightService := NewMoveWeights(&mockedReweighRequestor) + mockedWeightService := NewMoveWeights(&mockedReweighRequestor, waf) approvedMove := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) now := time.Now() diff --git a/pkg/services/move_task_order/move_task_order_fetcher.go b/pkg/services/move_task_order/move_task_order_fetcher.go index 63ff6585891..a53bd4f62c2 100644 --- a/pkg/services/move_task_order/move_task_order_fetcher.go +++ b/pkg/services/move_task_order/move_task_order_fetcher.go @@ -16,7 +16,6 @@ import ( "github.com/transcom/mymove/pkg/cli" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" - "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/featureflag" ) @@ -25,10 +24,9 @@ type moveTaskOrderFetcher struct { } // NewMoveTaskOrderFetcher creates a new struct with the service dependencies -func NewMoveTaskOrderFetcher() services.MoveTaskOrderFetcher { - waf := entitlements.NewWeightAllotmentFetcher() +func NewMoveTaskOrderFetcher(weightAllotmentFetcher services.WeightAllotmentFetcher) services.MoveTaskOrderFetcher { return &moveTaskOrderFetcher{ - waf: waf, + waf: weightAllotmentFetcher, } } diff --git a/pkg/services/move_task_order/move_task_order_fetcher_test.go b/pkg/services/move_task_order/move_task_order_fetcher_test.go index 9ba9f7a8ba2..d209a6604de 100644 --- a/pkg/services/move_task_order/move_task_order_fetcher_test.go +++ b/pkg/services/move_task_order/move_task_order_fetcher_test.go @@ -11,12 +11,14 @@ import ( "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" m "github.com/transcom/mymove/pkg/services/move_task_order" "github.com/transcom/mymove/pkg/testdatagen" "github.com/transcom/mymove/pkg/uploader" ) func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderFetcher() { + waf := entitlements.NewWeightAllotmentFetcher() setupTestData := func() (models.Move, models.MTOShipment) { @@ -61,7 +63,7 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderFetcher() { return expectedMTO, primeShipment } - mtoFetcher := m.NewMoveTaskOrderFetcher() + mtoFetcher := m.NewMoveTaskOrderFetcher(waf) suite.Run("Success with fetching a MTO that has a shipment address update", func() { traits := []factory.Trait{factory.GetTraitShipmentAddressUpdateApproved} @@ -480,8 +482,9 @@ func (suite *MoveTaskOrderServiceSuite) TestGetMoveTaskOrderFetcher() { return expectedMTO } + waf := entitlements.NewWeightAllotmentFetcher() - mtoFetcher := m.NewMoveTaskOrderFetcher() + mtoFetcher := m.NewMoveTaskOrderFetcher(waf) suite.Run("success getting a move using GetMove for Prime user", func() { expectedMTO := setupTestData() @@ -609,6 +612,8 @@ func (suite *MoveTaskOrderServiceSuite) TestListAllMoveTaskOrdersFetcher() { // Set up a hidden move so we can check if it's in the output: now := time.Now() show := false + waf := entitlements.NewWeightAllotmentFetcher() + setupTestData := func() (models.Move, models.Move, models.MTOShipment) { hiddenMTO := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ { @@ -647,7 +652,7 @@ func (suite *MoveTaskOrderServiceSuite) TestListAllMoveTaskOrdersFetcher() { return hiddenMTO, mto, primeShipment } - mtoFetcher := m.NewMoveTaskOrderFetcher() + mtoFetcher := m.NewMoveTaskOrderFetcher(waf) suite.Run("all move task orders", func() { hiddenMTO, mto, _ := setupTestData() @@ -754,6 +759,7 @@ func (suite *MoveTaskOrderServiceSuite) TestListAllMoveTaskOrdersFetcher() { func (suite *MoveTaskOrderServiceSuite) TestListPrimeMoveTaskOrdersFetcher() { now := time.Now() + waf := entitlements.NewWeightAllotmentFetcher() // Set up a hidden move so we can check if it's in the output: hiddenMove := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ { @@ -783,7 +789,7 @@ func (suite *MoveTaskOrderServiceSuite) TestListPrimeMoveTaskOrdersFetcher() { suite.Require().NoError(suite.DB().RawQuery("UPDATE mto_shipments SET updated_at=$1 WHERE id=$2;", now.Add(-10*time.Second), shipmentForPrimeMove4.ID).Exec()) - fetcher := m.NewMoveTaskOrderFetcher() + fetcher := m.NewMoveTaskOrderFetcher(waf) page := int64(1) perPage := int64(20) // filling out search params to allow for pagination @@ -816,6 +822,8 @@ func (suite *MoveTaskOrderServiceSuite) TestListPrimeMoveTaskOrdersFetcher() { } func (suite *MoveTaskOrderServiceSuite) TestListPrimeMoveTaskOrdersAmendmentsFetcher() { + waf := entitlements.NewWeightAllotmentFetcher() + suite.Run("Test with and without filter of moves containing amendments", func() { now := time.Now() // Set up a hidden move so we can check if it's in the output: @@ -899,7 +907,7 @@ func (suite *MoveTaskOrderServiceSuite) TestListPrimeMoveTaskOrdersAmendmentsFet suite.Require().NoError(suite.DB().RawQuery("UPDATE mto_shipments SET updated_at=$1 WHERE id=$2;", now.Add(-10*time.Second), shipmentForPrimeMove4.ID).Exec()) - fetcher := m.NewMoveTaskOrderFetcher() + fetcher := m.NewMoveTaskOrderFetcher(waf) page := int64(1) perPage := int64(20) // filling out search params to allow for pagination @@ -975,7 +983,7 @@ func (suite *MoveTaskOrderServiceSuite) TestListPrimeMoveTaskOrdersAmendmentsFet suite.Require().NoError(suite.DB().RawQuery("UPDATE moves SET updated_at=$1 WHERE id IN ($2, $3);", now.Add(-10*time.Second), primeMove1.ID, primeMove2.ID).Exec()) - fetcher := m.NewMoveTaskOrderFetcher() + fetcher := m.NewMoveTaskOrderFetcher(waf) page := int64(1) perPage := int64(20) // filling out search params to allow for pagination diff --git a/pkg/services/mto_shipment/mto_shipment_updater_test.go b/pkg/services/mto_shipment/mto_shipment_updater_test.go index aabab4f1a16..cd54982ea6a 100644 --- a/pkg/services/mto_shipment/mto_shipment_updater_test.go +++ b/pkg/services/mto_shipment/mto_shipment_updater_test.go @@ -19,6 +19,7 @@ import ( notificationMocks "github.com/transcom/mymove/pkg/notifications/mocks" "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services/address" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/fetch" "github.com/transcom/mymove/pkg/services/ghcrateengine" mockservices "github.com/transcom/mymove/pkg/services/mocks" @@ -52,7 +53,8 @@ func (suite *MTOShipmentServiceSuite) TestMTOShipmentUpdater() { mock.Anything, ).Return(1000, nil) moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(NewShipmentReweighRequester()) + waf := entitlements.NewWeightAllotmentFetcher() + moveWeights := moveservices.NewMoveWeights(NewShipmentReweighRequester(), waf) mockShipmentRecalculator := mockservices.PaymentRequestShipmentRecalculator{} mockShipmentRecalculator.On("ShipmentRecalculatePaymentRequest", mock.AnythingOfType("*appcontext.appContext"), @@ -2372,6 +2374,8 @@ func (suite *MTOShipmentServiceSuite) TestUpdateMTOShipmentStatus() { func (suite *MTOShipmentServiceSuite) TestMTOShipmentsMTOAvailableToPrime() { now := time.Now() + waf := entitlements.NewWeightAllotmentFetcher() + hide := false var primeShipment models.MTOShipment var nonPrimeShipment models.MTOShipment @@ -2402,7 +2406,7 @@ func (suite *MTOShipmentServiceSuite) TestMTOShipmentsMTOAvailableToPrime() { fetcher := fetch.NewFetcher(builder) planner := &mocks.Planner{} moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(NewShipmentReweighRequester(), waf) mockShipmentRecalculator := mockservices.PaymentRequestShipmentRecalculator{} mockShipmentRecalculator.On("ShipmentRecalculatePaymentRequest", mock.AnythingOfType("*appcontext.appContext"), @@ -2470,8 +2474,10 @@ func (suite *MTOShipmentServiceSuite) TestUpdateShipmentEstimatedWeightMoveExces builder := query.NewQueryBuilder() fetcher := fetch.NewFetcher(builder) planner := &mocks.Planner{} + waf := entitlements.NewWeightAllotmentFetcher() + moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(NewShipmentReweighRequester(), waf) mockShipmentRecalculator := mockservices.PaymentRequestShipmentRecalculator{} mockShipmentRecalculator.On("ShipmentRecalculatePaymentRequest", mock.AnythingOfType("*appcontext.appContext"), @@ -2651,10 +2657,12 @@ func (suite *MTOShipmentServiceSuite) TestUpdateShipmentEstimatedWeightMoveExces func (suite *MTOShipmentServiceSuite) TestUpdateShipmentActualWeightAutoReweigh() { builder := query.NewQueryBuilder() + waf := entitlements.NewWeightAllotmentFetcher() + fetcher := fetch.NewFetcher(builder) planner := &mocks.Planner{} moveRouter := moveservices.NewMoveRouter() - moveWeights := moveservices.NewMoveWeights(NewShipmentReweighRequester()) + moveWeights := moveservices.NewMoveWeights(NewShipmentReweighRequester(), waf) mockShipmentRecalculator := mockservices.PaymentRequestShipmentRecalculator{} mockShipmentRecalculator.On("ShipmentRecalculatePaymentRequest", mock.AnythingOfType("*appcontext.appContext"), diff --git a/pkg/services/mto_shipment/shipment_approver_test.go b/pkg/services/mto_shipment/shipment_approver_test.go index 943cd4edb72..81f81ef4f64 100644 --- a/pkg/services/mto_shipment/shipment_approver_test.go +++ b/pkg/services/mto_shipment/shipment_approver_test.go @@ -17,6 +17,7 @@ import ( "github.com/transcom/mymove/pkg/route" "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" "github.com/transcom/mymove/pkg/services/ghcrateengine" shipmentmocks "github.com/transcom/mymove/pkg/services/mocks" moverouter "github.com/transcom/mymove/pkg/services/move" @@ -85,6 +86,7 @@ func (suite *MTOShipmentServiceSuite) createApproveShipmentSubtestData() (subtes router := NewShipmentRouter() builder := query.NewQueryBuilder() + waf := entitlements.NewWeightAllotmentFetcher() moveRouter := moverouter.NewMoveRouter() planner := &mocks.Planner{} planner.On("ZipTransitDistance", @@ -94,7 +96,7 @@ func (suite *MTOShipmentServiceSuite) createApproveShipmentSubtestData() (subtes ).Return(400, nil) siCreator := mtoserviceitem.NewMTOServiceItemCreator(planner, builder, moveRouter, ghcrateengine.NewDomesticUnpackPricer(), ghcrateengine.NewDomesticPackPricer(), ghcrateengine.NewDomesticLinehaulPricer(), ghcrateengine.NewDomesticShorthaulPricer(), ghcrateengine.NewDomesticOriginPricer(), ghcrateengine.NewDomesticDestinationPricer(), ghcrateengine.NewFuelSurchargePricer()) subtestData.planner = &mocks.Planner{} - subtestData.moveWeights = moverouter.NewMoveWeights(NewShipmentReweighRequester()) + subtestData.moveWeights = moverouter.NewMoveWeights(NewShipmentReweighRequester(), waf) subtestData.shipmentApprover = NewShipmentApprover(router, siCreator, subtestData.planner, subtestData.moveWeights) subtestData.mockedShipmentApprover = NewShipmentApprover(subtestData.mockedShipmentRouter, siCreator, subtestData.planner, subtestData.moveWeights) diff --git a/pkg/services/sit_extension/sit_extension_creator_test.go b/pkg/services/sit_extension/sit_extension_creator_test.go index 9a3a15afc98..04c53f04d87 100644 --- a/pkg/services/sit_extension/sit_extension_creator_test.go +++ b/pkg/services/sit_extension/sit_extension_creator_test.go @@ -7,6 +7,7 @@ import ( "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" moverouter "github.com/transcom/mymove/pkg/services/move" movefetcher "github.com/transcom/mymove/pkg/services/move_task_order" ) @@ -16,7 +17,8 @@ func (suite *SitExtensionServiceSuite) TestSITExtensionCreator() { // Create move router for SitExtension Createor moveRouter := moverouter.NewMoveRouter() sitExtensionCreator := NewSitExtensionCreator(moveRouter) - movefetcher := movefetcher.NewMoveTaskOrderFetcher() + waf := entitlements.NewWeightAllotmentFetcher() + movefetcher := movefetcher.NewMoveTaskOrderFetcher(waf) suite.Run("Success - CreateSITExtension with no status passed in", func() { // Under test: CreateSITExtension From 8e3a7b7779212e605e9115d52687e83b007cdab1 Mon Sep 17 00:00:00 2001 From: Tae Jung Date: Mon, 6 Jan 2025 17:51:25 +0000 Subject: [PATCH 048/113] gen --- pkg/gen/ghcapi/embedded_spec.go | 404 +++++++++++++++++++++++++++ pkg/gen/internalapi/embedded_spec.go | 398 ++++++++++++++++++++++++++ pkg/gen/primeapi/embedded_spec.go | 140 +++++++++- pkg/gen/primev2api/embedded_spec.go | 20 +- pkg/gen/primev3api/embedded_spec.go | 272 +++++++++++++++++- swagger/ghc.yaml | 173 ++++++++++++ swagger/internal.yaml | 172 ++++++++++++ swagger/prime.yaml | 77 +++++ swagger/prime_v2.yaml | 6 + swagger/prime_v3.yaml | 114 ++++++++ 10 files changed, 1758 insertions(+), 18 deletions(-) diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 5268aa78f23..922ac889187 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -36,6 +36,44 @@ func init() { }, "basePath": "/ghc/v1", "paths": { + "/addresses/zip-city-lookup/{search}": { + "get": { + "description": "Find by API using full/partial postal code or city name that returns an us_post_region_cities json object containing city, state, county and postal code.", + "tags": [ + "addresses" + ], + "summary": "Returns city, state, postal code, and county associated with the specified full/partial postal code or city and state string", + "operationId": "getLocationByZipCityState", + "parameters": [ + { + "type": "string", + "name": "search", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "the requested list of city, state, county, and postal code matches", + "schema": { + "$ref": "#/definitions/VLocations" + } + }, + "400": { + "$ref": "#/responses/InvalidRequest" + }, + "403": { + "$ref": "#/responses/PermissionDenied" + }, + "404": { + "$ref": "#/responses/NotFound" + }, + "500": { + "$ref": "#/responses/ServerError" + } + } + } + }, "/application_parameters/{parameterName}": { "get": { "description": "Searches for an application parameter by name, returns nil if not found", @@ -6513,6 +6551,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -11351,6 +11394,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -14677,6 +14725,151 @@ func init() { } } }, + "VLocation": { + "description": "A postal code, city, and state lookup", + "type": "object", + "properties": { + "city": { + "type": "string", + "title": "City", + "example": "Anytown" + }, + "county": { + "type": "string", + "title": "County", + "x-nullable": true, + "example": "LOS ANGELES" + }, + "postalCode": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}?)$", + "example": "90210" + }, + "state": { + "type": "string", + "title": "State", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "x-display-value": { + "AK": "AK", + "AL": "AL", + "AR": "AR", + "AZ": "AZ", + "CA": "CA", + "CO": "CO", + "CT": "CT", + "DC": "DC", + "DE": "DE", + "FL": "FL", + "GA": "GA", + "HI": "HI", + "IA": "IA", + "ID": "ID", + "IL": "IL", + "IN": "IN", + "KS": "KS", + "KY": "KY", + "LA": "LA", + "MA": "MA", + "MD": "MD", + "ME": "ME", + "MI": "MI", + "MN": "MN", + "MO": "MO", + "MS": "MS", + "MT": "MT", + "NC": "NC", + "ND": "ND", + "NE": "NE", + "NH": "NH", + "NJ": "NJ", + "NM": "NM", + "NV": "NV", + "NY": "NY", + "OH": "OH", + "OK": "OK", + "OR": "OR", + "PA": "PA", + "RI": "RI", + "SC": "SC", + "SD": "SD", + "TN": "TN", + "TX": "TX", + "UT": "UT", + "VA": "VA", + "VT": "VT", + "WA": "WA", + "WI": "WI", + "WV": "WV", + "WY": "WY" + } + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + } + } + }, + "VLocations": { + "type": "array", + "items": { + "$ref": "#/definitions/VLocation" + } + }, "ValidationError": { "required": [ "invalid_fields" @@ -15013,6 +15206,9 @@ func init() { { "name": "transportationOffice" }, + { + "name": "addresses" + }, { "name": "uploads" }, @@ -15043,6 +15239,56 @@ func init() { }, "basePath": "/ghc/v1", "paths": { + "/addresses/zip-city-lookup/{search}": { + "get": { + "description": "Find by API using full/partial postal code or city name that returns an us_post_region_cities json object containing city, state, county and postal code.", + "tags": [ + "addresses" + ], + "summary": "Returns city, state, postal code, and county associated with the specified full/partial postal code or city and state string", + "operationId": "getLocationByZipCityState", + "parameters": [ + { + "type": "string", + "name": "search", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "the requested list of city, state, county, and postal code matches", + "schema": { + "$ref": "#/definitions/VLocations" + } + }, + "400": { + "description": "The request payload is invalid", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "403": { + "description": "The request was denied", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "404": { + "description": "The requested resource wasn't found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "A server error occurred", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, "/application_parameters/{parameterName}": { "get": { "description": "Searches for an application parameter by name, returns nil if not found", @@ -23045,6 +23291,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -27888,6 +28139,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -31350,6 +31606,151 @@ func init() { } } }, + "VLocation": { + "description": "A postal code, city, and state lookup", + "type": "object", + "properties": { + "city": { + "type": "string", + "title": "City", + "example": "Anytown" + }, + "county": { + "type": "string", + "title": "County", + "x-nullable": true, + "example": "LOS ANGELES" + }, + "postalCode": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}?)$", + "example": "90210" + }, + "state": { + "type": "string", + "title": "State", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "x-display-value": { + "AK": "AK", + "AL": "AL", + "AR": "AR", + "AZ": "AZ", + "CA": "CA", + "CO": "CO", + "CT": "CT", + "DC": "DC", + "DE": "DE", + "FL": "FL", + "GA": "GA", + "HI": "HI", + "IA": "IA", + "ID": "ID", + "IL": "IL", + "IN": "IN", + "KS": "KS", + "KY": "KY", + "LA": "LA", + "MA": "MA", + "MD": "MD", + "ME": "ME", + "MI": "MI", + "MN": "MN", + "MO": "MO", + "MS": "MS", + "MT": "MT", + "NC": "NC", + "ND": "ND", + "NE": "NE", + "NH": "NH", + "NJ": "NJ", + "NM": "NM", + "NV": "NV", + "NY": "NY", + "OH": "OH", + "OK": "OK", + "OR": "OR", + "PA": "PA", + "RI": "RI", + "SC": "SC", + "SD": "SD", + "TN": "TN", + "TX": "TX", + "UT": "UT", + "VA": "VA", + "VT": "VT", + "WA": "WA", + "WI": "WI", + "WV": "WV", + "WY": "WY" + } + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + } + } + }, + "VLocations": { + "type": "array", + "items": { + "$ref": "#/definitions/VLocation" + } + }, "ValidationError": { "required": [ "invalid_fields" @@ -31694,6 +32095,9 @@ func init() { { "name": "transportationOffice" }, + { + "name": "addresses" + }, { "name": "uploads" }, diff --git a/pkg/gen/internalapi/embedded_spec.go b/pkg/gen/internalapi/embedded_spec.go index bc3422344ae..9706d6c936b 100644 --- a/pkg/gen/internalapi/embedded_spec.go +++ b/pkg/gen/internalapi/embedded_spec.go @@ -39,6 +39,44 @@ func init() { }, "basePath": "/internal", "paths": { + "/addresses/zip-city-lookup/{search}": { + "get": { + "description": "Find by API using full/partial postal code or city name that returns an us_post_region_cities json object containing city, state, county and postal code.", + "tags": [ + "addresses" + ], + "summary": "Returns city, state, postal code, and county associated with the specified full/partial postal code or city state string", + "operationId": "getLocationByZipCityState", + "parameters": [ + { + "type": "string", + "name": "search", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "the requested list of city, state, county, and postal code matches", + "schema": { + "$ref": "#/definitions/VLocations" + } + }, + "400": { + "$ref": "#/responses/InvalidRequest" + }, + "403": { + "$ref": "#/responses/PermissionDenied" + }, + "404": { + "$ref": "#/responses/NotFound" + }, + "500": { + "$ref": "#/responses/ServerError" + } + } + } + }, "/addresses/{addressId}": { "get": { "description": "Find by API using address ID that returns an address json object containing address 1, address 2, address 3, city and postal code.", @@ -3473,6 +3511,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -6239,6 +6282,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -8066,6 +8114,151 @@ func init() { } } }, + "VLocation": { + "description": "A postal code, city, and state lookup", + "type": "object", + "properties": { + "city": { + "type": "string", + "title": "City", + "example": "Anytown" + }, + "county": { + "type": "string", + "title": "County", + "x-nullable": true, + "example": "LOS ANGELES" + }, + "postalCode": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}?)$", + "example": "90210" + }, + "state": { + "type": "string", + "title": "State", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "x-display-value": { + "AK": "AK", + "AL": "AL", + "AR": "AR", + "AZ": "AZ", + "CA": "CA", + "CO": "CO", + "CT": "CT", + "DC": "DC", + "DE": "DE", + "FL": "FL", + "GA": "GA", + "HI": "HI", + "IA": "IA", + "ID": "ID", + "IL": "IL", + "IN": "IN", + "KS": "KS", + "KY": "KY", + "LA": "LA", + "MA": "MA", + "MD": "MD", + "ME": "ME", + "MI": "MI", + "MN": "MN", + "MO": "MO", + "MS": "MS", + "MT": "MT", + "NC": "NC", + "ND": "ND", + "NE": "NE", + "NH": "NH", + "NJ": "NJ", + "NM": "NM", + "NV": "NV", + "NY": "NY", + "OH": "OH", + "OK": "OK", + "OR": "OR", + "PA": "PA", + "RI": "RI", + "SC": "SC", + "SD": "SD", + "TN": "TN", + "TX": "TX", + "UT": "UT", + "VA": "VA", + "VT": "VT", + "WA": "WA", + "WI": "WI", + "WV": "WV", + "WY": "WY" + } + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + } + } + }, + "VLocations": { + "type": "array", + "items": { + "$ref": "#/definitions/VLocation" + } + }, "ValidationError": { "required": [ "invalidFields" @@ -8500,6 +8693,56 @@ func init() { }, "basePath": "/internal", "paths": { + "/addresses/zip-city-lookup/{search}": { + "get": { + "description": "Find by API using full/partial postal code or city name that returns an us_post_region_cities json object containing city, state, county and postal code.", + "tags": [ + "addresses" + ], + "summary": "Returns city, state, postal code, and county associated with the specified full/partial postal code or city state string", + "operationId": "getLocationByZipCityState", + "parameters": [ + { + "type": "string", + "name": "search", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "the requested list of city, state, county, and postal code matches", + "schema": { + "$ref": "#/definitions/VLocations" + } + }, + "400": { + "description": "The request payload is invalid.", + "schema": { + "$ref": "#/definitions/ClientError" + } + }, + "403": { + "description": "The request was denied.", + "schema": { + "$ref": "#/definitions/ClientError" + } + }, + "404": { + "description": "The requested resource wasn't found.", + "schema": { + "$ref": "#/definitions/ClientError" + } + }, + "500": { + "description": "A server error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, "/addresses/{addressId}": { "get": { "description": "Find by API using address ID that returns an address json object containing address 1, address 2, address 3, city and postal code.", @@ -12387,6 +12630,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -15157,6 +15405,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -16991,6 +17244,151 @@ func init() { } } }, + "VLocation": { + "description": "A postal code, city, and state lookup", + "type": "object", + "properties": { + "city": { + "type": "string", + "title": "City", + "example": "Anytown" + }, + "county": { + "type": "string", + "title": "County", + "x-nullable": true, + "example": "LOS ANGELES" + }, + "postalCode": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}?)$", + "example": "90210" + }, + "state": { + "type": "string", + "title": "State", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "x-display-value": { + "AK": "AK", + "AL": "AL", + "AR": "AR", + "AZ": "AZ", + "CA": "CA", + "CO": "CO", + "CT": "CT", + "DC": "DC", + "DE": "DE", + "FL": "FL", + "GA": "GA", + "HI": "HI", + "IA": "IA", + "ID": "ID", + "IL": "IL", + "IN": "IN", + "KS": "KS", + "KY": "KY", + "LA": "LA", + "MA": "MA", + "MD": "MD", + "ME": "ME", + "MI": "MI", + "MN": "MN", + "MO": "MO", + "MS": "MS", + "MT": "MT", + "NC": "NC", + "ND": "ND", + "NE": "NE", + "NH": "NH", + "NJ": "NJ", + "NM": "NM", + "NV": "NV", + "NY": "NY", + "OH": "OH", + "OK": "OK", + "OR": "OR", + "PA": "PA", + "RI": "RI", + "SC": "SC", + "SD": "SD", + "TN": "TN", + "TX": "TX", + "UT": "UT", + "VA": "VA", + "VT": "VT", + "WA": "WA", + "WI": "WI", + "WV": "WV", + "WY": "WY" + } + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + } + } + }, + "VLocations": { + "type": "array", + "items": { + "$ref": "#/definitions/VLocation" + } + }, "ValidationError": { "required": [ "invalidFields" diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index bba1b1d628e..88ddc2b1085 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -355,7 +355,7 @@ func init() { }, "/mto-service-items/{mtoServiceItemID}": { "patch": { - "description": "Updates MTOServiceItems after creation. Not all service items or fields may be updated, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to update and the documentation will update with the new definition.\n\n* Addresses: To update a destination service item's SIT destination final address, update the shipment delivery address.\nFor approved shipments, please use [updateShipmentDestinationAddress](#mtoShipment/updateShipmentDestinationAddress).\nFor shipments not yet approved, please use [updateMTOShipmentAddress](#mtoShipment/updateMTOShipmentAddress).\n\n* SIT Service Items: Take note that when updating ` + "`" + `sitCustomerContacted` + "`" + `, ` + "`" + `sitDepartureDate` + "`" + `, or ` + "`" + `sitRequestedDelivery` + "`" + `, we want\nthose to be updated on ` + "`" + `DOASIT` + "`" + ` (for origin SIT) and ` + "`" + `DDASIT` + "`" + ` (for destination SIT). If updating those values in other service\nitems, the office users will not have as much attention to those values.\n\nTo create a service item, please use [createMTOServiceItem](#mtoServiceItem/createMTOServiceItem)) endpoint.\n\n* Resubmitting rejected SIT service items: This endpoint will handle the logic of changing the status of rejected SIT service items from\nREJECTED to SUBMITTED. Please provide the ` + "`" + `requestedApprovalsRequestedStatus: true` + "`" + ` when resubmitting as this will give attention to the TOO to\nreview the resubmitted SIT service item. Another note, ` + "`" + `updateReason` + "`" + ` must have a different value than the current ` + "`" + `reason` + "`" + ` value on the service item.\nIf this value is not updated, then an error will be sent back.\n\nThe following SIT service items can be resubmitted following a rejection:\n- DDASIT\n- DDDSIT\n- DDFSIT\n- DOASIT\n- DOPSIT\n- DOFSIT\n- DDSFSC\n- DOSFSC\n\nAt a MINIMUM, the payload for resubmitting a rejected SIT service item must look like this:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"reServiceCode\": \"DDFSIT\",\n \"updateReason\": \"A reason that differs from the previous reason\",\n \"modelType\": \"UpdateMTOServiceItemSIT\",\n \"requestApprovalsRequestedStatus\": true\n}\n` + "`" + `` + "`" + `` + "`" + `\n", + "description": "Updates MTOServiceItems after creation. Not all service items or fields may be updated, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to update and the documentation will update with the new definition.\n\n* Addresses: To update a destination service item's SIT destination final address, update the shipment delivery address.\nFor approved shipments, please use [updateShipmentDestinationAddress](#mtoShipment/updateShipmentDestinationAddress).\nFor shipments not yet approved, please use [updateMTOShipmentAddress](#mtoShipment/updateMTOShipmentAddress).\n\n* SIT Service Items: Take note that when updating ` + "`" + `sitCustomerContacted` + "`" + `, ` + "`" + `sitDepartureDate` + "`" + `, or ` + "`" + `sitRequestedDelivery` + "`" + `, we want\nthose to be updated on ` + "`" + `DOASIT` + "`" + ` (for origin SIT) and ` + "`" + `DDASIT` + "`" + ` (for destination SIT). If updating those values in other service\nitems, the office users will not have as much attention to those values.\n\nTo create a service item, please use [createMTOServiceItem](#mtoServiceItem/createMTOServiceItem)) endpoint.\n\n* Resubmitting rejected SIT service items: This endpoint will handle the logic of changing the status of rejected SIT service items from\nREJECTED to SUBMITTED. Please provide the ` + "`" + `requestedApprovalsRequestedStatus: true` + "`" + ` when resubmitting as this will give attention to the TOO to\nreview the resubmitted SIT service item. Another note, ` + "`" + `updateReason` + "`" + ` must have a different value than the current ` + "`" + `reason` + "`" + ` value on the service item.\nIf this value is not updated, then an error will be sent back.\n\nThe following SIT service items can be resubmitted following a rejection:\n- DDASIT\n- DDDSIT\n- DDFSIT\n- DOASIT\n- DOPSIT\n- DOFSIT\n- DDSFSC\n- DOSFSC\n\nAt a MINIMUM, the payload for resubmitting a rejected SIT service item must look like this:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"reServiceCode\": \"DDFSIT\",\n \"updateReason\": \"A reason that differs from the previous reason\",\n \"modelType\": \"UpdateMTOServiceItemSIT\",\n \"requestApprovalsRequestedStatus\": true\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nThe following service items allow you to update the Port that the shipment will use:\n- PODFSC (Port of Debarkation can be updated)\n- POEFSC (Port of Embarkation can be updated)\n\nAt a MINIMUM, the payload for updating the port should contain the reServiceCode (PODFSC or POEFSC), modelType (UpdateMTOServiceItemInternationalPortFSC), portCode, and id for the service item.\nPlease see the example payload below:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"id\": \"1ed224b6-c65e-4616-b88e-8304d26c9562\",\n \"modelType\": \"UpdateMTOServiceItemInternationalPortFSC\",\n \"portCode\": \"SEA\",\n \"reServiceCode\": \"POEFSC\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n", "consumes": [ "application/json" ], @@ -1362,6 +1362,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -2338,8 +2343,33 @@ func init() { } ] }, + "MTOServiceItemInternationalFuelSurcharge": { + "description": "Describes a international Port of Embarkation/Debarkation fuel surcharge service item subtype of a MTOServiceItem.", + "allOf": [ + { + "$ref": "#/definitions/MTOServiceItem" + }, + { + "type": "object", + "properties": { + "portCode": { + "description": "A unique code for a Port", + "type": "string" + }, + "reServiceCode": { + "description": "A unique code for the service item. Indicates if the service is for Port of Embarkation (POEFSC) or Port of Debarkation (PODFSC).", + "type": "string", + "enum": [ + "PODFSC", + "POEFSC" + ] + } + } + } + ] + }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", @@ -2347,7 +2377,8 @@ func init() { "MTOServiceItemDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", - "MTOServiceItemInternationalCrating" + "MTOServiceItemInternationalCrating", + "MTOSerivceItemInternationalFuelSurcharge" ] }, "MTOServiceItemOriginSIT": { @@ -3896,12 +3927,41 @@ func init() { }, "discriminator": "modelType" }, + "UpdateMTOServiceItemInternationalPortFSC": { + "description": "Subtype used to provide the port for fuel surcharge. This is not creating a new service item but rather updating an existing service item.\n", + "allOf": [ + { + "$ref": "#/definitions/UpdateMTOServiceItem" + }, + { + "type": "object", + "properties": { + "portCode": { + "description": "Port used for the shipment. Relevant for moving (PODFSC \u0026 POEFSC) service items.", + "type": "string", + "x-nullable": true, + "x-omitempty": false, + "example": "PDX" + }, + "reServiceCode": { + "description": "Service code allowed for this model type.", + "type": "string", + "enum": [ + "PODFSC", + "POEFSC" + ] + } + } + } + ] + }, "UpdateMTOServiceItemModelType": { - "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DDFSIT - UpdateMTOServiceItemSIT\n * DDASIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DOSFSC - UpdateMTOServiceItemSIT\n * DDSFSC - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n\nThe documentation will then update with the supported fields.\n", + "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DDFSIT - UpdateMTOServiceItemSIT\n * DDASIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DOSFSC - UpdateMTOServiceItemSIT\n * DDSFSC - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * PODFSC - UpdateMTOServiceItemInternationalPortFSC\n * POEFSC - UpdateMTOServiceItemInternationalPortFSC\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "UpdateMTOServiceItemSIT", - "UpdateMTOServiceItemShuttle" + "UpdateMTOServiceItemShuttle", + "UpdateMTOServiceItemInternationalPortFSC" ] }, "UpdateMTOServiceItemSIT": { @@ -4914,7 +4974,7 @@ func init() { }, "/mto-service-items/{mtoServiceItemID}": { "patch": { - "description": "Updates MTOServiceItems after creation. Not all service items or fields may be updated, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to update and the documentation will update with the new definition.\n\n* Addresses: To update a destination service item's SIT destination final address, update the shipment delivery address.\nFor approved shipments, please use [updateShipmentDestinationAddress](#mtoShipment/updateShipmentDestinationAddress).\nFor shipments not yet approved, please use [updateMTOShipmentAddress](#mtoShipment/updateMTOShipmentAddress).\n\n* SIT Service Items: Take note that when updating ` + "`" + `sitCustomerContacted` + "`" + `, ` + "`" + `sitDepartureDate` + "`" + `, or ` + "`" + `sitRequestedDelivery` + "`" + `, we want\nthose to be updated on ` + "`" + `DOASIT` + "`" + ` (for origin SIT) and ` + "`" + `DDASIT` + "`" + ` (for destination SIT). If updating those values in other service\nitems, the office users will not have as much attention to those values.\n\nTo create a service item, please use [createMTOServiceItem](#mtoServiceItem/createMTOServiceItem)) endpoint.\n\n* Resubmitting rejected SIT service items: This endpoint will handle the logic of changing the status of rejected SIT service items from\nREJECTED to SUBMITTED. Please provide the ` + "`" + `requestedApprovalsRequestedStatus: true` + "`" + ` when resubmitting as this will give attention to the TOO to\nreview the resubmitted SIT service item. Another note, ` + "`" + `updateReason` + "`" + ` must have a different value than the current ` + "`" + `reason` + "`" + ` value on the service item.\nIf this value is not updated, then an error will be sent back.\n\nThe following SIT service items can be resubmitted following a rejection:\n- DDASIT\n- DDDSIT\n- DDFSIT\n- DOASIT\n- DOPSIT\n- DOFSIT\n- DDSFSC\n- DOSFSC\n\nAt a MINIMUM, the payload for resubmitting a rejected SIT service item must look like this:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"reServiceCode\": \"DDFSIT\",\n \"updateReason\": \"A reason that differs from the previous reason\",\n \"modelType\": \"UpdateMTOServiceItemSIT\",\n \"requestApprovalsRequestedStatus\": true\n}\n` + "`" + `` + "`" + `` + "`" + `\n", + "description": "Updates MTOServiceItems after creation. Not all service items or fields may be updated, please see details below.\n\nThis endpoint supports different body definitions. In the modelType field below, select the modelType corresponding\n to the service item you wish to update and the documentation will update with the new definition.\n\n* Addresses: To update a destination service item's SIT destination final address, update the shipment delivery address.\nFor approved shipments, please use [updateShipmentDestinationAddress](#mtoShipment/updateShipmentDestinationAddress).\nFor shipments not yet approved, please use [updateMTOShipmentAddress](#mtoShipment/updateMTOShipmentAddress).\n\n* SIT Service Items: Take note that when updating ` + "`" + `sitCustomerContacted` + "`" + `, ` + "`" + `sitDepartureDate` + "`" + `, or ` + "`" + `sitRequestedDelivery` + "`" + `, we want\nthose to be updated on ` + "`" + `DOASIT` + "`" + ` (for origin SIT) and ` + "`" + `DDASIT` + "`" + ` (for destination SIT). If updating those values in other service\nitems, the office users will not have as much attention to those values.\n\nTo create a service item, please use [createMTOServiceItem](#mtoServiceItem/createMTOServiceItem)) endpoint.\n\n* Resubmitting rejected SIT service items: This endpoint will handle the logic of changing the status of rejected SIT service items from\nREJECTED to SUBMITTED. Please provide the ` + "`" + `requestedApprovalsRequestedStatus: true` + "`" + ` when resubmitting as this will give attention to the TOO to\nreview the resubmitted SIT service item. Another note, ` + "`" + `updateReason` + "`" + ` must have a different value than the current ` + "`" + `reason` + "`" + ` value on the service item.\nIf this value is not updated, then an error will be sent back.\n\nThe following SIT service items can be resubmitted following a rejection:\n- DDASIT\n- DDDSIT\n- DDFSIT\n- DOASIT\n- DOPSIT\n- DOFSIT\n- DDSFSC\n- DOSFSC\n\nAt a MINIMUM, the payload for resubmitting a rejected SIT service item must look like this:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"reServiceCode\": \"DDFSIT\",\n \"updateReason\": \"A reason that differs from the previous reason\",\n \"modelType\": \"UpdateMTOServiceItemSIT\",\n \"requestApprovalsRequestedStatus\": true\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nThe following service items allow you to update the Port that the shipment will use:\n- PODFSC (Port of Debarkation can be updated)\n- POEFSC (Port of Embarkation can be updated)\n\nAt a MINIMUM, the payload for updating the port should contain the reServiceCode (PODFSC or POEFSC), modelType (UpdateMTOServiceItemInternationalPortFSC), portCode, and id for the service item.\nPlease see the example payload below:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"id\": \"1ed224b6-c65e-4616-b88e-8304d26c9562\",\n \"modelType\": \"UpdateMTOServiceItemInternationalPortFSC\",\n \"portCode\": \"SEA\",\n \"reServiceCode\": \"POEFSC\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n", "consumes": [ "application/json" ], @@ -6196,6 +6256,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -7172,8 +7237,33 @@ func init() { } ] }, + "MTOServiceItemInternationalFuelSurcharge": { + "description": "Describes a international Port of Embarkation/Debarkation fuel surcharge service item subtype of a MTOServiceItem.", + "allOf": [ + { + "$ref": "#/definitions/MTOServiceItem" + }, + { + "type": "object", + "properties": { + "portCode": { + "description": "A unique code for a Port", + "type": "string" + }, + "reServiceCode": { + "description": "A unique code for the service item. Indicates if the service is for Port of Embarkation (POEFSC) or Port of Debarkation (PODFSC).", + "type": "string", + "enum": [ + "PODFSC", + "POEFSC" + ] + } + } + } + ] + }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", @@ -7181,7 +7271,8 @@ func init() { "MTOServiceItemDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", - "MTOServiceItemInternationalCrating" + "MTOServiceItemInternationalCrating", + "MTOSerivceItemInternationalFuelSurcharge" ] }, "MTOServiceItemOriginSIT": { @@ -8735,12 +8826,41 @@ func init() { }, "discriminator": "modelType" }, + "UpdateMTOServiceItemInternationalPortFSC": { + "description": "Subtype used to provide the port for fuel surcharge. This is not creating a new service item but rather updating an existing service item.\n", + "allOf": [ + { + "$ref": "#/definitions/UpdateMTOServiceItem" + }, + { + "type": "object", + "properties": { + "portCode": { + "description": "Port used for the shipment. Relevant for moving (PODFSC \u0026 POEFSC) service items.", + "type": "string", + "x-nullable": true, + "x-omitempty": false, + "example": "PDX" + }, + "reServiceCode": { + "description": "Service code allowed for this model type.", + "type": "string", + "enum": [ + "PODFSC", + "POEFSC" + ] + } + } + } + ] + }, "UpdateMTOServiceItemModelType": { - "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DDFSIT - UpdateMTOServiceItemSIT\n * DDASIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DOSFSC - UpdateMTOServiceItemSIT\n * DDSFSC - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n\nThe documentation will then update with the supported fields.\n", + "description": "Using this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DDDSIT - UpdateMTOServiceItemSIT\n * DDFSIT - UpdateMTOServiceItemSIT\n * DDASIT - UpdateMTOServiceItemSIT\n * DOPSIT - UpdateMTOServiceItemSIT\n * DOASIT - UpdateMTOServiceItemSIT\n * DOFSIT - UpdateMTOServiceItemSIT\n * DOSFSC - UpdateMTOServiceItemSIT\n * DDSFSC - UpdateMTOServiceItemSIT\n * DDSHUT - UpdateMTOServiceItemShuttle\n * DOSHUT - UpdateMTOServiceItemShuttle\n * PODFSC - UpdateMTOServiceItemInternationalPortFSC\n * POEFSC - UpdateMTOServiceItemInternationalPortFSC\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "UpdateMTOServiceItemSIT", - "UpdateMTOServiceItemShuttle" + "UpdateMTOServiceItemShuttle", + "UpdateMTOServiceItemInternationalPortFSC" ] }, "UpdateMTOServiceItemSIT": { diff --git a/pkg/gen/primev2api/embedded_spec.go b/pkg/gen/primev2api/embedded_spec.go index cc81cce8c7b..2b05dd45610 100644 --- a/pkg/gen/primev2api/embedded_spec.go +++ b/pkg/gen/primev2api/embedded_spec.go @@ -547,6 +547,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -1470,7 +1475,7 @@ func init() { ] }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", @@ -1478,7 +1483,8 @@ func init() { "MTOServiceItemDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", - "MTOServiceItemInternationalCrating" + "MTOServiceItemInternationalCrating", + "MTOSerivceItemInternationalFuelSurcharge" ] }, "MTOServiceItemOriginSIT": { @@ -4148,6 +4154,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -5071,7 +5082,7 @@ func init() { ] }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", @@ -5079,7 +5090,8 @@ func init() { "MTOServiceItemDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", - "MTOServiceItemInternationalCrating" + "MTOServiceItemInternationalCrating", + "MTOSerivceItemInternationalFuelSurcharge" ] }, "MTOServiceItemOriginSIT": { diff --git a/pkg/gen/primev3api/embedded_spec.go b/pkg/gen/primev3api/embedded_spec.go index f7cb95c0bb5..2d36d552e13 100644 --- a/pkg/gen/primev3api/embedded_spec.go +++ b/pkg/gen/primev3api/embedded_spec.go @@ -553,6 +553,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -1632,7 +1637,7 @@ func init() { ] }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", @@ -1640,7 +1645,8 @@ func init() { "MTOServiceItemDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", - "MTOServiceItemInternationalCrating" + "MTOServiceItemInternationalCrating", + "MTOSerivceItemInternationalFuelSurcharge" ] }, "MTOServiceItemOriginSIT": { @@ -1965,6 +1971,12 @@ func init() { "description": "Email or ID of the person who will be contacted in the event of questions or concerns about this update. May be the person performing the update, or someone else working with the Prime contractor.\n", "type": "string" }, + "portOfDebarkation": { + "$ref": "#/definitions/Port" + }, + "portOfEmbarkation": { + "$ref": "#/definitions/Port" + }, "ppmShipment": { "$ref": "#/definitions/PPMShipment" }, @@ -2530,6 +2542,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -2980,6 +2997,115 @@ func init() { "$ref": "#/definitions/PaymentServiceItem" } }, + "Port": { + "description": "A port that is used to move an international shipment.", + "type": "object", + "properties": { + "city": { + "type": "string", + "example": "PORTLAND" + }, + "country": { + "description": "Two-letter country code", + "type": "string", + "pattern": "^[A-Z]{2}$", + "example": "US" + }, + "county": { + "type": "string", + "example": "MULTNOMAH" + }, + "id": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + }, + "portCode": { + "description": "3 or 4 digit port code", + "type": "string", + "example": "0431" + }, + "portName": { + "description": "Name of the port", + "type": "string", + "example": "PORTLAND INTL" + }, + "portType": { + "description": "Port type A (Air), B (Border Crossing), S (Sea)", + "type": "string", + "enum": [ + "A", + "B", + "S" + ] + }, + "state": { + "description": "US state", + "type": "string", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "example": "OR" + }, + "zip": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}([\\-]\\d{4})?)$", + "example": "99501" + } + } + }, "ProofOfServiceDoc": { "type": "object", "properties": { @@ -3672,6 +3798,12 @@ func init() { "description": "Email or ID of the person who will be contacted in the event of questions or concerns about this update. May be the person performing the update, or someone else working with the Prime contractor.\n", "type": "string" }, + "portOfDebarkation": { + "$ref": "#/definitions/Port" + }, + "portOfEmbarkation": { + "$ref": "#/definitions/Port" + }, "ppmShipment": { "$ref": "#/definitions/UpdatePPMShipment" }, @@ -4675,6 +4807,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -5754,7 +5891,7 @@ func init() { ] }, "MTOServiceItemModelType": { - "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n\nThe documentation will then update with the supported fields.\n", + "description": "Describes all model sub-types for a MTOServiceItem model.\n\nUsing this list, choose the correct modelType in the dropdown, corresponding to the service item type.\n * DOFSIT, DOASIT - MTOServiceItemOriginSIT\n * DDFSIT, DDASIT - MTOServiceItemDestSIT\n * DOSHUT, DDSHUT - MTOServiceItemShuttle\n * DCRT, DUCRT - MTOServiceItemDomesticCrating\n * ICRT, IUCRT - MTOServiceItemInternationalCrating\n * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge\n\nThe documentation will then update with the supported fields.\n", "type": "string", "enum": [ "MTOServiceItemBasic", @@ -5762,7 +5899,8 @@ func init() { "MTOServiceItemDestSIT", "MTOServiceItemShuttle", "MTOServiceItemDomesticCrating", - "MTOServiceItemInternationalCrating" + "MTOServiceItemInternationalCrating", + "MTOSerivceItemInternationalFuelSurcharge" ] }, "MTOServiceItemOriginSIT": { @@ -6087,6 +6225,12 @@ func init() { "description": "Email or ID of the person who will be contacted in the event of questions or concerns about this update. May be the person performing the update, or someone else working with the Prime contractor.\n", "type": "string" }, + "portOfDebarkation": { + "$ref": "#/definitions/Port" + }, + "portOfEmbarkation": { + "$ref": "#/definitions/Port" + }, "ppmShipment": { "$ref": "#/definitions/PPMShipment" }, @@ -6652,6 +6796,11 @@ func init() { "title": "Address Line 3", "x-nullable": true, "example": "Montmârtre" + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" } } }, @@ -7102,6 +7251,115 @@ func init() { "$ref": "#/definitions/PaymentServiceItem" } }, + "Port": { + "description": "A port that is used to move an international shipment.", + "type": "object", + "properties": { + "city": { + "type": "string", + "example": "PORTLAND" + }, + "country": { + "description": "Two-letter country code", + "type": "string", + "pattern": "^[A-Z]{2}$", + "example": "US" + }, + "county": { + "type": "string", + "example": "MULTNOMAH" + }, + "id": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + }, + "portCode": { + "description": "3 or 4 digit port code", + "type": "string", + "example": "0431" + }, + "portName": { + "description": "Name of the port", + "type": "string", + "example": "PORTLAND INTL" + }, + "portType": { + "description": "Port type A (Air), B (Border Crossing), S (Sea)", + "type": "string", + "enum": [ + "A", + "B", + "S" + ] + }, + "state": { + "description": "US state", + "type": "string", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "example": "OR" + }, + "zip": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}([\\-]\\d{4})?)$", + "example": "99501" + } + } + }, "ProofOfServiceDoc": { "type": "object", "properties": { @@ -7796,6 +8054,12 @@ func init() { "description": "Email or ID of the person who will be contacted in the event of questions or concerns about this update. May be the person performing the update, or someone else working with the Prime contractor.\n", "type": "string" }, + "portOfDebarkation": { + "$ref": "#/definitions/Port" + }, + "portOfEmbarkation": { + "$ref": "#/definitions/Port" + }, "ppmShipment": { "$ref": "#/definitions/UpdatePPMShipment" }, diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index 64e70b75634..9f6ff6750b2 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -38,6 +38,7 @@ tags: - name: ppm - name: tac - name: transportationOffice + - name: addresses - name: uploads - name: paymentRequests - name: reServiceItems @@ -4221,6 +4222,36 @@ paths: $ref: '#/responses/NotFound' '500': $ref: '#/responses/ServerError' + /addresses/zip-city-lookup/{search}: + get: + summary: >- + Returns city, state, postal code, and county associated with the + specified full/partial postal code or city and state string + description: >- + Find by API using full/partial postal code or city name that returns an + us_post_region_cities json object containing city, state, county and + postal code. + operationId: getLocationByZipCityState + tags: + - addresses + parameters: + - in: path + name: search + type: string + required: true + responses: + '200': + description: the requested list of city, state, county, and postal code matches + schema: + $ref: '#/definitions/VLocations' + '400': + $ref: '#/responses/InvalidRequest' + '403': + $ref: '#/responses/PermissionDenied' + '404': + $ref: '#/responses/NotFound' + '500': + $ref: '#/responses/ServerError' /uploads: post: summary: Create a new upload @@ -7974,6 +8005,10 @@ definitions: type: array items: $ref: '#/definitions/TransportationOffice' + VLocations: + type: array + items: + $ref: '#/definitions/VLocation' ReServiceItems: type: array items: @@ -8262,6 +8297,10 @@ definitions: title: isOconus x-nullable: true example: false + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 required: - streetAddress1 - city @@ -8994,6 +9033,10 @@ definitions: title: County x-nullable: true example: LOS ANGELES + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 required: - city - state @@ -11418,6 +11461,136 @@ definitions: validHhgProgramCodeForLoa: type: boolean x-nullable: true + VLocation: + description: A postal code, city, and state lookup + type: object + properties: + city: + type: string + example: Anytown + title: City + state: + title: State + type: string + x-display-value: + AL: AL + AK: AK + AR: AR + AZ: AZ + CA: CA + CO: CO + CT: CT + DC: DC + DE: DE + FL: FL + GA: GA + HI: HI + IA: IA + ID: ID + IL: IL + IN: IN + KS: KS + KY: KY + LA: LA + MA: MA + MD: MD + ME: ME + MI: MI + MN: MN + MO: MO + MS: MS + MT: MT + NC: NC + ND: ND + NE: NE + NH: NH + NJ: NJ + NM: NM + NV: NV + NY: NY + OH: OH + OK: OK + OR: OR + PA: PA + RI: RI + SC: SC + SD: SD + TN: TN + TX: TX + UT: UT + VA: VA + VT: VT + WA: WA + WI: WI + WV: WV + WY: WY + enum: + - AL + - AK + - AR + - AZ + - CA + - CO + - CT + - DC + - DE + - FL + - GA + - HI + - IA + - ID + - IL + - IN + - KS + - KY + - LA + - MA + - MD + - ME + - MI + - MN + - MO + - MS + - MT + - NC + - ND + - NE + - NH + - NJ + - NM + - NV + - NY + - OH + - OK + - OR + - PA + - RI + - SC + - SD + - TN + - TX + - UT + - VA + - VT + - WA + - WI + - WV + - WY + postalCode: + type: string + format: zip + title: ZIP + example: '90210' + pattern: ^(\d{5}?)$ + county: + type: string + title: County + x-nullable: true + example: LOS ANGELES + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 ReServiceItem: description: A Service Item which ties an ReService, Market, and Shipment Type together type: object diff --git a/swagger/internal.yaml b/swagger/internal.yaml index a9695a64600..91bf98ecf28 100644 --- a/swagger/internal.yaml +++ b/swagger/internal.yaml @@ -608,6 +608,10 @@ definitions: type: array items: $ref: '#/definitions/TransportationOffice' + VLocations: + type: array + items: + $ref: '#/definitions/VLocation' OfficeUser: type: object properties: @@ -2750,6 +2754,10 @@ definitions: title: isOconus x-nullable: true example: false + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 required: - streetAddress1 - city @@ -4048,6 +4056,136 @@ definitions: RENTAL_EQUIPMENT: Rental equipment TOLLS: Tolls WEIGHING_FEE: Weighing fee + VLocation: + description: A postal code, city, and state lookup + type: object + properties: + city: + type: string + example: Anytown + title: City + state: + title: State + type: string + x-display-value: + AL: AL + AK: AK + AR: AR + AZ: AZ + CA: CA + CO: CO + CT: CT + DC: DC + DE: DE + FL: FL + GA: GA + HI: HI + IA: IA + ID: ID + IL: IL + IN: IN + KS: KS + KY: KY + LA: LA + MA: MA + MD: MD + ME: ME + MI: MI + MN: MN + MO: MO + MS: MS + MT: MT + NC: NC + ND: ND + NE: NE + NH: NH + NJ: NJ + NM: NM + NV: NV + NY: NY + OH: OH + OK: OK + OR: OR + PA: PA + RI: RI + SC: SC + SD: SD + TN: TN + TX: TX + UT: UT + VA: VA + VT: VT + WA: WA + WI: WI + WV: WV + WY: WY + enum: + - AL + - AK + - AR + - AZ + - CA + - CO + - CT + - DC + - DE + - FL + - GA + - HI + - IA + - ID + - IL + - IN + - KS + - KY + - LA + - MA + - MD + - ME + - MI + - MN + - MO + - MS + - MT + - NC + - ND + - NE + - NH + - NJ + - NM + - NV + - NY + - OH + - OK + - OR + - PA + - RI + - SC + - SD + - TN + - TX + - UT + - VA + - VT + - WA + - WI + - WV + - WY + postalCode: + type: string + format: zip + title: ZIP + example: '90210' + pattern: ^(\d{5}?)$ + county: + type: string + title: County + x-nullable: true + example: LOS ANGELES + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 PPMDestinationAddress: description: A postal address type: object @@ -4202,6 +4340,10 @@ definitions: title: County x-nullable: true example: LOS ANGELES + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 required: - city - state @@ -6234,6 +6376,36 @@ paths: description: not found '500': description: server error + /addresses/zip-city-lookup/{search}: + get: + summary: >- + Returns city, state, postal code, and county associated with the + specified full/partial postal code or city state string + description: >- + Find by API using full/partial postal code or city name that returns an + us_post_region_cities json object containing city, state, county and + postal code. + operationId: getLocationByZipCityState + tags: + - addresses + parameters: + - in: path + name: search + type: string + required: true + responses: + '200': + description: the requested list of city, state, county, and postal code matches + schema: + $ref: '#/definitions/VLocations' + '400': + $ref: '#/responses/InvalidRequest' + '403': + $ref: '#/responses/PermissionDenied' + '404': + $ref: '#/responses/NotFound' + '500': + $ref: '#/responses/ServerError' /mto_shipments: post: summary: createMTOShipment diff --git a/swagger/prime.yaml b/swagger/prime.yaml index 37219b47181..f0de7643be3 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -1072,6 +1072,33 @@ paths: } ``` + + + The following service items allow you to update the Port that the + shipment will use: + + - PODFSC (Port of Debarkation can be updated) + + - POEFSC (Port of Embarkation can be updated) + + + At a MINIMUM, the payload for updating the port should contain the + reServiceCode (PODFSC or POEFSC), modelType + (UpdateMTOServiceItemInternationalPortFSC), portCode, and id for the + service item. + + Please see the example payload below: + + ```json + + { + "id": "1ed224b6-c65e-4616-b88e-8304d26c9562", + "modelType": "UpdateMTOServiceItemInternationalPortFSC", + "portCode": "SEA", + "reServiceCode": "POEFSC" + } + + ``` operationId: updateMTOServiceItem tags: - mtoServiceItem @@ -2234,6 +2261,25 @@ definitions: required: - reason - reServiceCode + MTOServiceItemInternationalFuelSurcharge: + description: >- + Describes a international Port of Embarkation/Debarkation fuel surcharge + service item subtype of a MTOServiceItem. + allOf: + - $ref: '#/definitions/MTOServiceItem' + - type: object + properties: + reServiceCode: + type: string + description: >- + A unique code for the service item. Indicates if the service is + for Port of Embarkation (POEFSC) or Port of Debarkation (PODFSC). + enum: + - PODFSC + - POEFSC + portCode: + description: A unique code for a Port + type: string MTOShipment: type: object properties: @@ -2326,12 +2372,15 @@ definitions: * DDSFSC - UpdateMTOServiceItemSIT * DDSHUT - UpdateMTOServiceItemShuttle * DOSHUT - UpdateMTOServiceItemShuttle + * PODFSC - UpdateMTOServiceItemInternationalPortFSC + * POEFSC - UpdateMTOServiceItemInternationalPortFSC The documentation will then update with the supported fields. type: string enum: - UpdateMTOServiceItemSIT - UpdateMTOServiceItemShuttle + - UpdateMTOServiceItemInternationalPortFSC UpdateMTOServiceItemShuttle: description: > Subtype used to provide the estimated weight and actual weight for @@ -2463,6 +2512,28 @@ definitions: description: Indicates if "Approvals Requested" status is being requested. type: boolean x-nullable: true + UpdateMTOServiceItemInternationalPortFSC: + description: > + Subtype used to provide the port for fuel surcharge. This is not creating + a new service item but rather updating an existing service item. + allOf: + - $ref: '#/definitions/UpdateMTOServiceItem' + - type: object + properties: + portCode: + type: string + example: PDX + description: >- + Port used for the shipment. Relevant for moving (PODFSC & POEFSC) + service items. + x-nullable: true + x-omitempty: false + reServiceCode: + type: string + description: Service code allowed for this model type. + enum: + - PODFSC + - POEFSC UpdateMTOShipment: properties: actualProGearWeight: @@ -2880,6 +2951,10 @@ definitions: title: isOconus x-nullable: true example: false + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 required: - streetAddress1 - city @@ -3368,6 +3443,7 @@ definitions: * DOSHUT, DDSHUT - MTOServiceItemShuttle * DCRT, DUCRT - MTOServiceItemDomesticCrating * ICRT, IUCRT - MTOServiceItemInternationalCrating + * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge The documentation will then update with the supported fields. type: string @@ -3378,6 +3454,7 @@ definitions: - MTOServiceItemShuttle - MTOServiceItemDomesticCrating - MTOServiceItemInternationalCrating + - MTOSerivceItemInternationalFuelSurcharge ServiceRequestDocument: properties: uploads: diff --git a/swagger/prime_v2.yaml b/swagger/prime_v2.yaml index e7cd2cefd81..20008a7dd7e 100644 --- a/swagger/prime_v2.yaml +++ b/swagger/prime_v2.yaml @@ -1562,6 +1562,10 @@ definitions: title: isOconus x-nullable: true example: false + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 required: - streetAddress1 - city @@ -2062,6 +2066,7 @@ definitions: * DOSHUT, DDSHUT - MTOServiceItemShuttle * DCRT, DUCRT - MTOServiceItemDomesticCrating * ICRT, IUCRT - MTOServiceItemInternationalCrating + * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge The documentation will then update with the supported fields. type: string @@ -2072,6 +2077,7 @@ definitions: - MTOServiceItemShuttle - MTOServiceItemDomesticCrating - MTOServiceItemInternationalCrating + - MTOSerivceItemInternationalFuelSurcharge ServiceRequestDocument: properties: uploads: diff --git a/swagger/prime_v3.yaml b/swagger/prime_v3.yaml index e32a420d158..7037ba6d735 100644 --- a/swagger/prime_v3.yaml +++ b/swagger/prime_v3.yaml @@ -1312,6 +1312,10 @@ definitions: x-nullable: true ppmShipment: $ref: '#/definitions/UpdatePPMShipment' + portOfEmbarkation: + $ref: '#/definitions/Port' + portOfDebarkation: + $ref: '#/definitions/Port' UpdateMTOShipmentStatus: description: >- Contains the statuses available to the Prime when updating the state of a @@ -1646,6 +1650,10 @@ definitions: title: isOconus x-nullable: true example: false + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 required: - streetAddress1 - city @@ -2146,6 +2154,7 @@ definitions: * DOSHUT, DDSHUT - MTOServiceItemShuttle * DCRT, DUCRT - MTOServiceItemDomesticCrating * ICRT, IUCRT - MTOServiceItemInternationalCrating + * PODFSC, POEFSC - MTOSerivceItemInternationalFuelSurcharge The documentation will then update with the supported fields. type: string @@ -2156,6 +2165,7 @@ definitions: - MTOServiceItemShuttle - MTOServiceItemDomesticCrating - MTOServiceItemInternationalCrating + - MTOSerivceItemInternationalFuelSurcharge ServiceRequestDocument: properties: uploads: @@ -2643,6 +2653,10 @@ definitions: title: County x-nullable: true example: LOS ANGELES + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 required: - city - state @@ -3101,6 +3115,102 @@ definitions: - originalAddress - newAddress - contractorRemarks + Port: + description: A port that is used to move an international shipment. + type: object + properties: + id: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 + portType: + type: string + description: Port type A (Air), B (Border Crossing), S (Sea) + enum: + - A + - B + - S + portCode: + type: string + description: 3 or 4 digit port code + example: '0431' + portName: + type: string + description: Name of the port + example: PORTLAND INTL + city: + type: string + example: PORTLAND + county: + type: string + example: MULTNOMAH + state: + type: string + description: US state + example: OR + enum: + - AL + - AK + - AR + - AZ + - CA + - CO + - CT + - DC + - DE + - FL + - GA + - HI + - IA + - ID + - IL + - IN + - KS + - KY + - LA + - MA + - MD + - ME + - MI + - MN + - MO + - MS + - MT + - NC + - ND + - NE + - NH + - NJ + - NM + - NV + - NY + - OH + - OK + - OR + - PA + - RI + - SC + - SD + - TN + - TX + - UT + - VA + - VT + - WA + - WI + - WV + - WY + zip: + type: string + format: zip + title: ZIP + example: '99501' + pattern: ^(\d{5}([\-]\d{4})?)$ + country: + type: string + example: US + pattern: ^[A-Z]{2}$ + description: Two-letter country code MTOShipmentWithoutServiceItems: type: object properties: @@ -3387,6 +3497,10 @@ definitions: description: >- Single-letter designator for domestic (d) or international (i) shipments + portOfDebarkation: + $ref: '#/definitions/Port' + portOfEmbarkation: + $ref: '#/definitions/Port' MTOShipmentsWithoutServiceObjects: description: A list of shipments without their associated service items. items: From d3a7800e3a6a44cd1f457065dde13fe533c55f24 Mon Sep 17 00:00:00 2001 From: AaronW Date: Mon, 6 Jan 2025 17:56:28 +0000 Subject: [PATCH 049/113] brought in changes done on PR 14127 --- migrations/app/migrations_manifest.txt | 2 +- .../payloads/model_to_payload_test.go | 88 +++++++++++++ pkg/handlers/internalapi/orders.go | 6 + pkg/handlers/internalapi/orders_test.go | 124 ++++++++++++++++++ pkg/models/errors.go | 3 + pkg/models/order.go | 43 ++++++ pkg/testdatagen/scenario/shared.go | 54 ++++++++ pkg/testdatagen/testharness/dispatch.go | 3 + pkg/testdatagen/testharness/make_move.go | 16 +++ .../servicesCounselingFlows.spec.js | 29 ++++ playwright/tests/utils/testharness.js | 11 ++ 11 files changed, 378 insertions(+), 1 deletion(-) diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index 18c13cdaa73..a5330171a14 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1052,9 +1052,9 @@ 20241203024453_add_ppm_max_incentive_column.up.sql 20241204155919_update_ordering_proc.up.sql 20241204210208_retroactive_update_of_ppm_max_and_estimated_incentives_prd.up.sql -20241218201833_add_PPPO_BASE_ELIZABETH.up.sql 20241217163231_update_duty_locations_bad_zips.up.sql 20241217180136_add_AK_zips_to_zip3_distances.up.sql +20241218201833_add_PPPO_BASE_ELIZABETH.up.sql 20241220171035_add_additional_AK_zips_to_zip3_distances.up.sql 20241227153723_remove_empty_string_emplid_values.up.sql 20241230190638_remove_AK_zips_from_zip3.up.sql diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go index ec3072d2ca0..40fb6e67ebf 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go @@ -9,6 +9,7 @@ import ( "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/gen/ghcmessages" + "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/models/roles" @@ -315,6 +316,93 @@ func (suite *PayloadsSuite) TestShipmentAddressUpdate() { }) } +func (suite *PayloadsSuite) TestMoveWithGBLOC() { + defaultOrdersNumber := "ORDER3" + defaultTACNumber := "F8E1" + defaultDepartmentIndicator := "AIR_AND_SPACE_FORCE" + defaultGrade := "E_1" + defaultHasDependents := false + defaultSpouseHasProGear := false + defaultOrdersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION + defaultOrdersTypeDetail := internalmessages.OrdersTypeDetail("HHG_PERMITTED") + defaultStatus := models.OrderStatusDRAFT + testYear := 2018 + defaultIssueDate := time.Date(testYear, time.March, 15, 0, 0, 0, 0, time.UTC) + defaultReportByDate := time.Date(testYear, time.August, 1, 0, 0, 0, 0, time.UTC) + defaultGBLOC := "KKFA" + + originDutyLocation := models.DutyLocation{ + Name: "Custom Origin", + } + originDutyLocationTOName := "origin duty location transportation office" + firstName := "customFirst" + lastName := "customLast" + serviceMember := models.ServiceMember{ + FirstName: &firstName, + LastName: &lastName, + } + uploadedOrders := models.Document{ + ID: uuid.Must(uuid.NewV4()), + } + dependents := 7 + entitlement := models.Entitlement{ + TotalDependents: &dependents, + } + amendedOrders := models.Document{ + ID: uuid.Must(uuid.NewV4()), + } + // Create order + order := factory.BuildOrder(suite.DB(), []factory.Customization{ + { + Model: originDutyLocation, + Type: &factory.DutyLocations.OriginDutyLocation, + }, + { + Model: models.TransportationOffice{ + Name: originDutyLocationTOName, + }, + Type: &factory.TransportationOffices.OriginDutyLocation, + }, + { + Model: serviceMember, + }, + { + Model: uploadedOrders, + Type: &factory.Documents.UploadedOrders, + }, + { + Model: entitlement, + }, + { + Model: amendedOrders, + Type: &factory.Documents.UploadedAmendedOrders, + }, + }, nil) + + suite.Equal(defaultOrdersNumber, *order.OrdersNumber) + suite.Equal(defaultTACNumber, *order.TAC) + suite.Equal(defaultDepartmentIndicator, *order.DepartmentIndicator) + suite.Equal(defaultGrade, string(*order.Grade)) + suite.Equal(defaultHasDependents, order.HasDependents) + suite.Equal(defaultSpouseHasProGear, order.SpouseHasProGear) + suite.Equal(defaultOrdersType, order.OrdersType) + suite.Equal(defaultOrdersTypeDetail, *order.OrdersTypeDetail) + suite.Equal(defaultStatus, order.Status) + suite.Equal(defaultIssueDate, order.IssueDate) + suite.Equal(defaultReportByDate, order.ReportByDate) + suite.Equal(defaultGBLOC, *order.OriginDutyLocationGBLOC) + + suite.Equal(originDutyLocation.Name, order.OriginDutyLocation.Name) + suite.Equal(originDutyLocationTOName, order.OriginDutyLocation.TransportationOffice.Name) + suite.Equal(*serviceMember.FirstName, *order.ServiceMember.FirstName) + suite.Equal(*serviceMember.LastName, *order.ServiceMember.LastName) + suite.Equal(uploadedOrders.ID, order.UploadedOrdersID) + suite.Equal(uploadedOrders.ID, order.UploadedOrders.ID) + suite.Equal(*entitlement.TotalDependents, *order.Entitlement.TotalDependents) + suite.Equal(amendedOrders.ID, *order.UploadedAmendedOrdersID) + suite.Equal(amendedOrders.ID, order.UploadedAmendedOrders.ID) +} + func (suite *PayloadsSuite) TestWeightTicketUpload() { uploadID, _ := uuid.NewV4() testURL := "https://testurl.com" diff --git a/pkg/handlers/internalapi/orders.go b/pkg/handlers/internalapi/orders.go index c9b5125c827..3936dcb39e2 100644 --- a/pkg/handlers/internalapi/orders.go +++ b/pkg/handlers/internalapi/orders.go @@ -382,6 +382,12 @@ func (h UpdateOrdersHandler) Handle(params ordersop.UpdateOrdersParams) middlewa order.OriginDutyLocation = &originDutyLocation order.OriginDutyLocationID = &originDutyLocationID + originGBLOC, originGBLOCerr := models.FetchGBLOCForPostalCode(appCtx.DB(), originDutyLocation.Address.PostalCode) + if originGBLOCerr != nil { + return handlers.ResponseForError(appCtx.Logger(), originGBLOCerr), originGBLOCerr + } + order.OriginDutyLocationGBLOC = &originGBLOC.GBLOC + if payload.MoveID != "" { moveID, err := uuid.FromString(payload.MoveID.String()) diff --git a/pkg/handlers/internalapi/orders_test.go b/pkg/handlers/internalapi/orders_test.go index b2ad7897f8a..59df8daf475 100644 --- a/pkg/handlers/internalapi/orders_test.go +++ b/pkg/handlers/internalapi/orders_test.go @@ -723,6 +723,130 @@ func (suite *HandlerSuite) TestUpdateOrdersHandler() { }) } +func (suite *HandlerSuite) TestUpdateOrdersHandlerOriginPostalCodeAndGBLOC() { + factory.BuildPostalCodeToGBLOC(suite.DB(), []factory.Customization{ + { + Model: models.PostalCodeToGBLOC{ + PostalCode: "90210", + GBLOC: "KKFA", + }, + }, + }, nil) + factory.BuildPostalCodeToGBLOC(suite.DB(), []factory.Customization{ + { + Model: models.PostalCodeToGBLOC{ + PostalCode: "35023", + GBLOC: "CNNQ", + }, + }, + }, nil) + + firstAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: "90210", + }, + }, + }, nil) + updatedAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: "35023", + }, + }, + }, nil) + dutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + { + Model: models.DutyLocation{ + AddressID: firstAddress.ID, + }, + }, + }, nil) + updatedDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + { + Model: models.DutyLocation{ + AddressID: updatedAddress.ID, + }, + }, + }, nil) + newDutyLocation := factory.BuildDutyLocation(suite.DB(), nil, nil) + + order := factory.BuildOrder(suite.DB(), []factory.Customization{ + { + Model: models.Order{ + OriginDutyLocationID: &dutyLocation.ID, + NewDutyLocationID: newDutyLocation.ID, + }, + }, + }, nil) + + fetchedOrder, err := models.FetchOrder(suite.DB(), order.ID) + suite.NoError(err) + + var fetchedPostalCode, fetchedGBLOC string + fetchedPostalCode, err = fetchedOrder.GetOriginPostalCode(suite.DB()) + suite.NoError(err) + fetchedGBLOC, err = fetchedOrder.GetOriginGBLOC(suite.DB()) + suite.NoError(err) + + suite.Equal("90210", fetchedPostalCode) + suite.Equal("KKFA", fetchedGBLOC) + + newOrdersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION + issueDate := time.Date(2018, time.March, 10, 0, 0, 0, 0, time.UTC) + reportByDate := time.Date(2018, time.August, 1, 0, 0, 0, 0, time.UTC) + deptIndicator := internalmessages.DeptIndicatorAIRANDSPACEFORCE + + payload := &internalmessages.CreateUpdateOrders{ + OrdersType: &order.OrdersType, + NewDutyLocationID: handlers.FmtUUID(order.NewDutyLocationID), + OriginDutyLocationID: *handlers.FmtUUID(updatedDutyLocation.ID), + IssueDate: handlers.FmtDate(issueDate), + ReportByDate: handlers.FmtDate(reportByDate), + DepartmentIndicator: &deptIndicator, + HasDependents: handlers.FmtBool(false), + SpouseHasProGear: handlers.FmtBool(false), + ServiceMemberID: handlers.FmtUUID(order.ServiceMemberID), + Grade: models.ServiceMemberGradeE4.Pointer(), + } + + path := fmt.Sprintf("/orders/%v", order.ID.String()) + req := httptest.NewRequest("PUT", path, nil) + req = suite.AuthenticateRequest(req, order.ServiceMember) + + params := ordersop.UpdateOrdersParams{ + HTTPRequest: req, + OrdersID: *handlers.FmtUUID(order.ID), + UpdateOrders: payload, + } + + fakeS3 := storageTest.NewFakeS3Storage(true) + handlerConfig := suite.HandlerConfig() + handlerConfig.SetFileStorer(fakeS3) + + handler := UpdateOrdersHandler{handlerConfig} + + response := handler.Handle(params) + + suite.IsType(&ordersop.UpdateOrdersOK{}, response) + + okResponse := response.(*ordersop.UpdateOrdersOK) + + suite.NoError(okResponse.Payload.Validate(strfmt.Default)) + suite.Equal(string(newOrdersType), string(*okResponse.Payload.OrdersType)) + + fetchedOrder, err = models.FetchOrder(suite.DB(), order.ID) + suite.NoError(err) + + fetchedPostalCode, err = fetchedOrder.GetOriginPostalCode(suite.DB()) + suite.NoError(err) + fetchedGBLOC = *fetchedOrder.OriginDutyLocationGBLOC + suite.NoError(err) + + suite.Equal("35023", fetchedPostalCode) + suite.Equal("CNNQ", fetchedGBLOC) +} + func (suite *HandlerSuite) TestEntitlementHelperFunc() { orderGrade := internalmessages.OrderPayGrade("O-3") int64Dependents := int64(2) diff --git a/pkg/models/errors.go b/pkg/models/errors.go index 130432a4008..498cf03e771 100644 --- a/pkg/models/errors.go +++ b/pkg/models/errors.go @@ -51,3 +51,6 @@ const UniqueConstraintViolationOfficeUserOtherUniqueIDErrorString = "pq: duplica // ErrInvalidMoveID is used if a argument is provided in cases where a move ID is provided, but may be malformed, empty, or nonexistent var ErrInvalidMoveID = errors.New("INVALID_MOVE_ID") + +// ErrInvalidOrderID is used if a argument is provided in cases where a order ID is provided, but may be malformed, empty, or nonexistent +var ErrInvalidOrderID = errors.New("INVALID_ORDER_ID") diff --git a/pkg/models/order.go b/pkg/models/order.go index add1e705938..f4628894a84 100644 --- a/pkg/models/order.go +++ b/pkg/models/order.go @@ -308,6 +308,49 @@ func (o *Order) CreateNewMove(db *pop.Connection, moveOptions MoveOptions) (*Mov return createNewMove(db, *o, moveOptions) } +/* + * GetOriginPostalCode returns the GBLOC for the postal code of the the origin duty location of the order. + */ +func (o Order) GetOriginPostalCode(db *pop.Connection) (string, error) { + // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. + if uuid.UUID.IsNil(o.ID) { + return "", errors.WithMessage(ErrInvalidOrderID, "You must create the order in the DB before getting the origin GBLOC.") + } + + err := db.Load(&o, "OriginDutyLocation.Address") + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return "", errors.WithMessage(err, "No Origin Duty Location was found for the order ID "+o.ID.String()) + } + return "", err + } + + return o.OriginDutyLocation.Address.PostalCode, nil +} + +/* + * GetOriginGBLOC returns the GBLOC for the postal code of the the origin duty location of the order. + */ +func (o Order) GetOriginGBLOC(db *pop.Connection) (string, error) { + // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. + if uuid.UUID.IsNil(o.ID) { + return "", errors.WithMessage(ErrInvalidOrderID, "You must created the order in the DB before getting the destination GBLOC.") + } + + originPostalCode, err := o.GetOriginPostalCode(db) + if err != nil { + return "", err + } + + var originGBLOC PostalCodeToGBLOC + originGBLOC, err = FetchGBLOCForPostalCode(db, originPostalCode) + if err != nil { + return "", err + } + + return originGBLOC.GBLOC, nil +} + // IsComplete checks if orders have all fields necessary to approve a move func (o *Order) IsComplete() bool { diff --git a/pkg/testdatagen/scenario/shared.go b/pkg/testdatagen/scenario/shared.go index 8d04108fd6b..4450a6964e9 100644 --- a/pkg/testdatagen/scenario/shared.go +++ b/pkg/testdatagen/scenario/shared.go @@ -10507,6 +10507,60 @@ func CreateNeedsServicesCounseling(appCtx appcontext.AppContext, ordersType inte return move } +/* +Create Needs Service Counseling in a non-default GBLOC - pass in orders with all required information, shipment type, destination type, locator +*/ +func CreateNeedsServicesCounselingInOtherGBLOC(appCtx appcontext.AppContext, ordersType internalmessages.OrdersType, shipmentType models.MTOShipmentType, destinationType *models.DestinationType, locator string) models.Move { + db := appCtx.DB() + originDutyLocationAddress := factory.BuildAddress(db, []factory.Customization{ + { + Model: models.Address{ + PostalCode: "35023", + }, + }, + }, nil) + originDutyLocation := factory.BuildDutyLocation(db, []factory.Customization{ + { + Model: models.DutyLocation{ + Name: "Test Location", + AddressID: originDutyLocationAddress.ID, + }, + }, + }, nil) + order := factory.BuildOrder(db, []factory.Customization{ + { + Model: originDutyLocation, + }, + }, nil) + move := factory.BuildMove(db, []factory.Customization{ + { + Model: order, + LinkOnly: true, + }, + { + Model: models.Move{ + Locator: locator, + Status: models.MoveStatusNeedsServiceCounseling, + }, + }, + }, nil) + + factory.BuildMTOShipment(db, []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: models.MTOShipment{ + ShipmentType: shipmentType, + Status: models.MTOShipmentStatusSubmitted, + }, + }, + }, nil) + + return move +} + func CreateNeedsServicesCounselingWithAmendedOrders(appCtx appcontext.AppContext, userUploader *uploader.UserUploader, ordersType internalmessages.OrdersType, shipmentType models.MTOShipmentType, destinationType *models.DestinationType, locator string) models.Move { db := appCtx.DB() submittedAt := time.Now() diff --git a/pkg/testdatagen/testharness/dispatch.go b/pkg/testdatagen/testharness/dispatch.go index e9cf4c27d5f..dd4b421cc2c 100644 --- a/pkg/testdatagen/testharness/dispatch.go +++ b/pkg/testdatagen/testharness/dispatch.go @@ -50,6 +50,9 @@ var actionDispatcher = map[string]actionFunc{ "HHGMoveNeedsSC": func(appCtx appcontext.AppContext) testHarnessResponse { return MakeHHGMoveNeedsSC(appCtx) }, + "HHGMoveNeedsSCOtherGBLOC": func(appCtx appcontext.AppContext) testHarnessResponse { + return MakeHHGMoveNeedsSCOtherGBLOC(appCtx) + }, "HHGMoveAsUSMCNeedsSC": func(appCtx appcontext.AppContext) testHarnessResponse { return MakeHHGMoveNeedsServicesCounselingUSMC(appCtx) }, diff --git a/pkg/testdatagen/testharness/make_move.go b/pkg/testdatagen/testharness/make_move.go index fd337bb029f..50acd913415 100644 --- a/pkg/testdatagen/testharness/make_move.go +++ b/pkg/testdatagen/testharness/make_move.go @@ -4013,6 +4013,22 @@ func MakeHHGMoveNeedsSC(appCtx appcontext.AppContext) models.Move { return *newmove } +// MakeHHGMoveNeedsSCOtherGBLOC creates an fully ready move needing SC approval in a non-default GBLOC +func MakeHHGMoveNeedsSCOtherGBLOC(appCtx appcontext.AppContext) models.Move { + pcos := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION + hhg := models.MTOShipmentTypeHHG + locator := models.GenerateLocator() + move := scenario.CreateNeedsServicesCounselingInOtherGBLOC(appCtx, pcos, hhg, nil, locator) + + // re-fetch the move so that we ensure we have exactly what is in + // the db + newmove, err := models.FetchMove(appCtx.DB(), &auth.Session{}, move.ID) + if err != nil { + log.Panic(fmt.Errorf("failed to fetch move: %w", err)) + } + return *newmove +} + // MakeBoatHaulAwayMoveNeedsSC creates an fully ready move with a boat haul-away shipment needing SC approval func MakeBoatHaulAwayMoveNeedsSC(appCtx appcontext.AppContext) models.Move { userUploader := newUserUploader(appCtx) diff --git a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js index 6ab07e9026c..0285d60a72c 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js @@ -14,6 +14,33 @@ const LocationLookup = 'BEVERLY HILLS, CA 90210 (LOS ANGELES)'; test.describe('Services counselor user', () => { test.describe('with basic HHG move', () => { + test.describe('GBLOC tests', () => { + test.describe('Origin Duty Location', () => { + let moveLocatorKKFA = ''; + let moveLocatorCNNQ = ''; + test.beforeEach(async ({ scPage }) => { + const moveKKFA = await scPage.testHarness.buildHHGMoveNeedsSC(); + moveLocatorKKFA = moveKKFA.locator; + const moveCNNQ = await scPage.testHarness.buildHHGMoveNeedsSC(); + moveLocatorCNNQ = moveCNNQ.locator; + }); + + test('when origin duty location GBLOC matches services counselor GBLOC', async ({ page }) => { + const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); + await locatorFilter.fill(moveLocatorKKFA); + await locatorFilter.blur(); + await expect(page.getByTestId('locator-0')).toBeVisible(); + }); + + test('when origin duty location GBLOC does not match services counselor GBLOC', async ({ page }) => { + const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); + await locatorFilter.fill(moveLocatorCNNQ); + await locatorFilter.blur(); + await expect(page.getByTestId('locator-0')).not.toBeVisible(); + }); + }); + }); + test.beforeEach(async ({ scPage }) => { const move = await scPage.testHarness.buildHHGMoveNeedsSC(); await scPage.navigateToMove(move.locator); @@ -387,6 +414,8 @@ test.describe('Services counselor user', () => { // Edit the shipment so that the tag disappears await page.locator('[data-testid="ShipmentContainer"] .usa-button').last().click(); await page.locator('select[name="destinationType"]').selectOption({ label: 'Home of selection (HOS)' }); + await page.getByLabel('Requested pickup date').fill('16 Mar 2022'); + await page.locator('[data-testid="submitForm"]').click(); await scPage.waitForLoading(); diff --git a/playwright/tests/utils/testharness.js b/playwright/tests/utils/testharness.js index a01dac3461e..74feee4ffef 100644 --- a/playwright/tests/utils/testharness.js +++ b/playwright/tests/utils/testharness.js @@ -28,6 +28,9 @@ export class TestHarness { * @property {string} id * @property {string} locator * @property {Object} Orders + * @property {Object} OriginDutyLocation + * @property {Object} OriginDutyLocation.Address + * @property {string} OriginDutyLocation.Address.PostalCode * @property {Object} Orders.NewDutyLocation * @property {string} Orders.NewDutyLocation.name * @property {Object} Orders.ServiceMember @@ -392,6 +395,14 @@ export class TestHarness { return this.buildDefault('HHGMoveNeedsSC'); } + /** + * Use testharness to build hhg move needing SC approval in a non-default GBLOC + * @returns {Promise} + */ + async buildHHGMoveNeedsSCInOtherGBLOC() { + return this.buildDefault('HHGMoveNeedsSCOtherGBLOC'); + } + /** * Use testharness to build hhg move as USMC needing SC approval * @returns {Promise} From 0e5cca70b9c55fc94fd259187703dc87112c50e4 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 6 Jan 2025 18:12:41 +0000 Subject: [PATCH 050/113] fix sc playwright weight e2e test --- pkg/handlers/ghcapi/api.go | 10 ++-- pkg/handlers/ghcapi/orders_test.go | 4 +- pkg/handlers/ghcapi/queues_test.go | 28 ++++++--- pkg/handlers/primeapi/api.go | 2 +- pkg/services/order/order_fetcher.go | 14 ++++- pkg/services/order/order_fetcher_test.go | 73 ++++++++++++++---------- 6 files changed, 83 insertions(+), 48 deletions(-) diff --git a/pkg/handlers/ghcapi/api.go b/pkg/handlers/ghcapi/api.go index 4da669a9db9..b8d799a3464 100644 --- a/pkg/handlers/ghcapi/api.go +++ b/pkg/handlers/ghcapi/api.go @@ -174,7 +174,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { HandlerConfig: handlerConfig, EvaluationReportFetcher: evaluationreport.NewEvaluationReportFetcher(), MTOShipmentFetcher: mtoshipment.NewMTOShipmentFetcher(), - OrderFetcher: order.NewOrderFetcher(), + OrderFetcher: order.NewOrderFetcher(waf), ReportViolationFetcher: reportviolation.NewReportViolationFetcher(), } @@ -304,7 +304,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { } ghcAPI.OrderGetOrderHandler = GetOrdersHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), } ghcAPI.OrderCounselingUpdateOrderHandler = CounselingUpdateOrderHandler{ handlerConfig, @@ -535,7 +535,7 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { ghcAPI.QueuesGetMovesQueueHandler = GetMovesQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), movelocker.NewMoveUnlocker(), officeusercreator.NewOfficeUserFetcherPop(), } @@ -554,14 +554,14 @@ func NewGhcAPIHandler(handlerConfig handlers.HandlerConfig) *ghcops.MymoveAPI { ghcAPI.QueuesGetServicesCounselingQueueHandler = GetServicesCounselingQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), movelocker.NewMoveUnlocker(), officeusercreator.NewOfficeUserFetcherPop(), } ghcAPI.QueuesGetServicesCounselingOriginListHandler = GetServicesCounselingOriginListHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), officeusercreator.NewOfficeUserFetcherPop(), } diff --git a/pkg/handlers/ghcapi/orders_test.go b/pkg/handlers/ghcapi/orders_test.go index 38db697cec7..5abbf2f7d29 100644 --- a/pkg/handlers/ghcapi/orders_test.go +++ b/pkg/handlers/ghcapi/orders_test.go @@ -220,7 +220,7 @@ func (suite *HandlerSuite) TestCreateOrderWithOCONUSValues() { func (suite *HandlerSuite) TestGetOrderHandlerIntegration() { officeUser := factory.BuildOfficeUserWithRoles(nil, nil, []roles.RoleType{roles.RoleTypeTOO}) - + waf := entitlements.NewWeightAllotmentFetcher() move := factory.BuildMove(suite.DB(), nil, nil) order := move.Orders request := httptest.NewRequest("GET", "/orders/{orderID}", nil) @@ -233,7 +233,7 @@ func (suite *HandlerSuite) TestGetOrderHandlerIntegration() { handlerConfig := suite.HandlerConfig() handler := GetOrdersHandler{ handlerConfig, - orderservice.NewOrderFetcher(), + orderservice.NewOrderFetcher(waf), } // Validate incoming payload: no body to validate diff --git a/pkg/handlers/ghcapi/queues_test.go b/pkg/handlers/ghcapi/queues_test.go index e71499f1c50..5308c89e0b8 100644 --- a/pkg/handlers/ghcapi/queues_test.go +++ b/pkg/handlers/ghcapi/queues_test.go @@ -25,6 +25,8 @@ import ( ) func (suite *HandlerSuite) TestGetMoveQueuesHandler() { + waf := entitlements.NewWeightAllotmentFetcher() + officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), factory.GetTraitActiveOfficeUser(), []roles.RoleType{roles.RoleTypeTOO}) factory.BuildOfficeUserWithRoles(suite.DB(), factory.GetTraitActiveOfficeUser(), []roles.RoleType{roles.RoleTypeTIO}) officeUser.User.Roles = append(officeUser.User.Roles, roles.Role{ @@ -77,7 +79,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandler() { mockUnlocker := movelocker.NewMoveUnlocker() handler := GetMovesQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), mockUnlocker, officeusercreator.NewOfficeUserFetcherPop(), } @@ -235,6 +237,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesBranchFilter() { officeUser.User.Roles = append(officeUser.User.Roles, roles.Role{ RoleType: roles.RoleTypeTOO, }) + waf := entitlements.NewWeightAllotmentFetcher() move := models.Move{ Status: models.MoveStatusSUBMITTED, @@ -280,7 +283,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesBranchFilter() { mockUnlocker := movelocker.NewMoveUnlocker() handler := GetMovesQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), mockUnlocker, officeusercreator.NewOfficeUserFetcherPop(), } @@ -306,6 +309,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerStatuses() { officeUser.User.Roles = append(officeUser.User.Roles, roles.Role{ RoleType: roles.RoleTypeTOO, }) + waf := entitlements.NewWeightAllotmentFetcher() // Default Origin Duty Location GBLOC is KKFA hhgMove := factory.BuildSubmittedMove(suite.DB(), nil, nil) @@ -368,7 +372,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerStatuses() { mockUnlocker := movelocker.NewMoveUnlocker() handler := GetMovesQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), mockUnlocker, officeusercreator.NewOfficeUserFetcherPop(), } @@ -430,6 +434,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerFilters() { officeUser.User.Roles = append(officeUser.User.Roles, roles.Role{ RoleType: roles.RoleTypeTOO, }) + waf := entitlements.NewWeightAllotmentFetcher() submittedMove := models.Move{ Status: models.MoveStatusSUBMITTED, @@ -517,7 +522,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerFilters() { mockUnlocker := movelocker.NewMoveUnlocker() handler := GetMovesQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), mockUnlocker, officeusercreator.NewOfficeUserFetcherPop(), } @@ -678,6 +683,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerCustomerInfoFilters() { }, }, }, nil) + waf := entitlements.NewWeightAllotmentFetcher() dutyLocation2 := factory.BuildDutyLocation(suite.DB(), nil, nil) @@ -774,7 +780,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerCustomerInfoFilters() { mockUnlocker := movelocker.NewMoveUnlocker() handler := GetMovesQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), mockUnlocker, officeusercreator.NewOfficeUserFetcherPop(), } @@ -910,6 +916,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerCustomerInfoFilters() { func (suite *HandlerSuite) TestGetMoveQueuesHandlerUnauthorizedRole() { officeUser := factory.BuildOfficeUserWithRoles(nil, nil, []roles.RoleType{roles.RoleTypeTIO}) + waf := entitlements.NewWeightAllotmentFetcher() request := httptest.NewRequest("GET", "/queues/moves", nil) request = suite.AuthenticateOfficeRequest(request, officeUser) @@ -920,7 +927,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerUnauthorizedRole() { mockUnlocker := movelocker.NewMoveUnlocker() handler := GetMovesQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), mockUnlocker, officeusercreator.NewOfficeUserFetcherPop(), } @@ -941,6 +948,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerUnauthorizedUser() { serviceUser.User.Roles = append(serviceUser.User.Roles, roles.Role{ RoleType: roles.RoleTypeCustomer, }) + waf := entitlements.NewWeightAllotmentFetcher() request := httptest.NewRequest("GET", "/queues/moves", nil) request = suite.AuthenticateRequest(request, serviceUser) @@ -951,7 +959,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerUnauthorizedUser() { mockUnlocker := movelocker.NewMoveUnlocker() handler := GetMovesQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), mockUnlocker, officeusercreator.NewOfficeUserFetcherPop(), } @@ -972,6 +980,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerEmptyResults() { officeUser.User.Roles = append(officeUser.User.Roles, roles.Role{ RoleType: roles.RoleTypeTOO, }) + waf := entitlements.NewWeightAllotmentFetcher() // Create an order with an origin duty location outside of office user GBLOC excludedMove := factory.BuildMove(suite.DB(), []factory.Customization{ @@ -1003,7 +1012,7 @@ func (suite *HandlerSuite) TestGetMoveQueuesHandlerEmptyResults() { mockUnlocker := movelocker.NewMoveUnlocker() handler := GetMovesQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), mockUnlocker, officeusercreator.NewOfficeUserFetcherPop(), } @@ -1317,6 +1326,7 @@ type servicesCounselingSubtestData struct { func (suite *HandlerSuite) makeServicesCounselingSubtestData() (subtestData *servicesCounselingSubtestData) { subtestData = &servicesCounselingSubtestData{} subtestData.officeUser = factory.BuildOfficeUserWithRoles(suite.DB(), factory.GetTraitActiveOfficeUser(), []roles.RoleType{roles.RoleTypeServicesCounselor}) + waf := entitlements.NewWeightAllotmentFetcher() submittedAt := time.Date(2021, 03, 15, 0, 0, 0, 0, time.UTC) // Default Origin Duty Location GBLOC is KKFA @@ -1513,7 +1523,7 @@ func (suite *HandlerSuite) makeServicesCounselingSubtestData() (subtestData *ser mockUnlocker := movelocker.NewMoveUnlocker() subtestData.handler = GetServicesCounselingQueueHandler{ handlerConfig, - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), mockUnlocker, officeusercreator.NewOfficeUserFetcherPop(), } diff --git a/pkg/handlers/primeapi/api.go b/pkg/handlers/primeapi/api.go index 74f6427b9b6..2d8814925bc 100644 --- a/pkg/handlers/primeapi/api.go +++ b/pkg/handlers/primeapi/api.go @@ -190,7 +190,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primeoperations.MymoveAP primeAPI.MoveTaskOrderDownloadMoveOrderHandler = DownloadMoveOrderHandler{ handlerConfig, move.NewMoveSearcher(), - order.NewOrderFetcher(), + order.NewOrderFetcher(waf), primeDownloadMoveUploadPDFGenerator, } diff --git a/pkg/services/order/order_fetcher.go b/pkg/services/order/order_fetcher.go index e11d172ca37..b5e8ba961b9 100644 --- a/pkg/services/order/order_fetcher.go +++ b/pkg/services/order/order_fetcher.go @@ -25,6 +25,7 @@ import ( const RFC3339Micro = "2006-01-02T15:04:05.999999Z07:00" type orderFetcher struct { + waf services.WeightAllotmentFetcher } // QueryOption defines the type for the functional arguments used for private functions in OrderFetcher @@ -464,8 +465,8 @@ func (f orderFetcher) ListAllOrderLocations(appCtx appcontext.AppContext, office } // NewOrderFetcher creates a new struct with the service dependencies -func NewOrderFetcher() services.OrderFetcher { - return &orderFetcher{} +func NewOrderFetcher(weightAllotmentFetcher services.WeightAllotmentFetcher) services.OrderFetcher { + return &orderFetcher{waf: weightAllotmentFetcher} } // FetchOrder retrieves an Order for a given UUID @@ -489,6 +490,15 @@ func (f orderFetcher) FetchOrder(appCtx appcontext.AppContext, orderID uuid.UUID } } + // Construct weight allotted if grade is present + if order.Grade != nil { + allotment, err := f.waf.GetWeightAllotment(appCtx, string(*order.Grade), order.OrdersType) + if err != nil { + return nil, err + } + order.Entitlement.WeightAllotted = &allotment + } + // Due to a bug in pop (https://github.com/gobuffalo/pop/issues/578), we // cannot eager load the address as "OriginDutyLocation.Address" because // OriginDutyLocation is a pointer. diff --git a/pkg/services/order/order_fetcher_test.go b/pkg/services/order/order_fetcher_test.go index aa9e11fed89..2a4cdb1b0dc 100644 --- a/pkg/services/order/order_fetcher_test.go +++ b/pkg/services/order/order_fetcher_test.go @@ -11,6 +11,7 @@ import ( "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/models/roles" "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/services/entitlements" moveservice "github.com/transcom/mymove/pkg/services/move" officeuserservice "github.com/transcom/mymove/pkg/services/office_user" "github.com/transcom/mymove/pkg/testdatagen" @@ -19,7 +20,8 @@ import ( func (suite *OrderServiceSuite) TestFetchOrder() { expectedMove := factory.BuildMove(suite.DB(), nil, nil) expectedOrder := expectedMove.Orders - orderFetcher := NewOrderFetcher() + waf := entitlements.NewWeightAllotmentFetcher() + orderFetcher := NewOrderFetcher(waf) order, err := orderFetcher.FetchOrder(suite.AppContextForTest(), expectedOrder.ID) suite.FatalNoError(err) @@ -49,7 +51,7 @@ func (suite *OrderServiceSuite) TestFetchOrderWithEmptyFields() { // noticed an exception due to trying to load empty OriginDutyLocations. // This was not caught by any tests, so we're adding one now. expectedOrder := factory.BuildOrder(suite.DB(), nil, nil) - + waf := entitlements.NewWeightAllotmentFetcher() expectedOrder.Entitlement = nil expectedOrder.EntitlementID = nil expectedOrder.Grade = nil @@ -64,7 +66,7 @@ func (suite *OrderServiceSuite) TestFetchOrderWithEmptyFields() { }, }, nil) - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) order, err := orderFetcher.FetchOrder(suite.AppContextForTest(), expectedOrder.ID) suite.FatalNoError(err) @@ -74,7 +76,7 @@ func (suite *OrderServiceSuite) TestFetchOrderWithEmptyFields() { } func (suite *OrderServiceSuite) TestListOrders() { - + waf := entitlements.NewWeightAllotmentFetcher() agfmPostalCode := "06001" setupTestData := func() (models.OfficeUser, models.Move, auth.Session) { @@ -95,7 +97,7 @@ func (suite *OrderServiceSuite) TestListOrders() { factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), agfmPostalCode, "AGFM") return officeUser, move, session } - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) suite.Run("returns moves", func() { // Under test: ListOrders @@ -614,7 +616,8 @@ func (suite *OrderServiceSuite) TestListOrderWithAssignedUserSingle() { suite.Equal(createdMove.SCAssignedUser.LastName, moves[0].SCAssignedUser.LastName) } func (suite *OrderServiceSuite) TestListOrdersUSMCGBLOC() { - orderFetcher := NewOrderFetcher() + waf := entitlements.NewWeightAllotmentFetcher() + orderFetcher := NewOrderFetcher(waf) suite.Run("returns USMC order for USMC office user", func() { marines := models.AffiliationMARINES @@ -750,7 +753,8 @@ func buildPPMShipmentCloseoutComplete(suite *OrderServiceSuite, move models.Move return ppm } func (suite *OrderServiceSuite) TestListOrdersPPMCloseoutForArmyAirforce() { - orderFetcher := NewOrderFetcher() + waf := entitlements.NewWeightAllotmentFetcher() + orderFetcher := NewOrderFetcher(waf) var session auth.Session @@ -811,7 +815,8 @@ func (suite *OrderServiceSuite) TestListOrdersPPMCloseoutForArmyAirforce() { } func (suite *OrderServiceSuite) TestListOrdersPPMCloseoutForNavyCoastGuardAndMarines() { - orderFetcher := NewOrderFetcher() + waf := entitlements.NewWeightAllotmentFetcher() + orderFetcher := NewOrderFetcher(waf) suite.Run("returns Navy order for NAVY office user when there's a ppm shipment in closeout", func() { // It doesn't matter what the Origin GBLOC is for the move. Only the navy @@ -974,8 +979,10 @@ func (suite *OrderServiceSuite) TestListOrdersPPMCloseoutForNavyCoastGuardAndMar } func (suite *OrderServiceSuite) TestListOrdersMarines() { + waf := entitlements.NewWeightAllotmentFetcher() suite.Run("does not return moves where the service member affiliation is Marines for non-USMC office user", func() { - orderFetcher := NewOrderFetcher() + + orderFetcher := NewOrderFetcher(waf) marines := models.AffiliationMARINES factory.BuildMoveWithShipment(suite.DB(), []factory.Customization{ { @@ -1003,7 +1010,7 @@ func (suite *OrderServiceSuite) TestListOrdersMarines() { func (suite *OrderServiceSuite) TestListOrdersWithEmptyFields() { expectedOrder := factory.BuildOrder(suite.DB(), nil, nil) - + waf := entitlements.NewWeightAllotmentFetcher() expectedOrder.Entitlement = nil expectedOrder.EntitlementID = nil expectedOrder.Grade = nil @@ -1053,7 +1060,7 @@ func (suite *OrderServiceSuite) TestListOrdersWithEmptyFields() { AccessToken: "fakeAccessToken", } - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) moves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{PerPage: models.Int64Pointer(1), Page: models.Int64Pointer(1)}) suite.FatalNoError(err) @@ -1063,6 +1070,7 @@ func (suite *OrderServiceSuite) TestListOrdersWithEmptyFields() { func (suite *OrderServiceSuite) TestListOrdersWithPagination() { officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) + waf := entitlements.NewWeightAllotmentFetcher() session := auth.Session{ ApplicationName: auth.OfficeApp, Roles: officeUser.User.Roles, @@ -1075,7 +1083,7 @@ func (suite *OrderServiceSuite) TestListOrdersWithPagination() { factory.BuildMoveWithShipment(suite.DB(), nil, nil) } - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) params := services.ListOrderParams{Page: models.Int64Pointer(1), PerPage: models.Int64Pointer(1)} moves, count, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, ¶ms) @@ -1094,7 +1102,7 @@ func (suite *OrderServiceSuite) TestListOrdersWithSortOrder() { affiliation := models.AffiliationNAVY edipi := "9999999999" var officeUser models.OfficeUser - + waf := entitlements.NewWeightAllotmentFetcher() // SET UP: Dates for sorting by Requested Move Date // - We want dates 2 and 3 to sandwich requestedMoveDate1 so we can test that the min() query is working requestedMoveDate1 := time.Date(testdatagen.GHCTestYear, 02, 20, 0, 0, 0, 0, time.UTC) @@ -1156,7 +1164,7 @@ func (suite *OrderServiceSuite) TestListOrdersWithSortOrder() { return expectedMove1, expectedMove2, session } - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) suite.Run("Sort by locator code", func() { expectedMove1, expectedMove2, session := setupTestData() @@ -1319,6 +1327,7 @@ func getPPMShipmentWithCloseoutOfficeNeedsCloseout(suite *OrderServiceSuite, clo func (suite *OrderServiceSuite) TestListOrdersNeedingServicesCounselingWithPPMCloseoutColumnsSort() { defaultShipmentPickupPostalCode := "90210" + waf := entitlements.NewWeightAllotmentFetcher() setupTestData := func() models.OfficeUser { // Make an office user → GBLOC X officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) @@ -1331,7 +1340,7 @@ func (suite *OrderServiceSuite) TestListOrdersNeedingServicesCounselingWithPPMCl return officeUser } - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) var session auth.Session @@ -1595,7 +1604,7 @@ func (suite *OrderServiceSuite) TestListOrdersNeedingServicesCounselingWithPPMCl } func (suite *OrderServiceSuite) TestListOrdersNeedingServicesCounselingWithGBLOCSortFilter() { - + waf := entitlements.NewWeightAllotmentFetcher() suite.Run("Filter by origin GBLOC", func() { // TESTCASE SCENARIO @@ -1665,7 +1674,7 @@ func (suite *OrderServiceSuite) TestListOrdersNeedingServicesCounselingWithGBLOC }, }, nil) // Setup and run the function under test requesting status NEEDS SERVICE COUNSELING - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) statuses := []string{"NEEDS SERVICE COUNSELING"} // Sort by origin GBLOC, filter by status params := services.ListOrderParams{Sort: models.StringPointer("originGBLOC"), Order: models.StringPointer("asc"), Status: statuses} @@ -1687,6 +1696,7 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithNTSRelease() { }, }, }, nil) + waf := entitlements.NewWeightAllotmentFetcher() // Make a TOO user and the postal code to GBLOC link. tooOfficeUser := factory.BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) session := auth.Session{ @@ -1697,7 +1707,7 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithNTSRelease() { AccessToken: "fakeAccessToken", } - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) moves, moveCount, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), tooOfficeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{}) suite.FatalNoError(err) @@ -1708,7 +1718,7 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithNTSRelease() { func (suite *OrderServiceSuite) TestListOrdersForTOOWithPPM() { postalCode := "50309" partialPPMType := models.MovePPMTypePARTIAL - + waf := entitlements.NewWeightAllotmentFetcher() ppmShipment := factory.BuildPPMShipment(suite.DB(), []factory.Customization{ { Model: models.Order{ @@ -1741,7 +1751,7 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithPPM() { // GBLOC for the below doesn't really matter, it just means the query for the moves passes the inner join in ListOrders factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), ppmShipment.PickupAddress.PostalCode, tooOfficeUser.TransportationOffice.Gbloc) - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) moves, moveCount, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), tooOfficeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{}) suite.FatalNoError(err) suite.Equal(1, moveCount) @@ -1751,7 +1761,7 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithPPM() { func (suite *OrderServiceSuite) TestListOrdersWithViewAsGBLOCParam() { var hqOfficeUser models.OfficeUser var hqOfficeUserAGFM models.OfficeUser - + waf := entitlements.NewWeightAllotmentFetcher() requestedMoveDate1 := time.Date(testdatagen.GHCTestYear, 02, 20, 0, 0, 0, 0, time.UTC) requestedMoveDate2 := time.Date(testdatagen.GHCTestYear, 03, 03, 0, 0, 0, 0, time.UTC) @@ -1833,7 +1843,7 @@ func (suite *OrderServiceSuite) TestListOrdersWithViewAsGBLOCParam() { return expectedMove1, expectedMove2, expectedShipment3, hqSession, hqSessionAGFM } - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) suite.Run("Sort by locator code", func() { expectedMove1, expectedMove2, expectedShipment3, hqSession, hqSessionAGFM := setupTestData() @@ -1873,6 +1883,7 @@ func (suite *OrderServiceSuite) TestListOrdersWithViewAsGBLOCParam() { func (suite *OrderServiceSuite) TestListOrdersForTOOWithPPMWithDeletedShipment() { postalCode := "50309" deletedAt := time.Now() + waf := entitlements.NewWeightAllotmentFetcher() move := factory.BuildMove(suite.DB(), []factory.Customization{ { Model: models.Move{ @@ -1915,7 +1926,7 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithPPMWithDeletedShipment() AccessToken: "fakeAccessToken", } - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) moves, moveCount, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), tooOfficeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{Status: []string{string(models.MoveStatusSUBMITTED)}}) suite.FatalNoError(err) suite.Equal(0, moveCount) @@ -1925,6 +1936,7 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithPPMWithDeletedShipment() func (suite *OrderServiceSuite) TestListOrdersForTOOWithPPMWithOneDeletedShipmentButOtherExists() { postalCode := "50309" deletedAt := time.Now() + waf := entitlements.NewWeightAllotmentFetcher() move := factory.BuildMove(suite.DB(), []factory.Customization{ { Model: models.Move{ @@ -1995,7 +2007,7 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithPPMWithOneDeletedShipmen AccessToken: "fakeAccessToken", } - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) moves, moveCount, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), tooOfficeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{}) suite.FatalNoError(err) suite.Equal(1, moveCount) @@ -2003,8 +2015,9 @@ func (suite *OrderServiceSuite) TestListOrdersForTOOWithPPMWithOneDeletedShipmen } func (suite *OrderServiceSuite) TestListAllOrderLocations() { + waf := entitlements.NewWeightAllotmentFetcher() suite.Run("returns a list of all order locations in the current users queue", func() { - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeServicesCounselor}) session := auth.Session{ ApplicationName: auth.OfficeApp, @@ -2028,7 +2041,7 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { edipi := "9999999998" var officeUser models.OfficeUser var session auth.Session - + waf := entitlements.NewWeightAllotmentFetcher() requestedMoveDate1 := time.Date(testdatagen.GHCTestYear, 05, 20, 0, 0, 0, 0, time.UTC) requestedMoveDate2 := time.Date(testdatagen.GHCTestYear, 07, 03, 0, 0, 0, 0, time.UTC) @@ -2081,7 +2094,7 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { } }) - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) suite.Run("list moves by customer name - full name (last, first)", func() { // Search "Spacemen, Margaret" @@ -2150,8 +2163,9 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { } func (suite *OrderServiceSuite) TestListAllOrderLocationsWithViewAsGBLOCParam() { + waf := entitlements.NewWeightAllotmentFetcher() suite.Run("returns a list of all order locations in the current users queue", func() { - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) officeUserFetcher := officeuserservice.NewOfficeUserFetcherPop() movesContainOriginDutyLocation := func(moves models.Moves, keyword string) func() (success bool) { return func() (success bool) { @@ -2249,9 +2263,10 @@ func (suite *OrderServiceSuite) TestListAllOrderLocationsWithViewAsGBLOCParam() func (suite *OrderServiceSuite) TestOriginDutyLocationFilter() { var session auth.Session + waf := entitlements.NewWeightAllotmentFetcher() var expectedMove models.Move var officeUser models.OfficeUser - orderFetcher := NewOrderFetcher() + orderFetcher := NewOrderFetcher(waf) suite.PreloadData(func() { setupTestData := func() (models.OfficeUser, models.Move, auth.Session) { officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) From 9466cfada0b5d0709439a2b348bc167fca163b50 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 6 Jan 2025 19:44:48 +0000 Subject: [PATCH 051/113] continued test fixes --- pkg/handlers/pptasapi/api.go | 4 +- pkg/handlers/supportapi/api_test.go | 7 ++++ .../payloads/model_to_payload_test.go | 7 ++++ pkg/services/event/event_test.go | 6 +++ pkg/services/order/order_fetcher_test.go | 42 +++++++++++-------- .../pptas_report/pptas_report_list_fetcher.go | 3 +- .../pptas_report_list_fetcher_test.go | 4 +- 7 files changed, 53 insertions(+), 20 deletions(-) diff --git a/pkg/handlers/pptasapi/api.go b/pkg/handlers/pptasapi/api.go index 7278f68c0dd..b3e6a418831 100644 --- a/pkg/handlers/pptasapi/api.go +++ b/pkg/handlers/pptasapi/api.go @@ -9,6 +9,7 @@ import ( pptasops "github.com/transcom/mymove/pkg/gen/pptasapi/pptasoperations" "github.com/transcom/mymove/pkg/handlers" paymentrequesthelper "github.com/transcom/mymove/pkg/payment_request" + "github.com/transcom/mymove/pkg/services/entitlements" lineofaccounting "github.com/transcom/mymove/pkg/services/line_of_accounting" "github.com/transcom/mymove/pkg/services/move" "github.com/transcom/mymove/pkg/services/ppmshipment" @@ -18,6 +19,7 @@ import ( func NewPPTASAPI(handlerConfig handlers.HandlerConfig) *pptasops.MymoveAPI { pptasSpec, err := loads.Analyzed(pptasapi.SwaggerJSON, "") + waf := entitlements.NewWeightAllotmentFetcher() if err != nil { log.Fatalln(err) } @@ -31,7 +33,7 @@ func NewPPTASAPI(handlerConfig handlers.HandlerConfig) *pptasops.MymoveAPI { pptasAPI.MovesPptasReportsHandler = PPTASReportsHandler{ HandlerConfig: handlerConfig, - PPTASReportListFetcher: report.NewPPTASReportListFetcher(ppmEstimator, moveFetcher, tacFetcher, loaFetcher), + PPTASReportListFetcher: report.NewPPTASReportListFetcher(ppmEstimator, moveFetcher, tacFetcher, loaFetcher, waf), } return pptasAPI diff --git a/pkg/handlers/supportapi/api_test.go b/pkg/handlers/supportapi/api_test.go index 580dc4c4081..688581e3c3b 100644 --- a/pkg/handlers/supportapi/api_test.go +++ b/pkg/handlers/supportapi/api_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -15,6 +16,12 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } +func (suite *HandlerSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + // AfterTest completes tests by trying to close open files func (hs *HandlerSuite) AfterTest() { for _, file := range hs.TestFilesToClose() { diff --git a/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go index eeb93bc0394..cbf3fb8e66f 100644 --- a/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/transcom/mymove/pkg/etag" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" @@ -22,6 +23,12 @@ type PayloadsSuite struct { handlers.BaseHandlerTestSuite } +func (suite *PayloadsSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/services/event/event_test.go b/pkg/services/event/event_test.go index 3de1e255036..d50ba34a524 100644 --- a/pkg/services/event/event_test.go +++ b/pkg/services/event/event_test.go @@ -20,6 +20,12 @@ type EventServiceSuite struct { *testingsuite.PopTestSuite } +func (suite *EventServiceSuite) SetupSuite() { + suite.PreloadData(func() { + factory.SetupDefaultAllotments(suite.DB()) + }) +} + func TestEventServiceSuite(t *testing.T) { ts := &EventServiceSuite{ PopTestSuite: testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), testingsuite.WithPerTestTransaction()), diff --git a/pkg/services/order/order_fetcher_test.go b/pkg/services/order/order_fetcher_test.go index 2a4cdb1b0dc..7b3e6693cd4 100644 --- a/pkg/services/order/order_fetcher_test.go +++ b/pkg/services/order/order_fetcher_test.go @@ -2045,7 +2045,7 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { requestedMoveDate1 := time.Date(testdatagen.GHCTestYear, 05, 20, 0, 0, 0, 0, time.UTC) requestedMoveDate2 := time.Date(testdatagen.GHCTestYear, 07, 03, 0, 0, 0, 0, time.UTC) - suite.PreloadData(func() { + setupData := func() { factory.BuildMoveWithShipment(suite.DB(), []factory.Customization{ { Model: models.Move{ @@ -2092,11 +2092,12 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { IDToken: "fake_token", AccessToken: "fakeAccessToken", } - }) + } orderFetcher := NewOrderFetcher(waf) suite.Run("list moves by customer name - full name (last, first)", func() { + setupData() // Search "Spacemen, Margaret" params := services.ListOrderParams{CustomerName: models.StringPointer("Spacemen, Margaret"), Sort: models.StringPointer("customerName"), Order: models.StringPointer("asc")} moves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, ¶ms) @@ -2106,6 +2107,7 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { }) suite.Run("list moves by customer name - full name (first last)", func() { + setupData() // Search "Margaret Spacemen" params := services.ListOrderParams{CustomerName: models.StringPointer("Margaret Spacemen"), Sort: models.StringPointer("customerName"), Order: models.StringPointer("asc")} moves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, ¶ms) @@ -2115,6 +2117,7 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { }) suite.Run("list moves by customer name - partial last (multiple)", func() { + setupData() // Search "space" params := services.ListOrderParams{CustomerName: models.StringPointer("space"), Sort: models.StringPointer("customerName"), Order: models.StringPointer("asc")} moves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, ¶ms) @@ -2125,6 +2128,7 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { }) suite.Run("list moves by customer name - partial last (single)", func() { + setupData() // Search "Light" params := services.ListOrderParams{CustomerName: models.StringPointer("Light"), Sort: models.StringPointer("customerName"), Order: models.StringPointer("asc")} moves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, ¶ms) @@ -2134,6 +2138,7 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { }) suite.Run("list moves by customer name - partial first", func() { + setupData() // Search "leo" params := services.ListOrderParams{CustomerName: models.StringPointer("leo"), Sort: models.StringPointer("customerName"), Order: models.StringPointer("asc")} moves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, ¶ms) @@ -2144,6 +2149,7 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { }) suite.Run("list moves by customer name - partial matching within first or last", func() { + setupData() // Search "ar" params := services.ListOrderParams{CustomerName: models.StringPointer("ar"), Sort: models.StringPointer("customerName"), Order: models.StringPointer("asc")} moves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, ¶ms) @@ -2154,6 +2160,7 @@ func (suite *OrderServiceSuite) TestListOrdersFilteredByCustomerName() { }) suite.Run("list moves by customer name - empty", func() { + setupData() // Search "johnny" params := services.ListOrderParams{CustomerName: models.StringPointer("johnny"), Sort: models.StringPointer("customerName"), Order: models.StringPointer("asc")} moves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, ¶ms) @@ -2267,23 +2274,22 @@ func (suite *OrderServiceSuite) TestOriginDutyLocationFilter() { var expectedMove models.Move var officeUser models.OfficeUser orderFetcher := NewOrderFetcher(waf) - suite.PreloadData(func() { - setupTestData := func() (models.OfficeUser, models.Move, auth.Session) { - officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) - session := auth.Session{ - ApplicationName: auth.OfficeApp, - Roles: officeUser.User.Roles, - OfficeUserID: officeUser.ID, - IDToken: "fake_token", - AccessToken: "fakeAccessToken", - } - move := factory.BuildMoveWithShipment(suite.DB(), nil, nil) - return officeUser, move, session + setupTestData := func() (models.OfficeUser, models.Move, auth.Session) { + officeUser := factory.BuildOfficeUserWithRoles(suite.DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) + session := auth.Session{ + ApplicationName: auth.OfficeApp, + Roles: officeUser.User.Roles, + OfficeUserID: officeUser.ID, + IDToken: "fake_token", + AccessToken: "fakeAccessToken", } - officeUser, expectedMove, session = setupTestData() - }) - locationName := expectedMove.Orders.OriginDutyLocation.Name + move := factory.BuildMoveWithShipment(suite.DB(), nil, nil) + return officeUser, move, session + } + suite.Run("Returns orders matching full originDutyLocation name filter", func() { + officeUser, expectedMove, session = setupTestData() + locationName := expectedMove.Orders.OriginDutyLocation.Name expectedMoves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{OriginDutyLocation: strings.Split(locationName, " ")}) suite.NoError(err) suite.Equal(1, len(expectedMoves)) @@ -2291,6 +2297,8 @@ func (suite *OrderServiceSuite) TestOriginDutyLocationFilter() { }) suite.Run("Returns orders matching partial originDutyLocation name filter", func() { + officeUser, expectedMove, session = setupTestData() + locationName := expectedMove.Orders.OriginDutyLocation.Name //Split the location name and retrieve a substring (first string) for the search param partialParamSearch := strings.Split(locationName, " ")[0] expectedMoves, _, err := orderFetcher.ListOrders(suite.AppContextWithSessionForTest(&session), officeUser.ID, roles.RoleTypeTOO, &services.ListOrderParams{OriginDutyLocation: strings.Split(partialParamSearch, " ")}) diff --git a/pkg/services/pptas_report/pptas_report_list_fetcher.go b/pkg/services/pptas_report/pptas_report_list_fetcher.go index 85fc0c45f6c..95e0f5bb9d0 100644 --- a/pkg/services/pptas_report/pptas_report_list_fetcher.go +++ b/pkg/services/pptas_report/pptas_report_list_fetcher.go @@ -22,12 +22,13 @@ type pptasReportListFetcher struct { waf services.WeightAllotmentFetcher } -func NewPPTASReportListFetcher(estimator services.PPMEstimator, moveFetcher services.MoveFetcher, tacFetcher services.TransportationAccountingCodeFetcher, loaFetcher services.LineOfAccountingFetcher) services.PPTASReportListFetcher { +func NewPPTASReportListFetcher(estimator services.PPMEstimator, moveFetcher services.MoveFetcher, tacFetcher services.TransportationAccountingCodeFetcher, loaFetcher services.LineOfAccountingFetcher, weightAllotmentFetcher services.WeightAllotmentFetcher) services.PPTASReportListFetcher { return &pptasReportListFetcher{ estimator: estimator, moveFetcher: moveFetcher, tacFetcher: tacFetcher, loaFetcher: loaFetcher, + waf: weightAllotmentFetcher, } } diff --git a/pkg/services/pptas_report/pptas_report_list_fetcher_test.go b/pkg/services/pptas_report/pptas_report_list_fetcher_test.go index 49748c7bc0d..e5d441ea483 100644 --- a/pkg/services/pptas_report/pptas_report_list_fetcher_test.go +++ b/pkg/services/pptas_report/pptas_report_list_fetcher_test.go @@ -8,6 +8,7 @@ import ( "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services/entitlements" mocks "github.com/transcom/mymove/pkg/services/mocks" ) @@ -16,8 +17,9 @@ func (suite *ReportServiceSuite) TestReportFetcher() { moveFetcher := mocks.MoveFetcher{} tacFetcher := mocks.TransportationAccountingCodeFetcher{} loaFetcher := mocks.LineOfAccountingFetcher{} + waf := entitlements.NewWeightAllotmentFetcher() - reportListFetcher := NewPPTASReportListFetcher(&ppmEstimator, &moveFetcher, &tacFetcher, &loaFetcher) + reportListFetcher := NewPPTASReportListFetcher(&ppmEstimator, &moveFetcher, &tacFetcher, &loaFetcher, waf) // defaultSearchParams := services.MoveTaskOrderFetcherParams{} appCtx := suite.AppContextForTest() From f803ea4ad2294a339a395a3b20b0db05c5b716dc Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 2 Dec 2024 21:23:58 +0000 Subject: [PATCH 052/113] attempt slow tag at describe level due to playwright version --- .../office/servicescounseling/servicesCounselingFlows.spec.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js index 6ab07e9026c..9f6b76543d4 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js @@ -13,6 +13,7 @@ const supportingDocsEnabled = process.env.FEATURE_FLAG_MANAGE_SUPPORTING_DOCS; const LocationLookup = 'BEVERLY HILLS, CA 90210 (LOS ANGELES)'; test.describe('Services counselor user', () => { + test.slow(); test.describe('with basic HHG move', () => { test.beforeEach(async ({ scPage }) => { const move = await scPage.testHarness.buildHHGMoveNeedsSC(); @@ -381,6 +382,7 @@ test.describe('Services counselor user', () => { }); test('is able to see that the tag next to shipment is updated', async ({ page, scPage }) => { + test.slow(); // timeouts // Verify that there's a tag on the left nav that flags missing information await expect(page.locator('[data-testid="shipment-missing-info-alert"]')).toContainText('1'); @@ -398,6 +400,7 @@ test.describe('Services counselor user', () => { }); test('can complete review of PPM shipment documents and view documents after', async ({ page, scPage }) => { + test.slow(); // timeouts const move = await scPage.testHarness.buildApprovedMoveWithPPMAllDocTypesOffice(); await scPage.navigateToCloseoutMove(move.locator); From 4a0477ec676ed9db15046b4112b5b4b500cc3dc0 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 2 Dec 2024 21:30:47 +0000 Subject: [PATCH 053/113] move timeouts --- .../office/servicescounseling/servicesCounselingFlows.spec.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js index 9f6b76543d4..273393ffacf 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js @@ -382,7 +382,6 @@ test.describe('Services counselor user', () => { }); test('is able to see that the tag next to shipment is updated', async ({ page, scPage }) => { - test.slow(); // timeouts // Verify that there's a tag on the left nav that flags missing information await expect(page.locator('[data-testid="shipment-missing-info-alert"]')).toContainText('1'); @@ -400,7 +399,6 @@ test.describe('Services counselor user', () => { }); test('can complete review of PPM shipment documents and view documents after', async ({ page, scPage }) => { - test.slow(); // timeouts const move = await scPage.testHarness.buildApprovedMoveWithPPMAllDocTypesOffice(); await scPage.navigateToCloseoutMove(move.locator); From aa92b07a8bf3948cf9c7c9646f404e3bc2086247 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 2 Dec 2024 21:51:52 +0000 Subject: [PATCH 054/113] another flaky fix attempt --- .../servicescounseling/servicesCounselingQueueFilters.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/tests/office/servicescounseling/servicesCounselingQueueFilters.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingQueueFilters.spec.js index e4ef042c31d..abd3f13ea30 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingQueueFilters.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingQueueFilters.spec.js @@ -10,7 +10,7 @@ import { test, expect } from './servicesCounselingTestFixture'; test.describe('Services counselor user', () => { let moveLocator = ''; let moveWithNeedsCloseoutLocator = ''; - + test.slow(); test.describe('with PPM shipment ready for closeout', () => { let dutyLocationName = ''; test.beforeEach(async ({ scPage }) => { From 85bd18a58c5a4ec5e498379153a76a5a72471954 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 2 Dec 2024 16:17:18 -0600 Subject: [PATCH 055/113] more slow tests --- .../tests/my/milmove/ppms/entireShipmentCloseout.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js b/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js index 2ab93a21207..0a1b020fcc7 100644 --- a/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js +++ b/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js @@ -11,6 +11,7 @@ const multiMoveEnabled = process.env.FEATURE_FLAG_MULTI_MOVE; test.describe('Entire PPM closeout flow', () => { test.skip(multiMoveEnabled === 'true', 'Skip if MultiMove workflow is enabled.'); + test.slow(); forEachViewport(async () => { test(`flows through happy path for existing shipment`, async ({ customerPpmPage }) => { const move = await customerPpmPage.testHarness.buildApprovedMoveWithPPM(); @@ -126,7 +127,7 @@ test.describe('Entire PPM closeout flow', () => { test.describe('(MultiMove) Entire PPM closeout flow (MultiMove Workflow)', () => { test.skip(multiMoveEnabled === 'false', 'Skip if MultiMove workflow is not enabled.'); - + test.slow(); forEachViewport(async () => { test(`flows through happy path for existing shipment`, async ({ customerPpmPage }) => { const move = await customerPpmPage.testHarness.buildApprovedMoveWithPPM(); From cc8d765fb68dbd635b1c57e7f0d12da0c612bbc9 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 2 Dec 2024 22:35:55 +0000 Subject: [PATCH 056/113] moreee sloowww --- .../servicescounseling/servicesCounselingMovingExpenses.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/playwright/tests/office/servicescounseling/servicesCounselingMovingExpenses.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingMovingExpenses.spec.js index 4933c3616f2..8a12e813b20 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingMovingExpenses.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingMovingExpenses.spec.js @@ -1,6 +1,7 @@ import { test, expect } from './servicesCounselingTestFixture'; test('A service counselor can approve/reject moving expenses', async ({ page, scPage }) => { + test.slow(); // Create a move with TestHarness, and then navigate to the move details page for it const move = await scPage.testHarness.buildApprovedMoveWithPPMMovingExpenseOffice(); await scPage.navigateToCloseoutMove(move.locator); @@ -94,6 +95,7 @@ test('A service counselor can approve/reject moving expenses', async ({ page, sc }); test('Review documents page displays correct value for Total days in SIT', async ({ page, scPage }) => { + test.slow(); // Create a move with TestHarness, and then navigate to the move details page for it const move = await scPage.testHarness.buildApprovedMoveWithPPMMovingExpenseOffice(); await scPage.navigateToCloseoutMove(move.locator); From 416e9a7338c98f17c9478e365fc7b688ba7b5667 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 2 Dec 2024 23:00:50 +0000 Subject: [PATCH 057/113] move test slow again --- .../ppms/entireShipmentCloseout.spec.js | 12 +++++++-- .../servicesCounselingFlows.spec.js | 27 ++++++++++++++++++- .../servicesCounselingQueueFilters.spec.js | 7 ++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js b/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js index 0a1b020fcc7..7c14a957be7 100644 --- a/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js +++ b/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js @@ -11,9 +11,10 @@ const multiMoveEnabled = process.env.FEATURE_FLAG_MULTI_MOVE; test.describe('Entire PPM closeout flow', () => { test.skip(multiMoveEnabled === 'true', 'Skip if MultiMove workflow is enabled.'); - test.slow(); + forEachViewport(async () => { test(`flows through happy path for existing shipment`, async ({ customerPpmPage }) => { + test.slow(); const move = await customerPpmPage.testHarness.buildApprovedMoveWithPPM(); await customerPpmPage.signInForPPMWithMove(move); @@ -33,6 +34,7 @@ test.describe('Entire PPM closeout flow', () => { }); test(`happy path with edits and backs`, async ({ customerPpmPage }) => { + test.slow(); const move = await customerPpmPage.testHarness.buildMoveWithPPMShipmentReadyForFinalCloseout(); await customerPpmPage.signInForPPMWithMove(move); @@ -55,6 +57,7 @@ test.describe('Entire PPM closeout flow', () => { }); test(`delete complete and incomplete line items`, async ({ customerPpmPage }) => { + test.slow(); const move = await customerPpmPage.testHarness.buildMoveWithPPMShipmentReadyForFinalCloseout(); await customerPpmPage.signInForPPMWithMove(move); @@ -95,6 +98,7 @@ test.describe('Entire PPM closeout flow', () => { }); test(`deleting weight tickets updates final incentive`, async ({ customerPpmPage }) => { + test.slow(); const move = await customerPpmPage.testHarness.buildMoveWithPPMShipmentReadyForFinalCloseout(); await customerPpmPage.signInForPPMWithMove(move); @@ -127,9 +131,10 @@ test.describe('Entire PPM closeout flow', () => { test.describe('(MultiMove) Entire PPM closeout flow (MultiMove Workflow)', () => { test.skip(multiMoveEnabled === 'false', 'Skip if MultiMove workflow is not enabled.'); - test.slow(); + forEachViewport(async () => { test(`flows through happy path for existing shipment`, async ({ customerPpmPage }) => { + test.slow(); const move = await customerPpmPage.testHarness.buildApprovedMoveWithPPM(); await customerPpmPage.signInForPPMWithMove(move); @@ -150,6 +155,7 @@ test.describe('(MultiMove) Entire PPM closeout flow (MultiMove Workflow)', () => }); test(`happy path with edits and backs`, async ({ customerPpmPage }) => { + test.slow(); const move = await customerPpmPage.testHarness.buildMoveWithPPMShipmentReadyForFinalCloseout(); await customerPpmPage.signInForPPMWithMove(move); @@ -176,6 +182,7 @@ test.describe('(MultiMove) Entire PPM closeout flow (MultiMove Workflow)', () => }); test(`delete complete and incomplete line items`, async ({ customerPpmPage }) => { + test.slow(); const move = await customerPpmPage.testHarness.buildMoveWithPPMShipmentReadyForFinalCloseout(); await customerPpmPage.signInForPPMWithMove(move); @@ -223,6 +230,7 @@ test.describe('(MultiMove) Entire PPM closeout flow (MultiMove Workflow)', () => }); test(`deleting weight tickets updates final incentive`, async ({ customerPpmPage }) => { + test.slow(); const move = await customerPpmPage.testHarness.buildMoveWithPPMShipmentReadyForFinalCloseout(); await customerPpmPage.signInForPPMWithMove(move); diff --git a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js index 273393ffacf..1ccb0ade5d9 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js @@ -13,7 +13,6 @@ const supportingDocsEnabled = process.env.FEATURE_FLAG_MANAGE_SUPPORTING_DOCS; const LocationLookup = 'BEVERLY HILLS, CA 90210 (LOS ANGELES)'; test.describe('Services counselor user', () => { - test.slow(); test.describe('with basic HHG move', () => { test.beforeEach(async ({ scPage }) => { const move = await scPage.testHarness.buildHHGMoveNeedsSC(); @@ -21,6 +20,7 @@ test.describe('Services counselor user', () => { }); test('is able to click on move and submit after using the move code filter', async ({ page }) => { + test.slow(); /** * Move Details page */ @@ -39,6 +39,7 @@ test.describe('Services counselor user', () => { }); test('is able to flag a move for financial review', async ({ page, scPage }) => { + test.slow(); // click to trigger financial review modal await page.getByText('Flag move for financial review').click(); @@ -72,6 +73,7 @@ test.describe('Services counselor user', () => { }); test('is able to edit a shipment', async ({ page, scPage }) => { + test.slow(); await page.locator('[data-testid="ShipmentContainer"] .usa-button').first().click(); await page.locator('#requestedPickupDate').clear(); await page.locator('#requestedPickupDate').fill('16 Mar 2022'); @@ -99,6 +101,7 @@ test.describe('Services counselor user', () => { await expect(page.locator('.usa-alert__text')).toContainText('Your changes were saved.'); }); test('is able to view Origin GBLOC', async ({ page }) => { + test.slow(); // Check for Origin GBLOC label await expect(page.getByTestId('originGBLOC')).toHaveText('Origin GBLOC'); await expect(page.getByTestId('infoBlock')).toContainText('KKFA'); @@ -112,6 +115,7 @@ test.describe('Services counselor user', () => { }); test('is able to view USMC as Origin GBLOC', async ({ page }) => { + test.slow(); // Check for Origin GBLOC label await expect(page.getByTestId('originGBLOC')).toHaveText('Origin GBLOC'); await expect(page.getByTestId('infoBlock')).toContainText('KKFA / USMC'); @@ -125,6 +129,7 @@ test.describe('Services counselor user', () => { }); test('is able to view orders and amended orders', async ({ page }) => { + test.slow(); await page.getByRole('link', { name: 'View and edit orders' }).click(); await page.getByTestId('openMenu').click(); await expect(page.getByTestId('DocViewerMenu').getByTestId('button')).toHaveCount(3); @@ -144,6 +149,7 @@ test.describe('Services counselor user', () => { }); test('is able to add and delete orders and amended orders', async ({ page, officePage }) => { + test.slow(); await page.getByRole('link', { name: 'View and edit orders' }).click(); // check initial quanity of files @@ -200,6 +206,7 @@ test.describe('Services counselor user', () => { }); test('is able to add and delete supporting documents', async ({ page, officePage }) => { + test.slow(); test.skip(supportingDocsEnabled === 'false', 'Skip if Supporting Documents is not enabled.'); await page.getByRole('link', { name: 'Supporting Documents' }).click(); await expect(page.getByText('No supporting documents have been uploaded.')).toBeVisible(); @@ -234,6 +241,7 @@ test.describe('Services counselor user', () => { }); test('is able to add a shipment', async ({ page, scPage }) => { + test.slow(); const deliveryDate = new Date().toLocaleDateString('en-US'); await expect(page.locator('[data-testid="ShipmentContainer"] .usa-button')).toHaveCount(2); @@ -321,6 +329,7 @@ test.describe('Services counselor user', () => { }); test('is able to see and use the left navigation', async ({ page }) => { + test.slow(); await expect(page.locator('a[href*="#shipments"]')).toContainText('Shipments'); await expect(page.locator('a[href*="#orders"]')).toContainText('Orders'); await expect(page.locator('a[href*="#allowances"]')).toContainText('Allowances'); @@ -336,6 +345,7 @@ test.describe('Services counselor user', () => { }); test('is able to edit a shipment', async ({ page, scPage }) => { + test.slow(); await page.locator('[data-testid="ShipmentContainer"] .usa-button').first().click(); await page.locator('#requestedPickupDate').clear(); await page.locator('#requestedPickupDate').fill('16 Mar 2022'); @@ -359,6 +369,7 @@ test.describe('Services counselor user', () => { }); test('is able to update destination type if delivery address is unknown', async ({ page, scPage }) => { + test.slow(); await page.locator('[data-testid="ShipmentContainer"] .usa-button').first().click(); await page.locator('#requestedPickupDate').clear(); await page.locator('#requestedPickupDate').fill('16 Mar 2022'); @@ -382,6 +393,7 @@ test.describe('Services counselor user', () => { }); test('is able to see that the tag next to shipment is updated', async ({ page, scPage }) => { + test.slow(); // Verify that there's a tag on the left nav that flags missing information await expect(page.locator('[data-testid="shipment-missing-info-alert"]')).toContainText('1'); @@ -399,6 +411,7 @@ test.describe('Services counselor user', () => { }); test('can complete review of PPM shipment documents and view documents after', async ({ page, scPage }) => { + test.slow(); const move = await scPage.testHarness.buildApprovedMoveWithPPMAllDocTypesOffice(); await scPage.navigateToCloseoutMove(move.locator); @@ -452,6 +465,7 @@ test.describe('Services counselor user', () => { }); test('is able to edit/save actual move start date', async ({ page, scPage }) => { + test.slow(); // Navigate to the "Review documents" page await expect(page.getByRole('button', { name: /Review documents/i })).toBeVisible(); await page.getByRole('button', { name: 'Review documents' }).click(); @@ -468,6 +482,7 @@ test.describe('Services counselor user', () => { }); test('is able to edit/save pickup address', async ({ page, scPage }) => { + test.slow(); // Navigate to the "Review documents" page await expect(page.getByRole('button', { name: /Review documents/i })).toBeVisible(); await page.getByRole('button', { name: 'Review documents' }).click(); @@ -484,6 +499,7 @@ test.describe('Services counselor user', () => { }); test('is able to edit/save delivery address', async ({ page, scPage }) => { + test.slow(); // Navigate to the "Review documents" page await expect(page.getByRole('button', { name: /Review documents/i })).toBeVisible(); await page.getByRole('button', { name: 'Review documents' }).click(); @@ -500,6 +516,7 @@ test.describe('Services counselor user', () => { }); test('is able to edit/save advance received', async ({ page, scPage }) => { + test.slow(); // Navigate to the "Review documents" page await expect(page.getByRole('button', { name: /Review documents/i })).toBeVisible(); await page.getByRole('button', { name: 'Review documents' }).click(); @@ -535,6 +552,7 @@ test.describe('Services counselor user', () => { let fullPpmMoveLocator = ''; test('counselor can see partial PPM ready for closeout', async ({ page, scPage }) => { + test.slow(); const partialPpmMoveCloseout = await scPage.testHarness.buildPartialPPMMoveReadyForCloseout(); partialPpmCloseoutLocator = partialPpmMoveCloseout.locator; await scPage.searchForCloseoutMove(partialPpmCloseoutLocator); @@ -542,6 +560,7 @@ test.describe('Services counselor user', () => { }); test('counselor can see partial PPM ready for counseling', async ({ page, scPage }) => { + test.slow(); const partialPpmMoveCounseling = await scPage.testHarness.buildPartialPPMMoveReadyForCounseling(); partialPpmCounselingLocator = partialPpmMoveCounseling.locator; await scPage.searchForMove(partialPpmCounselingLocator); @@ -549,6 +568,7 @@ test.describe('Services counselor user', () => { }); test('counselor can see full PPM ready for closeout', async ({ page, scPage }) => { + test.slow(); const fullPpmMove = await scPage.testHarness.buildPPMMoveWithCloseout(); fullPpmMoveLocator = fullPpmMove.locator; await scPage.searchForCloseoutMove(fullPpmMoveLocator); @@ -559,6 +579,7 @@ test.describe('Services counselor user', () => { test.describe('Actual expense reimbursement tests', () => { test.describe('is able to view/edit actual expense reimbursement for non-civilian moves', () => { test('view/edit actual expense reimbursement - edit shipments page', async ({ page, scPage }) => { + test.slow(); const move = await scPage.testHarness.buildSubmittedMoveWithPPMShipmentForSC(); await scPage.navigateToMove(move.locator); @@ -589,6 +610,7 @@ test.describe('Services counselor user', () => { }); test('view/edit actual expense reimbursement - PPM closeout review documents', async ({ page, scPage }) => { + test.slow(); const move = await scPage.testHarness.buildApprovedMoveWithPPMProgearWeightTicketOffice(); await scPage.navigateToMoveUsingMoveSearch(move.locator); @@ -618,6 +640,7 @@ test.describe('Services counselor user', () => { test.describe('is unable to edit actual expense reimbursement for civilian moves', () => { test('cannot edit actual expense reimbursement - edit shipments page', async ({ page, scPage }) => { + test.slow(); const move = await scPage.testHarness.buildSubmittedMoveWithPPMShipmentForSC(); await scPage.navigateToMove(move.locator); @@ -638,6 +661,7 @@ test.describe('Services counselor user', () => { }); test('cannot edit actual expense reimbursement - PPM closeout review documents', async ({ page, scPage }) => { + test.slow(); const move = await scPage.testHarness.buildApprovedMoveWithPPMProgearWeightTicketOfficeCivilian(); await scPage.navigateToMoveUsingMoveSearch(move.locator); @@ -662,6 +686,7 @@ test.describe('Services counselor user', () => { }); test('is unable to view/edit orders after MTO has been created(sent to prime)', async ({ page }) => { + test.slow(); await expect(page.getByTestId('view-edit-orders')).toBeHidden(); await expect(page.getByTestId('edit-allowances')).toBeHidden(); }); diff --git a/playwright/tests/office/servicescounseling/servicesCounselingQueueFilters.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingQueueFilters.spec.js index abd3f13ea30..4bd1e13d97c 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingQueueFilters.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingQueueFilters.spec.js @@ -10,7 +10,7 @@ import { test, expect } from './servicesCounselingTestFixture'; test.describe('Services counselor user', () => { let moveLocator = ''; let moveWithNeedsCloseoutLocator = ''; - test.slow(); + test.describe('with PPM shipment ready for closeout', () => { let dutyLocationName = ''; test.beforeEach(async ({ scPage }) => { @@ -23,6 +23,7 @@ test.describe('Services counselor user', () => { }); test('is able to filter partial vs full moves based on ppm type', async ({ page }) => { + test.slow(); // closeout tab // Created a single Partial PPM move, so when we search for @@ -40,6 +41,7 @@ test.describe('Services counselor user', () => { }); test('is able to filter moves based on PPM status', async ({ page }) => { + test.slow(); // Check for Needs closeout filter await page.locator('th[data-testid="locator"] > div > input').clear(); await page.locator('th[data-testid="locator"] > div > input').fill(moveWithNeedsCloseoutLocator); @@ -49,6 +51,7 @@ test.describe('Services counselor user', () => { }); test('is able to filter moves based on destination duty location', async ({ page }) => { + test.slow(); // add filter for move code (PPM closeout that has Fort Gordon as // its destination duty location) @@ -80,6 +83,7 @@ test.describe('Services counselor user', () => { }); test('is able to filter moves based on PPM Closeout initiated', async ({ page }) => { + test.slow(); const closeoutDate = new Date().toLocaleDateString('en-US'); // first test with bogus date and no moves are found @@ -108,6 +112,7 @@ test.describe('Services counselor user', () => { }); test('is able to filter moves based on PPM Closeout location', async ({ page }) => { + test.slow(); await page.locator('th[data-testid="locator"] > div > input').fill(moveLocator); await page.locator('th[data-testid="locator"] > div > input').blur(); // add another filter for the closeout office column checking From feceecb112ad91853b57d78ae7070af1960eb3ca Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 6 Jan 2025 20:21:46 +0000 Subject: [PATCH 058/113] drastic timeout increase for flaky fix --- .../tests/my/milmove/ppms/entireShipmentCloseout.spec.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js b/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js index 7c14a957be7..78365aa5721 100644 --- a/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js +++ b/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js @@ -15,6 +15,7 @@ test.describe('Entire PPM closeout flow', () => { forEachViewport(async () => { test(`flows through happy path for existing shipment`, async ({ customerPpmPage }) => { test.slow(); + test.setTimeout(300000); // This one has been a headache forever. Shoehorn fix to go way above default "slow" timeout const move = await customerPpmPage.testHarness.buildApprovedMoveWithPPM(); await customerPpmPage.signInForPPMWithMove(move); @@ -35,6 +36,7 @@ test.describe('Entire PPM closeout flow', () => { test(`happy path with edits and backs`, async ({ customerPpmPage }) => { test.slow(); + test.setTimeout(300000); // This one has been a headache forever. Shoehorn fix to go way above default "slow" timeout const move = await customerPpmPage.testHarness.buildMoveWithPPMShipmentReadyForFinalCloseout(); await customerPpmPage.signInForPPMWithMove(move); @@ -135,6 +137,7 @@ test.describe('(MultiMove) Entire PPM closeout flow (MultiMove Workflow)', () => forEachViewport(async () => { test(`flows through happy path for existing shipment`, async ({ customerPpmPage }) => { test.slow(); + test.setTimeout(300000); // This one has been a headache forever. Shoehorn fix to go way above default "slow" timeout const move = await customerPpmPage.testHarness.buildApprovedMoveWithPPM(); await customerPpmPage.signInForPPMWithMove(move); From d456a0ade19934b1909a15b1c7a31728c939ec65 Mon Sep 17 00:00:00 2001 From: loganwc Date: Tue, 7 Jan 2025 00:39:54 +0000 Subject: [PATCH 059/113] just redid everything because stupid test coverage --- migrations/app/migrations_manifest.txt | 2 +- .../Shipment/PrimeUIShipmentCreate.jsx | 143 +++++------------- .../Shipment/PrimeUIShipmentCreate.test.jsx | 4 +- .../Shipment/PrimeUIShipmentCreateForm.jsx | 4 +- src/shared/utils.js | 38 +++++ src/shared/utils.test.js | 55 +++++++ 6 files changed, 138 insertions(+), 108 deletions(-) diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index 18c13cdaa73..a5330171a14 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1052,9 +1052,9 @@ 20241203024453_add_ppm_max_incentive_column.up.sql 20241204155919_update_ordering_proc.up.sql 20241204210208_retroactive_update_of_ppm_max_and_estimated_incentives_prd.up.sql -20241218201833_add_PPPO_BASE_ELIZABETH.up.sql 20241217163231_update_duty_locations_bad_zips.up.sql 20241217180136_add_AK_zips_to_zip3_distances.up.sql +20241218201833_add_PPPO_BASE_ELIZABETH.up.sql 20241220171035_add_additional_AK_zips_to_zip3_distances.up.sql 20241227153723_remove_empty_string_emplid_values.up.sql 20241230190638_remove_AK_zips_from_zip3.up.sql diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx index 9dae219dfbc..c7117373da0 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.jsx @@ -15,7 +15,7 @@ import styles from 'components/Office/CustomerContactInfoForm/CustomerContactInf import { Form } from 'components/form/Form'; import formStyles from 'styles/form.module.scss'; import WizardNavigation from 'components/Customer/WizardNavigation/WizardNavigation'; -import { isEmpty, isValidWeight } from 'shared/utils'; +import { checkAddressTogglesToClearAddresses, isEmpty, isValidWeight } from 'shared/utils'; import { formatAddressForPrimeAPI, formatSwaggerDate } from 'utils/formatters'; import { setFlashMessage as setFlashMessageAction } from 'store/flash/actions'; import { requiredAddressSchema, partialRequiredAddressSchema } from 'utils/validation'; @@ -115,29 +115,8 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { hasTertiaryDestinationAddress, }, } = values; - let { - ppmShipment: { - tertiaryPickupAddress, - tertiaryDestinationAddress, - secondaryPickupAddress, - secondaryDestinationAddress, - }, - } = values; - if (hasSecondaryPickupAddress !== 'true') { - secondaryPickupAddress = {}; - tertiaryPickupAddress = {}; - } - if (hasTertiaryPickupAddress !== 'true') { - tertiaryPickupAddress = {}; - } - if (hasSecondaryDestinationAddress !== 'true') { - secondaryDestinationAddress = {}; - tertiaryDestinationAddress = {}; - } - if (hasTertiaryDestinationAddress !== 'true') { - tertiaryDestinationAddress = {}; - } + const updatedValues = checkAddressTogglesToClearAddresses(values); body = { moveTaskOrderID: moveCodeOrID, @@ -146,19 +125,19 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { ppmShipment: { expectedDepartureDate: expectedDepartureDate ? formatSwaggerDate(expectedDepartureDate) : null, pickupAddress: isEmpty(pickupAddress) ? null : formatAddressForPrimeAPI(pickupAddress), - secondaryPickupAddress: isEmpty(secondaryPickupAddress) - ? null - : formatAddressForPrimeAPI(secondaryPickupAddress), destinationAddress: isEmpty(destinationAddress) ? null : formatAddressForPrimeAPI(destinationAddress), - secondaryDestinationAddress: isEmpty(secondaryDestinationAddress) + secondaryPickupAddress: isEmpty(updatedValues.secondaryPickupAddress) + ? null + : formatAddressForPrimeAPI(updatedValues.secondaryPickupAddress), + secondaryDestinationAddress: isEmpty(updatedValues.secondaryDestinationAddress) ? null - : formatAddressForPrimeAPI(secondaryDestinationAddress), - tertiaryPickupAddress: isEmpty(tertiaryPickupAddress) + : formatAddressForPrimeAPI(updatedValues.secondaryDestinationAddress), + tertiaryPickupAddress: isEmpty(updatedValues.tertiaryPickupAddress) ? null - : formatAddressForPrimeAPI(tertiaryPickupAddress), - tertiaryDestinationAddress: isEmpty(tertiaryDestinationAddress) + : formatAddressForPrimeAPI(updatedValues.tertiaryPickupAddress), + tertiaryDestinationAddress: isEmpty(updatedValues.tertiaryDestinationAddress) ? null - : formatAddressForPrimeAPI(tertiaryDestinationAddress), + : formatAddressForPrimeAPI(updatedValues.tertiaryDestinationAddress), sitExpected, ...(sitExpected && { sitLocation: sitLocation || null, @@ -206,22 +185,7 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { }, } = values; - let { tertiaryPickupAddress, tertiaryDestinationAddress, secondaryPickupAddress, secondaryDestinationAddress } = - values; - if (hasSecondaryPickupAddress !== 'true') { - secondaryPickupAddress = {}; - tertiaryPickupAddress = {}; - } - if (hasTertiaryPickupAddress !== 'true') { - tertiaryPickupAddress = {}; - } - if (hasSecondaryDestinationAddress !== 'true') { - secondaryDestinationAddress = {}; - tertiaryDestinationAddress = {}; - } - if (hasTertiaryDestinationAddress !== 'true') { - tertiaryDestinationAddress = {}; - } + const updatedValues = checkAddressTogglesToClearAddresses(values); // Sum the feet and inches fields into only inches for backend/db const totalLengthInInches = parseInt(lengthInFeet, 10) * 12 + parseInt(lengthInInches, 10); @@ -254,16 +218,18 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { hasSecondaryDestinationAddress: hasSecondaryDestinationAddress === 'true', hasTertiaryPickupAddress: hasTertiaryPickupAddress === 'true', hasTertiaryDestinationAddress: hasTertiaryDestinationAddress === 'true', - secondaryPickupAddress: isEmpty(secondaryPickupAddress) + secondaryPickupAddress: isEmpty(updatedValues.secondaryPickupAddress) ? null - : formatAddressForPrimeAPI(secondaryPickupAddress), - secondaryDestinationAddress: isEmpty(secondaryDestinationAddress) + : formatAddressForPrimeAPI(updatedValues.secondaryPickupAddress), + secondaryDestinationAddress: isEmpty(updatedValues.secondaryDestinationAddress) ? null - : formatAddressForPrimeAPI(secondaryDestinationAddress), - tertiaryPickupAddress: isEmpty(tertiaryPickupAddress) ? null : formatAddressForPrimeAPI(tertiaryPickupAddress), - tertiaryDestinationAddress: isEmpty(tertiaryDestinationAddress) + : formatAddressForPrimeAPI(updatedValues.secondaryDestinationAddress), + tertiaryPickupAddress: isEmpty(updatedValues.tertiaryPickupAddress) ? null - : formatAddressForPrimeAPI(tertiaryDestinationAddress), + : formatAddressForPrimeAPI(updatedValues.tertiaryPickupAddress), + tertiaryDestinationAddress: isEmpty(updatedValues.tertiaryDestinationAddress) + ? null + : formatAddressForPrimeAPI(updatedValues.tertiaryDestinationAddress), }; } else if (isMobileHome) { const { @@ -291,22 +257,7 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { }, } = values; - let { tertiaryPickupAddress, tertiaryDestinationAddress, secondaryPickupAddress, secondaryDestinationAddress } = - values; - if (hasSecondaryPickupAddress !== 'true') { - secondaryPickupAddress = {}; - tertiaryPickupAddress = {}; - } - if (hasTertiaryPickupAddress !== 'true') { - tertiaryPickupAddress = {}; - } - if (hasSecondaryDestinationAddress !== 'true') { - secondaryDestinationAddress = {}; - tertiaryDestinationAddress = {}; - } - if (hasTertiaryDestinationAddress !== 'true') { - tertiaryDestinationAddress = {}; - } + const updatedValues = checkAddressTogglesToClearAddresses(values); // Sum the feet and inches fields into only inches for backend/db const totalLengthInInches = parseInt(lengthInFeet, 10) * 12 + parseInt(lengthInInches, 10); @@ -335,16 +286,18 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { hasSecondaryDestinationAddress: hasSecondaryDestinationAddress === 'true', hasTertiaryPickupAddress: hasTertiaryPickupAddress === 'true', hasTertiaryDestinationAddress: hasTertiaryDestinationAddress === 'true', - secondaryPickupAddress: isEmpty(secondaryPickupAddress) + secondaryPickupAddress: isEmpty(updatedValues.secondaryPickupAddress) + ? null + : formatAddressForPrimeAPI(updatedValues.secondaryPickupAddress), + secondaryDestinationAddress: isEmpty(updatedValues.secondaryDestinationAddress) ? null - : formatAddressForPrimeAPI(secondaryPickupAddress), - secondaryDestinationAddress: isEmpty(secondaryDestinationAddress) + : formatAddressForPrimeAPI(updatedValues.secondaryDestinationAddress), + tertiaryPickupAddress: isEmpty(updatedValues.tertiaryPickupAddress) ? null - : formatAddressForPrimeAPI(secondaryDestinationAddress), - tertiaryPickupAddress: isEmpty(tertiaryPickupAddress) ? null : formatAddressForPrimeAPI(tertiaryPickupAddress), - tertiaryDestinationAddress: isEmpty(tertiaryDestinationAddress) + : formatAddressForPrimeAPI(updatedValues.tertiaryPickupAddress), + tertiaryDestinationAddress: isEmpty(updatedValues.tertiaryDestinationAddress) ? null - : formatAddressForPrimeAPI(tertiaryDestinationAddress), + : formatAddressForPrimeAPI(updatedValues.tertiaryDestinationAddress), }; } else { const { @@ -360,23 +313,7 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { hasTertiaryDestinationAddress, } = values; - let { tertiaryPickupAddress, tertiaryDestinationAddress, secondaryPickupAddress, secondaryDestinationAddress } = - values; - - if (hasSecondaryPickupAddress !== 'true') { - secondaryPickupAddress = {}; - tertiaryPickupAddress = {}; - } - if (hasTertiaryPickupAddress !== 'true') { - tertiaryPickupAddress = {}; - } - if (hasSecondaryDestinationAddress !== 'true') { - secondaryDestinationAddress = {}; - tertiaryDestinationAddress = {}; - } - if (hasTertiaryDestinationAddress !== 'true') { - tertiaryDestinationAddress = {}; - } + const updatedValues = checkAddressTogglesToClearAddresses(values); body = { moveTaskOrderID: moveCodeOrID, @@ -391,16 +328,18 @@ const PrimeUIShipmentCreate = ({ setFlashMessage }) => { hasSecondaryDestinationAddress: hasSecondaryDestinationAddress === 'true', hasTertiaryPickupAddress: hasTertiaryPickupAddress === 'true', hasTertiaryDestinationAddress: hasTertiaryDestinationAddress === 'true', - secondaryPickupAddress: isEmpty(secondaryPickupAddress) + secondaryPickupAddress: isEmpty(updatedValues.secondaryPickupAddress) + ? null + : formatAddressForPrimeAPI(updatedValues.secondaryPickupAddress), + secondaryDestinationAddress: isEmpty(updatedValues.secondaryDestinationAddress) ? null - : formatAddressForPrimeAPI(secondaryPickupAddress), - secondaryDestinationAddress: isEmpty(secondaryDestinationAddress) + : formatAddressForPrimeAPI(updatedValues.secondaryDestinationAddress), + tertiaryPickupAddress: isEmpty(updatedValues.tertiaryPickupAddress) ? null - : formatAddressForPrimeAPI(secondaryDestinationAddress), - tertiaryPickupAddress: isEmpty(tertiaryPickupAddress) ? null : formatAddressForPrimeAPI(tertiaryPickupAddress), - tertiaryDestinationAddress: isEmpty(tertiaryDestinationAddress) + : formatAddressForPrimeAPI(updatedValues.tertiaryPickupAddress), + tertiaryDestinationAddress: isEmpty(updatedValues.tertiaryDestinationAddress) ? null - : formatAddressForPrimeAPI(tertiaryDestinationAddress), + : formatAddressForPrimeAPI(updatedValues.tertiaryDestinationAddress), }; } diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx index 0c10c40d7bd..370372ffe9c 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreate.test.jsx @@ -32,7 +32,7 @@ jest.mock('services/primeApi', () => ({ jest.mock('utils/featureFlags', () => ({ ...jest.requireActual('utils/featureFlags'), - isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve(true)), + isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve(false)), })); const moveDetailsURL = generatePath(primeSimulatorRoutes.VIEW_MOVE_PATH, { moveCodeOrID: moveId }); @@ -462,8 +462,6 @@ describe('Create Mobile Home and Boat', () => { const saveButton = screen.getByRole('button', { name: 'Save' }); expect(saveButton).not.toBeDisabled(); await userEvent.click(saveButton); - - // Verify that API call resets addresses when flags are not 'true' }); expect(createPrimeMTOShipmentV3).toHaveBeenCalledWith({ diff --git a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.jsx b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.jsx index 22fd5f366df..ad0215141e2 100644 --- a/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.jsx +++ b/src/pages/PrimeUI/Shipment/PrimeUIShipmentCreateForm.jsx @@ -408,7 +408,7 @@ const PrimeUIShipmentCreateForm = ({ enableBoat, enableMobileHome }) => { name="hasSecondaryPickupAddress" value="false" title="No, there is not a second pickup address" - checked={hasSecondaryPickupAddress !== 'true'} + checked={hasSecondaryPickupAddress !== 'true' && hasTertiaryPickupAddress !== 'true'} />
@@ -491,7 +491,7 @@ const PrimeUIShipmentCreateForm = ({ enableBoat, enableMobileHome }) => { name="hasSecondaryDestinationAddress" value="false" title="No, there is not a second delivery address" - checked={hasSecondaryDestinationAddress !== 'true'} + checked={hasSecondaryDestinationAddress !== 'true' && hasTertiaryDestinationAddress !== 'true'} /> diff --git a/src/shared/utils.js b/src/shared/utils.js index ac6baa1307b..12ccf91c7a8 100644 --- a/src/shared/utils.js +++ b/src/shared/utils.js @@ -171,3 +171,41 @@ export function isEmpty(obj) { export function isNullUndefinedOrWhitespace(value) { return value == null || value === undefined || value.trim() === ''; } + +export function checkAddressTogglesToClearAddresses(body) { + let values = body; + + if (values.shipmentType === 'PPM') { + if (values.ppmShipment.hasSecondaryPickupAddress !== 'true') { + values.ppmShipment.secondaryPickupAddress = {}; + values.ppmShipment.tertiaryPickupAddress = {}; + } + if (values.ppmShipment.hasTertiaryPickupAddress !== 'true') { + values.ppmShipment.tertiaryPickupAddress = {}; + } + if (values.ppmShipment.hasSecondaryDestinationAddress !== 'true') { + values.ppmShipment.secondaryDestinationAddress = {}; + values.ppmShipment.tertiaryDestinationAddress = {}; + } + if (values.ppmShipment.hasTertiaryDestinationAddress !== 'true') { + values.ppmShipment.tertiaryDestinationAddress = {}; + } + } else { + if (values.hasSecondaryPickupAddress !== 'true') { + values.secondaryPickupAddress = {}; + values.tertiaryPickupAddress = {}; + } + if (values.hasTertiaryPickupAddress !== 'true') { + values.tertiaryPickupAddress = {}; + } + if (values.hasSecondaryDestinationAddress !== 'true') { + values.secondaryDestinationAddress = {}; + values.tertiaryDestinationAddress = {}; + } + if (values.hasTertiaryDestinationAddress !== 'true') { + values.tertiaryDestinationAddress = {}; + } + } + + return values; +} diff --git a/src/shared/utils.test.js b/src/shared/utils.test.js index eb5ea3e0160..dfc7cfcd956 100644 --- a/src/shared/utils.test.js +++ b/src/shared/utils.test.js @@ -86,4 +86,59 @@ describe('utils', () => { }); }); }); + + it('check if 2nd and 3rd addresses should be cleared from prime shipment create payload', () => { + const ppmValues = { + shipmentType: 'PPM', + ppmShipment: { + hasSecondaryPickupAddress: 'false', + hasTertiaryPickupAddress: 'false', + hasSecondaryDestinationAddress: 'false', + hasTertiaryDestinationAddress: 'false', + secondaryPickupAddress: '', + tertiaryPickupAddress: '', + secondaryDestinationAddress: '', + tertiaryDestinationAddress: '', + }, + }; + const hhgValues = { + shipmentType: 'HHG', + hasSecondaryPickupAddress: 'false', + hasTertiaryPickupAddress: 'false', + hasSecondaryDestinationAddress: 'false', + hasTertiaryDestinationAddress: 'false', + secondaryPickupAddress: '', + tertiaryPickupAddress: '', + secondaryDestinationAddress: '', + tertiaryDestinationAddress: '', + }; + + const updatedPPMValues = utils.checkAddressTogglesToClearAddresses(ppmValues); + expect(updatedPPMValues).toEqual({ + shipmentType: 'PPM', + ppmShipment: { + hasSecondaryPickupAddress: 'false', + hasTertiaryPickupAddress: 'false', + hasSecondaryDestinationAddress: 'false', + hasTertiaryDestinationAddress: 'false', + secondaryPickupAddress: {}, + tertiaryPickupAddress: {}, + secondaryDestinationAddress: {}, + tertiaryDestinationAddress: {}, + }, + }); + + const updatedHHGValues = utils.checkAddressTogglesToClearAddresses(hhgValues); + expect(updatedHHGValues).toEqual({ + shipmentType: 'HHG', + hasSecondaryPickupAddress: 'false', + hasTertiaryPickupAddress: 'false', + hasSecondaryDestinationAddress: 'false', + hasTertiaryDestinationAddress: 'false', + secondaryPickupAddress: {}, + tertiaryPickupAddress: {}, + secondaryDestinationAddress: {}, + tertiaryDestinationAddress: {}, + }); + }); }); From 1c27b188ccb6841b8db0d931e306aa471450ee16 Mon Sep 17 00:00:00 2001 From: AaronW Date: Tue, 7 Jan 2025 17:13:30 +0000 Subject: [PATCH 060/113] corrected for bad automatic conflict resolution --- .../servicesCounselingFlows.spec.js | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js index 0285d60a72c..73adcf940f2 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js @@ -13,34 +13,34 @@ const supportingDocsEnabled = process.env.FEATURE_FLAG_MANAGE_SUPPORTING_DOCS; const LocationLookup = 'BEVERLY HILLS, CA 90210 (LOS ANGELES)'; test.describe('Services counselor user', () => { - test.describe('with basic HHG move', () => { - test.describe('GBLOC tests', () => { - test.describe('Origin Duty Location', () => { - let moveLocatorKKFA = ''; - let moveLocatorCNNQ = ''; - test.beforeEach(async ({ scPage }) => { - const moveKKFA = await scPage.testHarness.buildHHGMoveNeedsSC(); - moveLocatorKKFA = moveKKFA.locator; - const moveCNNQ = await scPage.testHarness.buildHHGMoveNeedsSC(); - moveLocatorCNNQ = moveCNNQ.locator; - }); - - test('when origin duty location GBLOC matches services counselor GBLOC', async ({ page }) => { - const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); - await locatorFilter.fill(moveLocatorKKFA); - await locatorFilter.blur(); - await expect(page.getByTestId('locator-0')).toBeVisible(); - }); - - test('when origin duty location GBLOC does not match services counselor GBLOC', async ({ page }) => { - const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); - await locatorFilter.fill(moveLocatorCNNQ); - await locatorFilter.blur(); - await expect(page.getByTestId('locator-0')).not.toBeVisible(); - }); + test.describe('GBLOC tests', () => { + test.describe('Origin Duty Location', () => { + let moveLocatorKKFA = ''; + let moveLocatorCNNQ = ''; + test.beforeEach(async ({ scPage }) => { + const moveKKFA = await scPage.testHarness.buildHHGMoveNeedsSC(); + moveLocatorKKFA = moveKKFA.locator; + const moveCNNQ = await scPage.testHarness.buildHHGMoveNeedsSC(); + moveLocatorCNNQ = moveCNNQ.locator; + }); + + test('when origin duty location GBLOC matches services counselor GBLOC', async ({ page }) => { + const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); + await locatorFilter.fill(moveLocatorKKFA); + await locatorFilter.blur(); + await expect(page.getByTestId('locator-0')).toBeVisible(); + }); + + test('when origin duty location GBLOC does not match services counselor GBLOC', async ({ page }) => { + const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); + await locatorFilter.fill(moveLocatorCNNQ); + await locatorFilter.blur(); + await expect(page.getByTestId('locator-0')).not.toBeVisible(); }); }); + }); + test.describe('with basic HHG move', () => { test.beforeEach(async ({ scPage }) => { const move = await scPage.testHarness.buildHHGMoveNeedsSC(); await scPage.navigateToMove(move.locator); From 841b0b39addcff2b356429e5ccd50c80cab1523f Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Tue, 7 Jan 2025 18:52:07 +0000 Subject: [PATCH 061/113] more shoehorn flaky timeouts --- playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js | 1 + .../servicescounseling/servicesCounselingMovingExpenses.spec.js | 1 + playwright/tests/office/txo/tooFlows.spec.js | 1 + 3 files changed, 3 insertions(+) diff --git a/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js b/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js index 78365aa5721..6d593a1bfaa 100644 --- a/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js +++ b/playwright/tests/my/milmove/ppms/entireShipmentCloseout.spec.js @@ -159,6 +159,7 @@ test.describe('(MultiMove) Entire PPM closeout flow (MultiMove Workflow)', () => test(`happy path with edits and backs`, async ({ customerPpmPage }) => { test.slow(); + test.setTimeout(300000); // This one has been a headache forever. Shoehorn fix to go way above default "slow" timeout const move = await customerPpmPage.testHarness.buildMoveWithPPMShipmentReadyForFinalCloseout(); await customerPpmPage.signInForPPMWithMove(move); diff --git a/playwright/tests/office/servicescounseling/servicesCounselingMovingExpenses.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingMovingExpenses.spec.js index 8a12e813b20..ea3452e5827 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingMovingExpenses.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingMovingExpenses.spec.js @@ -96,6 +96,7 @@ test('A service counselor can approve/reject moving expenses', async ({ page, sc test('Review documents page displays correct value for Total days in SIT', async ({ page, scPage }) => { test.slow(); + test.setTimeout(300000); // This one has been a headache forever. Shoehorn fix to go way above default "slow" timeout // Create a move with TestHarness, and then navigate to the move details page for it const move = await scPage.testHarness.buildApprovedMoveWithPPMMovingExpenseOffice(); await scPage.navigateToCloseoutMove(move.locator); diff --git a/playwright/tests/office/txo/tooFlows.spec.js b/playwright/tests/office/txo/tooFlows.spec.js index e4ec319314f..66d8c6050cb 100644 --- a/playwright/tests/office/txo/tooFlows.spec.js +++ b/playwright/tests/office/txo/tooFlows.spec.js @@ -832,6 +832,7 @@ test.describe('TOO user', () => { }); test('approves a delivery address change request for an HHG shipment', async ({ officePage, page }) => { + test.setTimeout(300000); // This one has been a headache forever. Shoehorn fix to go way above default "slow" timeout const shipmentAddressUpdate = await officePage.testHarness.bulidHHGMoveWithAddressChangeRequest(); await officePage.signInAsNewTOOUser(); tooFlowPage = new TooFlowPage(officePage, shipmentAddressUpdate.Shipment.MoveTaskOrder); From 9ffdd19d6e5a948712ce376816f9f89e60a189c1 Mon Sep 17 00:00:00 2001 From: AaronW Date: Tue, 7 Jan 2025 21:42:11 +0000 Subject: [PATCH 062/113] added the rest of the changes in the errors file --- pkg/models/errors.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/models/errors.go b/pkg/models/errors.go index 498cf03e771..2a1cdeeff0e 100644 --- a/pkg/models/errors.go +++ b/pkg/models/errors.go @@ -54,3 +54,12 @@ var ErrInvalidMoveID = errors.New("INVALID_MOVE_ID") // ErrInvalidOrderID is used if a argument is provided in cases where a order ID is provided, but may be malformed, empty, or nonexistent var ErrInvalidOrderID = errors.New("INVALID_ORDER_ID") + +// ErrSqlRecordNotFound is used if an error is returned from the database indicating that no record was found +var ErrSqlRecordNotFound = errors.New("RECORD_NOT_FOUND") + +// ErrMissingDestinationAddress is used if destination address is missing from the shipment +var ErrMissingDestinationAddress = errors.New("DESTINATION_ADDRESS_MISSING") + +// ErrUnsupportedShipmentType is used if the shipment type is not supported by a method +var ErrUnsupportedShipmentType = errors.New("UNSUPPORTED_SHIPMENT_TYPE") From a9755ee1ed5e8d2f4e8926cd3efd148fa1089a86 Mon Sep 17 00:00:00 2001 From: AaronW Date: Tue, 7 Jan 2025 23:47:59 +0000 Subject: [PATCH 063/113] brought in all changes from B-21581 --- pkg/factory/order_factory.go | 8 +- pkg/gen/ghcapi/embedded_spec.go | 32 +- pkg/gen/ghcmessages/list_prime_move.go | 8 + pkg/gen/ghcmessages/search_move.go | 34 +- pkg/gen/primeapi/embedded_spec.go | 36 +++ pkg/gen/primemessages/list_move.go | 8 + pkg/gen/primemessages/move_task_order.go | 54 ++++ pkg/gen/primev2api/embedded_spec.go | 20 ++ pkg/gen/primev2messages/move_task_order.go | 54 ++++ pkg/gen/primev3api/embedded_spec.go | 20 ++ pkg/gen/primev3messages/move_task_order.go | 54 ++++ .../internal/payloads/model_to_payload.go | 47 +-- pkg/handlers/ghcapi/move_test.go | 303 +++++++++++++++++- pkg/handlers/ghcapi/orders.go | 4 +- pkg/handlers/primeapi/move_task_order.go | 6 +- .../primeapi/payloads/model_to_payload.go | 57 +++- .../payloads/model_to_payload_test.go | 51 ++- pkg/handlers/primeapiv2/move_task_order.go | 2 +- .../primeapiv2/payloads/model_to_payload.go | 17 +- .../payloads/model_to_payload_test.go | 51 ++- pkg/handlers/primeapiv3/move_task_order.go | 2 +- .../primeapiv3/payloads/model_to_payload.go | 21 +- .../payloads/model_to_payload_test.go | 59 +++- pkg/models/move.go | 44 +++ pkg/models/mto_shipments.go | 57 ++++ pkg/models/order.go | 198 ++++++++++++ pkg/models/service_member.go | 4 +- src/components/Table/SearchResultsTable.jsx | 2 +- .../definitions/prime/MoveTaskOrder.yaml | 8 + .../definitions/prime/v2/MoveTaskOrder.yaml | 8 + .../definitions/prime/v3/MoveTaskOrder.yaml | 8 + swagger-def/ghc.yaml | 8 +- swagger-def/prime.yaml | 6 + swagger/ghc.yaml | 8 +- swagger/prime.yaml | 14 + swagger/prime_v2.yaml | 8 + swagger/prime_v3.yaml | 8 + 37 files changed, 1239 insertions(+), 90 deletions(-) diff --git a/pkg/factory/order_factory.go b/pkg/factory/order_factory.go index d6f2165e095..9bc3c61fb1d 100644 --- a/pkg/factory/order_factory.go +++ b/pkg/factory/order_factory.go @@ -203,8 +203,8 @@ func buildOrderWithBuildType(db *pop.Connection, customs []Customization, traits defaultSpouseHasProGear := false defaultOrdersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION defaultOrdersTypeDetail := internalmessages.OrdersTypeDetail("HHG_PERMITTED") - defaultDestinationDutyLocationGbloc := "AGFM" - destinationDutyLocationGbloc := &defaultDestinationDutyLocationGbloc + defaultDestinationGbloc := "AGFM" + destinationGbloc := &defaultDestinationGbloc testYear := 2018 defaultIssueDate := time.Date(testYear, time.March, 15, 0, 0, 0, 0, time.UTC) defaultReportByDate := time.Date(testYear, time.August, 1, 0, 0, 0, 0, time.UTC) @@ -248,7 +248,7 @@ func buildOrderWithBuildType(db *pop.Connection, customs []Customization, traits log.Panicf("Error loading duty location by id %s: %s\n", newDutyLocation.ID.String(), err) } destinationPostalCodeToGBLOC := FetchOrBuildPostalCodeToGBLOC(db, newDutyLocation.Address.PostalCode, "AGFM") - destinationDutyLocationGbloc = &destinationPostalCodeToGBLOC.GBLOC + destinationGbloc = &destinationPostalCodeToGBLOC.GBLOC } } @@ -257,7 +257,7 @@ func buildOrderWithBuildType(db *pop.Connection, customs []Customization, traits ServiceMemberID: serviceMember.ID, NewDutyLocation: newDutyLocation, NewDutyLocationID: newDutyLocation.ID, - DestinationGBLOC: destinationDutyLocationGbloc, + DestinationGBLOC: destinationGbloc, UploadedOrders: uploadedOrders, UploadedOrdersID: uploadedOrders.ID, IssueDate: defaultIssueDate, diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 361f666c222..bedc53f5951 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -8944,6 +8944,14 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "example": "AGFM" + }, + "destinationPostalCode": { + "type": "string", + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true @@ -13148,16 +13156,16 @@ func init() { "branch": { "type": "string" }, - "destinationDutyLocationPostalCode": { + "destinationGBLOC": { + "$ref": "#/definitions/GBLOC" + }, + "destinationPostalCode": { "type": "string", "format": "zip", "title": "ZIP", "pattern": "^(\\d{5})$", "example": "90210" }, - "destinationGBLOC": { - "$ref": "#/definitions/GBLOC" - }, "edipi": { "type": "string", "x-nullable": true, @@ -25688,6 +25696,14 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "example": "AGFM" + }, + "destinationPostalCode": { + "type": "string", + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true @@ -30018,16 +30034,16 @@ func init() { "branch": { "type": "string" }, - "destinationDutyLocationPostalCode": { + "destinationGBLOC": { + "$ref": "#/definitions/GBLOC" + }, + "destinationPostalCode": { "type": "string", "format": "zip", "title": "ZIP", "pattern": "^(\\d{5})$", "example": "90210" }, - "destinationGBLOC": { - "$ref": "#/definitions/GBLOC" - }, "edipi": { "type": "string", "x-nullable": true, diff --git a/pkg/gen/ghcmessages/list_prime_move.go b/pkg/gen/ghcmessages/list_prime_move.go index 22a8f47b561..690f8097816 100644 --- a/pkg/gen/ghcmessages/list_prime_move.go +++ b/pkg/gen/ghcmessages/list_prime_move.go @@ -35,6 +35,14 @@ type ListPrimeMove struct { // Format: date-time CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + // destination g b l o c + // Example: AGFM + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + // destination postal code + // Example: 90210 + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` diff --git a/pkg/gen/ghcmessages/search_move.go b/pkg/gen/ghcmessages/search_move.go index c6690995151..f0f91015deb 100644 --- a/pkg/gen/ghcmessages/search_move.go +++ b/pkg/gen/ghcmessages/search_move.go @@ -22,13 +22,13 @@ type SearchMove struct { // branch Branch string `json:"branch,omitempty"` + // destination g b l o c + DestinationGBLOC GBLOC `json:"destinationGBLOC,omitempty"` + // ZIP // Example: 90210 // Pattern: ^(\d{5})$ - DestinationDutyLocationPostalCode string `json:"destinationDutyLocationPostalCode,omitempty"` - - // destination g b l o c - DestinationGBLOC GBLOC `json:"destinationGBLOC,omitempty"` + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` // edipi // Example: 1234567890 @@ -94,11 +94,11 @@ type SearchMove struct { func (m *SearchMove) Validate(formats strfmt.Registry) error { var res []error - if err := m.validateDestinationDutyLocationPostalCode(formats); err != nil { + if err := m.validateDestinationGBLOC(formats); err != nil { res = append(res, err) } - if err := m.validateDestinationGBLOC(formats); err != nil { + if err := m.validateDestinationPostalCode(formats); err != nil { res = append(res, err) } @@ -140,29 +140,29 @@ func (m *SearchMove) Validate(formats strfmt.Registry) error { return nil } -func (m *SearchMove) validateDestinationDutyLocationPostalCode(formats strfmt.Registry) error { - if swag.IsZero(m.DestinationDutyLocationPostalCode) { // not required +func (m *SearchMove) validateDestinationGBLOC(formats strfmt.Registry) error { + if swag.IsZero(m.DestinationGBLOC) { // not required return nil } - if err := validate.Pattern("destinationDutyLocationPostalCode", "body", m.DestinationDutyLocationPostalCode, `^(\d{5})$`); err != nil { + if err := m.DestinationGBLOC.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("destinationGBLOC") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("destinationGBLOC") + } return err } return nil } -func (m *SearchMove) validateDestinationGBLOC(formats strfmt.Registry) error { - if swag.IsZero(m.DestinationGBLOC) { // not required +func (m *SearchMove) validateDestinationPostalCode(formats strfmt.Registry) error { + if swag.IsZero(m.DestinationPostalCode) { // not required return nil } - if err := m.DestinationGBLOC.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("destinationGBLOC") - } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("destinationGBLOC") - } + if err := validate.Pattern("destinationPostalCode", "body", m.DestinationPostalCode, `^(\d{5})$`); err != nil { return err } diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index 52039d783cc..4f00b706379 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -1885,6 +1885,14 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "example": "AGFM" + }, + "destinationPostalCode": { + "type": "string", + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true @@ -2847,6 +2855,16 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "readOnly": true, + "example": "KKFA" + }, + "destinationPostalCode": { + "type": "string", + "readOnly": true, + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true @@ -6779,6 +6797,14 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "example": "AGFM" + }, + "destinationPostalCode": { + "type": "string", + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true @@ -7741,6 +7767,16 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "readOnly": true, + "example": "KKFA" + }, + "destinationPostalCode": { + "type": "string", + "readOnly": true, + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true diff --git a/pkg/gen/primemessages/list_move.go b/pkg/gen/primemessages/list_move.go index 4e9af4f1e52..866dd6e8810 100644 --- a/pkg/gen/primemessages/list_move.go +++ b/pkg/gen/primemessages/list_move.go @@ -38,6 +38,14 @@ type ListMove struct { // Format: date-time CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + // destination g b l o c + // Example: AGFM + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + // destination postal code + // Example: 90210 + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` diff --git a/pkg/gen/primemessages/move_task_order.go b/pkg/gen/primemessages/move_task_order.go index 3d5f5854487..befdf8fe3d0 100644 --- a/pkg/gen/primemessages/move_task_order.go +++ b/pkg/gen/primemessages/move_task_order.go @@ -39,6 +39,16 @@ type MoveTaskOrder struct { // Format: date-time CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + // destination g b l o c + // Example: KKFA + // Read Only: true + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + // destination postal code + // Example: 90210 + // Read Only: true + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` @@ -127,6 +137,10 @@ func (m *MoveTaskOrder) UnmarshalJSON(raw []byte) error { CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + ETag string `json:"eTag,omitempty"` ExcessWeightAcknowledgedAt *strfmt.DateTime `json:"excessWeightAcknowledgedAt"` @@ -183,6 +197,12 @@ func (m *MoveTaskOrder) UnmarshalJSON(raw []byte) error { // createdAt result.CreatedAt = data.CreatedAt + // destinationGBLOC + result.DestinationGBLOC = data.DestinationGBLOC + + // destinationPostalCode + result.DestinationPostalCode = data.DestinationPostalCode + // eTag result.ETag = data.ETag @@ -247,6 +267,10 @@ func (m MoveTaskOrder) MarshalJSON() ([]byte, error) { CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + ETag string `json:"eTag,omitempty"` ExcessWeightAcknowledgedAt *strfmt.DateTime `json:"excessWeightAcknowledgedAt"` @@ -284,6 +308,10 @@ func (m MoveTaskOrder) MarshalJSON() ([]byte, error) { CreatedAt: m.CreatedAt, + DestinationGBLOC: m.DestinationGBLOC, + + DestinationPostalCode: m.DestinationPostalCode, + ETag: m.ETag, ExcessWeightAcknowledgedAt: m.ExcessWeightAcknowledgedAt, @@ -655,6 +683,14 @@ func (m *MoveTaskOrder) ContextValidate(ctx context.Context, formats strfmt.Regi res = append(res, err) } + if err := m.contextValidateDestinationGBLOC(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateDestinationPostalCode(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateETag(ctx, formats); err != nil { res = append(res, err) } @@ -732,6 +768,24 @@ func (m *MoveTaskOrder) contextValidateCreatedAt(ctx context.Context, formats st return nil } +func (m *MoveTaskOrder) contextValidateDestinationGBLOC(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "destinationGBLOC", "body", string(m.DestinationGBLOC)); err != nil { + return err + } + + return nil +} + +func (m *MoveTaskOrder) contextValidateDestinationPostalCode(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "destinationPostalCode", "body", string(m.DestinationPostalCode)); err != nil { + return err + } + + return nil +} + func (m *MoveTaskOrder) contextValidateETag(ctx context.Context, formats strfmt.Registry) error { if err := validate.ReadOnly(ctx, "eTag", "body", string(m.ETag)); err != nil { diff --git a/pkg/gen/primev2api/embedded_spec.go b/pkg/gen/primev2api/embedded_spec.go index f0468e10884..c6c054f45ec 100644 --- a/pkg/gen/primev2api/embedded_spec.go +++ b/pkg/gen/primev2api/embedded_spec.go @@ -1957,6 +1957,16 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "readOnly": true, + "example": "KKFA" + }, + "destinationPostalCode": { + "type": "string", + "readOnly": true, + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true @@ -5564,6 +5574,16 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "readOnly": true, + "example": "KKFA" + }, + "destinationPostalCode": { + "type": "string", + "readOnly": true, + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true diff --git a/pkg/gen/primev2messages/move_task_order.go b/pkg/gen/primev2messages/move_task_order.go index af1390e30c7..c5e6cf21146 100644 --- a/pkg/gen/primev2messages/move_task_order.go +++ b/pkg/gen/primev2messages/move_task_order.go @@ -43,6 +43,16 @@ type MoveTaskOrder struct { // Format: date-time CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + // destination g b l o c + // Example: KKFA + // Read Only: true + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + // destination postal code + // Example: 90210 + // Read Only: true + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` @@ -133,6 +143,10 @@ func (m *MoveTaskOrder) UnmarshalJSON(raw []byte) error { CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + ETag string `json:"eTag,omitempty"` ExcessWeightAcknowledgedAt *strfmt.DateTime `json:"excessWeightAcknowledgedAt"` @@ -192,6 +206,12 @@ func (m *MoveTaskOrder) UnmarshalJSON(raw []byte) error { // createdAt result.CreatedAt = data.CreatedAt + // destinationGBLOC + result.DestinationGBLOC = data.DestinationGBLOC + + // destinationPostalCode + result.DestinationPostalCode = data.DestinationPostalCode + // eTag result.ETag = data.ETag @@ -258,6 +278,10 @@ func (m MoveTaskOrder) MarshalJSON() ([]byte, error) { CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + ETag string `json:"eTag,omitempty"` ExcessWeightAcknowledgedAt *strfmt.DateTime `json:"excessWeightAcknowledgedAt"` @@ -297,6 +321,10 @@ func (m MoveTaskOrder) MarshalJSON() ([]byte, error) { CreatedAt: m.CreatedAt, + DestinationGBLOC: m.DestinationGBLOC, + + DestinationPostalCode: m.DestinationPostalCode, + ETag: m.ETag, ExcessWeightAcknowledgedAt: m.ExcessWeightAcknowledgedAt, @@ -672,6 +700,14 @@ func (m *MoveTaskOrder) ContextValidate(ctx context.Context, formats strfmt.Regi res = append(res, err) } + if err := m.contextValidateDestinationGBLOC(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateDestinationPostalCode(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateETag(ctx, formats); err != nil { res = append(res, err) } @@ -758,6 +794,24 @@ func (m *MoveTaskOrder) contextValidateCreatedAt(ctx context.Context, formats st return nil } +func (m *MoveTaskOrder) contextValidateDestinationGBLOC(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "destinationGBLOC", "body", string(m.DestinationGBLOC)); err != nil { + return err + } + + return nil +} + +func (m *MoveTaskOrder) contextValidateDestinationPostalCode(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "destinationPostalCode", "body", string(m.DestinationPostalCode)); err != nil { + return err + } + + return nil +} + func (m *MoveTaskOrder) contextValidateETag(ctx context.Context, formats strfmt.Registry) error { if err := validate.ReadOnly(ctx, "eTag", "body", string(m.ETag)); err != nil { diff --git a/pkg/gen/primev3api/embedded_spec.go b/pkg/gen/primev3api/embedded_spec.go index 22f6c879b42..424ab464dbf 100644 --- a/pkg/gen/primev3api/embedded_spec.go +++ b/pkg/gen/primev3api/embedded_spec.go @@ -2191,6 +2191,16 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "readOnly": true, + "example": "KKFA" + }, + "destinationPostalCode": { + "type": "string", + "readOnly": true, + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true @@ -6483,6 +6493,16 @@ func init() { "format": "date-time", "readOnly": true }, + "destinationGBLOC": { + "type": "string", + "readOnly": true, + "example": "KKFA" + }, + "destinationPostalCode": { + "type": "string", + "readOnly": true, + "example": "90210" + }, "eTag": { "type": "string", "readOnly": true diff --git a/pkg/gen/primev3messages/move_task_order.go b/pkg/gen/primev3messages/move_task_order.go index 4d295abc872..02b991a0a16 100644 --- a/pkg/gen/primev3messages/move_task_order.go +++ b/pkg/gen/primev3messages/move_task_order.go @@ -43,6 +43,16 @@ type MoveTaskOrder struct { // Format: date-time CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + // destination g b l o c + // Example: KKFA + // Read Only: true + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + // destination postal code + // Example: 90210 + // Read Only: true + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` @@ -133,6 +143,10 @@ func (m *MoveTaskOrder) UnmarshalJSON(raw []byte) error { CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + ETag string `json:"eTag,omitempty"` ExcessWeightAcknowledgedAt *strfmt.DateTime `json:"excessWeightAcknowledgedAt"` @@ -192,6 +206,12 @@ func (m *MoveTaskOrder) UnmarshalJSON(raw []byte) error { // createdAt result.CreatedAt = data.CreatedAt + // destinationGBLOC + result.DestinationGBLOC = data.DestinationGBLOC + + // destinationPostalCode + result.DestinationPostalCode = data.DestinationPostalCode + // eTag result.ETag = data.ETag @@ -258,6 +278,10 @@ func (m MoveTaskOrder) MarshalJSON() ([]byte, error) { CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` + DestinationGBLOC string `json:"destinationGBLOC,omitempty"` + + DestinationPostalCode string `json:"destinationPostalCode,omitempty"` + ETag string `json:"eTag,omitempty"` ExcessWeightAcknowledgedAt *strfmt.DateTime `json:"excessWeightAcknowledgedAt"` @@ -297,6 +321,10 @@ func (m MoveTaskOrder) MarshalJSON() ([]byte, error) { CreatedAt: m.CreatedAt, + DestinationGBLOC: m.DestinationGBLOC, + + DestinationPostalCode: m.DestinationPostalCode, + ETag: m.ETag, ExcessWeightAcknowledgedAt: m.ExcessWeightAcknowledgedAt, @@ -672,6 +700,14 @@ func (m *MoveTaskOrder) ContextValidate(ctx context.Context, formats strfmt.Regi res = append(res, err) } + if err := m.contextValidateDestinationGBLOC(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateDestinationPostalCode(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateETag(ctx, formats); err != nil { res = append(res, err) } @@ -758,6 +794,24 @@ func (m *MoveTaskOrder) contextValidateCreatedAt(ctx context.Context, formats st return nil } +func (m *MoveTaskOrder) contextValidateDestinationGBLOC(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "destinationGBLOC", "body", string(m.DestinationGBLOC)); err != nil { + return err + } + + return nil +} + +func (m *MoveTaskOrder) contextValidateDestinationPostalCode(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "destinationPostalCode", "body", string(m.DestinationPostalCode)); err != nil { + return err + } + + return nil +} + func (m *MoveTaskOrder) contextValidateETag(ctx context.Context, formats strfmt.Registry) error { if err := validate.ReadOnly(ctx, "eTag", "body", string(m.ETag)); err != nil { diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index caa4aef11d5..8869d79511a 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -2260,20 +2260,21 @@ func QueueMoves(moves []models.Move, officeUsers []models.OfficeUser, requestedP deptIndicator = ghcmessages.DeptIndicator(*move.Orders.DepartmentIndicator) } - var gbloc string + var originGbloc string if move.Status == models.MoveStatusNeedsServiceCounseling { - gbloc = swag.StringValue(move.Orders.OriginDutyLocationGBLOC) + originGbloc = swag.StringValue(move.Orders.OriginDutyLocationGBLOC) } else if len(move.ShipmentGBLOC) > 0 && move.ShipmentGBLOC[0].GBLOC != nil { // There is a Pop bug that prevents us from using a has_one association for // Move.ShipmentGBLOC, so we have to treat move.ShipmentGBLOC as an array, even // though there can never be more than one GBLOC for a move. - gbloc = swag.StringValue(move.ShipmentGBLOC[0].GBLOC) + originGbloc = swag.StringValue(move.ShipmentGBLOC[0].GBLOC) } else { // If the move's first shipment doesn't have a pickup address (like with an NTS-Release), // we need to fall back to the origin duty location GBLOC. If that's not available for // some reason, then we should get the empty string (no GBLOC). - gbloc = swag.StringValue(move.Orders.OriginDutyLocationGBLOC) + originGbloc = swag.StringValue(move.Orders.OriginDutyLocationGBLOC) } + var closeoutLocation string if move.CloseoutOffice != nil { closeoutLocation = move.CloseoutOffice.Name @@ -2344,7 +2345,7 @@ func QueueMoves(moves []models.Move, officeUsers []models.OfficeUser, requestedP ShipmentsCount: int64(len(validMTOShipments)), OriginDutyLocation: DutyLocation(move.Orders.OriginDutyLocation), DestinationDutyLocation: DutyLocation(&move.Orders.NewDutyLocation), // #nosec G601 new in 1.22.2 - OriginGBLOC: ghcmessages.GBLOC(gbloc), + OriginGBLOC: ghcmessages.GBLOC(originGbloc), PpmType: move.PPMType, CloseoutInitiated: handlers.FmtDateTimePtr(&closeoutInitiated), CloseoutLocation: &closeoutLocation, @@ -2579,24 +2580,24 @@ func SearchMoves(appCtx appcontext.AppContext, moves models.Moves) *ghcmessages. } searchMoves[i] = &ghcmessages.SearchMove{ - FirstName: customer.FirstName, - LastName: customer.LastName, - Edipi: customer.Edipi, - Emplid: customer.Emplid, - Branch: customer.Affiliation.String(), - Status: ghcmessages.MoveStatus(move.Status), - ID: *handlers.FmtUUID(move.ID), - Locator: move.Locator, - ShipmentsCount: int64(numShipments), - OriginDutyLocationPostalCode: move.Orders.OriginDutyLocation.Address.PostalCode, - DestinationDutyLocationPostalCode: move.Orders.NewDutyLocation.Address.PostalCode, - OrderType: string(move.Orders.OrdersType), - RequestedPickupDate: pickupDate, - RequestedDeliveryDate: deliveryDate, - OriginGBLOC: ghcmessages.GBLOC(originGBLOC), - DestinationGBLOC: destinationGBLOC, - LockedByOfficeUserID: handlers.FmtUUIDPtr(move.LockedByOfficeUserID), - LockExpiresAt: handlers.FmtDateTimePtr(move.LockExpiresAt), + FirstName: customer.FirstName, + LastName: customer.LastName, + Edipi: customer.Edipi, + Emplid: customer.Emplid, + Branch: customer.Affiliation.String(), + Status: ghcmessages.MoveStatus(move.Status), + ID: *handlers.FmtUUID(move.ID), + Locator: move.Locator, + ShipmentsCount: int64(numShipments), + OriginDutyLocationPostalCode: move.Orders.OriginDutyLocation.Address.PostalCode, + DestinationPostalCode: move.Orders.NewDutyLocation.Address.PostalCode, + OrderType: string(move.Orders.OrdersType), + RequestedPickupDate: pickupDate, + RequestedDeliveryDate: deliveryDate, + OriginGBLOC: ghcmessages.GBLOC(originGBLOC), + DestinationGBLOC: destinationGBLOC, + LockedByOfficeUserID: handlers.FmtUUIDPtr(move.LockedByOfficeUserID), + LockExpiresAt: handlers.FmtDateTimePtr(move.LockExpiresAt), } } return &searchMoves diff --git a/pkg/handlers/ghcapi/move_test.go b/pkg/handlers/ghcapi/move_test.go index f345c7cfb85..3b9fda47401 100644 --- a/pkg/handlers/ghcapi/move_test.go +++ b/pkg/handlers/ghcapi/move_test.go @@ -290,6 +290,174 @@ func (suite *HandlerSuite) TestSearchMovesHandler() { } + /* setupGblocTestData is a helper function to set up test data for the search moves handler specifically for testing GBLOCs. + * returns a non-PPM move and a PPM move with different destination postal codes and GBLOCs. */ + setupGblocTestData := func() (*models.Move, *models.Move, *models.Move) { + // ZIPs takes a GBLOC and returns a ZIP + ZIPs := map[string]string{ + "AGFM": "62225", + "KKFA": "90210", + "BGNC": "47712", + "CLPK": "33009", + } + + for k, v := range ZIPs { + factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), v, k) + } + + serviceMember := factory.BuildServiceMember(suite.DB(), nil, nil) + + defaultPickupAddress := factory.BuildAddress(suite.DB(), nil, nil) + addressAGFM := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "123 Main St", + City: "Beverly Hills", + State: "CA", + PostalCode: ZIPs["AGFM"], + }, + }, + }, nil) + addressKKFA := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "123 Main St", + City: "Beverly Hills", + State: "CA", + PostalCode: ZIPs["KKFA"], + }, + }, + }, nil) + addressBGNC := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "123 Main St", + City: "Beverly Hills", + State: "CA", + PostalCode: ZIPs["BGNC"], + }, + }, + }, nil) + addressCLPK := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "123 Main St", + City: "Beverly Hills", + State: "CA", + PostalCode: ZIPs["CLPK"], + }, + }, + }, nil) + + destDutyLocationAGFM := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + { + Model: models.DutyLocation{ + Name: "Test AGFM", + AddressID: addressAGFM.ID, + }, + }, + }, nil) + destDutyLocationCLPK := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + { + Model: models.DutyLocation{ + Name: "Test CLPK", + AddressID: addressCLPK.ID, + }, + }, + }, nil) + + order := factory.BuildOrder(suite.DB(), []factory.Customization{ + { + Model: models.Order{ + ServiceMemberID: serviceMember.ID, + NewDutyLocationID: destDutyLocationAGFM.ID, + DestinationGBLOC: handlers.FmtString("AGFM"), + HasDependents: false, + SpouseHasProGear: false, + OrdersType: "PERMANENT_CHANGE_OF_STATION", + OrdersTypeDetail: nil, + }, + }, + }, nil) + + orderWithShipment := factory.BuildOrder(suite.DB(), []factory.Customization{ + { + Model: models.Order{ + ServiceMemberID: serviceMember.ID, + NewDutyLocationID: destDutyLocationAGFM.ID, + DestinationGBLOC: handlers.FmtString("AGFM"), + HasDependents: false, + SpouseHasProGear: false, + OrdersType: "PERMANENT_CHANGE_OF_STATION", + OrdersTypeDetail: nil, + }, + }, + }, nil) + + orderWithShipmentPPM := factory.BuildOrder(suite.DB(), []factory.Customization{ + { + Model: models.Order{ + ServiceMemberID: serviceMember.ID, + NewDutyLocationID: destDutyLocationCLPK.ID, + DestinationGBLOC: handlers.FmtString("CLPK"), + HasDependents: false, + SpouseHasProGear: false, + OrdersType: "PERMANENT_CHANGE_OF_STATION", + OrdersTypeDetail: nil, + }, + }, + }, nil) + + moveWithoutShipment := factory.BuildMove(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + OrdersID: order.ID, + }, + }, + }, nil) + + moveWithShipment := factory.BuildMoveWithShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + DestinationAddressID: &addressKKFA.ID, + PickupAddressID: &defaultPickupAddress.ID, + Status: models.MTOShipmentStatusSubmitted, + ShipmentType: models.MTOShipmentTypeHHG, + }, + }, + { + Model: models.Move{ + OrdersID: orderWithShipment.ID, + }, + }, + }, nil) + + moveWithShipmentPPM := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + Status: models.MoveStatusSUBMITTED, + OrdersID: orderWithShipmentPPM.ID, + }, + }, + { + Model: models.MTOShipment{ + Status: models.MTOShipmentStatusSubmitted, + DestinationAddressID: &addressBGNC.ID, + PickupAddressID: &defaultPickupAddress.ID, + }, + }, + { + Model: models.PPMShipment{ + Status: models.PPMShipmentStatusSubmitted, + DestinationAddressID: &addressBGNC.ID, + PickupAddressID: &defaultPickupAddress.ID, + }, + }, + }, nil) + + return &moveWithoutShipment, &moveWithShipment, &moveWithShipmentPPM + } + suite.Run("Successful move search by locator", func() { req := setupTestData() move := factory.BuildMove(suite.DB(), nil, nil) @@ -331,7 +499,7 @@ func (suite *HandlerSuite) TestSearchMovesHandler() { payloadMove := *(*payload).SearchMoves[0] suite.Equal(move.ID.String(), payloadMove.ID.String()) suite.Equal(*move.Orders.ServiceMember.Edipi, *payloadMove.Edipi) - suite.Equal(move.Orders.NewDutyLocation.Address.PostalCode, payloadMove.DestinationDutyLocationPostalCode) + suite.Equal(move.Orders.NewDutyLocation.Address.PostalCode, payloadMove.DestinationPostalCode) suite.Equal(move.Orders.OriginDutyLocation.Address.PostalCode, payloadMove.OriginDutyLocationPostalCode) suite.Equal(ghcmessages.MoveStatusDRAFT, payloadMove.Status) suite.Equal("ARMY", payloadMove.Branch) @@ -383,6 +551,139 @@ func (suite *HandlerSuite) TestSearchMovesHandler() { suite.Equal(move.ID.String(), (*payload).SearchMoves[0].ID.String()) }) + + suite.Run("Destination Postal Code and GBLOC is correct for different shipment types", func() { + req := setupTestData() + move, moveWithShipment, moveWithShipmentPPM := setupGblocTestData() + + moves := models.Moves{*move} + movesWithShipment := models.Moves{*moveWithShipment} + movesWithShipmentPPM := models.Moves{*moveWithShipmentPPM} + + // Mocks + mockSearcher := mocks.MoveSearcher{} + mockUnlocker := movelocker.NewMoveUnlocker() + handler := SearchMovesHandler{ + HandlerConfig: suite.HandlerConfig(), + MoveSearcher: &mockSearcher, + MoveUnlocker: mockUnlocker, + } + + // Set Mock Search settings for move without Shipment + mockSearcher.On("SearchMoves", + mock.AnythingOfType("*appcontext.appContext"), + mock.MatchedBy(func(params *services.SearchMovesParams) bool { + return *params.Locator == move.Locator + }), + ).Return(moves, 1, nil) + + // Move search params without Shipment + params := moveops.SearchMovesParams{ + HTTPRequest: req, + Body: moveops.SearchMovesBody{ + Locator: &move.Locator, + Edipi: nil, + }, + } + + // Validate incoming payload non-PPM + suite.NoError(params.Body.Validate(strfmt.Default)) + + // set and validate response and payload + response := handler.Handle(params) + suite.IsType(&moveops.SearchMovesOK{}, response) + payload := response.(*moveops.SearchMovesOK).Payload + + // Validate outgoing payload without shipment + suite.NoError(payload.Validate(strfmt.Default)) + + var moveDestinationPostalCode string + var moveDestinationGBLOC string + var err error + + // Get destination postal code and GBLOC based on business logic + moveDestinationPostalCode, err = move.GetDestinationPostalCode(suite.DB()) + suite.NoError(err) + moveDestinationGBLOC, err = move.GetDestinationGBLOC(suite.DB()) + suite.NoError(err) + + suite.Equal(moveDestinationPostalCode, "62225") + suite.Equal(ghcmessages.GBLOC(moveDestinationGBLOC), ghcmessages.GBLOC("AGFM")) + + // Set Mock Search settings for move with MTO Shipment + mockSearcher.On("SearchMoves", + mock.AnythingOfType("*appcontext.appContext"), + mock.MatchedBy(func(params *services.SearchMovesParams) bool { + return *params.Locator == moveWithShipment.Locator + }), + ).Return(movesWithShipment, 1, nil) + + // Move search params with MTO Shipment + params = moveops.SearchMovesParams{ + HTTPRequest: req, + Body: moveops.SearchMovesBody{ + Locator: &moveWithShipment.Locator, + Edipi: nil, + }, + } + + // Validate incoming payload with shipment + suite.NoError(params.Body.Validate(strfmt.Default)) + + // reset and validate response and payload + response = handler.Handle(params) + suite.IsType(&moveops.SearchMovesOK{}, response) + payload = response.(*moveops.SearchMovesOK).Payload + + // Validate outgoing payload with shipment + suite.NoError(payload.Validate(strfmt.Default)) + + // Get destination postal code and GBLOC based on business logic + moveDestinationPostalCode, err = moveWithShipment.GetDestinationPostalCode(suite.DB()) + suite.NoError(err) + moveDestinationGBLOC, err = moveWithShipment.GetDestinationGBLOC(suite.DB()) + suite.NoError(err) + + suite.Equal(moveDestinationPostalCode, "90210") + suite.Equal(ghcmessages.GBLOC(moveDestinationGBLOC), ghcmessages.GBLOC("KKFA")) + + // Set Mock Search settings for move with PPM Shipment + mockSearcher.On("SearchMoves", + mock.AnythingOfType("*appcontext.appContext"), + mock.MatchedBy(func(params *services.SearchMovesParams) bool { + return *params.Locator == moveWithShipmentPPM.Locator + }), + ).Return(movesWithShipmentPPM, 1, nil) + + // Move search params with PPM Shipment + params = moveops.SearchMovesParams{ + HTTPRequest: req, + Body: moveops.SearchMovesBody{ + Locator: &moveWithShipmentPPM.Locator, + Edipi: nil, + }, + } + + // Validate incoming payload with PPM shipment + suite.NoError(params.Body.Validate(strfmt.Default)) + + // reset and validate response and payload + response = handler.Handle(params) + suite.IsType(&moveops.SearchMovesOK{}, response) + payload = response.(*moveops.SearchMovesOK).Payload + + // Validate outgoing payload non-PPM + suite.NoError(payload.Validate(strfmt.Default)) + + // Get destination postal code and GBLOC based on business logic + moveDestinationPostalCode, err = moveWithShipmentPPM.GetDestinationPostalCode(suite.DB()) + suite.NoError(err) + moveDestinationGBLOC, err = moveWithShipmentPPM.GetDestinationGBLOC(suite.DB()) + suite.NoError(err) + + suite.Equal(moveDestinationPostalCode, payload.SearchMoves[0].DestinationPostalCode) + suite.Equal(ghcmessages.GBLOC(moveDestinationGBLOC), payload.SearchMoves[0].DestinationGBLOC) + }) } func (suite *HandlerSuite) TestSetFinancialReviewFlagHandler() { diff --git a/pkg/handlers/ghcapi/orders.go b/pkg/handlers/ghcapi/orders.go index ff1c47a9569..cd9ff484a59 100644 --- a/pkg/handlers/ghcapi/orders.go +++ b/pkg/handlers/ghcapi/orders.go @@ -218,7 +218,7 @@ func (h CreateOrderHandler) Handle(params orderop.CreateOrderParams) middleware. return orderop.NewCreateOrderUnprocessableEntity(), err } - newDutyLocationGBLOC, err := models.FetchGBLOCForPostalCode(appCtx.DB(), newDutyLocation.Address.PostalCode) + destinationGBLOC, err := models.FetchGBLOCForPostalCode(appCtx.DB(), newDutyLocation.Address.PostalCode) if err != nil { err = apperror.NewBadDataError("New duty location GBLOC cannot be verified") appCtx.Logger().Error(err.Error()) @@ -313,7 +313,7 @@ func (h CreateOrderHandler) Handle(params orderop.CreateOrderParams) middleware. &entitlement, &originDutyLocationGBLOC.GBLOC, packingAndShippingInstructions, - &newDutyLocationGBLOC.GBLOC, + &destinationGBLOC.GBLOC, ) if err != nil || verrs.HasAny() { return handlers.ResponseForVErrors(appCtx.Logger(), verrs, err), err diff --git a/pkg/handlers/primeapi/move_task_order.go b/pkg/handlers/primeapi/move_task_order.go index 6c6795cb56c..d82f2b72fb8 100644 --- a/pkg/handlers/primeapi/move_task_order.go +++ b/pkg/handlers/primeapi/move_task_order.go @@ -45,7 +45,7 @@ func (h ListMovesHandler) Handle(params movetaskorderops.ListMovesParams) middle return movetaskorderops.NewListMovesInternalServerError().WithPayload(payloads.InternalServerError(nil, h.GetTraceIDFromRequest(params.HTTPRequest))), err } - payload := payloads.ListMoves(&mtos, amendmentCountInfo) + payload := payloads.ListMoves(&mtos, appCtx, amendmentCountInfo) return movetaskorderops.NewListMovesOK().WithPayload(payload), nil }) @@ -141,7 +141,7 @@ func (h GetMoveTaskOrderHandler) Handle(params movetaskorderops.GetMoveTaskOrder } /** End of Feature Flag **/ - moveTaskOrderPayload := payloads.MoveTaskOrder(mto) + moveTaskOrderPayload := payloads.MoveTaskOrder(appCtx, mto) return movetaskorderops.NewGetMoveTaskOrderOK().WithPayload(moveTaskOrderPayload), nil }) @@ -250,7 +250,7 @@ func (h UpdateMTOPostCounselingInformationHandler) Handle(params movetaskorderop } } - mtoPayload := payloads.MoveTaskOrder(mto) + mtoPayload := payloads.MoveTaskOrder(appCtx, mto) /* Don't send prime related emails on BLUEBARK moves */ if mto.Orders.CanSendEmailWithOrdersType() { diff --git a/pkg/handlers/primeapi/payloads/model_to_payload.go b/pkg/handlers/primeapi/payloads/model_to_payload.go index d5c76723fa4..10a1e30be8c 100644 --- a/pkg/handlers/primeapi/payloads/model_to_payload.go +++ b/pkg/handlers/primeapi/payloads/model_to_payload.go @@ -20,7 +20,8 @@ import ( ) // MoveTaskOrder payload -func MoveTaskOrder(moveTaskOrder *models.Move) *primemessages.MoveTaskOrder { +func MoveTaskOrder(appCtx appcontext.AppContext, moveTaskOrder *models.Move) *primemessages.MoveTaskOrder { + db := appCtx.DB() if moveTaskOrder == nil { return nil } @@ -28,6 +29,17 @@ func MoveTaskOrder(moveTaskOrder *models.Move) *primemessages.MoveTaskOrder { mtoServiceItems := MTOServiceItems(&moveTaskOrder.MTOServiceItems) mtoShipments := MTOShipmentsWithoutServiceItems(&moveTaskOrder.MTOShipments) + var destGbloc, destZip string + var err error + destGbloc, err = moveTaskOrder.GetDestinationGBLOC(db) + if err != nil { + destGbloc = "" + } + destZip, err = moveTaskOrder.GetDestinationPostalCode(db) + if err != nil { + destZip = "" + } + payload := &primemessages.MoveTaskOrder{ ID: strfmt.UUID(moveTaskOrder.ID.String()), MoveCode: moveTaskOrder.Locator, @@ -40,6 +52,8 @@ func MoveTaskOrder(moveTaskOrder *models.Move) *primemessages.MoveTaskOrder { ExcessWeightUploadID: handlers.FmtUUIDPtr(moveTaskOrder.ExcessWeightUploadID), OrderID: strfmt.UUID(moveTaskOrder.OrdersID.String()), Order: Order(&moveTaskOrder.Orders), + DestinationGBLOC: destGbloc, + DestinationPostalCode: destZip, ReferenceID: *moveTaskOrder.ReferenceID, PaymentRequests: *paymentRequests, MtoShipments: *mtoShipments, @@ -63,21 +77,36 @@ func MoveTaskOrder(moveTaskOrder *models.Move) *primemessages.MoveTaskOrder { } // ListMove payload -func ListMove(move *models.Move, moveOrderAmendmentsCount *services.MoveOrderAmendmentAvailableSinceCount) *primemessages.ListMove { +func ListMove(move *models.Move, appCtx appcontext.AppContext, moveOrderAmendmentsCount *services.MoveOrderAmendmentAvailableSinceCount) *primemessages.ListMove { if move == nil { return nil } + db := appCtx.DB() + + var destGbloc, destZip string + var err error + destGbloc, err = move.GetDestinationGBLOC(db) + if err != nil { + destGbloc = "" + } + destZip, err = move.GetDestinationPostalCode(db) + if err != nil { + destZip = "" + } + payload := &primemessages.ListMove{ - ID: strfmt.UUID(move.ID.String()), - MoveCode: move.Locator, - CreatedAt: strfmt.DateTime(move.CreatedAt), - AvailableToPrimeAt: handlers.FmtDateTimePtr(move.AvailableToPrimeAt), - ApprovedAt: handlers.FmtDateTimePtr(move.ApprovedAt), - OrderID: strfmt.UUID(move.OrdersID.String()), - ReferenceID: *move.ReferenceID, - UpdatedAt: strfmt.DateTime(move.UpdatedAt), - ETag: etag.GenerateEtag(move.UpdatedAt), + ID: strfmt.UUID(move.ID.String()), + MoveCode: move.Locator, + CreatedAt: strfmt.DateTime(move.CreatedAt), + AvailableToPrimeAt: handlers.FmtDateTimePtr(move.AvailableToPrimeAt), + ApprovedAt: handlers.FmtDateTimePtr(move.ApprovedAt), + DestinationGBLOC: destGbloc, + DestinationPostalCode: destZip, + OrderID: strfmt.UUID(move.OrdersID.String()), + ReferenceID: *move.ReferenceID, + UpdatedAt: strfmt.DateTime(move.UpdatedAt), + ETag: etag.GenerateEtag(move.UpdatedAt), Amendments: &primemessages.Amendments{ Total: handlers.FmtInt64(0), AvailableSince: handlers.FmtInt64(0), @@ -97,7 +126,7 @@ func ListMove(move *models.Move, moveOrderAmendmentsCount *services.MoveOrderAme } // ListMoves payload -func ListMoves(moves *models.Moves, moveOrderAmendmentAvailableSinceCounts services.MoveOrderAmendmentAvailableSinceCounts) []*primemessages.ListMove { +func ListMoves(moves *models.Moves, appCtx appcontext.AppContext, moveOrderAmendmentAvailableSinceCounts services.MoveOrderAmendmentAvailableSinceCounts) []*primemessages.ListMove { payload := make(primemessages.ListMoves, len(*moves)) moveOrderAmendmentsFilterCountMap := make(map[uuid.UUID]services.MoveOrderAmendmentAvailableSinceCount, len(*moves)) @@ -108,9 +137,9 @@ func ListMoves(moves *models.Moves, moveOrderAmendmentAvailableSinceCounts servi for i, m := range *moves { copyOfM := m // Make copy to avoid implicit memory aliasing of items from a range statement. if value, ok := moveOrderAmendmentsFilterCountMap[m.ID]; ok { - payload[i] = ListMove(©OfM, &value) + payload[i] = ListMove(©OfM, appCtx, &value) } else { - payload[i] = ListMove(©OfM, nil) + payload[i] = ListMove(©OfM, appCtx, nil) } } diff --git a/pkg/handlers/primeapi/payloads/model_to_payload_test.go b/pkg/handlers/primeapi/payloads/model_to_payload_test.go index 73342af2430..f1f038d5f6a 100644 --- a/pkg/handlers/primeapi/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapi/payloads/model_to_payload_test.go @@ -55,7 +55,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { } suite.Run("Success - Returns a basic move payload with no payment requests, service items or shipments", func() { - returnedModel := MoveTaskOrder(&basicMove) + returnedModel := MoveTaskOrder(suite.AppContextForTest(), &basicMove) suite.IsType(&primemessages.MoveTaskOrder{}, returnedModel) suite.Equal(strfmt.UUID(basicMove.ID.String()), returnedModel.ID) @@ -745,6 +745,55 @@ func (suite *PayloadsSuite) TestMTOServiceItemDDSHUT() { suite.True(ok) } +func (suite *PayloadsSuite) TestDestinationPostalCodeAndGBLOC() { + moveID := uuid.Must(uuid.NewV4()) + moveLocator := "TESTTEST" + primeTime := time.Now() + ordersID := uuid.Must(uuid.NewV4()) + refID := "123456" + contractNum := "HTC-123-456" + address := models.Address{PostalCode: "35023"} + shipment := models.MTOShipment{ + ID: uuid.Must(uuid.NewV4()), + DestinationAddress: &address, + } + shipments := models.MTOShipments{shipment} + contractor := models.Contractor{ + ContractNumber: contractNum, + } + + basicMove := models.Move{ + ID: moveID, + Locator: moveLocator, + CreatedAt: primeTime, + ReferenceID: &refID, + AvailableToPrimeAt: &primeTime, + ApprovedAt: &primeTime, + OrdersID: ordersID, + Contractor: &contractor, + PaymentRequests: models.PaymentRequests{}, + SubmittedAt: &primeTime, + UpdatedAt: primeTime, + Status: models.MoveStatusAPPROVED, + SignedCertifications: models.SignedCertifications{}, + MTOServiceItems: models.MTOServiceItems{}, + MTOShipments: shipments, + } + + suite.Run("Returns values needed to get the destination postal code and GBLOC", func() { + returnedModel := MoveTaskOrder(suite.AppContextForTest(), &basicMove) + + suite.IsType(&primemessages.MoveTaskOrder{}, returnedModel) + suite.Equal(strfmt.UUID(basicMove.ID.String()), returnedModel.ID) + suite.Equal(basicMove.Locator, returnedModel.MoveCode) + suite.Equal(strfmt.DateTime(basicMove.CreatedAt), returnedModel.CreatedAt) + suite.Equal(handlers.FmtDateTimePtr(basicMove.AvailableToPrimeAt), returnedModel.AvailableToPrimeAt) + suite.Equal(strfmt.UUID(basicMove.OrdersID.String()), returnedModel.OrderID) + suite.Equal(strfmt.DateTime(basicMove.UpdatedAt), returnedModel.UpdatedAt) + suite.NotEmpty(returnedModel.ETag) + }) +} + func (suite *PayloadsSuite) TestStorageFacilityPayload() { phone := "555" email := "email" diff --git a/pkg/handlers/primeapiv2/move_task_order.go b/pkg/handlers/primeapiv2/move_task_order.go index 0ac24d23651..f3ecd6db066 100644 --- a/pkg/handlers/primeapiv2/move_task_order.go +++ b/pkg/handlers/primeapiv2/move_task_order.go @@ -104,7 +104,7 @@ func (h GetMoveTaskOrderHandler) Handle(params movetaskorderops.GetMoveTaskOrder } /** End of Feature Flag **/ - moveTaskOrderPayload := payloads.MoveTaskOrder(mto) + moveTaskOrderPayload := payloads.MoveTaskOrder(appCtx, mto) return movetaskorderops.NewGetMoveTaskOrderOK().WithPayload(moveTaskOrderPayload), nil }) diff --git a/pkg/handlers/primeapiv2/payloads/model_to_payload.go b/pkg/handlers/primeapiv2/payloads/model_to_payload.go index 56ad6caabcb..c3e83a07cf3 100644 --- a/pkg/handlers/primeapiv2/payloads/model_to_payload.go +++ b/pkg/handlers/primeapiv2/payloads/model_to_payload.go @@ -9,6 +9,7 @@ import ( "github.com/gobuffalo/validate/v3" "github.com/gofrs/uuid" + "github.com/transcom/mymove/pkg/appcontext" "github.com/transcom/mymove/pkg/etag" "github.com/transcom/mymove/pkg/gen/primev2messages" "github.com/transcom/mymove/pkg/handlers" @@ -16,7 +17,8 @@ import ( ) // MoveTaskOrder payload -func MoveTaskOrder(moveTaskOrder *models.Move) *primev2messages.MoveTaskOrder { +func MoveTaskOrder(appCtx appcontext.AppContext, moveTaskOrder *models.Move) *primev2messages.MoveTaskOrder { + db := appCtx.DB() if moveTaskOrder == nil { return nil } @@ -24,6 +26,17 @@ func MoveTaskOrder(moveTaskOrder *models.Move) *primev2messages.MoveTaskOrder { mtoServiceItems := MTOServiceItems(&moveTaskOrder.MTOServiceItems) mtoShipments := MTOShipmentsWithoutServiceItems(&moveTaskOrder.MTOShipments) + var destGbloc, destZip string + var err error + destGbloc, err = moveTaskOrder.GetDestinationGBLOC(db) + if err != nil { + destGbloc = "" + } + destZip, err = moveTaskOrder.GetDestinationPostalCode(db) + if err != nil { + destZip = "" + } + payload := &primev2messages.MoveTaskOrder{ ID: strfmt.UUID(moveTaskOrder.ID.String()), MoveCode: moveTaskOrder.Locator, @@ -36,6 +49,8 @@ func MoveTaskOrder(moveTaskOrder *models.Move) *primev2messages.MoveTaskOrder { ExcessWeightUploadID: handlers.FmtUUIDPtr(moveTaskOrder.ExcessWeightUploadID), OrderID: strfmt.UUID(moveTaskOrder.OrdersID.String()), Order: Order(&moveTaskOrder.Orders), + DestinationGBLOC: destGbloc, + DestinationPostalCode: destZip, ReferenceID: *moveTaskOrder.ReferenceID, PaymentRequests: *paymentRequests, MtoShipments: *mtoShipments, diff --git a/pkg/handlers/primeapiv2/payloads/model_to_payload_test.go b/pkg/handlers/primeapiv2/payloads/model_to_payload_test.go index 13e3bd35e3c..ec125590da5 100644 --- a/pkg/handlers/primeapiv2/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapiv2/payloads/model_to_payload_test.go @@ -64,7 +64,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { } suite.Run("Success - Returns a basic move payload with no payment requests, service items or shipments", func() { - returnedModel := MoveTaskOrder(&basicMove) + returnedModel := MoveTaskOrder(suite.AppContextForTest(), &basicMove) suite.IsType(&primev2messages.MoveTaskOrder{}, returnedModel) suite.Equal(strfmt.UUID(basicMove.ID.String()), returnedModel.ID) @@ -89,6 +89,55 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { }) } +func (suite *PayloadsSuite) TestDestinationPostalCodeAndGBLOC() { + moveID := uuid.Must(uuid.NewV4()) + moveLocator := "TESTTEST" + primeTime := time.Now() + ordersID := uuid.Must(uuid.NewV4()) + refID := "123456" + contractNum := "HTC-123-456" + address := models.Address{PostalCode: "35023"} + shipment := models.MTOShipment{ + ID: uuid.Must(uuid.NewV4()), + DestinationAddress: &address, + } + shipments := models.MTOShipments{shipment} + contractor := models.Contractor{ + ContractNumber: contractNum, + } + + basicMove := models.Move{ + ID: moveID, + Locator: moveLocator, + CreatedAt: primeTime, + ReferenceID: &refID, + AvailableToPrimeAt: &primeTime, + ApprovedAt: &primeTime, + OrdersID: ordersID, + Contractor: &contractor, + PaymentRequests: models.PaymentRequests{}, + SubmittedAt: &primeTime, + UpdatedAt: primeTime, + Status: models.MoveStatusAPPROVED, + SignedCertifications: models.SignedCertifications{}, + MTOServiceItems: models.MTOServiceItems{}, + MTOShipments: shipments, + } + + suite.Run("Returns values needed to get the destination postal code and GBLOC", func() { + returnedModel := MoveTaskOrder(suite.AppContextForTest(), &basicMove) + + suite.IsType(&primev2messages.MoveTaskOrder{}, returnedModel) + suite.Equal(strfmt.UUID(basicMove.ID.String()), returnedModel.ID) + suite.Equal(basicMove.Locator, returnedModel.MoveCode) + suite.Equal(strfmt.DateTime(basicMove.CreatedAt), returnedModel.CreatedAt) + suite.Equal(handlers.FmtDateTimePtr(basicMove.AvailableToPrimeAt), returnedModel.AvailableToPrimeAt) + suite.Equal(strfmt.UUID(basicMove.OrdersID.String()), returnedModel.OrderID) + suite.Equal(strfmt.DateTime(basicMove.UpdatedAt), returnedModel.UpdatedAt) + suite.NotEmpty(returnedModel.ETag) + }) +} + func (suite *PayloadsSuite) TestReweigh() { id, _ := uuid.NewV4() shipmentID, _ := uuid.NewV4() diff --git a/pkg/handlers/primeapiv3/move_task_order.go b/pkg/handlers/primeapiv3/move_task_order.go index e7b5f01149c..c725fe7210b 100644 --- a/pkg/handlers/primeapiv3/move_task_order.go +++ b/pkg/handlers/primeapiv3/move_task_order.go @@ -113,7 +113,7 @@ func (h GetMoveTaskOrderHandler) Handle(params movetaskorderops.GetMoveTaskOrder payloads.InternalServerError(handlers.FmtString(err.Error()), h.GetTraceIDFromRequest(params.HTTPRequest))), err } - moveTaskOrderPayload := payloads.MoveTaskOrderWithShipmentOconusRateArea(mto, shipmentPostalCodeRateArea) + moveTaskOrderPayload := payloads.MoveTaskOrderWithShipmentOconusRateArea(appCtx, mto, shipmentPostalCodeRateArea) return movetaskorderops.NewGetMoveTaskOrderOK().WithPayload(moveTaskOrderPayload), nil }) diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload.go b/pkg/handlers/primeapiv3/payloads/model_to_payload.go index 4676e9b3d47..841192ec0e6 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload.go @@ -9,6 +9,7 @@ import ( "github.com/gobuffalo/validate/v3" "github.com/gofrs/uuid" + "github.com/transcom/mymove/pkg/appcontext" "github.com/transcom/mymove/pkg/etag" "github.com/transcom/mymove/pkg/gen/primev3messages" "github.com/transcom/mymove/pkg/handlers" @@ -17,7 +18,8 @@ import ( ) // MoveTaskOrder payload -func MoveTaskOrder(moveTaskOrder *models.Move) *primev3messages.MoveTaskOrder { +func MoveTaskOrder(appCtx appcontext.AppContext, moveTaskOrder *models.Move) *primev3messages.MoveTaskOrder { + db := appCtx.DB() if moveTaskOrder == nil { return nil } @@ -27,6 +29,17 @@ func MoveTaskOrder(moveTaskOrder *models.Move) *primev3messages.MoveTaskOrder { setPortsOnShipments(&moveTaskOrder.MTOServiceItems, mtoShipments) + var destGbloc, destZip string + var err error + destGbloc, err = moveTaskOrder.GetDestinationGBLOC(db) + if err != nil { + destGbloc = "" + } + destZip, err = moveTaskOrder.GetDestinationPostalCode(db) + if err != nil { + destZip = "" + } + payload := &primev3messages.MoveTaskOrder{ ID: strfmt.UUID(moveTaskOrder.ID.String()), MoveCode: moveTaskOrder.Locator, @@ -38,6 +51,8 @@ func MoveTaskOrder(moveTaskOrder *models.Move) *primev3messages.MoveTaskOrder { ExcessWeightUploadID: handlers.FmtUUIDPtr(moveTaskOrder.ExcessWeightUploadID), OrderID: strfmt.UUID(moveTaskOrder.OrdersID.String()), Order: Order(&moveTaskOrder.Orders), + DestinationGBLOC: destGbloc, + DestinationPostalCode: destZip, ReferenceID: *moveTaskOrder.ReferenceID, PaymentRequests: *paymentRequests, MtoShipments: *mtoShipments, @@ -61,9 +76,9 @@ func MoveTaskOrder(moveTaskOrder *models.Move) *primev3messages.MoveTaskOrder { return payload } -func MoveTaskOrderWithShipmentOconusRateArea(moveTaskOrder *models.Move, shipmentRateArea *[]services.ShipmentPostalCodeRateArea) *primev3messages.MoveTaskOrder { +func MoveTaskOrderWithShipmentOconusRateArea(appCtx appcontext.AppContext, moveTaskOrder *models.Move, shipmentRateArea *[]services.ShipmentPostalCodeRateArea) *primev3messages.MoveTaskOrder { // create default payload - var payload = MoveTaskOrder(moveTaskOrder) + var payload = MoveTaskOrder(appCtx, moveTaskOrder) // decorate payload with oconus rateArea information if payload != nil && shipmentRateArea != nil { diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go b/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go index e6de847b908..cdc4dc13b7c 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go @@ -82,7 +82,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { } suite.Run("Success - Returns a basic move payload with no payment requests, service items or shipments", func() { - returnedModel := MoveTaskOrder(&basicMove) + returnedModel := MoveTaskOrder(suite.AppContextForTest(), &basicMove) suite.IsType(&primev3messages.MoveTaskOrder{}, returnedModel) suite.Equal(strfmt.UUID(basicMove.ID.String()), returnedModel.ID) @@ -249,7 +249,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { }) // no ShipmentPostalCodeRateArea passed in - returnedModel := MoveTaskOrderWithShipmentOconusRateArea(newMove, nil) + returnedModel := MoveTaskOrderWithShipmentOconusRateArea(suite.AppContextForTest(), newMove, nil) suite.IsType(&primev3messages.MoveTaskOrder{}, returnedModel) suite.Equal(strfmt.UUID(newMove.ID.String()), returnedModel.ID) @@ -312,7 +312,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { }, } - returnedModel = MoveTaskOrderWithShipmentOconusRateArea(newMove, &shipmentPostalCodeRateArea) + returnedModel = MoveTaskOrderWithShipmentOconusRateArea(suite.AppContextForTest(), newMove, &shipmentPostalCodeRateArea) var shipmentPostalCodeRateAreaLookupMap = make(map[string]services.ShipmentPostalCodeRateArea) for _, i := range shipmentPostalCodeRateArea { @@ -1214,6 +1214,55 @@ func (suite *PayloadsSuite) TestBoatShipment() { suite.NotNil(result) } +func (suite *PayloadsSuite) TestDestinationPostalCodeAndGBLOC() { + moveID := uuid.Must(uuid.NewV4()) + moveLocator := "TESTTEST" + primeTime := time.Now() + ordersID := uuid.Must(uuid.NewV4()) + refID := "123456" + contractNum := "HTC-123-456" + address := models.Address{PostalCode: "35023"} + shipment := models.MTOShipment{ + ID: uuid.Must(uuid.NewV4()), + DestinationAddress: &address, + } + shipments := models.MTOShipments{shipment} + contractor := models.Contractor{ + ContractNumber: contractNum, + } + + basicMove := models.Move{ + ID: moveID, + Locator: moveLocator, + CreatedAt: primeTime, + ReferenceID: &refID, + AvailableToPrimeAt: &primeTime, + ApprovedAt: &primeTime, + OrdersID: ordersID, + Contractor: &contractor, + PaymentRequests: models.PaymentRequests{}, + SubmittedAt: &primeTime, + UpdatedAt: primeTime, + Status: models.MoveStatusAPPROVED, + SignedCertifications: models.SignedCertifications{}, + MTOServiceItems: models.MTOServiceItems{}, + MTOShipments: shipments, + } + + suite.Run("Returns values needed to get the destination postal code and GBLOC", func() { + returnedModel := MoveTaskOrder(suite.AppContextForTest(), &basicMove) + + suite.IsType(&primev3messages.MoveTaskOrder{}, returnedModel) + suite.Equal(strfmt.UUID(basicMove.ID.String()), returnedModel.ID) + suite.Equal(basicMove.Locator, returnedModel.MoveCode) + suite.Equal(strfmt.DateTime(basicMove.CreatedAt), returnedModel.CreatedAt) + suite.Equal(handlers.FmtDateTimePtr(basicMove.AvailableToPrimeAt), returnedModel.AvailableToPrimeAt) + suite.Equal(strfmt.UUID(basicMove.OrdersID.String()), returnedModel.OrderID) + suite.Equal(strfmt.DateTime(basicMove.UpdatedAt), returnedModel.UpdatedAt) + suite.NotEmpty(returnedModel.ETag) + }) +} + func (suite *PayloadsSuite) TestMarketCode() { suite.Run("returns nil when marketCode is nil", func() { var marketCode *models.MarketCode = nil @@ -1275,7 +1324,7 @@ func (suite *PayloadsSuite) TestMTOServiceItemPOEFSC() { }, } - mtoPayload := MoveTaskOrder(&move) + mtoPayload := MoveTaskOrder(suite.AppContextForTest(), &move) suite.NotNil(mtoPayload) suite.Equal(mtoPayload.MtoShipments[0].PortOfEmbarkation.PortType, portLocation.Port.PortType.String()) suite.Equal(mtoPayload.MtoShipments[0].PortOfEmbarkation.PortCode, portLocation.Port.PortCode) @@ -1326,7 +1375,7 @@ func (suite *PayloadsSuite) TestMTOServiceItemPODFSC() { }, } - mtoPayload := MoveTaskOrder(&move) + mtoPayload := MoveTaskOrder(suite.AppContextForTest(), &move) suite.NotNil(mtoPayload) suite.Equal(mtoPayload.MtoShipments[0].PortOfDebarkation.PortType, portLocation.Port.PortType.String()) suite.Equal(mtoPayload.MtoShipments[0].PortOfDebarkation.PortCode, portLocation.Port.PortCode) diff --git a/pkg/models/move.go b/pkg/models/move.go index 23c1dd7c479..8201a7f0dc6 100644 --- a/pkg/models/move.go +++ b/pkg/models/move.go @@ -196,6 +196,50 @@ func FetchMove(db *pop.Connection, session *auth.Session, id uuid.UUID) (*Move, return &move, nil } +// GetDestinationPostalCode returns the postal code for the move. This ensures that business logic is centralized. +func (m Move) GetDestinationPostalCode(db *pop.Connection) (string, error) { + // Since this requires looking up the move in the DB, the move must have an ID. This means, the move has to have been created first. + if uuid.UUID.IsNil(m.ID) { + return "", errors.WithMessage(ErrInvalidOrderID, "You must created the move in the DB before getting the destination Postal Code.") + } + + err := db.Load(&m, "Orders") + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return "", errors.WithMessage(err, "No Orders found in the DB associated with moveID "+m.ID.String()) + } + return "", err + } + + var gblocsMap map[uuid.UUID]string + gblocsMap, err = m.Orders.GetDestinationPostalCodeForAssociatedMoves(db) + if err != nil { + return "", err + } + return gblocsMap[m.ID], nil +} + +// GetDestinationGBLOC returns the GBLOC for the move. This ensures that business logic is centralized. +func (m Move) GetDestinationGBLOC(db *pop.Connection) (string, error) { + // Since this requires looking up the move in the DB, the move must have an ID. This means, the move has to have been created first. + if uuid.UUID.IsNil(m.ID) { + return "", errors.WithMessage(ErrInvalidOrderID, "You must created the move in the DB before getting the destination GBLOC.") + } + + postalCode, err := m.GetDestinationPostalCode(db) + if err != nil { + return "", err + } + + var gblocResult PostalCodeToGBLOC + gblocResult, err = FetchGBLOCForPostalCode(db, postalCode) + if err != nil { + return "", err + } + + return gblocResult.GBLOC, err +} + // CreateSignedCertification creates a new SignedCertification associated with this move func (m Move) CreateSignedCertification(db *pop.Connection, submittingUserID uuid.UUID, diff --git a/pkg/models/mto_shipments.go b/pkg/models/mto_shipments.go index da5e021a698..6311ce24d6e 100644 --- a/pkg/models/mto_shipments.go +++ b/pkg/models/mto_shipments.go @@ -8,6 +8,7 @@ import ( "github.com/gobuffalo/validate/v3" "github.com/gobuffalo/validate/v3/validators" "github.com/gofrs/uuid" + "github.com/pkg/errors" "github.com/transcom/mymove/pkg/unit" ) @@ -275,6 +276,33 @@ func GetCustomerFromShipment(db *pop.Connection, shipmentID uuid.UUID) (*Service return &serviceMember, nil } +func (m *MTOShipment) UpdateOrdersDestinationGBLOC(db *pop.Connection) error { + // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. + if uuid.UUID.IsNil(m.ID) { + return fmt.Errorf("error updating orders destination GBLOC for shipment due to no shipment ID provided") + } + + var err error + var order Order + + err = db.Load(&m, "MoveTaskOrder.OrdersID") + if err != nil { + return fmt.Errorf("error loading orders for shipment ID: %s with error %w", m.ID, err) + } + + order, err = FetchOrder(db, m.MoveTaskOrder.OrdersID) + if err != nil { + return fmt.Errorf("error fetching order for shipment ID: %s with error %w", m.ID, err) + } + + err = order.UpdateDestinationGBLOC(db) + if err != nil { + return fmt.Errorf("error fetching GBLOC for postal code with error %w", err) + } + + return nil +} + // Helper function to check that an MTO Shipment contains a PPM Shipment func (m MTOShipment) ContainsAPPMShipment() bool { return m.PPMShipment != nil @@ -341,6 +369,35 @@ func DetermineShipmentMarketCode(shipment *MTOShipment) *MTOShipment { return shipment } +func (s MTOShipment) GetDestinationAddress(db *pop.Connection) (*Address, error) { + if uuid.UUID.IsNil(s.ID) { + return nil, errors.New("MTOShipment ID is required to fetch destination address.") + } + + err := db.Load(&s, "DestinationAddress", "PPMShipment.DestinationAddress") + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return nil, errors.WithMessage(ErrSqlRecordNotFound, string(s.ShipmentType)+" ShipmentID: "+s.ID.String()) + } + return nil, err + } + + if s.ShipmentType == MTOShipmentTypePPM { + if s.PPMShipment.DestinationAddress != nil { + return s.PPMShipment.DestinationAddress, nil + } else if s.DestinationAddress != nil { + return s.DestinationAddress, nil + } + return nil, errors.WithMessage(ErrMissingDestinationAddress, string(s.ShipmentType)) + } + + if s.DestinationAddress != nil { + return s.DestinationAddress, nil + } + + return nil, errors.WithMessage(ErrMissingDestinationAddress, string(s.ShipmentType)) +} + // this function takes in two addresses and determines the market code string func DetermineMarketCode(address1 *Address, address2 *Address) (MarketCode, error) { if address1 == nil || address2 == nil { diff --git a/pkg/models/order.go b/pkg/models/order.go index f4628894a84..56c71e2265a 100644 --- a/pkg/models/order.go +++ b/pkg/models/order.go @@ -1,6 +1,7 @@ package models import ( + "sort" "time" "github.com/gobuffalo/pop/v6" @@ -371,6 +372,203 @@ func (o *Order) IsComplete() bool { return true } +// FetchAllShipmentsExcludingRejected returns all the shipments associated with an order excluding rejected shipments +func (o Order) FetchAllShipmentsExcludingRejected(db *pop.Connection) (map[uuid.UUID]MTOShipments, error) { + // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. + if uuid.UUID.IsNil(o.ID) { + return nil, errors.WithMessage(ErrInvalidOrderID, "You must created the order in the DB before fetching associated shipments.") + } + + var err error + + err = db.Load(&o, "Moves") + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return nil, errors.WithMessage(err, "No Moves were found for the order ID "+o.ID.String()) + } + return nil, errors.WithMessage(err, "Could not load moves for order ID "+o.ID.String()) + } + + // shipmentsMap is a map of key, value pairs where the key is the move id and the value is a list of associated MTOShipments + shipmentsMap := make(map[uuid.UUID]MTOShipments) + + for _, m := range o.Moves { + var shipments MTOShipments + err = db.Load(&m, "MTOShipments") + if err != nil { + return nil, errors.WithMessage(err, "Could not load shipments for move "+m.ID.String()) + } + + for _, s := range m.MTOShipments { + err = db.Load(&s, "Status", "DeletedAt", "CreatedAt", "DestinationAddress") + if err != nil { + return nil, errors.WithMessage(err, "Could not load shipment with ID of "+s.ID.String()+" for move ID "+m.ID.String()) + } + + if s.Status != MTOShipmentStatusRejected && s.Status != MTOShipmentStatusCanceled && s.DeletedAt == nil { + shipments = append(shipments, s) + } + } + + sort.Slice(shipments, func(i, j int) bool { + return shipments[i].CreatedAt.Before(shipments[j].CreatedAt) + }) + + shipmentsMap[m.ID] = shipments + } + + return shipmentsMap, nil +} + +/* + * GetDestinationGBLOC returns a map of destination GBLOCs for the first shipments from all of + * the moves that are associated with an order. If there are no shipments returned on a particular move, + * it will return the GBLOC of the new duty station address for that move. + */ +func (o Order) GetDestinationGBLOC(db *pop.Connection) (map[uuid.UUID]string, error) { + // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. + if uuid.UUID.IsNil(o.ID) { + return nil, errors.WithMessage(ErrInvalidOrderID, "You must created the order in the DB before getting the destination GBLOC.") + } + + destinationPostalCodesMap, err := o.GetDestinationPostalCodeForAssociatedMoves(db) + if err != nil { + return nil, err + } + + destinationGBLOCsMap := make(map[uuid.UUID]string) + for k, v := range destinationPostalCodesMap { + var gblocResult PostalCodeToGBLOC + gblocResult, err = FetchGBLOCForPostalCode(db, v) + if err != nil { + return nil, errors.WithMessage(err, "Could not get GBLOC for postal code "+v+" for move ID "+k.String()) + } + destinationGBLOCsMap[k] = gblocResult.GBLOC + } + + return destinationGBLOCsMap, nil +} + +/* +* GetDestinationPostalCodeForAssociatedMove returns a map of Postal Codes of the destination address for the first shipments from each of +* the moves that are associated with an order. If there are no shipments returned, it will return the +* Postal Code of the new duty station addresses. + */ +func (o Order) GetDestinationPostalCodeForAssociatedMoves(db *pop.Connection) (map[uuid.UUID]string, error) { + if uuid.UUID.IsNil(o.ID) { + return nil, errors.WithMessage(ErrInvalidOrderID, "You must created the order in the DB before getting the destination Postal Code.") + } + + err := db.Load(&o, "Moves", "NewDutyLocation.Address.PostalCode") + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return nil, errors.WithMessage(err, "No Moves were found for the order ID "+o.ID.String()) + } + return nil, err + } + + // zipsMap is a map of key, value pairs where the key is the move id and the value is the destination postal code + zipsMap := make(map[uuid.UUID]string) + for i, m := range o.Moves { + err = db.Load(&o.Moves[i], "MTOShipments") + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return nil, errors.WithMessage(err, "Could not find shipments for move "+m.ID.String()) + } + return nil, err + } + + var shipments MTOShipments + for j, s := range o.Moves[i].MTOShipments { + err = db.Load(&o.Moves[i].MTOShipments[j], "CreatedAt", "Status", "DeletedAt", "DestinationAddress", "ShipmentType", "PPMShipment", "PPMShipment.Status", "PPMShipment.DestinationAddress") + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return nil, errors.WithMessage(err, "Could not load shipment with ID of "+s.ID.String()+" for move ID "+m.ID.String()) + } + return nil, err + } + + if o.Moves[i].MTOShipments[j].Status != MTOShipmentStatusRejected && + o.Moves[i].MTOShipments[j].Status != MTOShipmentStatusCanceled && + o.Moves[i].MTOShipments[j].ShipmentType != MTOShipmentTypeHHGIntoNTSDom && + o.Moves[i].MTOShipments[j].DeletedAt == nil { + shipments = append(shipments, o.Moves[i].MTOShipments[j]) + } + } + + // If we have valid shipments, use the first one's destination address + if len(shipments) > 0 { + sort.Slice(shipments, func(i, j int) bool { + return shipments[i].CreatedAt.Before(shipments[j].CreatedAt) + }) + + var addressResult *Address + addressResult, err = shipments[0].GetDestinationAddress(db) + if err != nil { + if err == ErrMissingDestinationAddress || err == ErrUnsupportedShipmentType { + zipsMap[o.Moves[i].ID] = o.NewDutyLocation.Address.PostalCode + } + return nil, err + } + + if addressResult != nil { + zipsMap[o.Moves[i].ID] = addressResult.PostalCode + } else { + return nil, errors.WithMessage(ErrMissingDestinationAddress, "No destination address was able to be found for the order ID "+o.ID.String()) + } + } else { + // No valid shipments, use new duty location + zipsMap[o.Moves[i].ID] = o.NewDutyLocation.Address.PostalCode + } + } + + if len(zipsMap) == 0 { + return nil, errors.New("No destination postal codes were found for the order ID " + o.ID.String()) + } + + return zipsMap, nil +} + +// UpdateDestinationGBLOC updates the destination GBLOC for the associated Order in the DB +func (o Order) UpdateDestinationGBLOC(db *pop.Connection) error { + // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. + if uuid.UUID.IsNil(o.ID) { + return errors.WithMessage(ErrInvalidOrderID, "You must created the order in the DB before updating the destination GBLOC.") + } + + var dbOrder Order + err := db.Find(&dbOrder, o.ID) + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return errors.WithMessage(err, "No Order was found for the order ID "+o.ID.String()) + } + return err + } + + err = db.Load(&o, "NewDutyLocation.Address.PostalCode") + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return errors.WithMessage(err, "No New Duty Location Address Postal Code was found for the order ID "+o.ID.String()) + } + return err + } + + var gblocResult PostalCodeToGBLOC + gblocResult, err = FetchGBLOCForPostalCode(db, o.NewDutyLocation.Address.PostalCode) + if err != nil { + return errors.WithMessage(err, "Could not get GBLOC for postal code "+o.NewDutyLocation.Address.PostalCode) + } + + dbOrder.DestinationGBLOC = &gblocResult.GBLOC + + err = db.Save(&dbOrder) + if err != nil { + return errors.WithMessage(err, "Could not save the updated destination GBLOC for order ID "+o.ID.String()) + } + + return nil +} + // IsCompleteForGBL checks if orders have all fields necessary to generate a GBL func (o *Order) IsCompleteForGBL() bool { diff --git a/pkg/models/service_member.go b/pkg/models/service_member.go index 28edcf7b2a3..ac70f00306a 100644 --- a/pkg/models/service_member.go +++ b/pkg/models/service_member.go @@ -366,7 +366,7 @@ func (s ServiceMember) CreateOrder(appCtx appcontext.AppContext, entitlement *Entitlement, originDutyLocationGBLOC *string, packingAndShippingInstructions string, - newDutyLocationGBLOC *string) (Order, *validate.Errors, error) { + destinationGBLOC *string) (Order, *validate.Errors, error) { var newOrders Order responseVErrors := validate.NewErrors() @@ -395,7 +395,7 @@ func (s ServiceMember) CreateOrder(appCtx appcontext.AppContext, SpouseHasProGear: spouseHasProGear, NewDutyLocationID: newDutyLocation.ID, NewDutyLocation: newDutyLocation, - DestinationGBLOC: newDutyLocationGBLOC, + DestinationGBLOC: destinationGBLOC, UploadedOrders: uploadedOrders, UploadedOrdersID: uploadedOrders.ID, Status: OrderStatusDRAFT, diff --git a/src/components/Table/SearchResultsTable.jsx b/src/components/Table/SearchResultsTable.jsx index 9369fe66f32..b6cfe3fc2af 100644 --- a/src/components/Table/SearchResultsTable.jsx +++ b/src/components/Table/SearchResultsTable.jsx @@ -166,7 +166,7 @@ const moveSearchColumns = (moveLockFlag, handleEditProfileClick) => [ createHeader( 'Destination ZIP', (row) => { - return row.destinationDutyLocationPostalCode; + return row.destinationPostalCode; }, { id: 'destinationPostalCode', diff --git a/swagger-def/definitions/prime/MoveTaskOrder.yaml b/swagger-def/definitions/prime/MoveTaskOrder.yaml index 1cc5e0458d8..6540bd0543e 100644 --- a/swagger-def/definitions/prime/MoveTaskOrder.yaml +++ b/swagger-def/definitions/prime/MoveTaskOrder.yaml @@ -22,6 +22,14 @@ properties: type: string order: $ref: 'Order.yaml' + destinationGBLOC: + type: string + example: 'KKFA' + readOnly: true + destinationPostalCode: + type: string + example: '90210' + readOnly: true referenceId: example: 1001-3456 type: string diff --git a/swagger-def/definitions/prime/v2/MoveTaskOrder.yaml b/swagger-def/definitions/prime/v2/MoveTaskOrder.yaml index a609c3d4e41..3c73a547556 100644 --- a/swagger-def/definitions/prime/v2/MoveTaskOrder.yaml +++ b/swagger-def/definitions/prime/v2/MoveTaskOrder.yaml @@ -22,6 +22,14 @@ properties: type: string order: $ref: 'Order.yaml' + destinationGBLOC: + type: string + example: 'KKFA' + readOnly: true + destinationPostalCode: + type: string + example: '90210' + readOnly: true referenceId: example: 1001-3456 type: string diff --git a/swagger-def/definitions/prime/v3/MoveTaskOrder.yaml b/swagger-def/definitions/prime/v3/MoveTaskOrder.yaml index d10dae35eab..1cd3a9d41b1 100644 --- a/swagger-def/definitions/prime/v3/MoveTaskOrder.yaml +++ b/swagger-def/definitions/prime/v3/MoveTaskOrder.yaml @@ -22,6 +22,14 @@ properties: type: string order: $ref: 'Order.yaml' + destinationGBLOC: + type: string + example: 'KKFA' + readOnly: true + destinationPostalCode: + type: string + example: '90210' + readOnly: true referenceId: example: 1001-3456 type: string diff --git a/swagger-def/ghc.yaml b/swagger-def/ghc.yaml index a0df07f3800..43061a82e8f 100644 --- a/swagger-def/ghc.yaml +++ b/swagger-def/ghc.yaml @@ -7105,6 +7105,12 @@ definitions: example: c56a4180-65aa-42ec-a945-5fd21dec0538 format: uuid type: string + destinationGBLOC: + example: 'AGFM' + type: string + destinationPostalCode: + example: '90210' + type: string referenceId: example: 1001-3456 type: string @@ -7259,7 +7265,7 @@ definitions: title: ZIP example: '90210' pattern: ^(\d{5})$ - destinationDutyLocationPostalCode: + destinationPostalCode: format: zip type: string title: ZIP diff --git a/swagger-def/prime.yaml b/swagger-def/prime.yaml index 5fcb6cdd5e3..60e686475ba 100644 --- a/swagger-def/prime.yaml +++ b/swagger-def/prime.yaml @@ -1487,6 +1487,12 @@ definitions: example: c56a4180-65aa-42ec-a945-5fd21dec0538 format: uuid type: string + destinationGBLOC: + example: 'AGFM' + type: string + destinationPostalCode: + example: '90210' + type: string referenceId: example: 1001-3456 type: string diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index ab568b434c8..7faf82b166f 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -7445,6 +7445,12 @@ definitions: example: c56a4180-65aa-42ec-a945-5fd21dec0538 format: uuid type: string + destinationGBLOC: + example: AGFM + type: string + destinationPostalCode: + example: '90210' + type: string referenceId: example: 1001-3456 type: string @@ -7601,7 +7607,7 @@ definitions: title: ZIP example: '90210' pattern: ^(\d{5})$ - destinationDutyLocationPostalCode: + destinationPostalCode: format: zip type: string title: ZIP diff --git a/swagger/prime.yaml b/swagger/prime.yaml index 83ef52b06c7..5c661452350 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -1848,6 +1848,12 @@ definitions: example: c56a4180-65aa-42ec-a945-5fd21dec0538 format: uuid type: string + destinationGBLOC: + example: AGFM + type: string + destinationPostalCode: + example: '90210' + type: string referenceId: example: 1001-3456 type: string @@ -1904,6 +1910,14 @@ definitions: type: string order: $ref: '#/definitions/Order' + destinationGBLOC: + type: string + example: KKFA + readOnly: true + destinationPostalCode: + type: string + example: '90210' + readOnly: true referenceId: example: 1001-3456 type: string diff --git a/swagger/prime_v2.yaml b/swagger/prime_v2.yaml index 00c4e8d169b..e77b1367ec7 100644 --- a/swagger/prime_v2.yaml +++ b/swagger/prime_v2.yaml @@ -3028,6 +3028,14 @@ definitions: type: string order: $ref: '#/definitions/Order' + destinationGBLOC: + type: string + example: KKFA + readOnly: true + destinationPostalCode: + type: string + example: '90210' + readOnly: true referenceId: example: 1001-3456 type: string diff --git a/swagger/prime_v3.yaml b/swagger/prime_v3.yaml index c478289f287..c7fce10d0da 100644 --- a/swagger/prime_v3.yaml +++ b/swagger/prime_v3.yaml @@ -3559,6 +3559,14 @@ definitions: type: string order: $ref: '#/definitions/Order' + destinationGBLOC: + type: string + example: KKFA + readOnly: true + destinationPostalCode: + type: string + example: '90210' + readOnly: true referenceId: example: 1001-3456 type: string From b870130d79309e34d79c269e64b6f32d0503e20b Mon Sep 17 00:00:00 2001 From: AaronW Date: Wed, 8 Jan 2025 15:49:09 +0000 Subject: [PATCH 064/113] reverting changes erroneously brought in from B-21581 --- .../payloads/model_to_payload_test.go | 88 ------------- pkg/handlers/internalapi/orders.go | 6 - pkg/handlers/internalapi/orders_test.go | 124 ------------------ pkg/models/order.go | 43 ------ pkg/testdatagen/scenario/shared.go | 54 -------- pkg/testdatagen/testharness/dispatch.go | 3 - pkg/testdatagen/testharness/make_move.go | 16 --- .../servicesCounselingFlows.spec.js | 27 ---- playwright/tests/utils/testharness.js | 11 -- 9 files changed, 372 deletions(-) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go index 40fb6e67ebf..ec3072d2ca0 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go @@ -9,7 +9,6 @@ import ( "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/gen/ghcmessages" - "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/models/roles" @@ -316,93 +315,6 @@ func (suite *PayloadsSuite) TestShipmentAddressUpdate() { }) } -func (suite *PayloadsSuite) TestMoveWithGBLOC() { - defaultOrdersNumber := "ORDER3" - defaultTACNumber := "F8E1" - defaultDepartmentIndicator := "AIR_AND_SPACE_FORCE" - defaultGrade := "E_1" - defaultHasDependents := false - defaultSpouseHasProGear := false - defaultOrdersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - defaultOrdersTypeDetail := internalmessages.OrdersTypeDetail("HHG_PERMITTED") - defaultStatus := models.OrderStatusDRAFT - testYear := 2018 - defaultIssueDate := time.Date(testYear, time.March, 15, 0, 0, 0, 0, time.UTC) - defaultReportByDate := time.Date(testYear, time.August, 1, 0, 0, 0, 0, time.UTC) - defaultGBLOC := "KKFA" - - originDutyLocation := models.DutyLocation{ - Name: "Custom Origin", - } - originDutyLocationTOName := "origin duty location transportation office" - firstName := "customFirst" - lastName := "customLast" - serviceMember := models.ServiceMember{ - FirstName: &firstName, - LastName: &lastName, - } - uploadedOrders := models.Document{ - ID: uuid.Must(uuid.NewV4()), - } - dependents := 7 - entitlement := models.Entitlement{ - TotalDependents: &dependents, - } - amendedOrders := models.Document{ - ID: uuid.Must(uuid.NewV4()), - } - // Create order - order := factory.BuildOrder(suite.DB(), []factory.Customization{ - { - Model: originDutyLocation, - Type: &factory.DutyLocations.OriginDutyLocation, - }, - { - Model: models.TransportationOffice{ - Name: originDutyLocationTOName, - }, - Type: &factory.TransportationOffices.OriginDutyLocation, - }, - { - Model: serviceMember, - }, - { - Model: uploadedOrders, - Type: &factory.Documents.UploadedOrders, - }, - { - Model: entitlement, - }, - { - Model: amendedOrders, - Type: &factory.Documents.UploadedAmendedOrders, - }, - }, nil) - - suite.Equal(defaultOrdersNumber, *order.OrdersNumber) - suite.Equal(defaultTACNumber, *order.TAC) - suite.Equal(defaultDepartmentIndicator, *order.DepartmentIndicator) - suite.Equal(defaultGrade, string(*order.Grade)) - suite.Equal(defaultHasDependents, order.HasDependents) - suite.Equal(defaultSpouseHasProGear, order.SpouseHasProGear) - suite.Equal(defaultOrdersType, order.OrdersType) - suite.Equal(defaultOrdersTypeDetail, *order.OrdersTypeDetail) - suite.Equal(defaultStatus, order.Status) - suite.Equal(defaultIssueDate, order.IssueDate) - suite.Equal(defaultReportByDate, order.ReportByDate) - suite.Equal(defaultGBLOC, *order.OriginDutyLocationGBLOC) - - suite.Equal(originDutyLocation.Name, order.OriginDutyLocation.Name) - suite.Equal(originDutyLocationTOName, order.OriginDutyLocation.TransportationOffice.Name) - suite.Equal(*serviceMember.FirstName, *order.ServiceMember.FirstName) - suite.Equal(*serviceMember.LastName, *order.ServiceMember.LastName) - suite.Equal(uploadedOrders.ID, order.UploadedOrdersID) - suite.Equal(uploadedOrders.ID, order.UploadedOrders.ID) - suite.Equal(*entitlement.TotalDependents, *order.Entitlement.TotalDependents) - suite.Equal(amendedOrders.ID, *order.UploadedAmendedOrdersID) - suite.Equal(amendedOrders.ID, order.UploadedAmendedOrders.ID) -} - func (suite *PayloadsSuite) TestWeightTicketUpload() { uploadID, _ := uuid.NewV4() testURL := "https://testurl.com" diff --git a/pkg/handlers/internalapi/orders.go b/pkg/handlers/internalapi/orders.go index 3936dcb39e2..c9b5125c827 100644 --- a/pkg/handlers/internalapi/orders.go +++ b/pkg/handlers/internalapi/orders.go @@ -382,12 +382,6 @@ func (h UpdateOrdersHandler) Handle(params ordersop.UpdateOrdersParams) middlewa order.OriginDutyLocation = &originDutyLocation order.OriginDutyLocationID = &originDutyLocationID - originGBLOC, originGBLOCerr := models.FetchGBLOCForPostalCode(appCtx.DB(), originDutyLocation.Address.PostalCode) - if originGBLOCerr != nil { - return handlers.ResponseForError(appCtx.Logger(), originGBLOCerr), originGBLOCerr - } - order.OriginDutyLocationGBLOC = &originGBLOC.GBLOC - if payload.MoveID != "" { moveID, err := uuid.FromString(payload.MoveID.String()) diff --git a/pkg/handlers/internalapi/orders_test.go b/pkg/handlers/internalapi/orders_test.go index 59df8daf475..b2ad7897f8a 100644 --- a/pkg/handlers/internalapi/orders_test.go +++ b/pkg/handlers/internalapi/orders_test.go @@ -723,130 +723,6 @@ func (suite *HandlerSuite) TestUpdateOrdersHandler() { }) } -func (suite *HandlerSuite) TestUpdateOrdersHandlerOriginPostalCodeAndGBLOC() { - factory.BuildPostalCodeToGBLOC(suite.DB(), []factory.Customization{ - { - Model: models.PostalCodeToGBLOC{ - PostalCode: "90210", - GBLOC: "KKFA", - }, - }, - }, nil) - factory.BuildPostalCodeToGBLOC(suite.DB(), []factory.Customization{ - { - Model: models.PostalCodeToGBLOC{ - PostalCode: "35023", - GBLOC: "CNNQ", - }, - }, - }, nil) - - firstAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ - { - Model: models.Address{ - PostalCode: "90210", - }, - }, - }, nil) - updatedAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ - { - Model: models.Address{ - PostalCode: "35023", - }, - }, - }, nil) - dutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - { - Model: models.DutyLocation{ - AddressID: firstAddress.ID, - }, - }, - }, nil) - updatedDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ - { - Model: models.DutyLocation{ - AddressID: updatedAddress.ID, - }, - }, - }, nil) - newDutyLocation := factory.BuildDutyLocation(suite.DB(), nil, nil) - - order := factory.BuildOrder(suite.DB(), []factory.Customization{ - { - Model: models.Order{ - OriginDutyLocationID: &dutyLocation.ID, - NewDutyLocationID: newDutyLocation.ID, - }, - }, - }, nil) - - fetchedOrder, err := models.FetchOrder(suite.DB(), order.ID) - suite.NoError(err) - - var fetchedPostalCode, fetchedGBLOC string - fetchedPostalCode, err = fetchedOrder.GetOriginPostalCode(suite.DB()) - suite.NoError(err) - fetchedGBLOC, err = fetchedOrder.GetOriginGBLOC(suite.DB()) - suite.NoError(err) - - suite.Equal("90210", fetchedPostalCode) - suite.Equal("KKFA", fetchedGBLOC) - - newOrdersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - issueDate := time.Date(2018, time.March, 10, 0, 0, 0, 0, time.UTC) - reportByDate := time.Date(2018, time.August, 1, 0, 0, 0, 0, time.UTC) - deptIndicator := internalmessages.DeptIndicatorAIRANDSPACEFORCE - - payload := &internalmessages.CreateUpdateOrders{ - OrdersType: &order.OrdersType, - NewDutyLocationID: handlers.FmtUUID(order.NewDutyLocationID), - OriginDutyLocationID: *handlers.FmtUUID(updatedDutyLocation.ID), - IssueDate: handlers.FmtDate(issueDate), - ReportByDate: handlers.FmtDate(reportByDate), - DepartmentIndicator: &deptIndicator, - HasDependents: handlers.FmtBool(false), - SpouseHasProGear: handlers.FmtBool(false), - ServiceMemberID: handlers.FmtUUID(order.ServiceMemberID), - Grade: models.ServiceMemberGradeE4.Pointer(), - } - - path := fmt.Sprintf("/orders/%v", order.ID.String()) - req := httptest.NewRequest("PUT", path, nil) - req = suite.AuthenticateRequest(req, order.ServiceMember) - - params := ordersop.UpdateOrdersParams{ - HTTPRequest: req, - OrdersID: *handlers.FmtUUID(order.ID), - UpdateOrders: payload, - } - - fakeS3 := storageTest.NewFakeS3Storage(true) - handlerConfig := suite.HandlerConfig() - handlerConfig.SetFileStorer(fakeS3) - - handler := UpdateOrdersHandler{handlerConfig} - - response := handler.Handle(params) - - suite.IsType(&ordersop.UpdateOrdersOK{}, response) - - okResponse := response.(*ordersop.UpdateOrdersOK) - - suite.NoError(okResponse.Payload.Validate(strfmt.Default)) - suite.Equal(string(newOrdersType), string(*okResponse.Payload.OrdersType)) - - fetchedOrder, err = models.FetchOrder(suite.DB(), order.ID) - suite.NoError(err) - - fetchedPostalCode, err = fetchedOrder.GetOriginPostalCode(suite.DB()) - suite.NoError(err) - fetchedGBLOC = *fetchedOrder.OriginDutyLocationGBLOC - suite.NoError(err) - - suite.Equal("35023", fetchedPostalCode) - suite.Equal("CNNQ", fetchedGBLOC) -} - func (suite *HandlerSuite) TestEntitlementHelperFunc() { orderGrade := internalmessages.OrderPayGrade("O-3") int64Dependents := int64(2) diff --git a/pkg/models/order.go b/pkg/models/order.go index 56c71e2265a..da23b6f6c6e 100644 --- a/pkg/models/order.go +++ b/pkg/models/order.go @@ -309,49 +309,6 @@ func (o *Order) CreateNewMove(db *pop.Connection, moveOptions MoveOptions) (*Mov return createNewMove(db, *o, moveOptions) } -/* - * GetOriginPostalCode returns the GBLOC for the postal code of the the origin duty location of the order. - */ -func (o Order) GetOriginPostalCode(db *pop.Connection) (string, error) { - // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. - if uuid.UUID.IsNil(o.ID) { - return "", errors.WithMessage(ErrInvalidOrderID, "You must create the order in the DB before getting the origin GBLOC.") - } - - err := db.Load(&o, "OriginDutyLocation.Address") - if err != nil { - if err.Error() == RecordNotFoundErrorString { - return "", errors.WithMessage(err, "No Origin Duty Location was found for the order ID "+o.ID.String()) - } - return "", err - } - - return o.OriginDutyLocation.Address.PostalCode, nil -} - -/* - * GetOriginGBLOC returns the GBLOC for the postal code of the the origin duty location of the order. - */ -func (o Order) GetOriginGBLOC(db *pop.Connection) (string, error) { - // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. - if uuid.UUID.IsNil(o.ID) { - return "", errors.WithMessage(ErrInvalidOrderID, "You must created the order in the DB before getting the destination GBLOC.") - } - - originPostalCode, err := o.GetOriginPostalCode(db) - if err != nil { - return "", err - } - - var originGBLOC PostalCodeToGBLOC - originGBLOC, err = FetchGBLOCForPostalCode(db, originPostalCode) - if err != nil { - return "", err - } - - return originGBLOC.GBLOC, nil -} - // IsComplete checks if orders have all fields necessary to approve a move func (o *Order) IsComplete() bool { diff --git a/pkg/testdatagen/scenario/shared.go b/pkg/testdatagen/scenario/shared.go index 4450a6964e9..8d04108fd6b 100644 --- a/pkg/testdatagen/scenario/shared.go +++ b/pkg/testdatagen/scenario/shared.go @@ -10507,60 +10507,6 @@ func CreateNeedsServicesCounseling(appCtx appcontext.AppContext, ordersType inte return move } -/* -Create Needs Service Counseling in a non-default GBLOC - pass in orders with all required information, shipment type, destination type, locator -*/ -func CreateNeedsServicesCounselingInOtherGBLOC(appCtx appcontext.AppContext, ordersType internalmessages.OrdersType, shipmentType models.MTOShipmentType, destinationType *models.DestinationType, locator string) models.Move { - db := appCtx.DB() - originDutyLocationAddress := factory.BuildAddress(db, []factory.Customization{ - { - Model: models.Address{ - PostalCode: "35023", - }, - }, - }, nil) - originDutyLocation := factory.BuildDutyLocation(db, []factory.Customization{ - { - Model: models.DutyLocation{ - Name: "Test Location", - AddressID: originDutyLocationAddress.ID, - }, - }, - }, nil) - order := factory.BuildOrder(db, []factory.Customization{ - { - Model: originDutyLocation, - }, - }, nil) - move := factory.BuildMove(db, []factory.Customization{ - { - Model: order, - LinkOnly: true, - }, - { - Model: models.Move{ - Locator: locator, - Status: models.MoveStatusNeedsServiceCounseling, - }, - }, - }, nil) - - factory.BuildMTOShipment(db, []factory.Customization{ - { - Model: move, - LinkOnly: true, - }, - { - Model: models.MTOShipment{ - ShipmentType: shipmentType, - Status: models.MTOShipmentStatusSubmitted, - }, - }, - }, nil) - - return move -} - func CreateNeedsServicesCounselingWithAmendedOrders(appCtx appcontext.AppContext, userUploader *uploader.UserUploader, ordersType internalmessages.OrdersType, shipmentType models.MTOShipmentType, destinationType *models.DestinationType, locator string) models.Move { db := appCtx.DB() submittedAt := time.Now() diff --git a/pkg/testdatagen/testharness/dispatch.go b/pkg/testdatagen/testharness/dispatch.go index dd4b421cc2c..e9cf4c27d5f 100644 --- a/pkg/testdatagen/testharness/dispatch.go +++ b/pkg/testdatagen/testharness/dispatch.go @@ -50,9 +50,6 @@ var actionDispatcher = map[string]actionFunc{ "HHGMoveNeedsSC": func(appCtx appcontext.AppContext) testHarnessResponse { return MakeHHGMoveNeedsSC(appCtx) }, - "HHGMoveNeedsSCOtherGBLOC": func(appCtx appcontext.AppContext) testHarnessResponse { - return MakeHHGMoveNeedsSCOtherGBLOC(appCtx) - }, "HHGMoveAsUSMCNeedsSC": func(appCtx appcontext.AppContext) testHarnessResponse { return MakeHHGMoveNeedsServicesCounselingUSMC(appCtx) }, diff --git a/pkg/testdatagen/testharness/make_move.go b/pkg/testdatagen/testharness/make_move.go index 50acd913415..fd337bb029f 100644 --- a/pkg/testdatagen/testharness/make_move.go +++ b/pkg/testdatagen/testharness/make_move.go @@ -4013,22 +4013,6 @@ func MakeHHGMoveNeedsSC(appCtx appcontext.AppContext) models.Move { return *newmove } -// MakeHHGMoveNeedsSCOtherGBLOC creates an fully ready move needing SC approval in a non-default GBLOC -func MakeHHGMoveNeedsSCOtherGBLOC(appCtx appcontext.AppContext) models.Move { - pcos := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION - hhg := models.MTOShipmentTypeHHG - locator := models.GenerateLocator() - move := scenario.CreateNeedsServicesCounselingInOtherGBLOC(appCtx, pcos, hhg, nil, locator) - - // re-fetch the move so that we ensure we have exactly what is in - // the db - newmove, err := models.FetchMove(appCtx.DB(), &auth.Session{}, move.ID) - if err != nil { - log.Panic(fmt.Errorf("failed to fetch move: %w", err)) - } - return *newmove -} - // MakeBoatHaulAwayMoveNeedsSC creates an fully ready move with a boat haul-away shipment needing SC approval func MakeBoatHaulAwayMoveNeedsSC(appCtx appcontext.AppContext) models.Move { userUploader := newUserUploader(appCtx) diff --git a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js index 73adcf940f2..39fe893bc73 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js @@ -13,33 +13,6 @@ const supportingDocsEnabled = process.env.FEATURE_FLAG_MANAGE_SUPPORTING_DOCS; const LocationLookup = 'BEVERLY HILLS, CA 90210 (LOS ANGELES)'; test.describe('Services counselor user', () => { - test.describe('GBLOC tests', () => { - test.describe('Origin Duty Location', () => { - let moveLocatorKKFA = ''; - let moveLocatorCNNQ = ''; - test.beforeEach(async ({ scPage }) => { - const moveKKFA = await scPage.testHarness.buildHHGMoveNeedsSC(); - moveLocatorKKFA = moveKKFA.locator; - const moveCNNQ = await scPage.testHarness.buildHHGMoveNeedsSC(); - moveLocatorCNNQ = moveCNNQ.locator; - }); - - test('when origin duty location GBLOC matches services counselor GBLOC', async ({ page }) => { - const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); - await locatorFilter.fill(moveLocatorKKFA); - await locatorFilter.blur(); - await expect(page.getByTestId('locator-0')).toBeVisible(); - }); - - test('when origin duty location GBLOC does not match services counselor GBLOC', async ({ page }) => { - const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); - await locatorFilter.fill(moveLocatorCNNQ); - await locatorFilter.blur(); - await expect(page.getByTestId('locator-0')).not.toBeVisible(); - }); - }); - }); - test.describe('with basic HHG move', () => { test.beforeEach(async ({ scPage }) => { const move = await scPage.testHarness.buildHHGMoveNeedsSC(); diff --git a/playwright/tests/utils/testharness.js b/playwright/tests/utils/testharness.js index 74feee4ffef..a01dac3461e 100644 --- a/playwright/tests/utils/testharness.js +++ b/playwright/tests/utils/testharness.js @@ -28,9 +28,6 @@ export class TestHarness { * @property {string} id * @property {string} locator * @property {Object} Orders - * @property {Object} OriginDutyLocation - * @property {Object} OriginDutyLocation.Address - * @property {string} OriginDutyLocation.Address.PostalCode * @property {Object} Orders.NewDutyLocation * @property {string} Orders.NewDutyLocation.name * @property {Object} Orders.ServiceMember @@ -395,14 +392,6 @@ export class TestHarness { return this.buildDefault('HHGMoveNeedsSC'); } - /** - * Use testharness to build hhg move needing SC approval in a non-default GBLOC - * @returns {Promise} - */ - async buildHHGMoveNeedsSCInOtherGBLOC() { - return this.buildDefault('HHGMoveNeedsSCOtherGBLOC'); - } - /** * Use testharness to build hhg move as USMC needing SC approval * @returns {Promise} From a40ccf42a3b266503a38cf33084e9c8b41edab35 Mon Sep 17 00:00:00 2001 From: AaronW Date: Wed, 8 Jan 2025 16:30:20 +0000 Subject: [PATCH 065/113] changes requested on PR --- .../internal/payloads/model_to_payload.go | 29 ++++++++++--------- .../servicesCounselingFlows.spec.js | 2 -- swagger-def/prime.yaml | 6 ++-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go index 8869d79511a..e15a6fea2c4 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload.go @@ -2561,22 +2561,23 @@ func SearchMoves(appCtx appcontext.AppContext, moves models.Moves) *ghcmessages. originGBLOC = swag.StringValue(move.Orders.OriginDutyLocationGBLOC) } - var destinationGBLOC ghcmessages.GBLOC - var PostalCodeToGBLOC models.PostalCodeToGBLOC + // populates the destination gbloc of the move + var destinationGBLOC string var err error - if numShipments > 0 && move.MTOShipments[0].DestinationAddress != nil { - PostalCodeToGBLOC, err = models.FetchGBLOCForPostalCode(appCtx.DB(), move.MTOShipments[0].DestinationAddress.PostalCode) - } else { - // If the move has no shipments or the shipment has no destination address fall back to the origin duty location GBLOC - PostalCodeToGBLOC, err = models.FetchGBLOCForPostalCode(appCtx.DB(), move.Orders.NewDutyLocation.Address.PostalCode) + destinationGBLOC, err = move.GetDestinationGBLOC(appCtx.DB()) + if err != nil { + destinationGBLOC = "" } + if len(destinationGBLOC) > 0 && customer.Affiliation.String() == "MARINES" { + destinationGBLOC = "USMC/" + destinationGBLOC + } + destinationGblocMessage := ghcmessages.GBLOC(destinationGBLOC) + // populates the destination postal code of the move + var destinationPostalCode string + destinationPostalCode, err = move.GetDestinationPostalCode(appCtx.DB()) if err != nil { - destinationGBLOC = *ghcmessages.NewGBLOC("") - } else if customer.Affiliation.String() == "MARINES" { - destinationGBLOC = ghcmessages.GBLOC("USMC/" + PostalCodeToGBLOC.GBLOC) - } else { - destinationGBLOC = ghcmessages.GBLOC(PostalCodeToGBLOC.GBLOC) + destinationPostalCode = "" } searchMoves[i] = &ghcmessages.SearchMove{ @@ -2590,12 +2591,12 @@ func SearchMoves(appCtx appcontext.AppContext, moves models.Moves) *ghcmessages. Locator: move.Locator, ShipmentsCount: int64(numShipments), OriginDutyLocationPostalCode: move.Orders.OriginDutyLocation.Address.PostalCode, - DestinationPostalCode: move.Orders.NewDutyLocation.Address.PostalCode, + DestinationPostalCode: destinationPostalCode, OrderType: string(move.Orders.OrdersType), RequestedPickupDate: pickupDate, RequestedDeliveryDate: deliveryDate, OriginGBLOC: ghcmessages.GBLOC(originGBLOC), - DestinationGBLOC: destinationGBLOC, + DestinationGBLOC: destinationGblocMessage, LockedByOfficeUserID: handlers.FmtUUIDPtr(move.LockedByOfficeUserID), LockExpiresAt: handlers.FmtDateTimePtr(move.LockExpiresAt), } diff --git a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js index 39fe893bc73..6ab07e9026c 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js @@ -387,8 +387,6 @@ test.describe('Services counselor user', () => { // Edit the shipment so that the tag disappears await page.locator('[data-testid="ShipmentContainer"] .usa-button').last().click(); await page.locator('select[name="destinationType"]').selectOption({ label: 'Home of selection (HOS)' }); - await page.getByLabel('Requested pickup date').fill('16 Mar 2022'); - await page.locator('[data-testid="submitForm"]').click(); await scPage.waitForLoading(); diff --git a/swagger-def/prime.yaml b/swagger-def/prime.yaml index 60e686475ba..8265e6ecfb6 100644 --- a/swagger-def/prime.yaml +++ b/swagger-def/prime.yaml @@ -1488,11 +1488,13 @@ definitions: format: uuid type: string destinationGBLOC: - example: 'AGFM' type: string + example: 'JFK' + readOnly: true destinationPostalCode: - example: '90210' type: string + example: '90210' + readOnly: true referenceId: example: 1001-3456 type: string From 03b723f37f3a32fabb359b8d950d20394d225f00 Mon Sep 17 00:00:00 2001 From: AaronW Date: Wed, 8 Jan 2025 16:44:41 +0000 Subject: [PATCH 066/113] let swagger regen scripts --- swagger/prime.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/swagger/prime.yaml b/swagger/prime.yaml index 5c661452350..a88c878a51a 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -1849,11 +1849,13 @@ definitions: format: uuid type: string destinationGBLOC: - example: AGFM type: string + example: JFK + readOnly: true destinationPostalCode: - example: '90210' type: string + example: '90210' + readOnly: true referenceId: example: 1001-3456 type: string From 91d2ebaf3e95715b191651f6afd40d834cf99240 Mon Sep 17 00:00:00 2001 From: AaronW Date: Wed, 8 Jan 2025 16:51:13 +0000 Subject: [PATCH 067/113] changes to swagger-def requested --- swagger-def/prime.yaml | 8 ++++---- swagger/prime.yaml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/swagger-def/prime.yaml b/swagger-def/prime.yaml index 8265e6ecfb6..3145b3a8e08 100644 --- a/swagger-def/prime.yaml +++ b/swagger-def/prime.yaml @@ -1483,10 +1483,6 @@ definitions: format: date-time type: string readOnly: true - orderID: - example: c56a4180-65aa-42ec-a945-5fd21dec0538 - format: uuid - type: string destinationGBLOC: type: string example: 'JFK' @@ -1495,6 +1491,10 @@ definitions: type: string example: '90210' readOnly: true + orderID: + example: c56a4180-65aa-42ec-a945-5fd21dec0538 + format: uuid + type: string referenceId: example: 1001-3456 type: string diff --git a/swagger/prime.yaml b/swagger/prime.yaml index a88c878a51a..e1af75ce85b 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -1844,10 +1844,6 @@ definitions: format: date-time type: string readOnly: true - orderID: - example: c56a4180-65aa-42ec-a945-5fd21dec0538 - format: uuid - type: string destinationGBLOC: type: string example: JFK @@ -1856,6 +1852,10 @@ definitions: type: string example: '90210' readOnly: true + orderID: + example: c56a4180-65aa-42ec-a945-5fd21dec0538 + format: uuid + type: string referenceId: example: 1001-3456 type: string From dcfec8233ffa7a344f2b305f38623bcc245693e6 Mon Sep 17 00:00:00 2001 From: AaronW Date: Wed, 8 Jan 2025 17:10:06 +0000 Subject: [PATCH 068/113] some gen needed --- pkg/gen/primeapi/embedded_spec.go | 8 ++++++-- pkg/gen/primemessages/list_move.go | 30 +++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index 4f00b706379..3cb80be3a33 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -1887,10 +1887,12 @@ func init() { }, "destinationGBLOC": { "type": "string", - "example": "AGFM" + "readOnly": true, + "example": "JFK" }, "destinationPostalCode": { "type": "string", + "readOnly": true, "example": "90210" }, "eTag": { @@ -6799,10 +6801,12 @@ func init() { }, "destinationGBLOC": { "type": "string", - "example": "AGFM" + "readOnly": true, + "example": "JFK" }, "destinationPostalCode": { "type": "string", + "readOnly": true, "example": "90210" }, "eTag": { diff --git a/pkg/gen/primemessages/list_move.go b/pkg/gen/primemessages/list_move.go index 866dd6e8810..27440ca263b 100644 --- a/pkg/gen/primemessages/list_move.go +++ b/pkg/gen/primemessages/list_move.go @@ -39,11 +39,13 @@ type ListMove struct { CreatedAt strfmt.DateTime `json:"createdAt,omitempty"` // destination g b l o c - // Example: AGFM + // Example: JFK + // Read Only: true DestinationGBLOC string `json:"destinationGBLOC,omitempty"` // destination postal code // Example: 90210 + // Read Only: true DestinationPostalCode string `json:"destinationPostalCode,omitempty"` // e tag @@ -274,6 +276,14 @@ func (m *ListMove) ContextValidate(ctx context.Context, formats strfmt.Registry) res = append(res, err) } + if err := m.contextValidateDestinationGBLOC(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateDestinationPostalCode(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateETag(ctx, formats); err != nil { res = append(res, err) } @@ -340,6 +350,24 @@ func (m *ListMove) contextValidateCreatedAt(ctx context.Context, formats strfmt. return nil } +func (m *ListMove) contextValidateDestinationGBLOC(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "destinationGBLOC", "body", string(m.DestinationGBLOC)); err != nil { + return err + } + + return nil +} + +func (m *ListMove) contextValidateDestinationPostalCode(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "destinationPostalCode", "body", string(m.DestinationPostalCode)); err != nil { + return err + } + + return nil +} + func (m *ListMove) contextValidateETag(ctx context.Context, formats strfmt.Registry) error { if err := validate.ReadOnly(ctx, "eTag", "body", string(m.ETag)); err != nil { From 4221cc2b3b4816185df8b97384167229261d98e9 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 26 Dec 2024 21:16:44 +0000 Subject: [PATCH 069/113] initial commit, added pricers, waiting on ordering issue regarding packing/unpacking --- migrations/app/migrations_manifest.txt | 1 + ...aram_values_to_service_params_table.up.sql | 103 +++++++++++++ .../allowed_payment_service_item_params.go | 15 ++ pkg/handlers/primeapi/payment_request_test.go | 14 -- pkg/services/ghc_rate_engine.go | 32 ++++ .../ghcrateengine/fuel_surcharge_pricer.go | 2 +- .../ghcrateengine/intl_hhg_pack_pricer.go | 56 +++++++ .../ghcrateengine/intl_hhg_unpack_pricer.go | 56 +++++++ .../intl_port_fuel_surcharge_pricer.go | 111 ++++++++++++++ .../intl_shipping_and_linehaul_pricer.go | 137 ++++++++++++++++++ .../ghcrateengine/pricer_helpers_intl.go | 86 +++++++++++ .../ghcrateengine/pricer_query_helpers.go | 18 +++ .../ghcrateengine/service_item_pricer.go | 10 ++ pkg/services/ghcrateengine/shared.go | 3 + .../payment_request_creator.go | 1 - 15 files changed, 629 insertions(+), 16 deletions(-) create mode 100644 migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql create mode 100644 pkg/services/ghcrateengine/intl_hhg_pack_pricer.go create mode 100644 pkg/services/ghcrateengine/intl_hhg_unpack_pricer.go create mode 100644 pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go create mode 100644 pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go create mode 100644 pkg/services/ghcrateengine/pricer_helpers_intl.go diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index 32afaa072da..be5080ccd3d 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1058,6 +1058,7 @@ 20241217180136_add_AK_zips_to_zip3_distances.up.sql 20241218201833_add_PPPO_BASE_ELIZABETH.up.sql 20241220171035_add_additional_AK_zips_to_zip3_distances.up.sql +20241226173330_add_intl_param_values_to_service_params_table.up.sql 20241227153723_remove_empty_string_emplid_values.up.sql 20241227202424_insert_transportation_offices_camp_pendelton.up.sql 20241230190638_remove_AK_zips_from_zip3.up.sql diff --git a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql new file mode 100644 index 00000000000..26ccf6dff90 --- /dev/null +++ b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql @@ -0,0 +1,103 @@ + +-- inserting params for PODFSC +INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES + ('9848562b-50c1-4e6e-aef0-f9539bf243fa'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','adeb57e5-6b1c-4c0f-b5c9-9e57e600303f','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('9c244768-07ce-4368-936b-0ac14a8078a4'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','14a93209-370d-42f3-8ca2-479c953be839','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('5668accf-afac-46a3-b097-177b74076fc9'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','54c9cc4e-0d46-4956-b92e-be9847f894de','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('0e8bd8d5-40fd-46fb-8228-5b66088681a2'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','b9739817-6408-4829-8719-1e26f8a9ceb3','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('a22090b4-3ce6-448d-82b0-36592655d822'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','117da2f5-fff0-41e0-bba1-837124373098','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('d59c674a-eaf9-4158-8303-dbcb50a7230b'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','6ba0aeca-19f8-4247-a317-fffa81c5d5c1','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('450dad51-dcc9-4258-ba56-db39de6a8637'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','0c95581d-67de-48ae-a54b-a3748851d613','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('bf006fc0-8f33-4553-b567-a529af04eafe'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','45fce5ce-6a4c-4a6c-ab37-16ee0133628c','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('ad005c4b-dd71-4d42-99d8-95de7b1ed571'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','e6096350-9ac4-40aa-90c4-bbdff6e0b194','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('274356bc-8139-4e34-9332-ce7396f42c79'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('aa68a318-fe17-445c-ab53-0505fe48d0bb'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','b79978a7-21b7-4656-af83-25585acffb20','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('882e7978-9754-4c8e-bb71-8fe4f4059503'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','d87d82da-3ac2-44e8-bce0-cb4de40f9a72','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('8fc4571d-235b-4d4f-90e4-77e7ad9250d5'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','1e6257e9-757d-4d59-8846-727dd8a055e7','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('836606ce-894e-4765-bba5-b696cb5fe8cc'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','2cbc2251-eb7d-4c69-a120-9a83785c994b','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + +-- inserting params for POEFSC +INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES + ('a57c01b1-cb1c-40f7-87e0-99d1dfd69902'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','adeb57e5-6b1c-4c0f-b5c9-9e57e600303f','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('8f49b289-c1d0-438d-b9fc-3cb234167987'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','14a93209-370d-42f3-8ca2-479c953be839','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('46742d5d-dde9-4e3c-9e59-f2cf87ff016a'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','54c9cc4e-0d46-4956-b92e-be9847f894de','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('f6e178a9-5de3-4312-87c5-81d88ae0b45b'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','b9739817-6408-4829-8719-1e26f8a9ceb3','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('34d4c1a3-b218-4083-93b5-cbbc57688594'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','117da2f5-fff0-41e0-bba1-837124373098','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('6b07db8d-9f7a-4d33-9a5f-7d98fc7038f1'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','6ba0aeca-19f8-4247-a317-fffa81c5d5c1','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('9d8cc94b-4d5f-4c62-b9db-b87bb0213b8d'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','0c95581d-67de-48ae-a54b-a3748851d613','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('d63a9079-c99b-4d92-864f-46cc9bb18388'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','45fce5ce-6a4c-4a6c-ab37-16ee0133628c','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('625970f9-7c93-4c3d-97fe-62f5a9d598f1'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','e6096350-9ac4-40aa-90c4-bbdff6e0b194','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('34fa9839-8289-473a-9095-c2b1159ef5d3'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('f747f231-66f5-4a52-bb71-8d7b5f618d23'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','b79978a7-21b7-4656-af83-25585acffb20','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('0177f93a-15f6-41e5-a3ca-dc8f5bb727ab'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','d87d82da-3ac2-44e8-bce0-cb4de40f9a72','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('cbf5b41f-2d89-4284-858f-d2cda7b060f7'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','1e6257e9-757d-4d59-8846-727dd8a055e7','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('ebed3001-93f1-49ba-a935-3d463b0d76fc'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','2cbc2251-eb7d-4c69-a120-9a83785c994b','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + + +-- inserting params for ISLH +INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES + ('7e2e4b79-2f4c-451e-a28f-df1ad61c4f3b'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','164050e3-e35b-480d-bf6e-ed2fab86f370','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('adff4edb-1f78-45de-9269-29016d09d597'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','e6096350-9ac4-40aa-90c4-bbdff6e0b194','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('2239c77e-e073-47f3-aed7-e1edc6b8a9a4'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','45fce5ce-6a4c-4a6c-ab37-16ee0133628c','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('53ab68f9-8da7-48fa-80ac-bf91f05a4650'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','b9739817-6408-4829-8719-1e26f8a9ceb3','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('e9d3bc63-bc4a-43d0-98f1-48e3e51d2307'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','0c95581d-67de-48ae-a54b-a3748851d613','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('6632cbad-3fa0-46b8-a1ac-0b2bb5123401'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','b79978a7-21b7-4656-af83-25585acffb20','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('4ef58b87-8a93-44ec-b5c8-5b5779d8392e'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','599bbc21-8d1d-4039-9a89-ff52e3582144','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('d53e5f61-e92f-4ecb-9e1d-72ab8a99790f'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','a335e38a-7d95-4ba3-9c8b-75a5e00948bc','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('d94d2c5e-e91a-47d1-96b3-1c5d68a745dd'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','b03af5dc-7701-4e22-a986-d1889a2a8f27','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('7b8db256-e881-451e-a722-6431784e957f'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','add5114b-2a23-4e23-92b3-6dd0778dfc33','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('fedbd62d-d2fa-42b1-b6f6-c9c07e8c4014'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('c7a66e66-dabe-4f0b-a70d-f9639e87761a'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','2e091a7d-a1fd-4017-9f2d-73ad752a30c2','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('18f8b39f-37a5-4536-85d8-4b8b0a6bff94'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','739bbc23-cd08-4612-8e5d-da992202344e','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('7f20ed2e-1bdf-4370-b028-1251c07d3da1'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','95ee2e21-b232-4d74-9ec5-218564a8a8b9','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('a5be6b6f-e007-4d9f-8b1b-63e8ed5c4337'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','9de7fd2a-75c7-4c5c-ba5d-1a92f0b2f5f4','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('df5665d7-7b3d-487d-9d71-95d0e2832ae1'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','d87d82da-3ac2-44e8-bce0-cb4de40f9a72','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('c08f7ab1-6c3c-4627-b22f-1e987ba6f4f2'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','1e6257e9-757d-4d59-8846-727dd8a055e7','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('4d9ed9b0-957d-4e6a-a3d4-5e2e2784ef62'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','14a93209-370d-42f3-8ca2-479c953be839','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('6acb30b9-65a0-4902-85ed-1acb6f4ac930'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','5335e243-ab5b-4906-b84f-bd8c35ba64b3','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('fd83c2ba-0c59-4598-81d6-b56cc8d9979d'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','2cbc2251-eb7d-4c69-a120-9a83785c994b','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + +-- inserting params fo IOSFSC +INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES + ('f61ab040-dab4-4505-906d-d9a3a5da3515'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','0c95581d-67de-48ae-a54b-a3748851d613','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('421053c8-7b4a-44fc-8c73-14b72755c1f7'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','117da2f5-fff0-41e0-bba1-837124373098','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('8f8a8783-0ca2-4f0f-961f-07d1e3cdbf64'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','14a93209-370d-42f3-8ca2-479c953be839','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('ab332c8f-f46e-4d49-b29a-6adf7e67f9c7'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','1e6257e9-757d-4d59-8846-727dd8a055e7','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('a6e24b83-9cb4-4e56-9e38-7bdbd9d5c5fe'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','84d86517-9b88-4520-8d67-5ba892b85d10','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('4e05a7c7-bd0a-4c94-a99b-f052a8812aef'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','54c9cc4e-0d46-4956-b92e-be9847f894de','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('8ec5cd5d-d249-42e9-b11d-76a243d4045f'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','6ba0aeca-19f8-4247-a317-fffa81c5d5c1','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('2b6b9d89-65f3-4291-9e44-48d18d2a4070'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('a3d3c08f-d2a3-4ad1-b85f-b1daee12d71c'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','adeb57e5-6b1c-4c0f-b5c9-9e57e600303f','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('94d45661-82ac-479f-b7be-8e7a50ad46db'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','b79978a7-21b7-4656-af83-25585acffb20','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('c897cfa3-9b06-47b5-8e12-522f0897e59a'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','b9739817-6408-4829-8719-1e26f8a9ceb3','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('61832b60-2a2d-4e35-a799-b7ff9fa6a01e'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','d87d82da-3ac2-44e8-bce0-cb4de40f9a72','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('46531d32-91a5-4d98-a206-d0f1e14e2ff4'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','cd6d6ddf-7104-4d24-a8d6-d37fed61defe','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('a1d4f95e-f28f-4b6a-b83f-8f328f2b2498'::uuid,'81e29d0c-02a6-4a7a-be02-554deb3ee49e','f9753611-4b3e-4bf5-8e00-6d9ce9900f50','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + + +-- inserting params fo IDSFSC +INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES + ('25d90d5b-c58f-45e7-8c60-e7f63a0535b6'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','0c95581d-67de-48ae-a54b-a3748851d613','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('8bb29da3-32e7-4e98-b241-63b8c8c81c3b'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','117da2f5-fff0-41e0-bba1-837124373098','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('b8c12287-dcf6-4f88-bf7b-f7e99283f23d'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','14a93209-370d-42f3-8ca2-479c953be839','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('3b9f3eab-8e18-4888-81f4-c442b4e951cf'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','1e6257e9-757d-4d59-8846-727dd8a055e7','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('7a8430f7-ff55-4a80-b174-e1d4a2f21f25'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','70eecf7f-beae-4906-95ba-cbfe6797cf3a','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('2eaf2e5b-254e-48f0-a2c5-98d04087293f'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','54c9cc4e-0d46-4956-b92e-be9847f894de','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('71f571c5-99a0-420a-b375-bb859e3488a2'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','6ba0aeca-19f8-4247-a317-fffa81c5d5c1','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('cce73f9e-e3db-4d7f-a908-d9985f1b3f27'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('46c559e5-9f49-4b7e-98b6-b9d8e2a4e2cf'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','adeb57e5-6b1c-4c0f-b5c9-9e57e600303f','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('4512b905-cb68-4087-90b5-74e80ba9ec16'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','b79978a7-21b7-4656-af83-25585acffb20','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('d9887f60-e930-4f95-b53e-e9f0d8a445d3'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','b9739817-6408-4829-8719-1e26f8a9ceb3','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('f65b54fa-0e1c-45cc-b7f4-b41d356b970d'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','d87d82da-3ac2-44e8-bce0-cb4de40f9a72','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), + ('ce36b6e0-bcbf-4e96-9c5e-bd93fe9084c9'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','cd6d6ddf-7104-4d24-a8d6-d37fed61defe','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('d9acb388-09a5-464b-bb50-bf418b25e96a'::uuid,'690a5fc1-0ea5-4554-8294-a367b5daefa9','f9753611-4b3e-4bf5-8e00-6d9ce9900f50','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + +-- inserting params fo IHPK +INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES + ('d9acb388-09a5-464b-bb50-bf418b25e96b'::uuid,'67ba1eaf-6ffd-49de-9a69-497be7789877','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('3c2297b0-1ec7-4261-a41d-37e58999258b'::uuid,'67ba1eaf-6ffd-49de-9a69-497be7789877','cd37b2a6-ac7d-4c93-a148-ca67f7f67cff','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + +-- inserting params fo IHUPK +INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES + ('d9acb388-09a5-464b-bb50-bf418b25e96c'::uuid,'56e91c2d-015d-4243-9657-3ed34867abaa','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('c045524a-90ec-4116-80a1-e2edb5cdf38f'::uuid,'56e91c2d-015d-4243-9657-3ed34867abaa','cd37b2a6-ac7d-4c93-a148-ca67f7f67cff','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); diff --git a/pkg/handlers/primeapi/allowed_payment_service_item_params.go b/pkg/handlers/primeapi/allowed_payment_service_item_params.go index cc40edc4ff8..809582a592a 100644 --- a/pkg/handlers/primeapi/allowed_payment_service_item_params.go +++ b/pkg/handlers/primeapi/allowed_payment_service_item_params.go @@ -67,6 +67,21 @@ var ( models.ReServiceCodeDOSFSC: { models.ServiceItemParamNameWeightBilled, }, + models.ReServiceCodeISLH: { + models.ServiceItemParamNameWeightBilled, + }, + models.ReServiceCodeIHPK: { + models.ServiceItemParamNameWeightBilled, + }, + models.ReServiceCodeIHUPK: { + models.ServiceItemParamNameWeightBilled, + }, + models.ReServiceCodePOEFSC: { + models.ServiceItemParamNameWeightBilled, + }, + models.ReServiceCodePODFSC: { + models.ServiceItemParamNameWeightBilled, + }, } ) diff --git a/pkg/handlers/primeapi/payment_request_test.go b/pkg/handlers/primeapi/payment_request_test.go index 768e10c0033..114f03aa49b 100644 --- a/pkg/handlers/primeapi/payment_request_test.go +++ b/pkg/handlers/primeapi/payment_request_test.go @@ -947,13 +947,6 @@ func (suite *HandlerSuite) TestCreatePaymentRequestHandlerInvalidMTOReferenceID( suite.IsType(&paymentrequestop.CreatePaymentRequestUnprocessableEntity{}, response) typedResponse := response.(*paymentrequestop.CreatePaymentRequestUnprocessableEntity) - // Validate outgoing payload - // TODO: Can't validate the response because of the issue noted below. Figure out a way to - // either alter the service or relax the swagger requirements. - // suite.NoError(typedResponse.Payload.Validate(strfmt.Default)) - // CreatePaymentRequestCheck is returning apperror.InvalidCreateInputError without any validation errors - // so InvalidFields won't be added to the payload. - suite.Contains(*typedResponse.Payload.Detail, "has missing ReferenceID") }) @@ -1013,13 +1006,6 @@ func (suite *HandlerSuite) TestCreatePaymentRequestHandlerInvalidMTOReferenceID( suite.IsType(&paymentrequestop.CreatePaymentRequestUnprocessableEntity{}, response) typedResponse := response.(*paymentrequestop.CreatePaymentRequestUnprocessableEntity) - // Validate outgoing payload - // TODO: Can't validate the response because of the issue noted below. Figure out a way to - // either alter the service or relax the swagger requirements. - // suite.NoError(typedResponse.Payload.Validate(strfmt.Default)) - // CreatePaymentRequestCheck is returning apperror.InvalidCreateInputError without any validation errors - // so InvalidFields won't be added to the payload. - suite.Contains(*typedResponse.Payload.Detail, "has missing ReferenceID") }) } diff --git a/pkg/services/ghc_rate_engine.go b/pkg/services/ghc_rate_engine.go index 5d17e0388ce..9bee5c8f59f 100644 --- a/pkg/services/ghc_rate_engine.go +++ b/pkg/services/ghc_rate_engine.go @@ -232,3 +232,35 @@ type DomesticOriginSITFuelSurchargePricer interface { ) ParamsPricer } + +// IntlShippingAndLinehaulPricer prices international shipping and linehaul for a move +// +//go:generate mockery --name IntlShippingAndLinehaulPricer +type IntlShippingAndLinehaulPricer interface { + Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, distance unit.Miles, weight unit.Pound, serviceArea string, isPPM bool) (unit.Cents, PricingDisplayParams, error) + ParamsPricer +} + +// IntlHHGPackPricer prices international packing for an HHG shipment within a move +// +//go:generate mockery --name IntlHHGPackPricer +type IntlHHGPackPricer interface { + Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, weight unit.Pound, servicesScheduleOrigin int, isPPM bool) (unit.Cents, PricingDisplayParams, error) + ParamsPricer +} + +// IntlHHGUnpackPricer prices international unpacking for an HHG shipment within a move +// +//go:generate mockery --name IntlHHGUnpackPricer +type IntlHHGUnpackPricer interface { + Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, weight unit.Pound, servicesScheduleDest int, isPPM bool) (unit.Cents, PricingDisplayParams, error) + ParamsPricer +} + +// IntlPortFuelSurchargePricer prices the POEFSC/PODFSC service items on a shipment within a move +// +//go:generate mockery --name IntlPortFuelSurchargePricer +type IntlPortFuelSurchargePricer interface { + Price(appCtx appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, isPPM bool) (unit.Cents, PricingDisplayParams, error) + ParamsPricer +} diff --git a/pkg/services/ghcrateengine/fuel_surcharge_pricer.go b/pkg/services/ghcrateengine/fuel_surcharge_pricer.go index 7371a7f397f..72b71bafce6 100644 --- a/pkg/services/ghcrateengine/fuel_surcharge_pricer.go +++ b/pkg/services/ghcrateengine/fuel_surcharge_pricer.go @@ -27,7 +27,7 @@ func NewFuelSurchargePricer() services.FuelSurchargePricer { return &fuelSurchargePricer{} } -// Price determines the price for a counseling service +// Price determines the price for fuel surcharge func (p fuelSurchargePricer) Price(_ appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { // Validate parameters if actualPickupDate.IsZero() { diff --git a/pkg/services/ghcrateengine/intl_hhg_pack_pricer.go b/pkg/services/ghcrateengine/intl_hhg_pack_pricer.go new file mode 100644 index 00000000000..9318f930e10 --- /dev/null +++ b/pkg/services/ghcrateengine/intl_hhg_pack_pricer.go @@ -0,0 +1,56 @@ +package ghcrateengine + +import ( + "time" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/unit" +) + +type intlHHGPackPricer struct { +} + +// NewDomesticPackPricer creates a new pricer for the domestic pack service +func NewIntlHHGPackPricer() services.IntlHHGPackPricer { + return &intlHHGPackPricer{} +} + +// Price determines the price for a domestic pack service +func (p intlHHGPackPricer) Price(appCtx appcontext.AppContext, contractCode string, referenceDate time.Time, weight unit.Pound, servicesScheduleOrigin int, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { + return priceIntlPackUnpack(appCtx, models.ReServiceCodeIHPK, contractCode, referenceDate, weight, servicesScheduleOrigin, isPPM) +} + +// PriceUsingParams determines the price for a domestic pack service given PaymentServiceItemParams +func (p intlHHGPackPricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { + contractCode, err := getParamString(params, models.ServiceItemParamNameContractCode) + if err != nil { + return unit.Cents(0), nil, err + } + + referenceDate, err := getParamTime(params, models.ServiceItemParamNameReferenceDate) + if err != nil { + return unit.Cents(0), nil, err + } + + servicesScheduleOrigin, err := getParamInt(params, models.ServiceItemParamNameServicesScheduleOrigin) + if err != nil { + return unit.Cents(0), nil, err + } + + weightBilled, err := getParamInt(params, models.ServiceItemParamNameWeightBilled) + if err != nil { + return unit.Cents(0), nil, err + } + + var isPPM = false + if params[0].PaymentServiceItem.MTOServiceItem.MTOShipment.ShipmentType == models.MTOShipmentTypePPM { + // PPMs do not require minimums for a shipment's weight + // this flag is passed into the Price function to ensure the weight min + // are not enforced for PPMs + isPPM = true + } + + return p.Price(appCtx, contractCode, referenceDate, unit.Pound(weightBilled), servicesScheduleOrigin, isPPM) +} diff --git a/pkg/services/ghcrateengine/intl_hhg_unpack_pricer.go b/pkg/services/ghcrateengine/intl_hhg_unpack_pricer.go new file mode 100644 index 00000000000..47e428f1e6d --- /dev/null +++ b/pkg/services/ghcrateengine/intl_hhg_unpack_pricer.go @@ -0,0 +1,56 @@ +package ghcrateengine + +import ( + "time" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/unit" +) + +type intlHHGUnpackPricer struct { +} + +// NewDomesticUnpackPricer creates a new pricer for the domestic unpack service +func NewIntlHHGUnpackPricer() services.IntlHHGUnpackPricer { + return &intlHHGUnpackPricer{} +} + +// Price determines the price for a domestic unpack service +func (p intlHHGUnpackPricer) Price(appCtx appcontext.AppContext, contractCode string, referenceDate time.Time, weight unit.Pound, servicesScheduleDest int, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { + return priceDomesticPackUnpack(appCtx, models.ReServiceCodeDUPK, contractCode, referenceDate, weight, servicesScheduleDest, isPPM) +} + +// PriceUsingParams determines the price for a domestic unpack service given PaymentServiceItemParams +func (p intlHHGUnpackPricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { + contractCode, err := getParamString(params, models.ServiceItemParamNameContractCode) + if err != nil { + return unit.Cents(0), nil, err + } + + referenceDate, err := getParamTime(params, models.ServiceItemParamNameReferenceDate) + if err != nil { + return unit.Cents(0), nil, err + } + + servicesScheduleDest, err := getParamInt(params, models.ServiceItemParamNameServicesScheduleDest) + if err != nil { + return unit.Cents(0), nil, err + } + + weightBilled, err := getParamInt(params, models.ServiceItemParamNameWeightBilled) + if err != nil { + return unit.Cents(0), nil, err + } + + var isPPM = false + if params[0].PaymentServiceItem.MTOServiceItem.MTOShipment.ShipmentType == models.MTOShipmentTypePPM { + // PPMs do not require minimums for a shipment's weight + // this flag is passed into the Price function to ensure the weight min + // are not enforced for PPMs + isPPM = true + } + + return p.Price(appCtx, contractCode, referenceDate, unit.Pound(weightBilled), servicesScheduleDest, isPPM) +} diff --git a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go new file mode 100644 index 00000000000..0acac1eaffa --- /dev/null +++ b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go @@ -0,0 +1,111 @@ +package ghcrateengine + +import ( + "database/sql" + "fmt" + "math" + "time" + + "github.com/gofrs/uuid" + "github.com/pkg/errors" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/apperror" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/unit" +) + +// FuelSurchargePricer is a service object to price domestic shorthaul +type portFuelSurchargePricer struct { +} + +// NewFuelSurchargePricer is the public constructor for a domesticFuelSurchargePricer using Pop +func NewPortFuelSurchargePricer() services.IntlPortFuelSurchargePricer { + return &portFuelSurchargePricer{} +} + +// Price determines the price for fuel surcharge +func (p portFuelSurchargePricer) Price(_ appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { + // Validate parameters + if actualPickupDate.IsZero() { + return 0, nil, errors.New("ActualPickupDate is required") + } + if distance <= 0 { + return 0, nil, errors.New("Distance must be greater than 0") + } + if !isPPM && weight < minIntlWeightHHG { + return 0, nil, fmt.Errorf("weight must be a minimum of %d", minIntlWeightHHG) + } + if fscWeightBasedDistanceMultiplier == 0 { + return 0, nil, errors.New("WeightBasedDistanceMultiplier is required") + } + if eiaFuelPrice == 0 { + return 0, nil, errors.New("EIAFuelPrice is required") + } + + fscPriceDifferenceInCents := (eiaFuelPrice - baseGHCDieselFuelPrice).Float64() / 1000.0 + fscMultiplier := fscWeightBasedDistanceMultiplier * distance.Float64() + fscPrice := fscMultiplier * fscPriceDifferenceInCents * 100 + totalCost := unit.Cents(math.Round(fscPrice)) + + displayParams := services.PricingDisplayParams{ + {Key: models.ServiceItemParamNameFSCPriceDifferenceInCents, Value: FormatFloat(fscPriceDifferenceInCents, 1)}, + {Key: models.ServiceItemParamNameFSCMultiplier, Value: FormatFloat(fscMultiplier, 7)}, + } + + return totalCost, displayParams, nil +} + +func (p portFuelSurchargePricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { + actualPickupDate, err := getParamTime(params, models.ServiceItemParamNameActualPickupDate) + if err != nil { + return unit.Cents(0), nil, err + } + + var paymentServiceItem models.PaymentServiceItem + mtoShipment := params[0].PaymentServiceItem.MTOServiceItem.MTOShipment + + if mtoShipment.ID == uuid.Nil { + err = appCtx.DB().Eager("MTOServiceItem", "MTOServiceItem.MTOShipment").Find(&paymentServiceItem, params[0].PaymentServiceItemID) + if err != nil { + switch err { + case sql.ErrNoRows: + return unit.Cents(0), nil, apperror.NewNotFoundError(params[0].PaymentServiceItemID, "looking for PaymentServiceItem") + default: + return unit.Cents(0), nil, apperror.NewQueryError("PaymentServiceItem", err, "") + } + } + mtoShipment = paymentServiceItem.MTOServiceItem.MTOShipment + } + + distance, err := getParamInt(params, models.ServiceItemParamNameDistanceZip) + if err != nil { + return unit.Cents(0), nil, err + } + + weightBilled, err := getParamInt(params, models.ServiceItemParamNameWeightBilled) + if err != nil { + return unit.Cents(0), nil, err + } + + fscWeightBasedDistanceMultiplier, err := getParamFloat(params, models.ServiceItemParamNameFSCWeightBasedDistanceMultiplier) + if err != nil { + return unit.Cents(0), nil, err + } + + eiaFuelPrice, err := getParamInt(params, models.ServiceItemParamNameEIAFuelPrice) + if err != nil { + return unit.Cents(0), nil, err + } + + var isPPM = false + if params[0].PaymentServiceItem.MTOServiceItem.MTOShipment.ShipmentType == models.MTOShipmentTypePPM { + // PPMs do not require minimums for a shipment's weight + // this flag is passed into the Price function to ensure the weight min + // are not enforced for PPMs + isPPM = true + } + + return p.Price(appCtx, actualPickupDate, unit.Miles(distance), unit.Pound(weightBilled), fscWeightBasedDistanceMultiplier, unit.Millicents(eiaFuelPrice), isPPM) +} diff --git a/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go b/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go new file mode 100644 index 00000000000..2fec86a9b57 --- /dev/null +++ b/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go @@ -0,0 +1,137 @@ +package ghcrateengine + +import ( + "fmt" + "math" + "time" + + "github.com/pkg/errors" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/unit" +) + +type intlShippingAndLinehaulPricer struct { +} + +// NewDomesticLinehaulPricer creates a new pricer for domestic linehaul services +func NewIntlShippingAndLinehaulPricer() services.DomesticLinehaulPricer { + return &intlShippingAndLinehaulPricer{} +} + +// Price determines the price for a domestic linehaul +func (p intlShippingAndLinehaulPricer) Price(appCtx appcontext.AppContext, contractCode string, referenceDate time.Time, distance unit.Miles, weight unit.Pound, serviceArea string, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { + // Validate parameters + if len(contractCode) == 0 { + return 0, nil, errors.New("ContractCode is required") + } + if referenceDate.IsZero() { + return 0, nil, errors.New("ReferenceDate is required") + } + if !isPPM && weight < dlhPricerMinimumWeight { + return 0, nil, fmt.Errorf("Weight must be at least %d", dlhPricerMinimumWeight) + } + if len(serviceArea) == 0 { + return 0, nil, errors.New("ServiceArea is required") + } + + isPeakPeriod := IsPeakPeriod(referenceDate) + finalWeight := weight + + if isPPM && weight < dlhPricerMinimumWeight { + finalWeight = dlhPricerMinimumWeight + } + + domesticLinehaulPrice, err := fetchDomesticLinehaulPrice(appCtx, contractCode, isPeakPeriod, distance, finalWeight, serviceArea) + if err != nil { + return unit.Cents(0), nil, fmt.Errorf("could not fetch domestic linehaul rate: %w", err) + } + + basePrice := domesticLinehaulPrice.PriceMillicents.Float64() / 1000 + escalatedPrice, contractYear, err := escalatePriceForContractYear( + appCtx, + domesticLinehaulPrice.ContractID, + referenceDate, + true, + basePrice) + if err != nil { + return 0, nil, fmt.Errorf("could not calculate escalated price: %w", err) + } + + totalPrice := finalWeight.ToCWTFloat64() * distance.Float64() * escalatedPrice + totalPriceCents := unit.Cents(math.Round(totalPrice)) + + params := services.PricingDisplayParams{ + {Key: models.ServiceItemParamNameContractYearName, Value: contractYear.Name}, + {Key: models.ServiceItemParamNameEscalationCompounded, Value: FormatEscalation(contractYear.EscalationCompounded)}, + {Key: models.ServiceItemParamNameIsPeak, Value: FormatBool(isPeakPeriod)}, + {Key: models.ServiceItemParamNamePriceRateOrFactor, Value: FormatFloat(domesticLinehaulPrice.PriceMillicents.ToDollarFloatNoRound(), 3)}, + } + + if isPPM && weight < dlhPricerMinimumWeight { + weightFactor := float64(weight) / float64(dlhPricerMinimumWeight) + cost := float64(weightFactor) * float64(totalPriceCents) + return unit.Cents(cost), params, nil + } + + return totalPriceCents, params, nil +} + +// PriceUsingParams determines the price for a domestic linehaul given PaymentServiceItemParams +func (p intlShippingAndLinehaulPricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { + contractCode, err := getParamString(params, models.ServiceItemParamNameContractCode) + if err != nil { + return unit.Cents(0), nil, err + } + + distance, err := getParamInt(params, models.ServiceItemParamNameDistanceZip) + if err != nil { + return unit.Cents(0), nil, err + } + + referenceDate, err := getParamTime(params, models.ServiceItemParamNameReferenceDate) + if err != nil { + return unit.Cents(0), nil, err + } + + serviceAreaOrigin, err := getParamString(params, models.ServiceItemParamNameServiceAreaOrigin) + if err != nil { + return unit.Cents(0), nil, err + } + + weightBilled, err := getParamInt(params, models.ServiceItemParamNameWeightBilled) + if err != nil { + return unit.Cents(0), nil, err + } + + var isPPM = false + if params[0].PaymentServiceItem.MTOServiceItem.MTOShipment.ShipmentType == models.MTOShipmentTypePPM { + // PPMs do not require minimums for a shipment's weight or distance + // this flag is passed into the Price function to ensure the weight and distance mins + // are not enforced for PPMs + isPPM = true + } + + return p.Price(appCtx, contractCode, referenceDate, unit.Miles(distance), unit.Pound(weightBilled), serviceAreaOrigin, isPPM) +} + +// func fetchDomesticLinehaulPrice(appCtx appcontext.AppContext, contractCode string, isPeakPeriod bool, distance unit.Miles, weight unit.Pound, serviceArea string) (models.ReDomesticLinehaulPrice, error) { +// var domesticLinehaulPrice models.ReDomesticLinehaulPrice +// err := appCtx.DB().Q(). +// Join("re_domestic_service_areas sa", "domestic_service_area_id = sa.id"). +// Join("re_contracts c", "re_domestic_linehaul_prices.contract_id = c.id"). +// Where("c.code = $1", contractCode). +// Where("re_domestic_linehaul_prices.is_peak_period = $2", isPeakPeriod). +// Where("$3 between weight_lower and weight_upper", weight). +// Where("$4 between miles_lower and miles_upper", distance). +// Where("sa.service_area = $5", serviceArea). +// First(&domesticLinehaulPrice) + +// if err != nil { +// return models.ReDomesticLinehaulPrice{}, err +// } + +// return domesticLinehaulPrice, nil +// } diff --git a/pkg/services/ghcrateengine/pricer_helpers_intl.go b/pkg/services/ghcrateengine/pricer_helpers_intl.go new file mode 100644 index 00000000000..9f88b2d4ef1 --- /dev/null +++ b/pkg/services/ghcrateengine/pricer_helpers_intl.go @@ -0,0 +1,86 @@ +package ghcrateengine + +import ( + "fmt" + "math" + "time" + + "github.com/pkg/errors" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/unit" +) + +func priceIntlPackUnpack(appCtx appcontext.AppContext, packUnpackCode models.ReServiceCode, contractCode string, referenceDate time.Time, weight unit.Pound, servicesSchedule int, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { + // Validate parameters + var intlOtherPriceCode models.ReServiceCode + switch packUnpackCode { + case models.ReServiceCodeIHPK: + intlOtherPriceCode = models.ReServiceCodeIHPK + case models.ReServiceCodeIHUPK: + intlOtherPriceCode = models.ReServiceCodeIHUPK + default: + return 0, nil, fmt.Errorf("unsupported pack/unpack code of %s", packUnpackCode) + } + if len(contractCode) == 0 { + return 0, nil, errors.New("ContractCode is required") + } + if referenceDate.IsZero() { + return 0, nil, errors.New("ReferenceDate is required") + } + if !isPPM && weight < minDomesticWeight { + return 0, nil, fmt.Errorf("Weight must be a minimum of %d", minDomesticWeight) + } + if servicesSchedule == 0 { + return 0, nil, errors.New("Services schedule is required") + } + + isPeakPeriod := IsPeakPeriod(referenceDate) + + intlOtherPrice, err := fetchIntlOtherPrice(appCtx, contractCode, intlOtherPriceCode, servicesSchedule, isPeakPeriod) + if err != nil { + return 0, nil, fmt.Errorf("could not lookup domestic other price: %w", err) + } + + finalWeight := weight + if isPPM && weight < minDomesticWeight { + finalWeight = minDomesticWeight + } + + basePrice := intlOtherPrice.PerUnitCents.Float64() + escalatedPrice, contractYear, err := escalatePriceForContractYear(appCtx, intlOtherPrice.ContractID, referenceDate, false, basePrice) + if err != nil { + return 0, nil, fmt.Errorf("could not calculate escalated price: %w", err) + } + + escalatedPrice = escalatedPrice * finalWeight.ToCWTFloat64() + + displayParams := services.PricingDisplayParams{ + { + Key: models.ServiceItemParamNameContractYearName, + Value: contractYear.Name, + }, + { + Key: models.ServiceItemParamNamePriceRateOrFactor, + Value: FormatCents(intlOtherPrice.PerUnitCents), + }, + { + Key: models.ServiceItemParamNameIsPeak, + Value: FormatBool(isPeakPeriod), + }, + { + Key: models.ServiceItemParamNameEscalationCompounded, + Value: FormatEscalation(contractYear.EscalationCompounded), + }, + } + + totalCost := unit.Cents(math.Round(escalatedPrice)) + if isPPM && weight < minDomesticWeight { + weightFactor := float64(weight) / float64(minDomesticWeight) + cost := float64(weightFactor) * float64(totalCost) + return unit.Cents(cost), displayParams, nil + } + return totalCost, displayParams, nil +} diff --git a/pkg/services/ghcrateengine/pricer_query_helpers.go b/pkg/services/ghcrateengine/pricer_query_helpers.go index 5fc88dd7d5e..05c7bf668df 100644 --- a/pkg/services/ghcrateengine/pricer_query_helpers.go +++ b/pkg/services/ghcrateengine/pricer_query_helpers.go @@ -45,6 +45,24 @@ func fetchDomOtherPrice(appCtx appcontext.AppContext, contractCode string, servi return domOtherPrice, nil } +func fetchIntlOtherPrice(appCtx appcontext.AppContext, contractCode string, serviceCode models.ReServiceCode, schedule int, isPeakPeriod bool) (models.ReIntlOtherPrice, error) { + var intlOtherPrice models.ReIntlOtherPrice + err := appCtx.DB().Q(). + Join("re_services", "service_id = re_services.id"). + Join("re_contracts", "re_contracts.id = re_intl_other_prices.contract_id"). + Where("re_contracts.code = $1", contractCode). + Where("re_services.code = $2", serviceCode). + Where("schedule = $3", schedule). + Where("is_peak_period = $4", isPeakPeriod). + First(&intlOtherPrice) + + if err != nil { + return models.ReIntlOtherPrice{}, err + } + + return intlOtherPrice, nil +} + func fetchDomServiceAreaPrice(appCtx appcontext.AppContext, contractCode string, serviceCode models.ReServiceCode, serviceArea string, isPeakPeriod bool) (models.ReDomesticServiceAreaPrice, error) { var domServiceAreaPrice models.ReDomesticServiceAreaPrice err := appCtx.DB().Q(). diff --git a/pkg/services/ghcrateengine/service_item_pricer.go b/pkg/services/ghcrateengine/service_item_pricer.go index 81ad0a42cf5..ecaf9b8139b 100644 --- a/pkg/services/ghcrateengine/service_item_pricer.go +++ b/pkg/services/ghcrateengine/service_item_pricer.go @@ -94,6 +94,16 @@ func PricerForServiceItem(serviceCode models.ReServiceCode) (services.ParamsPric return NewDomesticOriginSITPickupPricer(), nil case models.ReServiceCodeDDDSIT: return NewDomesticDestinationSITDeliveryPricer(), nil + case models.ReServiceCodeISLH: + return NewIntlShippingAndLinehaulPricer(), nil + case models.ReServiceCodeIHPK: + return NewIntlHHGPackPricer(), nil + case models.ReServiceCodeIHUPK: + return NewIntlHHGUnpackPricer(), nil + case models.ReServiceCodePOEFSC: + return NewPortFuelSurchargePricer(), nil + case models.ReServiceCodePODFSC: + return NewPortFuelSurchargePricer(), nil default: // TODO: We may want a different error type here after all pricers have been implemented return nil, apperror.NewNotImplementedError(fmt.Sprintf("pricer not found for code %s", serviceCode)) diff --git a/pkg/services/ghcrateengine/shared.go b/pkg/services/ghcrateengine/shared.go index 3f89a29cb40..171d8668bb1 100644 --- a/pkg/services/ghcrateengine/shared.go +++ b/pkg/services/ghcrateengine/shared.go @@ -9,6 +9,9 @@ import ( // minDomesticWeight is the minimum weight used in domestic calculations (weights below this are upgraded to the min) const minDomesticWeight = unit.Pound(500) +// minIntlWeightHHG is the minimum weight used in intl calculations (weights below this are upgraded to the min) +const minIntlWeightHHG = unit.Pound(500) + // dateInYear represents a specific date in a year (without caring what year it is) type dateInYear struct { month time.Month diff --git a/pkg/services/payment_request/payment_request_creator.go b/pkg/services/payment_request/payment_request_creator.go index 0b301193287..63b1f0950a7 100644 --- a/pkg/services/payment_request/payment_request_creator.go +++ b/pkg/services/payment_request/payment_request_creator.go @@ -402,7 +402,6 @@ func (p *paymentRequestCreator) createPaymentServiceItem(appCtx appcontext.AppCo paymentServiceItem.PaymentRequestID = paymentRequest.ID paymentServiceItem.PaymentRequest = *paymentRequest paymentServiceItem.Status = models.PaymentServiceItemStatusRequested - // No pricing at this point, so skipping the PriceCents field. paymentServiceItem.RequestedAt = requestedAt verrs, err := appCtx.DB().ValidateAndCreate(&paymentServiceItem) From 834cd2b2063dc0615501dbda34b215781e66ee41 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 2 Jan 2025 17:08:33 +0000 Subject: [PATCH 070/113] pack and unpack done, need tests --- ...aram_values_to_service_params_table.up.sql | 20 ++++-- pkg/models/re_rate_area.go | 17 +++++ pkg/models/service_item_param_key.go | 8 +++ .../per_unit_cents_lookup.go | 63 +++++++++++++++++++ .../port_name_lookup.go | 34 ++++++++++ .../service_param_value_lookups.go | 13 ++++ pkg/services/ghc_rate_engine.go | 4 +- .../ghcrateengine/intl_hhg_pack_pricer.go | 19 ++---- .../ghcrateengine/intl_hhg_unpack_pricer.go | 19 ++---- .../ghcrateengine/pricer_helpers_intl.go | 39 +++--------- .../ghcrateengine/pricer_query_helpers.go | 28 +++------ .../CreatePaymentRequestForm.jsx | 4 +- 12 files changed, 180 insertions(+), 88 deletions(-) create mode 100644 pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go create mode 100644 pkg/payment_request/service_param_value_lookups/port_name_lookup.go diff --git a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql index 26ccf6dff90..14dc1422582 100644 --- a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql +++ b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql @@ -1,3 +1,7 @@ +-- need to add in param keys for international shipments, this will be used to show breakdowns to the TIO +INSERT INTO service_item_param_keys (id, key,description,type,origin,created_at,updated_at) VALUES + ('d9ad3878-4b94-4722-bbaf-d4b8080f339d','PortName','Name of the port for an international shipment','STRING','PRICER','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957'), + ('597bb77e-0ce7-4ba2-9624-24300962625f','PerUnitCents','Per unit cents for a service item','INTEGER','SYSTEM','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957'); -- inserting params for PODFSC INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES @@ -14,7 +18,8 @@ INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,u ('aa68a318-fe17-445c-ab53-0505fe48d0bb'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','b79978a7-21b7-4656-af83-25585acffb20','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), ('882e7978-9754-4c8e-bb71-8fe4f4059503'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','d87d82da-3ac2-44e8-bce0-cb4de40f9a72','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), ('8fc4571d-235b-4d4f-90e4-77e7ad9250d5'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','1e6257e9-757d-4d59-8846-727dd8a055e7','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), - ('836606ce-894e-4765-bba5-b696cb5fe8cc'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','2cbc2251-eb7d-4c69-a120-9a83785c994b','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + ('836606ce-894e-4765-bba5-b696cb5fe8cc'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','2cbc2251-eb7d-4c69-a120-9a83785c994b','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('08701fa6-6352-4808-88b6-1fe103068f29'::uuid,'388115e8-abe9-441d-96cf-a39f24baa0a3','d9ad3878-4b94-4722-bbaf-d4b8080f339d','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); -- inserting params for POEFSC INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES @@ -31,7 +36,8 @@ INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,u ('f747f231-66f5-4a52-bb71-8d7b5f618d23'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','b79978a7-21b7-4656-af83-25585acffb20','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), ('0177f93a-15f6-41e5-a3ca-dc8f5bb727ab'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','d87d82da-3ac2-44e8-bce0-cb4de40f9a72','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), ('cbf5b41f-2d89-4284-858f-d2cda7b060f7'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','1e6257e9-757d-4d59-8846-727dd8a055e7','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), - ('ebed3001-93f1-49ba-a935-3d463b0d76fc'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','2cbc2251-eb7d-4c69-a120-9a83785c994b','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + ('ebed3001-93f1-49ba-a935-3d463b0d76fc'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','2cbc2251-eb7d-4c69-a120-9a83785c994b','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('aa7c3492-be44-46dd-983e-478623edc0be'::uuid,'f75758d8-2fcd-40ba-9432-3ff3032a71d1','d9ad3878-4b94-4722-bbaf-d4b8080f339d','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); -- inserting params for ISLH @@ -94,10 +100,12 @@ INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,u -- inserting params fo IHPK INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES - ('d9acb388-09a5-464b-bb50-bf418b25e96b'::uuid,'67ba1eaf-6ffd-49de-9a69-497be7789877','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), - ('3c2297b0-1ec7-4261-a41d-37e58999258b'::uuid,'67ba1eaf-6ffd-49de-9a69-497be7789877','cd37b2a6-ac7d-4c93-a148-ca67f7f67cff','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + ('d9acb388-09a5-464b-bb50-bf418b25e96b'::uuid,'67ba1eaf-6ffd-49de-9a69-497be7789877','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), -- ContractCode + ('0b31db7a-fbab-4e49-8526-00458ac3900c'::uuid,'67ba1eaf-6ffd-49de-9a69-497be7789877','597bb77e-0ce7-4ba2-9624-24300962625f','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), -- PerUnitCents + ('9b0a74e3-afc4-4f42-8eb3-828f80fbfaf0'::uuid,'67ba1eaf-6ffd-49de-9a69-497be7789877','95ee2e21-b232-4d74-9ec5-218564a8a8b9','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); -- IsPeak -- inserting params fo IHUPK INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES - ('d9acb388-09a5-464b-bb50-bf418b25e96c'::uuid,'56e91c2d-015d-4243-9657-3ed34867abaa','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), - ('c045524a-90ec-4116-80a1-e2edb5cdf38f'::uuid,'56e91c2d-015d-4243-9657-3ed34867abaa','cd37b2a6-ac7d-4c93-a148-ca67f7f67cff','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + ('cb110853-6b1d-452b-9607-345721a70313'::uuid,'56e91c2d-015d-4243-9657-3ed34867abaa','a1d31d35-c87d-4a7d-b0b8-8b2646b96e43','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), -- ContractCode + ('cc95d5df-1167-4fe9-8682-07f8fbe7c286'::uuid,'56e91c2d-015d-4243-9657-3ed34867abaa','597bb77e-0ce7-4ba2-9624-24300962625f','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), -- PerUnitCents + ('759bd482-b2f6-461b-a898-792415efa5f1'::uuid,'56e91c2d-015d-4243-9657-3ed34867abaa','95ee2e21-b232-4d74-9ec5-218564a8a8b9','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); -- IsPeak diff --git a/pkg/models/re_rate_area.go b/pkg/models/re_rate_area.go index 167b70577ce..a2b2a274761 100644 --- a/pkg/models/re_rate_area.go +++ b/pkg/models/re_rate_area.go @@ -1,6 +1,7 @@ package models import ( + "fmt" "time" "github.com/gobuffalo/pop/v6" @@ -53,3 +54,19 @@ func FetchReRateAreaItem(tx *pop.Connection, contractID uuid.UUID, code string) return &area, err } + +// a db stored proc that takes in an address id & a service code to get the rate area id for an address +func FetchRateAreaID(db *pop.Connection, addressID uuid.UUID, serviceID uuid.UUID) (uuid.UUID, error) { + if addressID != uuid.Nil && serviceID != uuid.Nil { + var rateAreaID uuid.UUID + err := db.RawQuery("SELECT get_rate_area_id($1, $2)", addressID, serviceID). + First(&rateAreaID) + + if err != nil { + return uuid.Nil, fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", addressID, serviceID, err) + } + return rateAreaID, nil + } + // Return error if required parameters are not provided + return uuid.Nil, fmt.Errorf("error fetching rate area ID - required parameters not provided") +} diff --git a/pkg/models/service_item_param_key.go b/pkg/models/service_item_param_key.go index 3bfd789dcc4..868c6785b55 100644 --- a/pkg/models/service_item_param_key.go +++ b/pkg/models/service_item_param_key.go @@ -61,6 +61,10 @@ const ( ServiceItemParamNameNTSPackingFactor ServiceItemParamName = "NTSPackingFactor" // ServiceItemParamNameNumberDaysSIT is the param key name NumberDaysSIT ServiceItemParamNameNumberDaysSIT ServiceItemParamName = "NumberDaysSIT" + // ServiceItemParamNamePerUnitCents is the param key name PerUnitCents + ServiceItemParamNamePerUnitCents ServiceItemParamName = "PerUnitCents" + // ServiceItemParamNamePortName is the param key name PortName + ServiceItemParamNamePortName ServiceItemParamName = "PortName" // ServiceItemParamNamePriceAreaDest is the param key name PriceAreaDest ServiceItemParamNamePriceAreaDest ServiceItemParamName = "PriceAreaDest" // ServiceItemParamNamePriceAreaIntlDest is the param key name PriceAreaIntlDest @@ -275,6 +279,8 @@ var ValidServiceItemParamNames = []ServiceItemParamName{ ServiceItemParamNameStandaloneCrateCap, ServiceItemParamNameUncappedRequestTotal, ServiceItemParamNameLockedPriceCents, + ServiceItemParamNamePerUnitCents, + ServiceItemParamNamePortName, } // ValidServiceItemParamNameStrings lists all valid service item param key names @@ -349,6 +355,8 @@ var ValidServiceItemParamNameStrings = []string{ string(ServiceItemParamNameStandaloneCrateCap), string(ServiceItemParamNameUncappedRequestTotal), string(ServiceItemParamNameLockedPriceCents), + string(ServiceItemParamNamePerUnitCents), + string(ServiceItemParamNamePortName), } // ValidServiceItemParamTypes lists all valid service item param types diff --git a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go new file mode 100644 index 00000000000..07670b009fa --- /dev/null +++ b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go @@ -0,0 +1,63 @@ +package serviceparamvaluelookups + +import ( + "database/sql" + "fmt" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services/ghcrateengine" +) + +// PerUnitCents does lookup on the per unit cents value associated with a service item +type PerUnitCentsLookup struct { + ServiceItem models.MTOServiceItem + MTOShipment models.MTOShipment +} + +func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemParamKeyData) (string, error) { + var isPeakPeriod bool + serviceID := p.ServiceItem.ReServiceID + contractID := s.ContractID + if p.ServiceItem.ReService.Code == models.ReServiceCodeIHPK { + // IHPK we need the rate area id for the pickup address + rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID) + if err != nil { + return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) + } + isPeakPeriod = ghcrateengine.IsPeakPeriod(*p.MTOShipment.RequestedPickupDate) + var reIntlOtherPrice models.ReIntlOtherPrice + err = appCtx.DB().Q(). + Where("contract_id = ?", contractID). + Where("service_id = ?", serviceID). + Where("is_peak_period = ?", isPeakPeriod). + Where("rate_area_id = ?", rateAreaID). + First(&reIntlOtherPrice) + if err != nil { + return "", fmt.Errorf("error fetching IHPK per unit cents for contractID: %s, serviceID %s, isPeakPeriod: %t, and rateAreaID: %s: %s", contractID, serviceID, isPeakPeriod, rateAreaID, err) + } + return reIntlOtherPrice.PerUnitCents.ToMillicents().ToCents().String(), nil + } + + if p.ServiceItem.ReService.Code == models.ReServiceCodeIHUPK { + // IHUPK we need the rate area id for the destination address + rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID) + if err != nil && err != sql.ErrNoRows { + return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) + } + isPeakPeriod = ghcrateengine.IsPeakPeriod(*p.MTOShipment.RequestedPickupDate) + var reIntlOtherPrice models.ReIntlOtherPrice + err = appCtx.DB().Q(). + Where("contract_id = ?", contractID). + Where("service_id = ?", serviceID). + Where("is_peak_period = ?", isPeakPeriod). + Where("rate_area_id = ?", rateAreaID). + First(&reIntlOtherPrice) + if err != nil { + return "", fmt.Errorf("error fetching IHUPK per unit cents for contractID: %s, serviceID %s, isPeakPeriod: %t, and rateAreaID: %s: %s", contractID, serviceID, isPeakPeriod, rateAreaID, err) + } + return reIntlOtherPrice.PerUnitCents.ToMillicents().ToCents().String(), nil + } else { + return "", fmt.Errorf("unsupported service code to retrieve service item param PerUnitCents") + } +} diff --git a/pkg/payment_request/service_param_value_lookups/port_name_lookup.go b/pkg/payment_request/service_param_value_lookups/port_name_lookup.go new file mode 100644 index 00000000000..5013d3ae2c8 --- /dev/null +++ b/pkg/payment_request/service_param_value_lookups/port_name_lookup.go @@ -0,0 +1,34 @@ +package serviceparamvaluelookups + +import ( + "fmt" + + "github.com/gofrs/uuid" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/models" +) + +// PortNameLookup does lookup on the shipment and finds the port name +type PortNameLookup struct { + ServiceItem models.MTOServiceItem +} + +func (p PortNameLookup) lookup(appCtx appcontext.AppContext, _ *ServiceItemParamKeyData) (string, error) { + var portLocationID *uuid.UUID + if p.ServiceItem.PODLocationID != nil { + portLocationID = p.ServiceItem.PODLocationID + } else if p.ServiceItem.POELocationID != nil { + portLocationID = p.ServiceItem.POELocationID + } else { + return "", nil + } + var portLocation models.PortLocation + err := appCtx.DB().Q(). + EagerPreload("Port"). + Where("id = $1", portLocationID).First(&portLocation) + if err != nil { + return "", fmt.Errorf("unable to find port location with id %s", portLocationID) + } + return portLocation.Port.PortName, nil +} diff --git a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go index 0bb499be70b..c3669e5cb41 100644 --- a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go +++ b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go @@ -21,6 +21,7 @@ type ServiceItemParamKeyData struct { MTOServiceItem models.MTOServiceItem PaymentRequestID uuid.UUID MoveTaskOrderID uuid.UUID + ContractID uuid.UUID ContractCode string mtoShipmentID *uuid.UUID paramCache *ServiceParamsCache @@ -85,6 +86,8 @@ var ServiceItemParamsWithLookups = []models.ServiceItemParamName{ models.ServiceItemParamNameStandaloneCrate, models.ServiceItemParamNameStandaloneCrateCap, models.ServiceItemParamNameLockedPriceCents, + models.ServiceItemParamNamePerUnitCents, + models.ServiceItemParamNamePortName, } // ServiceParamLookupInitialize initializes service parameter lookup @@ -120,6 +123,7 @@ func ServiceParamLookupInitialize( to this query. Otherwise the contract_code field could be added to the MTO. */ ContractCode: contract.Code, + ContractID: contract.ID, } // @@ -430,6 +434,15 @@ func InitializeLookups(appCtx appcontext.AppContext, shipment models.MTOShipment ServiceItem: serviceItem, } + lookups[models.ServiceItemParamNamePerUnitCents] = PerUnitCentsLookup{ + ServiceItem: serviceItem, + MTOShipment: shipment, + } + + lookups[models.ServiceItemParamNamePortName] = PortNameLookup{ + ServiceItem: serviceItem, + } + return lookups } diff --git a/pkg/services/ghc_rate_engine.go b/pkg/services/ghc_rate_engine.go index 9bee5c8f59f..440d710f4b2 100644 --- a/pkg/services/ghc_rate_engine.go +++ b/pkg/services/ghc_rate_engine.go @@ -245,7 +245,7 @@ type IntlShippingAndLinehaulPricer interface { // //go:generate mockery --name IntlHHGPackPricer type IntlHHGPackPricer interface { - Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, weight unit.Pound, servicesScheduleOrigin int, isPPM bool) (unit.Cents, PricingDisplayParams, error) + Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, weight unit.Pound, perUnitCents int) (unit.Cents, PricingDisplayParams, error) ParamsPricer } @@ -253,7 +253,7 @@ type IntlHHGPackPricer interface { // //go:generate mockery --name IntlHHGUnpackPricer type IntlHHGUnpackPricer interface { - Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, weight unit.Pound, servicesScheduleDest int, isPPM bool) (unit.Cents, PricingDisplayParams, error) + Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, weight unit.Pound, perUnitCents int) (unit.Cents, PricingDisplayParams, error) ParamsPricer } diff --git a/pkg/services/ghcrateengine/intl_hhg_pack_pricer.go b/pkg/services/ghcrateengine/intl_hhg_pack_pricer.go index 9318f930e10..12090aa3bde 100644 --- a/pkg/services/ghcrateengine/intl_hhg_pack_pricer.go +++ b/pkg/services/ghcrateengine/intl_hhg_pack_pricer.go @@ -12,17 +12,14 @@ import ( type intlHHGPackPricer struct { } -// NewDomesticPackPricer creates a new pricer for the domestic pack service func NewIntlHHGPackPricer() services.IntlHHGPackPricer { return &intlHHGPackPricer{} } -// Price determines the price for a domestic pack service -func (p intlHHGPackPricer) Price(appCtx appcontext.AppContext, contractCode string, referenceDate time.Time, weight unit.Pound, servicesScheduleOrigin int, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { - return priceIntlPackUnpack(appCtx, models.ReServiceCodeIHPK, contractCode, referenceDate, weight, servicesScheduleOrigin, isPPM) +func (p intlHHGPackPricer) Price(appCtx appcontext.AppContext, contractCode string, referenceDate time.Time, weight unit.Pound, perUnitCents int) (unit.Cents, services.PricingDisplayParams, error) { + return priceIntlPackUnpack(appCtx, models.ReServiceCodeIHPK, contractCode, referenceDate, weight, perUnitCents) } -// PriceUsingParams determines the price for a domestic pack service given PaymentServiceItemParams func (p intlHHGPackPricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { contractCode, err := getParamString(params, models.ServiceItemParamNameContractCode) if err != nil { @@ -34,7 +31,7 @@ func (p intlHHGPackPricer) PriceUsingParams(appCtx appcontext.AppContext, params return unit.Cents(0), nil, err } - servicesScheduleOrigin, err := getParamInt(params, models.ServiceItemParamNameServicesScheduleOrigin) + perUnitCents, err := getParamInt(params, models.ServiceItemParamNamePerUnitCents) if err != nil { return unit.Cents(0), nil, err } @@ -44,13 +41,5 @@ func (p intlHHGPackPricer) PriceUsingParams(appCtx appcontext.AppContext, params return unit.Cents(0), nil, err } - var isPPM = false - if params[0].PaymentServiceItem.MTOServiceItem.MTOShipment.ShipmentType == models.MTOShipmentTypePPM { - // PPMs do not require minimums for a shipment's weight - // this flag is passed into the Price function to ensure the weight min - // are not enforced for PPMs - isPPM = true - } - - return p.Price(appCtx, contractCode, referenceDate, unit.Pound(weightBilled), servicesScheduleOrigin, isPPM) + return p.Price(appCtx, contractCode, referenceDate, unit.Pound(weightBilled), perUnitCents) } diff --git a/pkg/services/ghcrateengine/intl_hhg_unpack_pricer.go b/pkg/services/ghcrateengine/intl_hhg_unpack_pricer.go index 47e428f1e6d..d4cb95dd315 100644 --- a/pkg/services/ghcrateengine/intl_hhg_unpack_pricer.go +++ b/pkg/services/ghcrateengine/intl_hhg_unpack_pricer.go @@ -12,17 +12,14 @@ import ( type intlHHGUnpackPricer struct { } -// NewDomesticUnpackPricer creates a new pricer for the domestic unpack service func NewIntlHHGUnpackPricer() services.IntlHHGUnpackPricer { return &intlHHGUnpackPricer{} } -// Price determines the price for a domestic unpack service -func (p intlHHGUnpackPricer) Price(appCtx appcontext.AppContext, contractCode string, referenceDate time.Time, weight unit.Pound, servicesScheduleDest int, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { - return priceDomesticPackUnpack(appCtx, models.ReServiceCodeDUPK, contractCode, referenceDate, weight, servicesScheduleDest, isPPM) +func (p intlHHGUnpackPricer) Price(appCtx appcontext.AppContext, contractCode string, referenceDate time.Time, weight unit.Pound, perUnitCents int) (unit.Cents, services.PricingDisplayParams, error) { + return priceIntlPackUnpack(appCtx, models.ReServiceCodeIHUPK, contractCode, referenceDate, weight, perUnitCents) } -// PriceUsingParams determines the price for a domestic unpack service given PaymentServiceItemParams func (p intlHHGUnpackPricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { contractCode, err := getParamString(params, models.ServiceItemParamNameContractCode) if err != nil { @@ -34,7 +31,7 @@ func (p intlHHGUnpackPricer) PriceUsingParams(appCtx appcontext.AppContext, para return unit.Cents(0), nil, err } - servicesScheduleDest, err := getParamInt(params, models.ServiceItemParamNameServicesScheduleDest) + perUnitCents, err := getParamInt(params, models.ServiceItemParamNamePerUnitCents) if err != nil { return unit.Cents(0), nil, err } @@ -44,13 +41,5 @@ func (p intlHHGUnpackPricer) PriceUsingParams(appCtx appcontext.AppContext, para return unit.Cents(0), nil, err } - var isPPM = false - if params[0].PaymentServiceItem.MTOServiceItem.MTOShipment.ShipmentType == models.MTOShipmentTypePPM { - // PPMs do not require minimums for a shipment's weight - // this flag is passed into the Price function to ensure the weight min - // are not enforced for PPMs - isPPM = true - } - - return p.Price(appCtx, contractCode, referenceDate, unit.Pound(weightBilled), servicesScheduleDest, isPPM) + return p.Price(appCtx, contractCode, referenceDate, unit.Pound(weightBilled), perUnitCents) } diff --git a/pkg/services/ghcrateengine/pricer_helpers_intl.go b/pkg/services/ghcrateengine/pricer_helpers_intl.go index 9f88b2d4ef1..73428847e51 100644 --- a/pkg/services/ghcrateengine/pricer_helpers_intl.go +++ b/pkg/services/ghcrateengine/pricer_helpers_intl.go @@ -13,15 +13,8 @@ import ( "github.com/transcom/mymove/pkg/unit" ) -func priceIntlPackUnpack(appCtx appcontext.AppContext, packUnpackCode models.ReServiceCode, contractCode string, referenceDate time.Time, weight unit.Pound, servicesSchedule int, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { - // Validate parameters - var intlOtherPriceCode models.ReServiceCode - switch packUnpackCode { - case models.ReServiceCodeIHPK: - intlOtherPriceCode = models.ReServiceCodeIHPK - case models.ReServiceCodeIHUPK: - intlOtherPriceCode = models.ReServiceCodeIHUPK - default: +func priceIntlPackUnpack(appCtx appcontext.AppContext, packUnpackCode models.ReServiceCode, contractCode string, referenceDate time.Time, weight unit.Pound, perUnitCents int) (unit.Cents, services.PricingDisplayParams, error) { + if packUnpackCode != models.ReServiceCodeIHPK && packUnpackCode != models.ReServiceCodeIHUPK { return 0, nil, fmt.Errorf("unsupported pack/unpack code of %s", packUnpackCode) } if len(contractCode) == 0 { @@ -30,32 +23,21 @@ func priceIntlPackUnpack(appCtx appcontext.AppContext, packUnpackCode models.ReS if referenceDate.IsZero() { return 0, nil, errors.New("ReferenceDate is required") } - if !isPPM && weight < minDomesticWeight { - return 0, nil, fmt.Errorf("Weight must be a minimum of %d", minDomesticWeight) - } - if servicesSchedule == 0 { - return 0, nil, errors.New("Services schedule is required") - } isPeakPeriod := IsPeakPeriod(referenceDate) - intlOtherPrice, err := fetchIntlOtherPrice(appCtx, contractCode, intlOtherPriceCode, servicesSchedule, isPeakPeriod) + contract, err := fetchContractsByContractCode(appCtx, contractCode) if err != nil { - return 0, nil, fmt.Errorf("could not lookup domestic other price: %w", err) + return 0, nil, fmt.Errorf("could not find contract with code: %s: %w", contractCode, err) } - finalWeight := weight - if isPPM && weight < minDomesticWeight { - finalWeight = minDomesticWeight - } - - basePrice := intlOtherPrice.PerUnitCents.Float64() - escalatedPrice, contractYear, err := escalatePriceForContractYear(appCtx, intlOtherPrice.ContractID, referenceDate, false, basePrice) + basePrice := float64(perUnitCents) + escalatedPrice, contractYear, err := escalatePriceForContractYear(appCtx, contract.ID, referenceDate, false, basePrice) if err != nil { return 0, nil, fmt.Errorf("could not calculate escalated price: %w", err) } - escalatedPrice = escalatedPrice * finalWeight.ToCWTFloat64() + escalatedPrice = escalatedPrice * weight.ToCWTFloat64() displayParams := services.PricingDisplayParams{ { @@ -64,7 +46,7 @@ func priceIntlPackUnpack(appCtx appcontext.AppContext, packUnpackCode models.ReS }, { Key: models.ServiceItemParamNamePriceRateOrFactor, - Value: FormatCents(intlOtherPrice.PerUnitCents), + Value: FormatCents(unit.Cents(perUnitCents)), }, { Key: models.ServiceItemParamNameIsPeak, @@ -77,10 +59,5 @@ func priceIntlPackUnpack(appCtx appcontext.AppContext, packUnpackCode models.ReS } totalCost := unit.Cents(math.Round(escalatedPrice)) - if isPPM && weight < minDomesticWeight { - weightFactor := float64(weight) / float64(minDomesticWeight) - cost := float64(weightFactor) * float64(totalCost) - return unit.Cents(cost), displayParams, nil - } return totalCost, displayParams, nil } diff --git a/pkg/services/ghcrateengine/pricer_query_helpers.go b/pkg/services/ghcrateengine/pricer_query_helpers.go index 05c7bf668df..51acb06f9bd 100644 --- a/pkg/services/ghcrateengine/pricer_query_helpers.go +++ b/pkg/services/ghcrateengine/pricer_query_helpers.go @@ -45,24 +45,6 @@ func fetchDomOtherPrice(appCtx appcontext.AppContext, contractCode string, servi return domOtherPrice, nil } -func fetchIntlOtherPrice(appCtx appcontext.AppContext, contractCode string, serviceCode models.ReServiceCode, schedule int, isPeakPeriod bool) (models.ReIntlOtherPrice, error) { - var intlOtherPrice models.ReIntlOtherPrice - err := appCtx.DB().Q(). - Join("re_services", "service_id = re_services.id"). - Join("re_contracts", "re_contracts.id = re_intl_other_prices.contract_id"). - Where("re_contracts.code = $1", contractCode). - Where("re_services.code = $2", serviceCode). - Where("schedule = $3", schedule). - Where("is_peak_period = $4", isPeakPeriod). - First(&intlOtherPrice) - - if err != nil { - return models.ReIntlOtherPrice{}, err - } - - return intlOtherPrice, nil -} - func fetchDomServiceAreaPrice(appCtx appcontext.AppContext, contractCode string, serviceCode models.ReServiceCode, serviceArea string, isPeakPeriod bool) (models.ReDomesticServiceAreaPrice, error) { var domServiceAreaPrice models.ReDomesticServiceAreaPrice err := appCtx.DB().Q(). @@ -121,6 +103,16 @@ func fetchContractsByContractId(appCtx appcontext.AppContext, contractID uuid.UU return contracts, nil } +func fetchContractsByContractCode(appCtx appcontext.AppContext, contractCode string) (models.ReContract, error) { + var contract models.ReContract + err := appCtx.DB().Where("code = $1", contractCode).First(&contract) + if err != nil { + return models.ReContract{}, err + } + + return contract, nil +} + func fetchShipmentTypePrice(appCtx appcontext.AppContext, contractCode string, serviceCode models.ReServiceCode, market models.Market) (models.ReShipmentTypePrice, error) { var shipmentTypePrice models.ReShipmentTypePrice err := appCtx.DB().Q(). diff --git a/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx b/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx index 3a3edafb556..5aa3fdc0187 100644 --- a/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx +++ b/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx @@ -143,7 +143,9 @@ const CreatePaymentRequestForm = ({ mtoServiceItem.reServiceCode === 'DPK' || mtoServiceItem.reServiceCode === 'DDSFSC' || mtoServiceItem.reServiceCode === 'DOSFSC' || - mtoServiceItem.reServiceCode === 'DDSHUT') && ( + mtoServiceItem.reServiceCode === 'DDSHUT' || + mtoServiceItem.reServiceCode === 'IHPK' || + mtoServiceItem.reServiceCode === 'IHUPK') && ( Date: Thu, 2 Jan 2025 17:53:50 +0000 Subject: [PATCH 071/113] POEFSC and PODFSC should be good, onto linehaul, still need tests --- ...aram_values_to_service_params_table.up.sql | 2 +- pkg/models/re_rate_area.go | 1 - pkg/services/ghc_rate_engine.go | 2 +- .../intl_port_fuel_surcharge_pricer.go | 21 ++++++++----------- pkg/services/ghcrateengine/pricer_helpers.go | 4 ++-- .../ghcrateengine/service_item_pricer.go | 1 - 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql index 14dc1422582..eec414779d0 100644 --- a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql +++ b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql @@ -1,6 +1,6 @@ -- need to add in param keys for international shipments, this will be used to show breakdowns to the TIO INSERT INTO service_item_param_keys (id, key,description,type,origin,created_at,updated_at) VALUES - ('d9ad3878-4b94-4722-bbaf-d4b8080f339d','PortName','Name of the port for an international shipment','STRING','PRICER','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957'), + ('d9ad3878-4b94-4722-bbaf-d4b8080f339d','PortName','Name of the port for an international shipment','STRING','SYSTEM','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957'), ('597bb77e-0ce7-4ba2-9624-24300962625f','PerUnitCents','Per unit cents for a service item','INTEGER','SYSTEM','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957'); -- inserting params for PODFSC diff --git a/pkg/models/re_rate_area.go b/pkg/models/re_rate_area.go index a2b2a274761..960f4258d8c 100644 --- a/pkg/models/re_rate_area.go +++ b/pkg/models/re_rate_area.go @@ -67,6 +67,5 @@ func FetchRateAreaID(db *pop.Connection, addressID uuid.UUID, serviceID uuid.UUI } return rateAreaID, nil } - // Return error if required parameters are not provided return uuid.Nil, fmt.Errorf("error fetching rate area ID - required parameters not provided") } diff --git a/pkg/services/ghc_rate_engine.go b/pkg/services/ghc_rate_engine.go index 440d710f4b2..4a74692a5bb 100644 --- a/pkg/services/ghc_rate_engine.go +++ b/pkg/services/ghc_rate_engine.go @@ -261,6 +261,6 @@ type IntlHHGUnpackPricer interface { // //go:generate mockery --name IntlPortFuelSurchargePricer type IntlPortFuelSurchargePricer interface { - Price(appCtx appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, isPPM bool) (unit.Cents, PricingDisplayParams, error) + Price(appCtx appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, portName string) (unit.Cents, PricingDisplayParams, error) ParamsPricer } diff --git a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go index 0acac1eaffa..7d2c94e7096 100644 --- a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go +++ b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go @@ -16,17 +16,14 @@ import ( "github.com/transcom/mymove/pkg/unit" ) -// FuelSurchargePricer is a service object to price domestic shorthaul type portFuelSurchargePricer struct { } -// NewFuelSurchargePricer is the public constructor for a domesticFuelSurchargePricer using Pop func NewPortFuelSurchargePricer() services.IntlPortFuelSurchargePricer { return &portFuelSurchargePricer{} } -// Price determines the price for fuel surcharge -func (p portFuelSurchargePricer) Price(_ appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { +func (p portFuelSurchargePricer) Price(_ appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, portName string) (unit.Cents, services.PricingDisplayParams, error) { // Validate parameters if actualPickupDate.IsZero() { return 0, nil, errors.New("ActualPickupDate is required") @@ -34,7 +31,7 @@ func (p portFuelSurchargePricer) Price(_ appcontext.AppContext, actualPickupDate if distance <= 0 { return 0, nil, errors.New("Distance must be greater than 0") } - if !isPPM && weight < minIntlWeightHHG { + if weight < minIntlWeightHHG { return 0, nil, fmt.Errorf("weight must be a minimum of %d", minIntlWeightHHG) } if fscWeightBasedDistanceMultiplier == 0 { @@ -43,6 +40,9 @@ func (p portFuelSurchargePricer) Price(_ appcontext.AppContext, actualPickupDate if eiaFuelPrice == 0 { return 0, nil, errors.New("EIAFuelPrice is required") } + if portName == "" { + return 0, nil, errors.New("PortName is required") + } fscPriceDifferenceInCents := (eiaFuelPrice - baseGHCDieselFuelPrice).Float64() / 1000.0 fscMultiplier := fscWeightBasedDistanceMultiplier * distance.Float64() @@ -99,13 +99,10 @@ func (p portFuelSurchargePricer) PriceUsingParams(appCtx appcontext.AppContext, return unit.Cents(0), nil, err } - var isPPM = false - if params[0].PaymentServiceItem.MTOServiceItem.MTOShipment.ShipmentType == models.MTOShipmentTypePPM { - // PPMs do not require minimums for a shipment's weight - // this flag is passed into the Price function to ensure the weight min - // are not enforced for PPMs - isPPM = true + portName, err := getParamString(params, models.ServiceItemParamNamePortName) + if err != nil { + return unit.Cents(0), nil, err } - return p.Price(appCtx, actualPickupDate, unit.Miles(distance), unit.Pound(weightBilled), fscWeightBasedDistanceMultiplier, unit.Millicents(eiaFuelPrice), isPPM) + return p.Price(appCtx, actualPickupDate, unit.Miles(distance), unit.Pound(weightBilled), fscWeightBasedDistanceMultiplier, unit.Millicents(eiaFuelPrice), portName) } diff --git a/pkg/services/ghcrateengine/pricer_helpers.go b/pkg/services/ghcrateengine/pricer_helpers.go index faa802afb0b..cb804b0da49 100644 --- a/pkg/services/ghcrateengine/pricer_helpers.go +++ b/pkg/services/ghcrateengine/pricer_helpers.go @@ -448,10 +448,10 @@ func createPricerGeneratedParams(appCtx appcontext.AppContext, paymentServiceIte Where("key = ?", param.Key). First(&serviceItemParamKey) if err != nil { - return paymentServiceItemParams, fmt.Errorf("Unable to find service item param key for %v", param.Key) + return paymentServiceItemParams, fmt.Errorf("unable to find service item param key for %v", param.Key) } if serviceItemParamKey.Origin != models.ServiceItemParamOriginPricer { - return paymentServiceItemParams, fmt.Errorf("Service item param key is not a pricer param. Param key: %v", serviceItemParamKey.Key) + return paymentServiceItemParams, fmt.Errorf("service item param key is not a pricer param. Param key: %v", serviceItemParamKey.Key) } // Create the PaymentServiceItemParam from the PricingDisplayParam and store it in the DB diff --git a/pkg/services/ghcrateengine/service_item_pricer.go b/pkg/services/ghcrateengine/service_item_pricer.go index ecaf9b8139b..a673f832b63 100644 --- a/pkg/services/ghcrateengine/service_item_pricer.go +++ b/pkg/services/ghcrateengine/service_item_pricer.go @@ -36,7 +36,6 @@ func (p serviceItemPricer) PriceServiceItem(appCtx appcontext.AppContext, item m // createPricerGeneratedParams will throw an error if pricingParams is an empty slice // currently our pricers are returning empty slices for pricingParams // once all pricers have been updated to return pricingParams - // TODO: this conditional logic should be removed var displayParams models.PaymentServiceItemParams if len(pricingParams) > 0 { displayParams, err = createPricerGeneratedParams(appCtx, item.ID, pricingParams) From 1a0073e2fd9d3c9fe45a035fb45b2cc4c4ff3d5e Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 2 Jan 2025 18:56:51 +0000 Subject: [PATCH 072/113] linehaul done, now it is time for tests and refinement please save me baby jesus in a tuxedo --- ...aram_values_to_service_params_table.up.sql | 3 +- .../per_unit_cents_lookup.go | 24 ++++- pkg/services/ghc_rate_engine.go | 2 +- .../intl_shipping_and_linehaul_pricer.go | 97 +++++++------------ .../ghcrateengine/pricer_helpers_intl.go | 2 +- 5 files changed, 61 insertions(+), 67 deletions(-) diff --git a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql index eec414779d0..63c1e404e50 100644 --- a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql +++ b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql @@ -61,7 +61,8 @@ INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,u ('c08f7ab1-6c3c-4627-b22f-1e987ba6f4f2'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','1e6257e9-757d-4d59-8846-727dd8a055e7','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), ('4d9ed9b0-957d-4e6a-a3d4-5e2e2784ef62'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','14a93209-370d-42f3-8ca2-479c953be839','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',true), ('6acb30b9-65a0-4902-85ed-1acb6f4ac930'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','5335e243-ab5b-4906-b84f-bd8c35ba64b3','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), - ('fd83c2ba-0c59-4598-81d6-b56cc8d9979d'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','2cbc2251-eb7d-4c69-a120-9a83785c994b','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); + ('fd83c2ba-0c59-4598-81d6-b56cc8d9979d'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','2cbc2251-eb7d-4c69-a120-9a83785c994b','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false), + ('b370c895-e356-4d2c-a200-c2c67ac51011'::uuid,'9f3d551a-0725-430e-897e-80ee9add3ae9','597bb77e-0ce7-4ba2-9624-24300962625f','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957',false); -- inserting params fo IOSFSC INSERT INTO service_params (id,service_id,service_item_param_key_id,created_at,updated_at,is_optional) VALUES diff --git a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go index 07670b009fa..a25913f8e2c 100644 --- a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go +++ b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go @@ -38,7 +38,6 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP } return reIntlOtherPrice.PerUnitCents.ToMillicents().ToCents().String(), nil } - if p.ServiceItem.ReService.Code == models.ReServiceCodeIHUPK { // IHUPK we need the rate area id for the destination address rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID) @@ -57,6 +56,29 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP return "", fmt.Errorf("error fetching IHUPK per unit cents for contractID: %s, serviceID %s, isPeakPeriod: %t, and rateAreaID: %s: %s", contractID, serviceID, isPeakPeriod, rateAreaID, err) } return reIntlOtherPrice.PerUnitCents.ToMillicents().ToCents().String(), nil + } else if p.ServiceItem.ReService.Code == models.ReServiceCodeISLH { + // IHUPK we need the rate area id for the destination address + originRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID) + if err != nil && err != sql.ErrNoRows { + return "", fmt.Errorf("error fetching rate area id for origina address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) + } + destRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.DestinationAddressID, p.ServiceItem.ReServiceID) + if err != nil && err != sql.ErrNoRows { + return "", fmt.Errorf("error fetching rate area id for destination address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) + } + isPeakPeriod = ghcrateengine.IsPeakPeriod(*p.MTOShipment.RequestedPickupDate) + var reIntlPrice models.ReIntlPrice + err = appCtx.DB().Q(). + Where("contract_id = ?", contractID). + Where("service_id = ?", serviceID). + Where("is_peak_period = ?", isPeakPeriod). + Where("origin_rate_area_id = ?", originRateAreaID). + Where("destination_rate_area_id = ?", destRateAreaID). + First(&reIntlPrice) + if err != nil { + return "", fmt.Errorf("error fetching ISLH per unit cents for contractID: %s, serviceID %s, isPeakPeriod: %t, originRateAreaID: %s, and destRateAreaid: %s: %s", contractID, serviceID, isPeakPeriod, originRateAreaID, destRateAreaID, err) + } + return reIntlPrice.PerUnitCents.ToMillicents().ToCents().String(), nil } else { return "", fmt.Errorf("unsupported service code to retrieve service item param PerUnitCents") } diff --git a/pkg/services/ghc_rate_engine.go b/pkg/services/ghc_rate_engine.go index 4a74692a5bb..b024ac1d4e3 100644 --- a/pkg/services/ghc_rate_engine.go +++ b/pkg/services/ghc_rate_engine.go @@ -237,7 +237,7 @@ type DomesticOriginSITFuelSurchargePricer interface { // //go:generate mockery --name IntlShippingAndLinehaulPricer type IntlShippingAndLinehaulPricer interface { - Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, distance unit.Miles, weight unit.Pound, serviceArea string, isPPM bool) (unit.Cents, PricingDisplayParams, error) + Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, distance unit.Miles, weight unit.Pound, perUnitCents int) (unit.Cents, PricingDisplayParams, error) ParamsPricer } diff --git a/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go b/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go index 2fec86a9b57..ca0624d75a2 100644 --- a/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go +++ b/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go @@ -13,46 +13,40 @@ import ( "github.com/transcom/mymove/pkg/unit" ) +const islhPricerMinimumWeight = unit.Pound(500) + type intlShippingAndLinehaulPricer struct { } -// NewDomesticLinehaulPricer creates a new pricer for domestic linehaul services -func NewIntlShippingAndLinehaulPricer() services.DomesticLinehaulPricer { +func NewIntlShippingAndLinehaulPricer() services.IntlShippingAndLinehaulPricer { return &intlShippingAndLinehaulPricer{} } -// Price determines the price for a domestic linehaul -func (p intlShippingAndLinehaulPricer) Price(appCtx appcontext.AppContext, contractCode string, referenceDate time.Time, distance unit.Miles, weight unit.Pound, serviceArea string, isPPM bool) (unit.Cents, services.PricingDisplayParams, error) { - // Validate parameters +func (p intlShippingAndLinehaulPricer) Price(appCtx appcontext.AppContext, contractCode string, referenceDate time.Time, distance unit.Miles, weight unit.Pound, perUnitCents int) (unit.Cents, services.PricingDisplayParams, error) { if len(contractCode) == 0 { return 0, nil, errors.New("ContractCode is required") } if referenceDate.IsZero() { - return 0, nil, errors.New("ReferenceDate is required") + return 0, nil, errors.New("referenceDate is required") } - if !isPPM && weight < dlhPricerMinimumWeight { - return 0, nil, fmt.Errorf("Weight must be at least %d", dlhPricerMinimumWeight) + if weight < islhPricerMinimumWeight { + return 0, nil, fmt.Errorf("weight must be at least %d", islhPricerMinimumWeight) } - if len(serviceArea) == 0 { - return 0, nil, errors.New("ServiceArea is required") + if perUnitCents == 0 { + return 0, nil, errors.New("PerUnitCents is required") } isPeakPeriod := IsPeakPeriod(referenceDate) - finalWeight := weight - - if isPPM && weight < dlhPricerMinimumWeight { - finalWeight = dlhPricerMinimumWeight - } - domesticLinehaulPrice, err := fetchDomesticLinehaulPrice(appCtx, contractCode, isPeakPeriod, distance, finalWeight, serviceArea) + contract, err := fetchContractsByContractCode(appCtx, contractCode) if err != nil { - return unit.Cents(0), nil, fmt.Errorf("could not fetch domestic linehaul rate: %w", err) + return 0, nil, fmt.Errorf("could not find contract with code: %s: %w", contractCode, err) } - basePrice := domesticLinehaulPrice.PriceMillicents.Float64() / 1000 + basePrice := float64(perUnitCents) escalatedPrice, contractYear, err := escalatePriceForContractYear( appCtx, - domesticLinehaulPrice.ContractID, + contract.ID, referenceDate, true, basePrice) @@ -60,26 +54,30 @@ func (p intlShippingAndLinehaulPricer) Price(appCtx appcontext.AppContext, contr return 0, nil, fmt.Errorf("could not calculate escalated price: %w", err) } - totalPrice := finalWeight.ToCWTFloat64() * distance.Float64() * escalatedPrice - totalPriceCents := unit.Cents(math.Round(totalPrice)) + escalatedPrice = escalatedPrice * weight.ToCWTFloat64() + totalPriceCents := unit.Cents(math.Round(escalatedPrice)) params := services.PricingDisplayParams{ - {Key: models.ServiceItemParamNameContractYearName, Value: contractYear.Name}, - {Key: models.ServiceItemParamNameEscalationCompounded, Value: FormatEscalation(contractYear.EscalationCompounded)}, - {Key: models.ServiceItemParamNameIsPeak, Value: FormatBool(isPeakPeriod)}, - {Key: models.ServiceItemParamNamePriceRateOrFactor, Value: FormatFloat(domesticLinehaulPrice.PriceMillicents.ToDollarFloatNoRound(), 3)}, - } - - if isPPM && weight < dlhPricerMinimumWeight { - weightFactor := float64(weight) / float64(dlhPricerMinimumWeight) - cost := float64(weightFactor) * float64(totalPriceCents) - return unit.Cents(cost), params, nil - } + { + Key: models.ServiceItemParamNameContractYearName, + Value: contractYear.Name, + }, + { + Key: models.ServiceItemParamNameEscalationCompounded, + Value: FormatEscalation(contractYear.EscalationCompounded), + }, + { + Key: models.ServiceItemParamNameIsPeak, + Value: FormatBool(isPeakPeriod), + }, + { + Key: models.ServiceItemParamNamePriceRateOrFactor, + Value: FormatCents(unit.Cents(perUnitCents)), + }} return totalPriceCents, params, nil } -// PriceUsingParams determines the price for a domestic linehaul given PaymentServiceItemParams func (p intlShippingAndLinehaulPricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { contractCode, err := getParamString(params, models.ServiceItemParamNameContractCode) if err != nil { @@ -96,42 +94,15 @@ func (p intlShippingAndLinehaulPricer) PriceUsingParams(appCtx appcontext.AppCon return unit.Cents(0), nil, err } - serviceAreaOrigin, err := getParamString(params, models.ServiceItemParamNameServiceAreaOrigin) + weightBilled, err := getParamInt(params, models.ServiceItemParamNameWeightBilled) if err != nil { return unit.Cents(0), nil, err } - weightBilled, err := getParamInt(params, models.ServiceItemParamNameWeightBilled) + perUnitCents, err := getParamInt(params, models.ServiceItemParamNamePerUnitCents) if err != nil { return unit.Cents(0), nil, err } - var isPPM = false - if params[0].PaymentServiceItem.MTOServiceItem.MTOShipment.ShipmentType == models.MTOShipmentTypePPM { - // PPMs do not require minimums for a shipment's weight or distance - // this flag is passed into the Price function to ensure the weight and distance mins - // are not enforced for PPMs - isPPM = true - } - - return p.Price(appCtx, contractCode, referenceDate, unit.Miles(distance), unit.Pound(weightBilled), serviceAreaOrigin, isPPM) + return p.Price(appCtx, contractCode, referenceDate, unit.Miles(distance), unit.Pound(weightBilled), perUnitCents) } - -// func fetchDomesticLinehaulPrice(appCtx appcontext.AppContext, contractCode string, isPeakPeriod bool, distance unit.Miles, weight unit.Pound, serviceArea string) (models.ReDomesticLinehaulPrice, error) { -// var domesticLinehaulPrice models.ReDomesticLinehaulPrice -// err := appCtx.DB().Q(). -// Join("re_domestic_service_areas sa", "domestic_service_area_id = sa.id"). -// Join("re_contracts c", "re_domestic_linehaul_prices.contract_id = c.id"). -// Where("c.code = $1", contractCode). -// Where("re_domestic_linehaul_prices.is_peak_period = $2", isPeakPeriod). -// Where("$3 between weight_lower and weight_upper", weight). -// Where("$4 between miles_lower and miles_upper", distance). -// Where("sa.service_area = $5", serviceArea). -// First(&domesticLinehaulPrice) - -// if err != nil { -// return models.ReDomesticLinehaulPrice{}, err -// } - -// return domesticLinehaulPrice, nil -// } diff --git a/pkg/services/ghcrateengine/pricer_helpers_intl.go b/pkg/services/ghcrateengine/pricer_helpers_intl.go index 73428847e51..37f01ee9966 100644 --- a/pkg/services/ghcrateengine/pricer_helpers_intl.go +++ b/pkg/services/ghcrateengine/pricer_helpers_intl.go @@ -38,6 +38,7 @@ func priceIntlPackUnpack(appCtx appcontext.AppContext, packUnpackCode models.ReS } escalatedPrice = escalatedPrice * weight.ToCWTFloat64() + totalCost := unit.Cents(math.Round(escalatedPrice)) displayParams := services.PricingDisplayParams{ { @@ -58,6 +59,5 @@ func priceIntlPackUnpack(appCtx appcontext.AppContext, packUnpackCode models.ReS }, } - totalCost := unit.Cents(math.Round(escalatedPrice)) return totalCost, displayParams, nil } From d43303f650c2ca7a3e6751a6dc29294a2fd9636f Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 2 Jan 2025 19:38:55 +0000 Subject: [PATCH 073/113] test for rate area id --- pkg/factory/address_factory.go | 10 +++++++++ pkg/factory/address_factory_test.go | 1 + pkg/models/re_rate_area.go | 10 ++++----- pkg/models/re_rate_area_test.go | 22 +++++++++++++++++++ .../per_unit_cents_lookup.go | 8 +++---- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/pkg/factory/address_factory.go b/pkg/factory/address_factory.go index 91a49da4445..d6b7dff6ce5 100644 --- a/pkg/factory/address_factory.go +++ b/pkg/factory/address_factory.go @@ -72,6 +72,16 @@ func BuildAddress(db *pop.Connection, customs []Customization, traits []Trait) m address.County = models.StringPointer("db nil when created") } + if db != nil { + usprc, err := models.FindByZipCode(db, address.PostalCode) + if err != nil { + return models.Address{} + } else { + address.UsPostRegionCityID = &usprc.ID + address.UsPostRegionCity = usprc + } + } + // If db is false, it's a stub. No need to create in database. if db != nil { mustCreate(db, &address) diff --git a/pkg/factory/address_factory_test.go b/pkg/factory/address_factory_test.go index 2e17e564605..7f72a13e9b6 100644 --- a/pkg/factory/address_factory_test.go +++ b/pkg/factory/address_factory_test.go @@ -40,6 +40,7 @@ func (suite *FactorySuite) TestBuildAddress() { suite.Equal(defaultPostalCode, address.PostalCode) suite.Equal(country.ID, *address.CountryId) suite.Equal(defaultCounty, *address.County) + suite.NotNil(*address.UsPostRegionCityID) }) suite.Run("Successful creation of an address with customization", func() { diff --git a/pkg/models/re_rate_area.go b/pkg/models/re_rate_area.go index 960f4258d8c..7b613b42a28 100644 --- a/pkg/models/re_rate_area.go +++ b/pkg/models/re_rate_area.go @@ -56,14 +56,12 @@ func FetchReRateAreaItem(tx *pop.Connection, contractID uuid.UUID, code string) } // a db stored proc that takes in an address id & a service code to get the rate area id for an address -func FetchRateAreaID(db *pop.Connection, addressID uuid.UUID, serviceID uuid.UUID) (uuid.UUID, error) { - if addressID != uuid.Nil && serviceID != uuid.Nil { +func FetchRateAreaID(db *pop.Connection, addressID uuid.UUID, serviceID uuid.UUID, contractID uuid.UUID) (uuid.UUID, error) { + if addressID != uuid.Nil && serviceID != uuid.Nil && contractID != uuid.Nil { var rateAreaID uuid.UUID - err := db.RawQuery("SELECT get_rate_area_id($1, $2)", addressID, serviceID). - First(&rateAreaID) - + err := db.RawQuery("SELECT get_rate_area_id($1, $2, $3)", addressID, serviceID, contractID).First(&rateAreaID) if err != nil { - return uuid.Nil, fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", addressID, serviceID, err) + return uuid.Nil, fmt.Errorf("error fetching rate area id for shipment ID: %s, service ID %s, and contract ID: %s: %s", addressID, serviceID, contractID, err) } return rateAreaID, nil } diff --git a/pkg/models/re_rate_area_test.go b/pkg/models/re_rate_area_test.go index a0769056783..87f310c2088 100644 --- a/pkg/models/re_rate_area_test.go +++ b/pkg/models/re_rate_area_test.go @@ -3,7 +3,9 @@ package models_test import ( "github.com/gofrs/uuid" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/testdatagen" ) func (suite *ModelSuite) TestReRateAreaValidation() { @@ -28,3 +30,23 @@ func (suite *ModelSuite) TestReRateAreaValidation() { suite.verifyValidationErrors(&emptyReRateArea, expErrors) }) } + +func (suite *ModelSuite) TestFetchRateAreaID() { + suite.Run("success - fetching a rate area ID", func() { + service := factory.FetchReServiceByCode(suite.DB(), models.ReServiceCodeIHPK) + contract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{}) + address := factory.BuildAddress(suite.DB(), nil, nil) + rateAreaId, err := models.FetchRateAreaID(suite.DB(), address.ID, service.ID, contract.ID) + suite.NotNil(rateAreaId) + suite.NoError(err) + }) + + suite.Run("fail - receive error when not all values are provided", func() { + var nilUuid uuid.UUID + contract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{}) + address := factory.BuildAddress(suite.DB(), nil, nil) + rateAreaId, err := models.FetchRateAreaID(suite.DB(), address.ID, nilUuid, contract.ID) + suite.Equal(uuid.Nil, rateAreaId) + suite.Error(err) + }) +} diff --git a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go index a25913f8e2c..88a46c98e44 100644 --- a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go +++ b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go @@ -21,7 +21,7 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP contractID := s.ContractID if p.ServiceItem.ReService.Code == models.ReServiceCodeIHPK { // IHPK we need the rate area id for the pickup address - rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID) + rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID, contractID) if err != nil { return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) } @@ -40,7 +40,7 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP } if p.ServiceItem.ReService.Code == models.ReServiceCodeIHUPK { // IHUPK we need the rate area id for the destination address - rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID) + rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID, contractID) if err != nil && err != sql.ErrNoRows { return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) } @@ -58,11 +58,11 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP return reIntlOtherPrice.PerUnitCents.ToMillicents().ToCents().String(), nil } else if p.ServiceItem.ReService.Code == models.ReServiceCodeISLH { // IHUPK we need the rate area id for the destination address - originRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID) + originRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID, contractID) if err != nil && err != sql.ErrNoRows { return "", fmt.Errorf("error fetching rate area id for origina address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) } - destRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.DestinationAddressID, p.ServiceItem.ReServiceID) + destRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.DestinationAddressID, p.ServiceItem.ReServiceID, contractID) if err != nil && err != sql.ErrNoRows { return "", fmt.Errorf("error fetching rate area id for destination address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) } From 776f7307919af7f5d3efeb963f0b812a75402cfb Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 2 Jan 2025 20:34:42 +0000 Subject: [PATCH 074/113] added tests for service param value lookups folder --- .../per_unit_cents_lookup.go | 52 +++---- .../per_unit_cents_lookup_test.go | 132 ++++++++++++++++++ .../port_name_lookup_test.go | 87 ++++++++++++ 3 files changed, 246 insertions(+), 25 deletions(-) create mode 100644 pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup_test.go create mode 100644 pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go diff --git a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go index 88a46c98e44..d2062218620 100644 --- a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go +++ b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go @@ -1,7 +1,6 @@ package serviceparamvaluelookups import ( - "database/sql" "fmt" "github.com/transcom/mymove/pkg/appcontext" @@ -16,16 +15,17 @@ type PerUnitCentsLookup struct { } func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemParamKeyData) (string, error) { - var isPeakPeriod bool serviceID := p.ServiceItem.ReServiceID contractID := s.ContractID - if p.ServiceItem.ReService.Code == models.ReServiceCodeIHPK { - // IHPK we need the rate area id for the pickup address - rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID, contractID) + + switch p.ServiceItem.ReService.Code { + case models.ReServiceCodeIHPK: + // IHPK: Need rate area ID for the pickup address + rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, serviceID, contractID) if err != nil { - return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) + return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, serviceID, err) } - isPeakPeriod = ghcrateengine.IsPeakPeriod(*p.MTOShipment.RequestedPickupDate) + isPeakPeriod := ghcrateengine.IsPeakPeriod(*p.MTOShipment.RequestedPickupDate) var reIntlOtherPrice models.ReIntlOtherPrice err = appCtx.DB().Q(). Where("contract_id = ?", contractID). @@ -37,14 +37,14 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP return "", fmt.Errorf("error fetching IHPK per unit cents for contractID: %s, serviceID %s, isPeakPeriod: %t, and rateAreaID: %s: %s", contractID, serviceID, isPeakPeriod, rateAreaID, err) } return reIntlOtherPrice.PerUnitCents.ToMillicents().ToCents().String(), nil - } - if p.ServiceItem.ReService.Code == models.ReServiceCodeIHUPK { - // IHUPK we need the rate area id for the destination address - rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID, contractID) - if err != nil && err != sql.ErrNoRows { - return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) + + case models.ReServiceCodeIHUPK: + // IHUPK: Need rate area ID for the destination address + rateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, serviceID, contractID) + if err != nil { + return "", fmt.Errorf("error fetching rate area id for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, serviceID, err) } - isPeakPeriod = ghcrateengine.IsPeakPeriod(*p.MTOShipment.RequestedPickupDate) + isPeakPeriod := ghcrateengine.IsPeakPeriod(*p.MTOShipment.RequestedPickupDate) var reIntlOtherPrice models.ReIntlOtherPrice err = appCtx.DB().Q(). Where("contract_id = ?", contractID). @@ -56,17 +56,18 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP return "", fmt.Errorf("error fetching IHUPK per unit cents for contractID: %s, serviceID %s, isPeakPeriod: %t, and rateAreaID: %s: %s", contractID, serviceID, isPeakPeriod, rateAreaID, err) } return reIntlOtherPrice.PerUnitCents.ToMillicents().ToCents().String(), nil - } else if p.ServiceItem.ReService.Code == models.ReServiceCodeISLH { - // IHUPK we need the rate area id for the destination address - originRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, p.ServiceItem.ReServiceID, contractID) - if err != nil && err != sql.ErrNoRows { - return "", fmt.Errorf("error fetching rate area id for origina address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) + + case models.ReServiceCodeISLH: + // ISLH: Need rate area IDs for origin and destination + originRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.PickupAddressID, serviceID, contractID) + if err != nil { + return "", fmt.Errorf("error fetching rate area id for origin address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, serviceID, err) } - destRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.DestinationAddressID, p.ServiceItem.ReServiceID, contractID) - if err != nil && err != sql.ErrNoRows { - return "", fmt.Errorf("error fetching rate area id for destination address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, p.ServiceItem.ReServiceID, err) + destRateAreaID, err := models.FetchRateAreaID(appCtx.DB(), *p.MTOShipment.DestinationAddressID, serviceID, contractID) + if err != nil { + return "", fmt.Errorf("error fetching rate area id for destination address for shipment ID: %s and service ID %s: %s", p.MTOShipment.ID, serviceID, err) } - isPeakPeriod = ghcrateengine.IsPeakPeriod(*p.MTOShipment.RequestedPickupDate) + isPeakPeriod := ghcrateengine.IsPeakPeriod(*p.MTOShipment.RequestedPickupDate) var reIntlPrice models.ReIntlPrice err = appCtx.DB().Q(). Where("contract_id = ?", contractID). @@ -76,10 +77,11 @@ func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemP Where("destination_rate_area_id = ?", destRateAreaID). First(&reIntlPrice) if err != nil { - return "", fmt.Errorf("error fetching ISLH per unit cents for contractID: %s, serviceID %s, isPeakPeriod: %t, originRateAreaID: %s, and destRateAreaid: %s: %s", contractID, serviceID, isPeakPeriod, originRateAreaID, destRateAreaID, err) + return "", fmt.Errorf("error fetching ISLH per unit cents for contractID: %s, serviceID %s, isPeakPeriod: %t, originRateAreaID: %s, and destRateAreaID: %s: %s", contractID, serviceID, isPeakPeriod, originRateAreaID, destRateAreaID, err) } return reIntlPrice.PerUnitCents.ToMillicents().ToCents().String(), nil - } else { + + default: return "", fmt.Errorf("unsupported service code to retrieve service item param PerUnitCents") } } diff --git a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup_test.go b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup_test.go new file mode 100644 index 00000000000..4d9ceab2512 --- /dev/null +++ b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup_test.go @@ -0,0 +1,132 @@ +package serviceparamvaluelookups + +import ( + "time" + + "github.com/gofrs/uuid" + + "github.com/transcom/mymove/pkg/factory" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/testdatagen" +) + +func (suite *ServiceParamValueLookupsSuite) TestPerUnitCentsLookup() { + key := models.ServiceItemParamNamePerUnitCents + var mtoServiceItem models.MTOServiceItem + setupTestData := func(serviceCode models.ReServiceCode) { + testdatagen.MakeReContractYear(suite.DB(), testdatagen.Assertions{ + ReContractYear: models.ReContractYear{ + StartDate: time.Now().Add(-24 * time.Hour), + EndDate: time.Now().Add(24 * time.Hour), + }, + }) + mtoServiceItem = factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: serviceCode, + }, + }, + }, []factory.Trait{factory.GetTraitAvailableToPrimeMove}) + + } + + suite.Run("success - returns perUnitCent value for IHPK", func() { + setupTestData(models.ReServiceCodeIHPK) + + paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) + suite.FatalNoError(err) + + perUnitCents, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + suite.FatalNoError(err) + suite.Equal(perUnitCents, "6997") + }) + + suite.Run("success - returns perUnitCent value for IHUPK", func() { + setupTestData(models.ReServiceCodeIHUPK) + + paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) + suite.FatalNoError(err) + + perUnitCents, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + suite.FatalNoError(err) + suite.Equal(perUnitCents, "752") + }) + + suite.Run("success - returns perUnitCent value for ISLH", func() { + testdatagen.MakeReContractYear(suite.DB(), testdatagen.Assertions{ + ReContractYear: models.ReContractYear{ + StartDate: time.Now().Add(-24 * time.Hour), + EndDate: time.Now().Add(24 * time.Hour), + }, + }) + move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) + destinationAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "JBER", + City: "Anchorage", + State: "AK", + PostalCode: "99505", + IsOconus: models.BoolPointer(true), + }, + }, + }, nil) + pickupAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "Tester Address", + City: "Des Moines", + State: "IA", + PostalCode: "50314", + IsOconus: models.BoolPointer(false), + }, + }, + }, nil) + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + PickupAddressID: &pickupAddress.ID, + DestinationAddressID: &destinationAddress.ID, + MarketCode: models.MarketCodeInternational, + }, + }, + { + Model: move, + LinkOnly: true, + }, + }, nil) + mtoServiceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: shipment, + LinkOnly: true, + }, + { + Model: models.ReService{ + Code: models.ReServiceCodeISLH, + }, + }, + }, nil) + + paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) + suite.FatalNoError(err) + + perUnitCents, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + suite.FatalNoError(err) + suite.Equal(perUnitCents, "1605") + }) + + suite.Run("failure - unauthorized service code", func() { + setupTestData(models.ReServiceCodeDUPK) + + paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) + suite.FatalNoError(err) + + perUnitCents, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + suite.Error(err) + suite.Equal(perUnitCents, "") + }) +} diff --git a/pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go b/pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go new file mode 100644 index 00000000000..b0ccb4b2bfe --- /dev/null +++ b/pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go @@ -0,0 +1,87 @@ +package serviceparamvaluelookups + +import ( + "time" + + "github.com/gofrs/uuid" + + "github.com/transcom/mymove/pkg/factory" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/testdatagen" +) + +func (suite *ServiceParamValueLookupsSuite) TestPortNameLookup() { + key := models.ServiceItemParamNamePortName + var mtoServiceItem models.MTOServiceItem + setupTestData := func(serviceCode models.ReServiceCode, portID uuid.UUID) { + testdatagen.MakeReContractYear(suite.DB(), testdatagen.Assertions{ + ReContractYear: models.ReContractYear{ + StartDate: time.Now().Add(-24 * time.Hour), + EndDate: time.Now().Add(24 * time.Hour), + }, + }) + if serviceCode == models.ReServiceCodePOEFSC { + mtoServiceItem = factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: serviceCode, + }, + }, + { + Model: models.MTOServiceItem{ + POELocationID: &portID, + }, + }, + }, []factory.Trait{factory.GetTraitAvailableToPrimeMove}) + } else { + mtoServiceItem = factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: serviceCode, + }, + }, + { + Model: models.MTOServiceItem{ + PODLocationID: &portID, + }, + }, + }, []factory.Trait{factory.GetTraitAvailableToPrimeMove}) + } + } + + suite.Run("success - returns PortName value for POEFSC", func() { + port := factory.FetchPortLocation(suite.DB(), []factory.Customization{ + { + Model: models.Port{ + PortCode: "SEA", + }, + }, + }, nil) + setupTestData(models.ReServiceCodePOEFSC, port.ID) + + paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) + suite.FatalNoError(err) + + portName, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + suite.FatalNoError(err) + suite.Equal(portName, "SEATTLE TACOMA INTL") + }) + + suite.Run("success - returns PortName value for PODFSC", func() { + port := factory.FetchPortLocation(suite.DB(), []factory.Customization{ + { + Model: models.Port{ + PortCode: "PDX", + }, + }, + }, nil) + setupTestData(models.ReServiceCodePODFSC, port.ID) + + paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) + suite.FatalNoError(err) + + portName, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + suite.FatalNoError(err) + suite.Equal(portName, "PORTLAND INTL") + }) +} From 440003f6a5975a15e24d6ce328a0b87b272cac12 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 2 Jan 2025 20:39:15 +0000 Subject: [PATCH 075/113] mocks --- pkg/services/ghc_rate_engine.go | 6 +- pkg/services/mocks/IntlHHGPackPricer.go | 109 ++++++++++++++++++ pkg/services/mocks/IntlHHGUnpackPricer.go | 109 ++++++++++++++++++ .../mocks/IntlPortFuelSurchargePricer.go | 109 ++++++++++++++++++ .../mocks/IntlShippingAndLinehaulPricer.go | 109 ++++++++++++++++++ 5 files changed, 439 insertions(+), 3 deletions(-) create mode 100644 pkg/services/mocks/IntlHHGPackPricer.go create mode 100644 pkg/services/mocks/IntlHHGUnpackPricer.go create mode 100644 pkg/services/mocks/IntlPortFuelSurchargePricer.go create mode 100644 pkg/services/mocks/IntlShippingAndLinehaulPricer.go diff --git a/pkg/services/ghc_rate_engine.go b/pkg/services/ghc_rate_engine.go index b024ac1d4e3..8b3ef07de0f 100644 --- a/pkg/services/ghc_rate_engine.go +++ b/pkg/services/ghc_rate_engine.go @@ -241,7 +241,7 @@ type IntlShippingAndLinehaulPricer interface { ParamsPricer } -// IntlHHGPackPricer prices international packing for an HHG shipment within a move +// IntlHHGPackPricer prices international packing for an iHHG shipment within a move // //go:generate mockery --name IntlHHGPackPricer type IntlHHGPackPricer interface { @@ -249,7 +249,7 @@ type IntlHHGPackPricer interface { ParamsPricer } -// IntlHHGUnpackPricer prices international unpacking for an HHG shipment within a move +// IntlHHGUnpackPricer prices international unpacking for an iHHG shipment within a move // //go:generate mockery --name IntlHHGUnpackPricer type IntlHHGUnpackPricer interface { @@ -257,7 +257,7 @@ type IntlHHGUnpackPricer interface { ParamsPricer } -// IntlPortFuelSurchargePricer prices the POEFSC/PODFSC service items on a shipment within a move +// IntlPortFuelSurchargePricer prices the POEFSC/PODFSC service items on an iHHG shipment within a move // //go:generate mockery --name IntlPortFuelSurchargePricer type IntlPortFuelSurchargePricer interface { diff --git a/pkg/services/mocks/IntlHHGPackPricer.go b/pkg/services/mocks/IntlHHGPackPricer.go new file mode 100644 index 00000000000..a12bed589ec --- /dev/null +++ b/pkg/services/mocks/IntlHHGPackPricer.go @@ -0,0 +1,109 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + appcontext "github.com/transcom/mymove/pkg/appcontext" + + models "github.com/transcom/mymove/pkg/models" + + services "github.com/transcom/mymove/pkg/services" + + time "time" + + unit "github.com/transcom/mymove/pkg/unit" +) + +// IntlHHGPackPricer is an autogenerated mock type for the IntlHHGPackPricer type +type IntlHHGPackPricer struct { + mock.Mock +} + +// Price provides a mock function with given fields: appCtx, contractCode, requestedPickupDate, weight, perUnitCents +func (_m *IntlHHGPackPricer) Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, weight unit.Pound, perUnitCents int) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + + if len(ret) == 0 { + panic("no return value specified for Price") + } + + var r0 unit.Cents + var r1 services.PricingDisplayParams + var r2 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Pound, int) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Pound, int) unit.Cents); ok { + r0 = rf(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + } else { + r0 = ret.Get(0).(unit.Cents) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, time.Time, unit.Pound, int) services.PricingDisplayParams); ok { + r1 = rf(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(services.PricingDisplayParams) + } + } + + if rf, ok := ret.Get(2).(func(appcontext.AppContext, string, time.Time, unit.Pound, int) error); ok { + r2 = rf(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// PriceUsingParams provides a mock function with given fields: appCtx, params +func (_m *IntlHHGPackPricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, params) + + if len(ret) == 0 { + panic("no return value specified for PriceUsingParams") + } + + var r0 unit.Cents + var r1 services.PricingDisplayParams + var r2 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, params) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.PaymentServiceItemParams) unit.Cents); ok { + r0 = rf(appCtx, params) + } else { + r0 = ret.Get(0).(unit.Cents) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, models.PaymentServiceItemParams) services.PricingDisplayParams); ok { + r1 = rf(appCtx, params) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(services.PricingDisplayParams) + } + } + + if rf, ok := ret.Get(2).(func(appcontext.AppContext, models.PaymentServiceItemParams) error); ok { + r2 = rf(appCtx, params) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// NewIntlHHGPackPricer creates a new instance of IntlHHGPackPricer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIntlHHGPackPricer(t interface { + mock.TestingT + Cleanup(func()) +}) *IntlHHGPackPricer { + mock := &IntlHHGPackPricer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/services/mocks/IntlHHGUnpackPricer.go b/pkg/services/mocks/IntlHHGUnpackPricer.go new file mode 100644 index 00000000000..d06f97791d5 --- /dev/null +++ b/pkg/services/mocks/IntlHHGUnpackPricer.go @@ -0,0 +1,109 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + appcontext "github.com/transcom/mymove/pkg/appcontext" + + models "github.com/transcom/mymove/pkg/models" + + services "github.com/transcom/mymove/pkg/services" + + time "time" + + unit "github.com/transcom/mymove/pkg/unit" +) + +// IntlHHGUnpackPricer is an autogenerated mock type for the IntlHHGUnpackPricer type +type IntlHHGUnpackPricer struct { + mock.Mock +} + +// Price provides a mock function with given fields: appCtx, contractCode, requestedPickupDate, weight, perUnitCents +func (_m *IntlHHGUnpackPricer) Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, weight unit.Pound, perUnitCents int) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + + if len(ret) == 0 { + panic("no return value specified for Price") + } + + var r0 unit.Cents + var r1 services.PricingDisplayParams + var r2 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Pound, int) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Pound, int) unit.Cents); ok { + r0 = rf(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + } else { + r0 = ret.Get(0).(unit.Cents) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, time.Time, unit.Pound, int) services.PricingDisplayParams); ok { + r1 = rf(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(services.PricingDisplayParams) + } + } + + if rf, ok := ret.Get(2).(func(appcontext.AppContext, string, time.Time, unit.Pound, int) error); ok { + r2 = rf(appCtx, contractCode, requestedPickupDate, weight, perUnitCents) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// PriceUsingParams provides a mock function with given fields: appCtx, params +func (_m *IntlHHGUnpackPricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, params) + + if len(ret) == 0 { + panic("no return value specified for PriceUsingParams") + } + + var r0 unit.Cents + var r1 services.PricingDisplayParams + var r2 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, params) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.PaymentServiceItemParams) unit.Cents); ok { + r0 = rf(appCtx, params) + } else { + r0 = ret.Get(0).(unit.Cents) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, models.PaymentServiceItemParams) services.PricingDisplayParams); ok { + r1 = rf(appCtx, params) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(services.PricingDisplayParams) + } + } + + if rf, ok := ret.Get(2).(func(appcontext.AppContext, models.PaymentServiceItemParams) error); ok { + r2 = rf(appCtx, params) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// NewIntlHHGUnpackPricer creates a new instance of IntlHHGUnpackPricer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIntlHHGUnpackPricer(t interface { + mock.TestingT + Cleanup(func()) +}) *IntlHHGUnpackPricer { + mock := &IntlHHGUnpackPricer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/services/mocks/IntlPortFuelSurchargePricer.go b/pkg/services/mocks/IntlPortFuelSurchargePricer.go new file mode 100644 index 00000000000..48e396d017e --- /dev/null +++ b/pkg/services/mocks/IntlPortFuelSurchargePricer.go @@ -0,0 +1,109 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + appcontext "github.com/transcom/mymove/pkg/appcontext" + + models "github.com/transcom/mymove/pkg/models" + + services "github.com/transcom/mymove/pkg/services" + + time "time" + + unit "github.com/transcom/mymove/pkg/unit" +) + +// IntlPortFuelSurchargePricer is an autogenerated mock type for the IntlPortFuelSurchargePricer type +type IntlPortFuelSurchargePricer struct { + mock.Mock +} + +// Price provides a mock function with given fields: appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName +func (_m *IntlPortFuelSurchargePricer) Price(appCtx appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, portName string) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) + + if len(ret) == 0 { + panic("no return value specified for Price") + } + + var r0 unit.Cents + var r1 services.PricingDisplayParams + var r2 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents, string) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents, string) unit.Cents); ok { + r0 = rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) + } else { + r0 = ret.Get(0).(unit.Cents) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents, string) services.PricingDisplayParams); ok { + r1 = rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(services.PricingDisplayParams) + } + } + + if rf, ok := ret.Get(2).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents, string) error); ok { + r2 = rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// PriceUsingParams provides a mock function with given fields: appCtx, params +func (_m *IntlPortFuelSurchargePricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, params) + + if len(ret) == 0 { + panic("no return value specified for PriceUsingParams") + } + + var r0 unit.Cents + var r1 services.PricingDisplayParams + var r2 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, params) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.PaymentServiceItemParams) unit.Cents); ok { + r0 = rf(appCtx, params) + } else { + r0 = ret.Get(0).(unit.Cents) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, models.PaymentServiceItemParams) services.PricingDisplayParams); ok { + r1 = rf(appCtx, params) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(services.PricingDisplayParams) + } + } + + if rf, ok := ret.Get(2).(func(appcontext.AppContext, models.PaymentServiceItemParams) error); ok { + r2 = rf(appCtx, params) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// NewIntlPortFuelSurchargePricer creates a new instance of IntlPortFuelSurchargePricer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIntlPortFuelSurchargePricer(t interface { + mock.TestingT + Cleanup(func()) +}) *IntlPortFuelSurchargePricer { + mock := &IntlPortFuelSurchargePricer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/services/mocks/IntlShippingAndLinehaulPricer.go b/pkg/services/mocks/IntlShippingAndLinehaulPricer.go new file mode 100644 index 00000000000..c6e4ca2b557 --- /dev/null +++ b/pkg/services/mocks/IntlShippingAndLinehaulPricer.go @@ -0,0 +1,109 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + appcontext "github.com/transcom/mymove/pkg/appcontext" + + models "github.com/transcom/mymove/pkg/models" + + services "github.com/transcom/mymove/pkg/services" + + time "time" + + unit "github.com/transcom/mymove/pkg/unit" +) + +// IntlShippingAndLinehaulPricer is an autogenerated mock type for the IntlShippingAndLinehaulPricer type +type IntlShippingAndLinehaulPricer struct { + mock.Mock +} + +// Price provides a mock function with given fields: appCtx, contractCode, requestedPickupDate, distance, weight, perUnitCents +func (_m *IntlShippingAndLinehaulPricer) Price(appCtx appcontext.AppContext, contractCode string, requestedPickupDate time.Time, distance unit.Miles, weight unit.Pound, perUnitCents int) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, contractCode, requestedPickupDate, distance, weight, perUnitCents) + + if len(ret) == 0 { + panic("no return value specified for Price") + } + + var r0 unit.Cents + var r1 services.PricingDisplayParams + var r2 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, int) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, contractCode, requestedPickupDate, distance, weight, perUnitCents) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, int) unit.Cents); ok { + r0 = rf(appCtx, contractCode, requestedPickupDate, distance, weight, perUnitCents) + } else { + r0 = ret.Get(0).(unit.Cents) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, int) services.PricingDisplayParams); ok { + r1 = rf(appCtx, contractCode, requestedPickupDate, distance, weight, perUnitCents) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(services.PricingDisplayParams) + } + } + + if rf, ok := ret.Get(2).(func(appcontext.AppContext, string, time.Time, unit.Miles, unit.Pound, int) error); ok { + r2 = rf(appCtx, contractCode, requestedPickupDate, distance, weight, perUnitCents) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// PriceUsingParams provides a mock function with given fields: appCtx, params +func (_m *IntlShippingAndLinehaulPricer) PriceUsingParams(appCtx appcontext.AppContext, params models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, params) + + if len(ret) == 0 { + panic("no return value specified for PriceUsingParams") + } + + var r0 unit.Cents + var r1 services.PricingDisplayParams + var r2 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.PaymentServiceItemParams) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, params) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.PaymentServiceItemParams) unit.Cents); ok { + r0 = rf(appCtx, params) + } else { + r0 = ret.Get(0).(unit.Cents) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, models.PaymentServiceItemParams) services.PricingDisplayParams); ok { + r1 = rf(appCtx, params) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(services.PricingDisplayParams) + } + } + + if rf, ok := ret.Get(2).(func(appcontext.AppContext, models.PaymentServiceItemParams) error); ok { + r2 = rf(appCtx, params) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// NewIntlShippingAndLinehaulPricer creates a new instance of IntlShippingAndLinehaulPricer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIntlShippingAndLinehaulPricer(t interface { + mock.TestingT + Cleanup(func()) +}) *IntlShippingAndLinehaulPricer { + mock := &IntlShippingAndLinehaulPricer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 88cba28f91b0d613609c6028c8e90c9d85944f7d Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 2 Jan 2025 22:17:22 +0000 Subject: [PATCH 076/113] tests on tests on tests --- .../intl_hhg_pack_pricer_test.go | 115 ++++++++++ .../intl_hhg_unpack_pricer_test.go | 114 ++++++++++ .../intl_port_fuel_surcharge_pricer_test.go | 209 ++++++++++++++++++ .../intl_shipping_and_linehaul_pricer.go | 2 +- .../intl_shipping_and_linehaul_pricer_test.go | 144 ++++++++++++ .../ghcrateengine/pricer_helpers_intl.go | 5 +- .../ghcrateengine/pricer_helpers_intl_test.go | 46 ++++ .../ghcrateengine/pricer_helpers_test.go | 2 +- .../ghcrateengine/pricer_query_helpers.go | 2 +- .../CreatePaymentRequestForm.jsx | 5 +- .../CreatePaymentRequestForm.test.jsx | 6 + 11 files changed, 645 insertions(+), 5 deletions(-) create mode 100644 pkg/services/ghcrateengine/intl_hhg_pack_pricer_test.go create mode 100644 pkg/services/ghcrateengine/intl_hhg_unpack_pricer_test.go create mode 100644 pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go create mode 100644 pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer_test.go create mode 100644 pkg/services/ghcrateengine/pricer_helpers_intl_test.go diff --git a/pkg/services/ghcrateengine/intl_hhg_pack_pricer_test.go b/pkg/services/ghcrateengine/intl_hhg_pack_pricer_test.go new file mode 100644 index 00000000000..9674775b906 --- /dev/null +++ b/pkg/services/ghcrateengine/intl_hhg_pack_pricer_test.go @@ -0,0 +1,115 @@ +package ghcrateengine + +import ( + "fmt" + "strconv" + "time" + + "github.com/transcom/mymove/pkg/factory" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/testdatagen" + "github.com/transcom/mymove/pkg/unit" +) + +const ( + ihpkTestContractYearName = "Base Period Year 1" + ihpkTestPerUnitCents = unit.Cents(15000) + ihpkTestTotalCost = unit.Cents(315000) + ihpkTestIsPeakPeriod = true + ihpkTestEscalationCompounded = 1.0000 + ihpkTestWeight = unit.Pound(2100) + ihpkTestPriceCents = unit.Cents(193064) +) + +var ihpkTestRequestedPickupDate = time.Date(testdatagen.TestYear, peakStart.month, peakStart.day, 0, 0, 0, 0, time.UTC) + +func (suite *GHCRateEngineServiceSuite) TestIntlHHGPackPricer() { + pricer := NewIntlHHGPackPricer() + + suite.Run("success using PaymentServiceItemParams", func() { + paymentServiceItem := suite.setupIntlPackServiceItem() + + totalCost, displayParams, err := pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.NoError(err) + suite.Equal(ihpkTestTotalCost, totalCost) + + expectedParams := services.PricingDisplayParams{ + {Key: models.ServiceItemParamNameContractYearName, Value: ihpkTestContractYearName}, + {Key: models.ServiceItemParamNameEscalationCompounded, Value: FormatEscalation(ihpkTestEscalationCompounded)}, + {Key: models.ServiceItemParamNameIsPeak, Value: FormatBool(ihpkTestIsPeakPeriod)}, + {Key: models.ServiceItemParamNamePriceRateOrFactor, Value: FormatCents(ihpkTestPerUnitCents)}, + } + suite.validatePricerCreatedParams(expectedParams, displayParams) + }) + + suite.Run("invalid parameters to PriceUsingParams", func() { + paymentServiceItem := suite.setupIntlPackServiceItem() + + // WeightBilled + paymentServiceItem.PaymentServiceItemParams[3].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err := pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to an int", models.ServiceItemParamNameWeightBilled)) + + // PerUnitCents + paymentServiceItem.PaymentServiceItemParams[2].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err = pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to an int", models.ServiceItemParamNamePerUnitCents)) + + // ReferenceDate + paymentServiceItem.PaymentServiceItemParams[1].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err = pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to a time", models.ServiceItemParamNameReferenceDate)) + + // ContractCode + paymentServiceItem.PaymentServiceItemParams[0].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err = pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to a string", models.ServiceItemParamNameContractCode)) + }) +} + +func (suite *GHCRateEngineServiceSuite) setupIntlPackServiceItem() models.PaymentServiceItem { + contract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{}) + startDate := time.Date(2018, time.January, 1, 12, 0, 0, 0, time.UTC) + endDate := time.Date(2018, time.December, 31, 12, 0, 0, 0, time.UTC) + testdatagen.FetchOrMakeReContractYear(suite.DB(), testdatagen.Assertions{ + ReContractYear: models.ReContractYear{ + Contract: contract, + ContractID: contract.ID, + StartDate: startDate, + EndDate: endDate, + Escalation: 1.0, + EscalationCompounded: 1.0, + }, + }) + return factory.BuildPaymentServiceItemWithParams( + suite.DB(), + models.ReServiceCodeIHPK, + []factory.CreatePaymentServiceItemParams{ + { + Key: models.ServiceItemParamNameContractCode, + KeyType: models.ServiceItemParamTypeString, + Value: contract.Code, + }, + { + Key: models.ServiceItemParamNameReferenceDate, + KeyType: models.ServiceItemParamTypeDate, + Value: ihpkTestRequestedPickupDate.Format(DateParamFormat), + }, + { + Key: models.ServiceItemParamNamePerUnitCents, + KeyType: models.ServiceItemParamTypeInteger, + Value: fmt.Sprintf("%d", int(ihpkTestPerUnitCents)), + }, + { + Key: models.ServiceItemParamNameWeightBilled, + KeyType: models.ServiceItemParamTypeInteger, + Value: strconv.Itoa(ihpkTestWeight.Int()), + }, + }, nil, nil, + ) +} diff --git a/pkg/services/ghcrateengine/intl_hhg_unpack_pricer_test.go b/pkg/services/ghcrateengine/intl_hhg_unpack_pricer_test.go new file mode 100644 index 00000000000..322027b37b5 --- /dev/null +++ b/pkg/services/ghcrateengine/intl_hhg_unpack_pricer_test.go @@ -0,0 +1,114 @@ +package ghcrateengine + +import ( + "fmt" + "strconv" + "time" + + "github.com/transcom/mymove/pkg/factory" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/testdatagen" + "github.com/transcom/mymove/pkg/unit" +) + +const ( + ihupkTestContractYearName = "Base Period Year 1" + ihupkTestPerUnitCents = unit.Cents(1200) + ihupkTestTotalCost = unit.Cents(25200) + ihupkTestIsPeakPeriod = true + ihupkTestEscalationCompounded = 1.0000 + ihpukTestWeight = unit.Pound(2100) +) + +var ihupkTestRequestedPickupDate = time.Date(testdatagen.TestYear, peakStart.month, peakStart.day, 0, 0, 0, 0, time.UTC) + +func (suite *GHCRateEngineServiceSuite) TestIntlHHGUnpackPricer() { + pricer := NewIntlHHGUnpackPricer() + + suite.Run("success using PaymentServiceItemParams", func() { + paymentServiceItem := suite.setupIntlUnpackServiceItem() + + totalCost, displayParams, err := pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.NoError(err) + suite.Equal(ihupkTestTotalCost, totalCost) + + expectedParams := services.PricingDisplayParams{ + {Key: models.ServiceItemParamNameContractYearName, Value: ihupkTestContractYearName}, + {Key: models.ServiceItemParamNameEscalationCompounded, Value: FormatEscalation(ihupkTestEscalationCompounded)}, + {Key: models.ServiceItemParamNameIsPeak, Value: FormatBool(ihupkTestIsPeakPeriod)}, + {Key: models.ServiceItemParamNamePriceRateOrFactor, Value: FormatCents(ihupkTestPerUnitCents)}, + } + suite.validatePricerCreatedParams(expectedParams, displayParams) + }) + + suite.Run("invalid parameters to PriceUsingParams", func() { + paymentServiceItem := suite.setupIntlPackServiceItem() + + // WeightBilled + paymentServiceItem.PaymentServiceItemParams[3].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err := pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to an int", models.ServiceItemParamNameWeightBilled)) + + // PerUnitCents + paymentServiceItem.PaymentServiceItemParams[2].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err = pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to an int", models.ServiceItemParamNamePerUnitCents)) + + // ReferenceDate + paymentServiceItem.PaymentServiceItemParams[1].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err = pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to a time", models.ServiceItemParamNameReferenceDate)) + + // ContractCode + paymentServiceItem.PaymentServiceItemParams[0].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err = pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to a string", models.ServiceItemParamNameContractCode)) + }) +} + +func (suite *GHCRateEngineServiceSuite) setupIntlUnpackServiceItem() models.PaymentServiceItem { + contract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{}) + startDate := time.Date(2018, time.January, 1, 12, 0, 0, 0, time.UTC) + endDate := time.Date(2018, time.December, 31, 12, 0, 0, 0, time.UTC) + testdatagen.FetchOrMakeReContractYear(suite.DB(), testdatagen.Assertions{ + ReContractYear: models.ReContractYear{ + Contract: contract, + ContractID: contract.ID, + StartDate: startDate, + EndDate: endDate, + Escalation: 1.0, + EscalationCompounded: 1.0, + }, + }) + return factory.BuildPaymentServiceItemWithParams( + suite.DB(), + models.ReServiceCodeIHUPK, + []factory.CreatePaymentServiceItemParams{ + { + Key: models.ServiceItemParamNameContractCode, + KeyType: models.ServiceItemParamTypeString, + Value: contract.Code, + }, + { + Key: models.ServiceItemParamNameReferenceDate, + KeyType: models.ServiceItemParamTypeDate, + Value: ihupkTestRequestedPickupDate.Format(DateParamFormat), + }, + { + Key: models.ServiceItemParamNamePerUnitCents, + KeyType: models.ServiceItemParamTypeInteger, + Value: fmt.Sprintf("%d", int(ihupkTestPerUnitCents)), + }, + { + Key: models.ServiceItemParamNameWeightBilled, + KeyType: models.ServiceItemParamTypeInteger, + Value: strconv.Itoa(ihpukTestWeight.Int()), + }, + }, nil, nil, + ) +} diff --git a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go new file mode 100644 index 00000000000..9c07773bdb8 --- /dev/null +++ b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go @@ -0,0 +1,209 @@ +package ghcrateengine + +import ( + "fmt" + "time" + + "github.com/gofrs/uuid" + + "github.com/transcom/mymove/pkg/apperror" + "github.com/transcom/mymove/pkg/factory" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/testdatagen" + "github.com/transcom/mymove/pkg/unit" +) + +const ( + intlPortFscTestDistance = unit.Miles(2276) + intlPortFscTestWeight = unit.Pound(4025) + intlPortFscWeightDistanceMultiplier = float64(0.000417) + intlPortFscFuelPrice = unit.Millicents(281400) + intlPortFscPriceCents = unit.Cents(2980) + intlPortFscPortName = "PORTLAND INTL" +) + +var intlPortFscActualPickupDate = time.Date(testdatagen.TestYear, time.June, 5, 7, 33, 11, 456, time.UTC) + +func (suite *GHCRateEngineServiceSuite) TestIntlPortFuelSurchargePricer() { + intlPortFuelSurchargePricer := NewPortFuelSurchargePricer() + + intlPortFscPriceDifferenceInCents := (intlPortFscFuelPrice - baseGHCDieselFuelPrice).Float64() / 1000.0 + intlPortFscMultiplier := intlPortFscWeightDistanceMultiplier * intlPortFscTestDistance.Float64() + + suite.Run("success using PaymentServiceItemParams", func() { + paymentServiceItem := suite.setupPortFuelSurchargeServiceItem() + priceCents, displayParams, err := intlPortFuelSurchargePricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.NoError(err) + suite.Equal(intlPortFscPriceCents, priceCents) + + expectedParams := services.PricingDisplayParams{ + {Key: models.ServiceItemParamNameFSCPriceDifferenceInCents, Value: FormatFloat(intlPortFscPriceDifferenceInCents, 1)}, + {Key: models.ServiceItemParamNameFSCMultiplier, Value: FormatFloat(intlPortFscMultiplier, 7)}, + } + suite.validatePricerCreatedParams(expectedParams, displayParams) + }) + + suite.Run("success without PaymentServiceItemParams", func() { + priceCents, _, err := intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice, intlPortFscPortName) + suite.NoError(err) + suite.Equal(intlPortFscPriceCents, priceCents) + }) + + suite.Run("sending PaymentServiceItemParams without expected param", func() { + _, _, err := intlPortFuelSurchargePricer.PriceUsingParams(suite.AppContextForTest(), models.PaymentServiceItemParams{}) + suite.Error(err) + }) + + suite.Run("fails using PaymentServiceItemParams with below minimum weight for WeightBilled", func() { + paymentServiceItem := suite.setupPortFuelSurchargeServiceItem() + paramsWithBelowMinimumWeight := paymentServiceItem.PaymentServiceItemParams + weightBilledIndex := 2 + if paramsWithBelowMinimumWeight[weightBilledIndex].ServiceItemParamKey.Key != models.ServiceItemParamNameWeightBilled { + suite.FailNow("failed", "Test needs to adjust the weight of %s but the index is pointing to %s ", models.ServiceItemParamNameWeightBilled, paramsWithBelowMinimumWeight[4].ServiceItemParamKey.Key) + } + paramsWithBelowMinimumWeight[weightBilledIndex].Value = "200" + priceCents, _, err := intlPortFuelSurchargePricer.PriceUsingParams(suite.AppContextForTest(), paramsWithBelowMinimumWeight) + if suite.Error(err) { + suite.Equal("weight must be a minimum of 500", err.Error()) + suite.Equal(unit.Cents(0), priceCents) + } + }) + + suite.Run("FSC is negative if fuel price from EIA is below $2.50", func() { + priceCents, _, err := intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, 242400, intlPortFscPortName) + suite.NoError(err) + suite.Equal(unit.Cents(-721), priceCents) + }) + + suite.Run("Price validation errors", func() { + + // No actual pickup date + _, _, err := intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), time.Time{}, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice, intlPortFscPortName) + suite.Error(err) + suite.Equal("ActualPickupDate is required", err.Error()) + + // No distance + _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, unit.Miles(0), intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice, intlPortFscPortName) + suite.Error(err) + suite.Equal("Distance must be greater than 0", err.Error()) + + // No weight + _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, unit.Pound(0), intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice, intlPortFscPortName) + suite.Error(err) + suite.Equal(fmt.Sprintf("weight must be a minimum of %d", minDomesticWeight), err.Error()) + + // No weight based distance multiplier + _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, 0, intlPortFscFuelPrice, intlPortFscPortName) + suite.Error(err) + suite.Equal("WeightBasedDistanceMultiplier is required", err.Error()) + + // No EIA fuel price + _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, 0, intlPortFscPortName) + suite.Error(err) + suite.Equal("EIAFuelPrice is required", err.Error()) + }) + + suite.Run("PriceUsingParams validation errors", func() { + paymentServiceItem := suite.setupPortFuelSurchargeServiceItem() + paramsWithBelowMinimumWeight := paymentServiceItem.PaymentServiceItemParams + weightBilledIndex := 2 + if paramsWithBelowMinimumWeight[weightBilledIndex].ServiceItemParamKey.Key != models.ServiceItemParamNameWeightBilled { + suite.FailNow("failed", "Test needs to adjust the weight of %s but the index is pointing to %s ", models.ServiceItemParamNameWeightBilled, paramsWithBelowMinimumWeight[4].ServiceItemParamKey.Key) + } + paramsWithBelowMinimumWeight[weightBilledIndex].Value = "200" + + // No ActualPickupDate + missingActualPickupDate := suite.removeOnePaymentServiceItem(paymentServiceItem.PaymentServiceItemParams, models.ServiceItemParamNameActualPickupDate) + _, _, err := intlPortFuelSurchargePricer.PriceUsingParams(suite.AppContextForTest(), missingActualPickupDate) + suite.Error(err) + suite.Equal("could not find param with key ActualPickupDate", err.Error()) + + // No WeightBilled + missingWeightBilled := suite.removeOnePaymentServiceItem(paymentServiceItem.PaymentServiceItemParams, models.ServiceItemParamNameWeightBilled) + _, _, err = intlPortFuelSurchargePricer.PriceUsingParams(suite.AppContextForTest(), missingWeightBilled) + suite.Error(err) + suite.Equal("could not find param with key WeightBilled", err.Error()) + + // No FSCWeightBasedDistanceMultiplier + missingFSCWeightBasedDistanceMultiplier := suite.removeOnePaymentServiceItem(paymentServiceItem.PaymentServiceItemParams, models.ServiceItemParamNameFSCWeightBasedDistanceMultiplier) + _, _, err = intlPortFuelSurchargePricer.PriceUsingParams(suite.AppContextForTest(), missingFSCWeightBasedDistanceMultiplier) + suite.Error(err) + suite.Equal("could not find param with key FSCWeightBasedDistanceMultiplier", err.Error()) + + // No EIAFuelPrice + missingEIAFuelPrice := suite.removeOnePaymentServiceItem(paymentServiceItem.PaymentServiceItemParams, models.ServiceItemParamNameEIAFuelPrice) + _, _, err = intlPortFuelSurchargePricer.PriceUsingParams(suite.AppContextForTest(), missingEIAFuelPrice) + suite.Error(err) + suite.Equal("could not find param with key EIAFuelPrice", err.Error()) + }) + + suite.Run("can't find distance", func() { + paymentServiceItem := suite.setupPortFuelSurchargeServiceItem() + paramsWithBelowMinimumWeight := paymentServiceItem.PaymentServiceItemParams + weightBilledIndex := 2 + if paramsWithBelowMinimumWeight[weightBilledIndex].ServiceItemParamKey.Key != models.ServiceItemParamNameWeightBilled { + suite.FailNow("failed", "Test needs to adjust the weight of %s but the index is pointing to %s ", models.ServiceItemParamNameWeightBilled, paramsWithBelowMinimumWeight[4].ServiceItemParamKey.Key) + } + paramsWithBelowMinimumWeight[weightBilledIndex].Value = "200" + + paramsWithBadReference := paymentServiceItem.PaymentServiceItemParams + paramsWithBadReference[0].PaymentServiceItemID = uuid.Nil + _, _, err := intlPortFuelSurchargePricer.PriceUsingParams(suite.AppContextForTest(), paramsWithBadReference) + suite.Error(err) + suite.IsType(apperror.NotFoundError{}, err) + }) +} + +func (suite *GHCRateEngineServiceSuite) setupPortFuelSurchargeServiceItem() models.PaymentServiceItem { + model := factory.BuildPaymentServiceItemWithParams( + suite.DB(), + models.ReServiceCodePOEFSC, + []factory.CreatePaymentServiceItemParams{ + { + Key: models.ServiceItemParamNameActualPickupDate, + KeyType: models.ServiceItemParamTypeDate, + Value: intlPortFscActualPickupDate.Format(DateParamFormat), + }, + { + Key: models.ServiceItemParamNameDistanceZip, + KeyType: models.ServiceItemParamTypeInteger, + Value: fmt.Sprintf("%d", int(intlPortFscTestDistance)), + }, + { + Key: models.ServiceItemParamNameWeightBilled, + KeyType: models.ServiceItemParamTypeInteger, + Value: fmt.Sprintf("%d", int(intlPortFscTestWeight)), + }, + { + Key: models.ServiceItemParamNameFSCWeightBasedDistanceMultiplier, + KeyType: models.ServiceItemParamTypeDecimal, + Value: fmt.Sprintf("%.7f", intlPortFscWeightDistanceMultiplier), + }, + { + Key: models.ServiceItemParamNameEIAFuelPrice, + KeyType: models.ServiceItemParamTypeInteger, + Value: fmt.Sprintf("%d", int(intlPortFscFuelPrice)), + }, + { + Key: models.ServiceItemParamNamePortName, + KeyType: models.ServiceItemParamTypeString, + Value: intlPortFscPortName, + }, + }, nil, nil, + ) + + var mtoServiceItem models.MTOServiceItem + err := suite.DB().Eager("MTOShipment").Find(&mtoServiceItem, model.MTOServiceItemID) + suite.NoError(err) + + distance := intlPortFscTestDistance + mtoServiceItem.MTOShipment.Distance = &distance + err = suite.DB().Save(&mtoServiceItem.MTOShipment) + suite.NoError(err) + + // the testdatagen factory has some dirty shipment data that we don't want to pass through to the pricer in the test + model.PaymentServiceItemParams[0].PaymentServiceItem.MTOServiceItem = models.MTOServiceItem{} + + return model +} diff --git a/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go b/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go index ca0624d75a2..3d0ea35c3ba 100644 --- a/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go +++ b/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer.go @@ -38,7 +38,7 @@ func (p intlShippingAndLinehaulPricer) Price(appCtx appcontext.AppContext, contr isPeakPeriod := IsPeakPeriod(referenceDate) - contract, err := fetchContractsByContractCode(appCtx, contractCode) + contract, err := fetchContractByContractCode(appCtx, contractCode) if err != nil { return 0, nil, fmt.Errorf("could not find contract with code: %s: %w", contractCode, err) } diff --git a/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer_test.go b/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer_test.go new file mode 100644 index 00000000000..ef3407b04f0 --- /dev/null +++ b/pkg/services/ghcrateengine/intl_shipping_and_linehaul_pricer_test.go @@ -0,0 +1,144 @@ +package ghcrateengine + +import ( + "fmt" + "strconv" + "time" + + "github.com/transcom/mymove/pkg/factory" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/testdatagen" + "github.com/transcom/mymove/pkg/unit" +) + +const ( + islhTestContractYearName = "Base Period Year 1" + islhTestPerUnitCents = unit.Cents(15000) + islhTestTotalCost = unit.Cents(315000) + islhTestIsPeakPeriod = true + islhTestEscalationCompounded = 1.0000 + islhTestWeight = unit.Pound(2100) + islhTestDistance = unit.Miles(1201) +) + +var islhTestRequestedPickupDate = time.Date(testdatagen.TestYear, peakStart.month, peakStart.day, 0, 0, 0, 0, time.UTC) + +func (suite *GHCRateEngineServiceSuite) TestIntlShippingAndLinehaulPricer() { + pricer := NewIntlShippingAndLinehaulPricer() + + suite.Run("success using PaymentServiceItemParams", func() { + paymentServiceItem := suite.setupIntlShippingAndLinehaulServiceItem() + + totalCost, displayParams, err := pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.NoError(err) + suite.Equal(islhTestTotalCost, totalCost) + + expectedParams := services.PricingDisplayParams{ + {Key: models.ServiceItemParamNameContractYearName, Value: islhTestContractYearName}, + {Key: models.ServiceItemParamNameEscalationCompounded, Value: FormatEscalation(islhTestEscalationCompounded)}, + {Key: models.ServiceItemParamNameIsPeak, Value: FormatBool(islhTestIsPeakPeriod)}, + {Key: models.ServiceItemParamNamePriceRateOrFactor, Value: FormatCents(islhTestPerUnitCents)}, + } + suite.validatePricerCreatedParams(expectedParams, displayParams) + }) + + suite.Run("invalid parameters to PriceUsingParams", func() { + paymentServiceItem := suite.setupIntlShippingAndLinehaulServiceItem() + + // PerUnitCents + paymentServiceItem.PaymentServiceItemParams[3].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err := pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to an int", models.ServiceItemParamNamePerUnitCents)) + + // ReferenceDate + paymentServiceItem.PaymentServiceItemParams[2].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err = pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to a time", models.ServiceItemParamNameReferenceDate)) + + // DistanceZip + paymentServiceItem.PaymentServiceItemParams[1].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err = pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to an int", models.ServiceItemParamNameDistanceZip)) + + // ContractCode + paymentServiceItem.PaymentServiceItemParams[0].ServiceItemParamKey.Type = models.ServiceItemParamTypeBoolean + _, _, err = pricer.PriceUsingParams(suite.AppContextForTest(), paymentServiceItem.PaymentServiceItemParams) + suite.Error(err) + suite.Contains(err.Error(), fmt.Sprintf("trying to convert %s to a string", models.ServiceItemParamNameContractCode)) + }) + + suite.Run("Price validation errors", func() { + + // No contract code + _, _, err := pricer.Price(suite.AppContextForTest(), "", islhTestRequestedPickupDate, islhTestDistance, islhTestWeight, islhTestPerUnitCents.Int()) + suite.Error(err) + suite.Equal("ContractCode is required", err.Error()) + + // No reference date + _, _, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, time.Time{}, islhTestDistance, islhTestWeight, islhTestPerUnitCents.Int()) + suite.Error(err) + suite.Equal("referenceDate is required", err.Error()) + + // No weight + _, _, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, islhTestRequestedPickupDate, islhTestDistance, 0, islhTestPerUnitCents.Int()) + suite.Error(err) + suite.Equal(fmt.Sprintf("weight must be at least %d", minIntlWeightHHG), err.Error()) + + // No per unit cents + _, _, err = pricer.Price(suite.AppContextForTest(), testdatagen.DefaultContractCode, islhTestRequestedPickupDate, islhTestDistance, islhTestWeight, 0) + suite.Error(err) + suite.Equal("PerUnitCents is required", err.Error()) + + }) +} + +func (suite *GHCRateEngineServiceSuite) setupIntlShippingAndLinehaulServiceItem() models.PaymentServiceItem { + contract := testdatagen.FetchOrMakeReContract(suite.DB(), testdatagen.Assertions{}) + startDate := time.Date(2018, time.January, 1, 12, 0, 0, 0, time.UTC) + endDate := time.Date(2018, time.December, 31, 12, 0, 0, 0, time.UTC) + testdatagen.FetchOrMakeReContractYear(suite.DB(), testdatagen.Assertions{ + ReContractYear: models.ReContractYear{ + Contract: contract, + ContractID: contract.ID, + StartDate: startDate, + EndDate: endDate, + Escalation: 1.0, + EscalationCompounded: 1.0, + }, + }) + return factory.BuildPaymentServiceItemWithParams( + suite.DB(), + models.ReServiceCodeISLH, + []factory.CreatePaymentServiceItemParams{ + { + Key: models.ServiceItemParamNameContractCode, + KeyType: models.ServiceItemParamTypeString, + Value: contract.Code, + }, + { + Key: models.ServiceItemParamNameDistanceZip, + KeyType: models.ServiceItemParamTypeInteger, + Value: fmt.Sprintf("%d", int(islhTestDistance)), + }, + { + Key: models.ServiceItemParamNameReferenceDate, + KeyType: models.ServiceItemParamTypeDate, + Value: islhTestRequestedPickupDate.Format(DateParamFormat), + }, + { + Key: models.ServiceItemParamNamePerUnitCents, + KeyType: models.ServiceItemParamTypeInteger, + Value: fmt.Sprintf("%d", int(islhTestPerUnitCents)), + }, + { + Key: models.ServiceItemParamNameWeightBilled, + KeyType: models.ServiceItemParamTypeInteger, + Value: strconv.Itoa(islhTestWeight.Int()), + }, + }, nil, nil, + ) +} diff --git a/pkg/services/ghcrateengine/pricer_helpers_intl.go b/pkg/services/ghcrateengine/pricer_helpers_intl.go index 37f01ee9966..924dad55537 100644 --- a/pkg/services/ghcrateengine/pricer_helpers_intl.go +++ b/pkg/services/ghcrateengine/pricer_helpers_intl.go @@ -23,10 +23,13 @@ func priceIntlPackUnpack(appCtx appcontext.AppContext, packUnpackCode models.ReS if referenceDate.IsZero() { return 0, nil, errors.New("ReferenceDate is required") } + if perUnitCents == 0 { + return 0, nil, errors.New("PerUnitCents is required") + } isPeakPeriod := IsPeakPeriod(referenceDate) - contract, err := fetchContractsByContractCode(appCtx, contractCode) + contract, err := fetchContractByContractCode(appCtx, contractCode) if err != nil { return 0, nil, fmt.Errorf("could not find contract with code: %s: %w", contractCode, err) } diff --git a/pkg/services/ghcrateengine/pricer_helpers_intl_test.go b/pkg/services/ghcrateengine/pricer_helpers_intl_test.go new file mode 100644 index 00000000000..19539e4c976 --- /dev/null +++ b/pkg/services/ghcrateengine/pricer_helpers_intl_test.go @@ -0,0 +1,46 @@ +package ghcrateengine + +import ( + "time" + + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/services" + "github.com/transcom/mymove/pkg/testdatagen" +) + +func (suite *GHCRateEngineServiceSuite) TestPriceIntlPackUnpack() { + suite.Run("success with IHPK", func() { + suite.setupIntlPackServiceItem() + totalCost, displayParams, err := priceIntlPackUnpack(suite.AppContextForTest(), models.ReServiceCodeIHPK, testdatagen.DefaultContractCode, ihpkTestRequestedPickupDate, ihpkTestWeight, ihpkTestPerUnitCents.Int()) + suite.NoError(err) + suite.Equal(ihpkTestTotalCost, totalCost) + + expectedParams := services.PricingDisplayParams{ + {Key: models.ServiceItemParamNameContractYearName, Value: ihpkTestContractYearName}, + {Key: models.ServiceItemParamNameEscalationCompounded, Value: FormatEscalation(ihpkTestEscalationCompounded)}, + {Key: models.ServiceItemParamNameIsPeak, Value: FormatBool(ihpkTestIsPeakPeriod)}, + {Key: models.ServiceItemParamNamePriceRateOrFactor, Value: FormatCents(ihpkTestPerUnitCents)}, + } + suite.validatePricerCreatedParams(expectedParams, displayParams) + }) + + suite.Run("Invalid parameters to Price", func() { + suite.setupIntlPackServiceItem() + _, _, err := priceIntlPackUnpack(suite.AppContextForTest(), models.ReServiceCodeDLH, testdatagen.DefaultContractCode, ihpkTestRequestedPickupDate, ihpkTestWeight, ihpkTestPerUnitCents.Int()) + suite.Error(err) + suite.Contains(err.Error(), "unsupported pack/unpack code") + + _, _, err = priceIntlPackUnpack(suite.AppContextForTest(), models.ReServiceCodeIHPK, "", ihpkTestRequestedPickupDate, ihpkTestWeight, ihpkTestPerUnitCents.Int()) + suite.Error(err) + suite.Contains(err.Error(), "ContractCode is required") + + _, _, err = priceIntlPackUnpack(suite.AppContextForTest(), models.ReServiceCodeIHPK, testdatagen.DefaultContractCode, time.Time{}, ihpkTestWeight, ihpkTestPerUnitCents.Int()) + suite.Error(err) + suite.Contains(err.Error(), "ReferenceDate is required") + + _, _, err = priceIntlPackUnpack(suite.AppContextForTest(), models.ReServiceCodeIHPK, testdatagen.DefaultContractCode, ihpkTestRequestedPickupDate, ihpkTestWeight, 0) + suite.Error(err) + suite.Contains(err.Error(), "PerUnitCents is required") + }) + +} diff --git a/pkg/services/ghcrateengine/pricer_helpers_test.go b/pkg/services/ghcrateengine/pricer_helpers_test.go index be3648927e2..06b9ec30044 100644 --- a/pkg/services/ghcrateengine/pricer_helpers_test.go +++ b/pkg/services/ghcrateengine/pricer_helpers_test.go @@ -529,7 +529,7 @@ func (suite *GHCRateEngineServiceSuite) Test_createPricerGeneratedParams() { _, err := createPricerGeneratedParams(suite.AppContextForTest(), subtestData.paymentServiceItem.ID, invalidParam) suite.Error(err) - suite.Contains(err.Error(), "Service item param key is not a pricer param") + suite.Contains(err.Error(), "service item param key is not a pricer param") }) suite.Run("errors if no PricingParms passed from the Pricer", func() { diff --git a/pkg/services/ghcrateengine/pricer_query_helpers.go b/pkg/services/ghcrateengine/pricer_query_helpers.go index 51acb06f9bd..84cde4fc64c 100644 --- a/pkg/services/ghcrateengine/pricer_query_helpers.go +++ b/pkg/services/ghcrateengine/pricer_query_helpers.go @@ -103,7 +103,7 @@ func fetchContractsByContractId(appCtx appcontext.AppContext, contractID uuid.UU return contracts, nil } -func fetchContractsByContractCode(appCtx appcontext.AppContext, contractCode string) (models.ReContract, error) { +func fetchContractByContractCode(appCtx appcontext.AppContext, contractCode string) (models.ReContract, error) { var contract models.ReContract err := appCtx.DB().Where("code = $1", contractCode).First(&contract) if err != nil { diff --git a/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx b/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx index 5aa3fdc0187..410e65506f1 100644 --- a/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx +++ b/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx @@ -145,7 +145,10 @@ const CreatePaymentRequestForm = ({ mtoServiceItem.reServiceCode === 'DOSFSC' || mtoServiceItem.reServiceCode === 'DDSHUT' || mtoServiceItem.reServiceCode === 'IHPK' || - mtoServiceItem.reServiceCode === 'IHUPK') && ( + mtoServiceItem.reServiceCode === 'IHUPK' || + mtoServiceItem.reServiceCode === 'ISLH' || + mtoServiceItem.reServiceCode === 'POEFSC' || + mtoServiceItem.reServiceCode === 'PODFSC') && ( { { id: '6', reServiceCode: 'DDFSIT', reServiceName: 'Domestic destination 1st day SIT' }, ], 2: [{ id: '5', reServiceCode: 'FSC' }], + 3: [ + { id: '7', reServiceCode: 'IHPK' }, + { id: '8', reServiceCode: 'IHUPK' }, + { id: '8', reServiceCode: 'ISLH' }, + { id: '8', reServiceCode: 'POEFSC' }, + ], }; it('renders the form', async () => { From c82e97c82e93477123ccaaa649cd42ee1956d85b Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 2 Jan 2025 22:28:29 +0000 Subject: [PATCH 077/113] updating address facotry to handle bogus postal code values when getting post region city ids --- pkg/factory/address_factory.go | 40 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/pkg/factory/address_factory.go b/pkg/factory/address_factory.go index d6b7dff6ce5..1ef93086ad8 100644 --- a/pkg/factory/address_factory.go +++ b/pkg/factory/address_factory.go @@ -1,7 +1,10 @@ package factory import ( + "database/sql" + "github.com/gobuffalo/pop/v6" + "github.com/gofrs/uuid" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/testdatagen" @@ -24,15 +27,17 @@ func BuildAddress(db *pop.Connection, customs []Customization, traits []Trait) m } // Create default Address + beverlyHillsUsprc := uuid.FromStringOrNil("3b9f0ae6-3b2b-44a6-9fcd-8ead346648c4") address := models.Address{ - StreetAddress1: "123 Any Street", - StreetAddress2: models.StringPointer("P.O. Box 12345"), - StreetAddress3: models.StringPointer("c/o Some Person"), - City: "Beverly Hills", - State: "CA", - PostalCode: "90210", - County: models.StringPointer("LOS ANGELES"), - IsOconus: models.BoolPointer(false), + StreetAddress1: "123 Any Street", + StreetAddress2: models.StringPointer("P.O. Box 12345"), + StreetAddress3: models.StringPointer("c/o Some Person"), + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + County: models.StringPointer("LOS ANGELES"), + IsOconus: models.BoolPointer(false), + UsPostRegionCityID: &beverlyHillsUsprc, } // Find/create the Country if customization is provided @@ -56,7 +61,7 @@ func BuildAddress(db *pop.Connection, customs []Customization, traits []Trait) m // Overwrite values with those from customizations testdatagen.MergeModels(&address, cAddress) - // This helps assign counties when the factory is called for seed data or tests + // This helps assign counties & us_post_region_cities_id values when the factory is called for seed data or tests // Additionally, also only run if not 90210. 90210's county is by default populated if db != nil && address.PostalCode != "90210" { county, err := models.FindCountyByZipCode(db, address.PostalCode) @@ -67,19 +72,18 @@ func BuildAddress(db *pop.Connection, customs []Customization, traits []Trait) m // The zip code successfully found a county address.County = county } - } else if db == nil && address.PostalCode != "90210" { - // If no db supplied, mark that - address.County = models.StringPointer("db nil when created") - } - - if db != nil { + // seeing if the postal code provided has a related us_post_region_cities_id usprc, err := models.FindByZipCode(db, address.PostalCode) - if err != nil { - return models.Address{} - } else { + if err != nil && err != sql.ErrNoRows { + address.UsPostRegionCityID = nil + address.UsPostRegionCity = nil + } else if usprc.ID != uuid.Nil { address.UsPostRegionCityID = &usprc.ID address.UsPostRegionCity = usprc } + } else if db == nil && address.PostalCode != "90210" { + // If no db supplied, mark that + address.County = models.StringPointer("db nil when created") } // If db is false, it's a stub. No need to create in database. From 5c81091f67693ad0c2199595f24b3cddfc9f82c9 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 3 Jan 2025 00:06:46 +0000 Subject: [PATCH 078/113] fixing some issues with tests and address factory --- pkg/factory/address_factory.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/factory/address_factory.go b/pkg/factory/address_factory.go index 1ef93086ad8..27d92999d00 100644 --- a/pkg/factory/address_factory.go +++ b/pkg/factory/address_factory.go @@ -72,7 +72,12 @@ func BuildAddress(db *pop.Connection, customs []Customization, traits []Trait) m // The zip code successfully found a county address.County = county } - // seeing if the postal code provided has a related us_post_region_cities_id + } else if db == nil && address.PostalCode != "90210" { + // If no db supplied, mark that + address.County = models.StringPointer("db nil when created") + } + + if db != nil && address.PostalCode != "90210" && cAddress.UsPostRegionCityID == nil { usprc, err := models.FindByZipCode(db, address.PostalCode) if err != nil && err != sql.ErrNoRows { address.UsPostRegionCityID = nil @@ -81,9 +86,6 @@ func BuildAddress(db *pop.Connection, customs []Customization, traits []Trait) m address.UsPostRegionCityID = &usprc.ID address.UsPostRegionCity = usprc } - } else if db == nil && address.PostalCode != "90210" { - // If no db supplied, mark that - address.County = models.StringPointer("db nil when created") } // If db is false, it's a stub. No need to create in database. From f39bfa372574e0e052ba52f32191c30c7a6c1740 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 3 Jan 2025 19:19:22 +0000 Subject: [PATCH 079/113] PR fix feedback --- ...aram_values_to_service_params_table.up.sql | 3 ++ .../per_unit_cents_lookup.go | 3 ++ .../per_unit_cents_lookup_test.go | 31 +++++++++++++ .../port_name_lookup.go | 2 +- .../port_name_lookup_test.go | 31 ++++++++++++- pkg/services/ghc_rate_engine.go | 2 +- .../intl_port_fuel_surcharge_pricer.go | 9 ++-- .../intl_port_fuel_surcharge_pricer_test.go | 14 +++--- .../CreatePaymentRequestForm.jsx | 43 ++++++++++--------- src/constants/serviceItems.js | 3 ++ 10 files changed, 103 insertions(+), 38 deletions(-) diff --git a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql index 63c1e404e50..859bbcb47b6 100644 --- a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql +++ b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql @@ -1,3 +1,6 @@ +-- dropping function that is not needed anymore +DROP FUNCTION IF EXISTS get_rate_area_id(UUID, UUID); + -- need to add in param keys for international shipments, this will be used to show breakdowns to the TIO INSERT INTO service_item_param_keys (id, key,description,type,origin,created_at,updated_at) VALUES ('d9ad3878-4b94-4722-bbaf-d4b8080f339d','PortName','Name of the port for an international shipment','STRING','SYSTEM','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957'), diff --git a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go index d2062218620..b339fbf43dd 100644 --- a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go +++ b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup.go @@ -17,6 +17,9 @@ type PerUnitCentsLookup struct { func (p PerUnitCentsLookup) lookup(appCtx appcontext.AppContext, s *ServiceItemParamKeyData) (string, error) { serviceID := p.ServiceItem.ReServiceID contractID := s.ContractID + if p.MTOShipment.RequestedPickupDate == nil { + return "", fmt.Errorf("requested pickup date is required for shipment with id: %s", p.MTOShipment.ID) + } switch p.ServiceItem.ReService.Code { case models.ReServiceCodeIHPK: diff --git a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup_test.go b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup_test.go index 4d9ceab2512..9937f86217b 100644 --- a/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup_test.go +++ b/pkg/payment_request/service_param_value_lookups/per_unit_cents_lookup_test.go @@ -129,4 +129,35 @@ func (suite *ServiceParamValueLookupsSuite) TestPerUnitCentsLookup() { suite.Error(err) suite.Equal(perUnitCents, "") }) + + suite.Run("failure - no requested pickup date on shipment", func() { + testdatagen.MakeReContractYear(suite.DB(), testdatagen.Assertions{ + ReContractYear: models.ReContractYear{ + StartDate: time.Now().Add(-24 * time.Hour), + EndDate: time.Now().Add(24 * time.Hour), + }, + }) + mtoServiceItem = factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodeIHPK, + }, + }, + { + Model: models.MTOShipment{ + RequestedPickupDate: nil, + }, + }, + }, []factory.Trait{factory.GetTraitAvailableToPrimeMove}) + + mtoServiceItem.MTOShipment.RequestedPickupDate = nil + suite.MustSave(&mtoServiceItem.MTOShipment) + + paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) + suite.FatalNoError(err) + + perUnitCents, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + suite.Error(err) + suite.Equal(perUnitCents, "") + }) } diff --git a/pkg/payment_request/service_param_value_lookups/port_name_lookup.go b/pkg/payment_request/service_param_value_lookups/port_name_lookup.go index 5013d3ae2c8..a925ceaa099 100644 --- a/pkg/payment_request/service_param_value_lookups/port_name_lookup.go +++ b/pkg/payment_request/service_param_value_lookups/port_name_lookup.go @@ -21,7 +21,7 @@ func (p PortNameLookup) lookup(appCtx appcontext.AppContext, _ *ServiceItemParam } else if p.ServiceItem.POELocationID != nil { portLocationID = p.ServiceItem.POELocationID } else { - return "", nil + return "", fmt.Errorf("unable to find port location for service item id: %s", p.ServiceItem.ID) } var portLocation models.PortLocation err := appCtx.DB().Q(). diff --git a/pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go b/pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go index b0ccb4b2bfe..a16b174dc1c 100644 --- a/pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go +++ b/pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go @@ -64,7 +64,7 @@ func (suite *ServiceParamValueLookupsSuite) TestPortNameLookup() { portName, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) suite.FatalNoError(err) - suite.Equal(portName, "SEATTLE TACOMA INTL") + suite.Equal(portName, port.Port.PortName) }) suite.Run("success - returns PortName value for PODFSC", func() { @@ -82,6 +82,33 @@ func (suite *ServiceParamValueLookupsSuite) TestPortNameLookup() { portName, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) suite.FatalNoError(err) - suite.Equal(portName, "PORTLAND INTL") + suite.Equal(portName, port.Port.PortName) + }) + + suite.Run("failure - no port value on service item", func() { + testdatagen.MakeReContractYear(suite.DB(), testdatagen.Assertions{ + ReContractYear: models.ReContractYear{ + StartDate: time.Now().Add(-24 * time.Hour), + EndDate: time.Now().Add(24 * time.Hour), + }, + }) + mtoServiceItem = factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodePOEFSC, + }, + }, + { + Model: models.MTOServiceItem{ + POELocationID: nil, + }, + }, + }, []factory.Trait{factory.GetTraitAvailableToPrimeMove}) + + paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) + suite.FatalNoError(err) + + _, err = paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + suite.Error(err) }) } diff --git a/pkg/services/ghc_rate_engine.go b/pkg/services/ghc_rate_engine.go index 8b3ef07de0f..2247e3d7426 100644 --- a/pkg/services/ghc_rate_engine.go +++ b/pkg/services/ghc_rate_engine.go @@ -261,6 +261,6 @@ type IntlHHGUnpackPricer interface { // //go:generate mockery --name IntlPortFuelSurchargePricer type IntlPortFuelSurchargePricer interface { - Price(appCtx appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, portName string) (unit.Cents, PricingDisplayParams, error) + Price(appCtx appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents) (unit.Cents, PricingDisplayParams, error) ParamsPricer } diff --git a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go index 7d2c94e7096..3e6dcfe1bdd 100644 --- a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go +++ b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go @@ -23,7 +23,7 @@ func NewPortFuelSurchargePricer() services.IntlPortFuelSurchargePricer { return &portFuelSurchargePricer{} } -func (p portFuelSurchargePricer) Price(_ appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, portName string) (unit.Cents, services.PricingDisplayParams, error) { +func (p portFuelSurchargePricer) Price(_ appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents) (unit.Cents, services.PricingDisplayParams, error) { // Validate parameters if actualPickupDate.IsZero() { return 0, nil, errors.New("ActualPickupDate is required") @@ -40,9 +40,6 @@ func (p portFuelSurchargePricer) Price(_ appcontext.AppContext, actualPickupDate if eiaFuelPrice == 0 { return 0, nil, errors.New("EIAFuelPrice is required") } - if portName == "" { - return 0, nil, errors.New("PortName is required") - } fscPriceDifferenceInCents := (eiaFuelPrice - baseGHCDieselFuelPrice).Float64() / 1000.0 fscMultiplier := fscWeightBasedDistanceMultiplier * distance.Float64() @@ -99,10 +96,10 @@ func (p portFuelSurchargePricer) PriceUsingParams(appCtx appcontext.AppContext, return unit.Cents(0), nil, err } - portName, err := getParamString(params, models.ServiceItemParamNamePortName) + _, err = getParamString(params, models.ServiceItemParamNamePortName) if err != nil { return unit.Cents(0), nil, err } - return p.Price(appCtx, actualPickupDate, unit.Miles(distance), unit.Pound(weightBilled), fscWeightBasedDistanceMultiplier, unit.Millicents(eiaFuelPrice), portName) + return p.Price(appCtx, actualPickupDate, unit.Miles(distance), unit.Pound(weightBilled), fscWeightBasedDistanceMultiplier, unit.Millicents(eiaFuelPrice)) } diff --git a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go index 9c07773bdb8..fa660bd796d 100644 --- a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go +++ b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go @@ -45,7 +45,7 @@ func (suite *GHCRateEngineServiceSuite) TestIntlPortFuelSurchargePricer() { }) suite.Run("success without PaymentServiceItemParams", func() { - priceCents, _, err := intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice, intlPortFscPortName) + priceCents, _, err := intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice) suite.NoError(err) suite.Equal(intlPortFscPriceCents, priceCents) }) @@ -71,7 +71,7 @@ func (suite *GHCRateEngineServiceSuite) TestIntlPortFuelSurchargePricer() { }) suite.Run("FSC is negative if fuel price from EIA is below $2.50", func() { - priceCents, _, err := intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, 242400, intlPortFscPortName) + priceCents, _, err := intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, 242400) suite.NoError(err) suite.Equal(unit.Cents(-721), priceCents) }) @@ -79,27 +79,27 @@ func (suite *GHCRateEngineServiceSuite) TestIntlPortFuelSurchargePricer() { suite.Run("Price validation errors", func() { // No actual pickup date - _, _, err := intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), time.Time{}, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice, intlPortFscPortName) + _, _, err := intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), time.Time{}, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice) suite.Error(err) suite.Equal("ActualPickupDate is required", err.Error()) // No distance - _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, unit.Miles(0), intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice, intlPortFscPortName) + _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, unit.Miles(0), intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice) suite.Error(err) suite.Equal("Distance must be greater than 0", err.Error()) // No weight - _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, unit.Pound(0), intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice, intlPortFscPortName) + _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, unit.Pound(0), intlPortFscWeightDistanceMultiplier, intlPortFscFuelPrice) suite.Error(err) suite.Equal(fmt.Sprintf("weight must be a minimum of %d", minDomesticWeight), err.Error()) // No weight based distance multiplier - _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, 0, intlPortFscFuelPrice, intlPortFscPortName) + _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, 0, intlPortFscFuelPrice) suite.Error(err) suite.Equal("WeightBasedDistanceMultiplier is required", err.Error()) // No EIA fuel price - _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, 0, intlPortFscPortName) + _, _, err = intlPortFuelSurchargePricer.Price(suite.AppContextForTest(), intlPortFscActualPickupDate, intlPortFscTestDistance, intlPortFscTestWeight, intlPortFscWeightDistanceMultiplier, 0) suite.Error(err) suite.Equal("EIAFuelPrice is required", err.Error()) }) diff --git a/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx b/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx index 410e65506f1..d5b3f283829 100644 --- a/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx +++ b/src/components/PrimeUI/CreatePaymentRequestForm/CreatePaymentRequestForm.jsx @@ -18,6 +18,7 @@ import ServiceItem from 'components/PrimeUI/ServiceItem/ServiceItem'; import Shipment from 'components/PrimeUI/Shipment/Shipment'; import { DatePickerInput } from 'components/form/fields'; import TextField from 'components/form/fields/TextField/TextField'; +import { SERVICE_ITEM_CODES } from 'constants/serviceItems'; const CreatePaymentRequestForm = ({ initialValues, @@ -128,27 +129,27 @@ const CreatePaymentRequestForm = ({ /> )} - {(mtoServiceItem.reServiceCode === 'DLH' || - mtoServiceItem.reServiceCode === 'DSH' || - mtoServiceItem.reServiceCode === 'FSC' || - mtoServiceItem.reServiceCode === 'DUPK' || - mtoServiceItem.reServiceCode === 'DNPK' || - mtoServiceItem.reServiceCode === 'DOFSIT' || - mtoServiceItem.reServiceCode === 'DOPSIT' || - mtoServiceItem.reServiceCode === 'DOSHUT' || - mtoServiceItem.reServiceCode === 'DDFSIT' || - mtoServiceItem.reServiceCode === 'DDDSIT' || - mtoServiceItem.reServiceCode === 'DOP' || - mtoServiceItem.reServiceCode === 'DDP' || - mtoServiceItem.reServiceCode === 'DPK' || - mtoServiceItem.reServiceCode === 'DDSFSC' || - mtoServiceItem.reServiceCode === 'DOSFSC' || - mtoServiceItem.reServiceCode === 'DDSHUT' || - mtoServiceItem.reServiceCode === 'IHPK' || - mtoServiceItem.reServiceCode === 'IHUPK' || - mtoServiceItem.reServiceCode === 'ISLH' || - mtoServiceItem.reServiceCode === 'POEFSC' || - mtoServiceItem.reServiceCode === 'PODFSC') && ( + {(mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DLH || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DSH || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.FSC || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DUPK || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DNPK || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DOFSIT || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DOPSIT || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DOSHUT || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DDFSIT || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DDDSIT || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DOP || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DDP || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DPK || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DDSFSC || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DOSFSC || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.DDSHUT || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.IHPK || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.IHUPK || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.ISLH || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.POEFSC || + mtoServiceItem.reServiceCode === SERVICE_ITEM_CODES.PODFSC) && ( Date: Fri, 3 Jan 2025 20:21:47 +0000 Subject: [PATCH 080/113] mocks again --- .../mocks/IntlPortFuelSurchargePricer.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/services/mocks/IntlPortFuelSurchargePricer.go b/pkg/services/mocks/IntlPortFuelSurchargePricer.go index 48e396d017e..1780857c419 100644 --- a/pkg/services/mocks/IntlPortFuelSurchargePricer.go +++ b/pkg/services/mocks/IntlPortFuelSurchargePricer.go @@ -20,9 +20,9 @@ type IntlPortFuelSurchargePricer struct { mock.Mock } -// Price provides a mock function with given fields: appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName -func (_m *IntlPortFuelSurchargePricer) Price(appCtx appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents, portName string) (unit.Cents, services.PricingDisplayParams, error) { - ret := _m.Called(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) +// Price provides a mock function with given fields: appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice +func (_m *IntlPortFuelSurchargePricer) Price(appCtx appcontext.AppContext, actualPickupDate time.Time, distance unit.Miles, weight unit.Pound, fscWeightBasedDistanceMultiplier float64, eiaFuelPrice unit.Millicents) (unit.Cents, services.PricingDisplayParams, error) { + ret := _m.Called(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice) if len(ret) == 0 { panic("no return value specified for Price") @@ -31,25 +31,25 @@ func (_m *IntlPortFuelSurchargePricer) Price(appCtx appcontext.AppContext, actua var r0 unit.Cents var r1 services.PricingDisplayParams var r2 error - if rf, ok := ret.Get(0).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents, string) (unit.Cents, services.PricingDisplayParams, error)); ok { - return rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) + if rf, ok := ret.Get(0).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents) (unit.Cents, services.PricingDisplayParams, error)); ok { + return rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice) } - if rf, ok := ret.Get(0).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents, string) unit.Cents); ok { - r0 = rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) + if rf, ok := ret.Get(0).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents) unit.Cents); ok { + r0 = rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice) } else { r0 = ret.Get(0).(unit.Cents) } - if rf, ok := ret.Get(1).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents, string) services.PricingDisplayParams); ok { - r1 = rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) + if rf, ok := ret.Get(1).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents) services.PricingDisplayParams); ok { + r1 = rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(services.PricingDisplayParams) } } - if rf, ok := ret.Get(2).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents, string) error); ok { - r2 = rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice, portName) + if rf, ok := ret.Get(2).(func(appcontext.AppContext, time.Time, unit.Miles, unit.Pound, float64, unit.Millicents) error); ok { + r2 = rf(appCtx, actualPickupDate, distance, weight, fscWeightBasedDistanceMultiplier, eiaFuelPrice) } else { r2 = ret.Error(2) } From 114ee8e5c6974ec5c6bb2dce739b8eba00ad89d3 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 3 Jan 2025 21:49:10 +0000 Subject: [PATCH 081/113] updating prime docs --- pkg/gen/primeapi/embedded_spec.go | 4 +- .../payment_request/create_payment_request.go | 201 ++++++++++++++- .../payment_request/payment_request_client.go | 201 ++++++++++++++- swagger-def/prime.yaml | 171 ++++++++++++- swagger/prime.yaml | 233 ++++++++++++++++-- 5 files changed, 757 insertions(+), 53 deletions(-) diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index 52039d783cc..69ed189d17f 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -1063,7 +1063,7 @@ func init() { }, "/payment-requests": { "post": { - "description": "Creates a new instance of a paymentRequest and is assigned the status ` + "`" + `PENDING` + "`" + `.\nA move task order can have multiple payment requests, and\na final payment request can be marked using boolean ` + "`" + `isFinal` + "`" + `.\n\nIf a ` + "`" + `PENDING` + "`" + ` payment request is recalculated,\na new payment request is created and the original request is\nmarked with the status ` + "`" + `DEPRECATED` + "`" + `.\n\n**NOTE**: In order to create a payment request for most service items, the shipment *must*\nbe updated with the ` + "`" + `PrimeActualWeight` + "`" + ` value via [updateMTOShipment](#operation/updateMTOShipment).\n\n**FSC - Fuel Surcharge** service items require ` + "`" + `ActualPickupDate` + "`" + ` to be updated on the shipment.\n\nA service item can be on several payment requests in the case of partial payment requests and payments.\n\nIn the request, if no params are necessary, then just the ` + "`" + `serviceItem` + "`" + ` ` + "`" + `id` + "`" + ` is required. For example:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"isFinal\": false,\n \"moveTaskOrderID\": \"uuid\",\n \"serviceItems\": [\n {\n \"id\": \"uuid\",\n },\n {\n \"id\": \"uuid\",\n \"params\": [\n {\n \"key\": \"Service Item Parameter Name\",\n \"value\": \"Service Item Parameter Value\"\n }\n ]\n }\n ],\n \"pointOfContact\": \"string\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nSIT Service Items \u0026 Accepted Payment Request Parameters:\n---\nIf ` + "`" + `WeightBilled` + "`" + ` is not provided then the full shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) will be considered in the calculation.\n\n**NOTE**: Diversions have a unique calcuation for payment requests without a ` + "`" + `WeightBilled` + "`" + ` parameter.\n\nIf you created a payment request for a diversion and ` + "`" + `WeightBilled` + "`" + ` is not provided, then the following will be used in the calculation:\n- The lowest shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) found in the diverted shipment chain.\n- The lowest reweigh weight found in the diverted shipment chain.\n\nThe diverted shipment chain is created by referencing the ` + "`" + `diversion` + "`" + ` boolean, ` + "`" + `divertedFromShipmentId` + "`" + ` UUID, and matching destination to pickup addresses.\nIf the chain cannot be established it will fall back to the ` + "`" + `PrimeActualWeight` + "`" + ` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations.\nThe lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found.\n\n**DOFSIT - Domestic origin 1st day SIT**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOASIT - Domestic origin add'l SIT** *(SITPaymentRequestStart \u0026 SITPaymentRequestEnd are **REQUIRED**)*\n*To create a paymentRequest for this service item, the ` + "`" + `SITPaymentRequestStart` + "`" + ` and ` + "`" + `SITPaymentRequestEnd` + "`" + ` dates must not overlap previously requested SIT dates.*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n },\n {\n \"key\": \"SITPaymentRequestStart\",\n \"value\": \"date\"\n },\n {\n \"key\": \"SITPaymentRequestEnd\",\n \"value\": \"date\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOPSIT - Domestic origin SIT pickup**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOSHUT - Domestic origin shuttle service**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDFSIT - Domestic destination 1st day SIT**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDASIT - Domestic destination add'l SIT** *(SITPaymentRequestStart \u0026 SITPaymentRequestEnd are **REQUIRED**)*\n*To create a paymentRequest for this service item, the ` + "`" + `SITPaymentRequestStart` + "`" + ` and ` + "`" + `SITPaymentRequestEnd` + "`" + ` dates must not overlap previously requested SIT dates.*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n },\n {\n \"key\": \"SITPaymentRequestStart\",\n \"value\": \"date\"\n },\n {\n \"key\": \"SITPaymentRequestEnd\",\n \"value\": \"date\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDDSIT - Domestic destination SIT delivery**\n*To create a paymentRequest for this service item, it must first have a final address set via [updateMTOServiceItem](#operation/updateMTOServiceItem).*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDSHUT - Domestic destination shuttle service**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n---\n", + "description": "Creates a new instance of a paymentRequest and is assigned the status ` + "`" + `PENDING` + "`" + `.\nA move task order can have multiple payment requests, and\na final payment request can be marked using boolean ` + "`" + `isFinal` + "`" + `.\n\nIf a ` + "`" + `PENDING` + "`" + ` payment request is recalculated,\na new payment request is created and the original request is\nmarked with the status ` + "`" + `DEPRECATED` + "`" + `.\n\n**NOTE**: In order to create a payment request for most service items, the shipment *must*\nbe updated with the ` + "`" + `PrimeActualWeight` + "`" + ` value via [updateMTOShipment](#operation/updateMTOShipment).\n\nIf ` + "`" + `WeightBilled` + "`" + ` is not provided then the full shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) will be considered in the calculation.\n\n**NOTE**: Diversions have a unique calcuation for payment requests without a ` + "`" + `WeightBilled` + "`" + ` parameter.\n\nIf you created a payment request for a diversion and ` + "`" + `WeightBilled` + "`" + ` is not provided, then the following will be used in the calculation:\n- The lowest shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) found in the diverted shipment chain.\n- The lowest reweigh weight found in the diverted shipment chain.\n\nThe diverted shipment chain is created by referencing the ` + "`" + `diversion` + "`" + ` boolean, ` + "`" + `divertedFromShipmentId` + "`" + ` UUID, and matching destination to pickup addresses.\nIf the chain cannot be established it will fall back to the ` + "`" + `PrimeActualWeight` + "`" + ` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations.\nThe lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found.\n\nA service item can be on several payment requests in the case of partial payment requests and payments.\n\nIn the request, if no params are necessary, then just the ` + "`" + `serviceItem` + "`" + ` ` + "`" + `id` + "`" + ` is required. For example:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"isFinal\": false,\n \"moveTaskOrderID\": \"uuid\",\n \"serviceItems\": [\n {\n \"id\": \"uuid\",\n },\n {\n \"id\": \"uuid\",\n \"params\": [\n {\n \"key\": \"Service Item Parameter Name\",\n \"value\": \"Service Item Parameter Value\"\n }\n ]\n }\n ],\n \"pointOfContact\": \"string\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nDomestic Basic Service Items \u0026 Accepted Payment Request Parameters:\n---\n\n**DLH - Domestic Linehaul**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DSH - Domestic Shorthaul**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**FSC - Fuel Surcharge**\n**NOTE**: FSC requires ` + "`" + `ActualPickupDate` + "`" + ` to be updated on the shipment.\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DUPK - Domestic Unpacking**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DPK - Domestic Packing**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DNPK - Domestic NTS Packing**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DPK - Domestic Packing**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOP - Domestic Origin Price**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDP - Domestic Destination Price**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\nDomestic SIT Service Items \u0026 Accepted Payment Request Parameters:\n---\n\n**DOFSIT - Domestic origin 1st day SIT**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOASIT - Domestic origin add'l SIT** *(SITPaymentRequestStart \u0026 SITPaymentRequestEnd are **REQUIRED**)*\n*To create a paymentRequest for this service item, the ` + "`" + `SITPaymentRequestStart` + "`" + ` and ` + "`" + `SITPaymentRequestEnd` + "`" + ` dates must not overlap previously requested SIT dates.*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n },\n {\n \"key\": \"SITPaymentRequestStart\",\n \"value\": \"date\"\n },\n {\n \"key\": \"SITPaymentRequestEnd\",\n \"value\": \"date\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOPSIT - Domestic origin SIT pickup**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOSHUT - Domestic origin shuttle service**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDFSIT - Domestic destination 1st day SIT**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDASIT - Domestic destination add'l SIT** *(SITPaymentRequestStart \u0026 SITPaymentRequestEnd are **REQUIRED**)*\n*To create a paymentRequest for this service item, the ` + "`" + `SITPaymentRequestStart` + "`" + ` and ` + "`" + `SITPaymentRequestEnd` + "`" + ` dates must not overlap previously requested SIT dates.*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n },\n {\n \"key\": \"SITPaymentRequestStart\",\n \"value\": \"date\"\n },\n {\n \"key\": \"SITPaymentRequestEnd\",\n \"value\": \"date\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDDSIT - Domestic destination SIT delivery**\n*To create a paymentRequest for this service item, it must first have a final address set via [updateMTOServiceItem](#operation/updateMTOServiceItem).*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDSHUT - Domestic destination shuttle service**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n---\n\nInternational Basic Service Items \u0026 Accepted Payment Request Parameters:\n---\nJust like domestic shipments \u0026 service items, if ` + "`" + `WeightBilled` + "`" + ` is not provided then the full shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) will be considered in the calculation.\n**NOTE**: ` + "`" + `POEFSC` + "`" + ` \u0026 ` + "`" + `PODFSC` + "`" + ` service items must have a port associated on the service item in order to successfully add it to a payment request. To update the port of a service item, you must use the (#operation/updateMTOServiceItem) endpoint.\n\n**ISLH - International Shipping \u0026 Linehaul**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**IHPK - International HHG Pack**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**IHUPK - International HHG Unpack**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**POEFSC - International Port of Embarkation Fuel Surcharge**\n **NOTE**: POEFSC requires ` + "`" + `ActualPickupDate` + "`" + ` to be updated on the shipment \u0026 ` + "`" + `POELocation` + "`" + ` on the service item.\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**PODFSC - International Port of Debarkation Fuel Surcharge**\n**NOTE**: PODFSC requires ` + "`" + `ActualPickupDate` + "`" + ` to be updated on the shipment \u0026 ` + "`" + `PODLocation` + "`" + ` on the service item.\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n---\n", "consumes": [ "application/json" ], @@ -5921,7 +5921,7 @@ func init() { }, "/payment-requests": { "post": { - "description": "Creates a new instance of a paymentRequest and is assigned the status ` + "`" + `PENDING` + "`" + `.\nA move task order can have multiple payment requests, and\na final payment request can be marked using boolean ` + "`" + `isFinal` + "`" + `.\n\nIf a ` + "`" + `PENDING` + "`" + ` payment request is recalculated,\na new payment request is created and the original request is\nmarked with the status ` + "`" + `DEPRECATED` + "`" + `.\n\n**NOTE**: In order to create a payment request for most service items, the shipment *must*\nbe updated with the ` + "`" + `PrimeActualWeight` + "`" + ` value via [updateMTOShipment](#operation/updateMTOShipment).\n\n**FSC - Fuel Surcharge** service items require ` + "`" + `ActualPickupDate` + "`" + ` to be updated on the shipment.\n\nA service item can be on several payment requests in the case of partial payment requests and payments.\n\nIn the request, if no params are necessary, then just the ` + "`" + `serviceItem` + "`" + ` ` + "`" + `id` + "`" + ` is required. For example:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"isFinal\": false,\n \"moveTaskOrderID\": \"uuid\",\n \"serviceItems\": [\n {\n \"id\": \"uuid\",\n },\n {\n \"id\": \"uuid\",\n \"params\": [\n {\n \"key\": \"Service Item Parameter Name\",\n \"value\": \"Service Item Parameter Value\"\n }\n ]\n }\n ],\n \"pointOfContact\": \"string\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nSIT Service Items \u0026 Accepted Payment Request Parameters:\n---\nIf ` + "`" + `WeightBilled` + "`" + ` is not provided then the full shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) will be considered in the calculation.\n\n**NOTE**: Diversions have a unique calcuation for payment requests without a ` + "`" + `WeightBilled` + "`" + ` parameter.\n\nIf you created a payment request for a diversion and ` + "`" + `WeightBilled` + "`" + ` is not provided, then the following will be used in the calculation:\n- The lowest shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) found in the diverted shipment chain.\n- The lowest reweigh weight found in the diverted shipment chain.\n\nThe diverted shipment chain is created by referencing the ` + "`" + `diversion` + "`" + ` boolean, ` + "`" + `divertedFromShipmentId` + "`" + ` UUID, and matching destination to pickup addresses.\nIf the chain cannot be established it will fall back to the ` + "`" + `PrimeActualWeight` + "`" + ` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations.\nThe lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found.\n\n**DOFSIT - Domestic origin 1st day SIT**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOASIT - Domestic origin add'l SIT** *(SITPaymentRequestStart \u0026 SITPaymentRequestEnd are **REQUIRED**)*\n*To create a paymentRequest for this service item, the ` + "`" + `SITPaymentRequestStart` + "`" + ` and ` + "`" + `SITPaymentRequestEnd` + "`" + ` dates must not overlap previously requested SIT dates.*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n },\n {\n \"key\": \"SITPaymentRequestStart\",\n \"value\": \"date\"\n },\n {\n \"key\": \"SITPaymentRequestEnd\",\n \"value\": \"date\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOPSIT - Domestic origin SIT pickup**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOSHUT - Domestic origin shuttle service**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDFSIT - Domestic destination 1st day SIT**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDASIT - Domestic destination add'l SIT** *(SITPaymentRequestStart \u0026 SITPaymentRequestEnd are **REQUIRED**)*\n*To create a paymentRequest for this service item, the ` + "`" + `SITPaymentRequestStart` + "`" + ` and ` + "`" + `SITPaymentRequestEnd` + "`" + ` dates must not overlap previously requested SIT dates.*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n },\n {\n \"key\": \"SITPaymentRequestStart\",\n \"value\": \"date\"\n },\n {\n \"key\": \"SITPaymentRequestEnd\",\n \"value\": \"date\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDDSIT - Domestic destination SIT delivery**\n*To create a paymentRequest for this service item, it must first have a final address set via [updateMTOServiceItem](#operation/updateMTOServiceItem).*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDSHUT - Domestic destination shuttle service**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n---\n", + "description": "Creates a new instance of a paymentRequest and is assigned the status ` + "`" + `PENDING` + "`" + `.\nA move task order can have multiple payment requests, and\na final payment request can be marked using boolean ` + "`" + `isFinal` + "`" + `.\n\nIf a ` + "`" + `PENDING` + "`" + ` payment request is recalculated,\na new payment request is created and the original request is\nmarked with the status ` + "`" + `DEPRECATED` + "`" + `.\n\n**NOTE**: In order to create a payment request for most service items, the shipment *must*\nbe updated with the ` + "`" + `PrimeActualWeight` + "`" + ` value via [updateMTOShipment](#operation/updateMTOShipment).\n\nIf ` + "`" + `WeightBilled` + "`" + ` is not provided then the full shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) will be considered in the calculation.\n\n**NOTE**: Diversions have a unique calcuation for payment requests without a ` + "`" + `WeightBilled` + "`" + ` parameter.\n\nIf you created a payment request for a diversion and ` + "`" + `WeightBilled` + "`" + ` is not provided, then the following will be used in the calculation:\n- The lowest shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) found in the diverted shipment chain.\n- The lowest reweigh weight found in the diverted shipment chain.\n\nThe diverted shipment chain is created by referencing the ` + "`" + `diversion` + "`" + ` boolean, ` + "`" + `divertedFromShipmentId` + "`" + ` UUID, and matching destination to pickup addresses.\nIf the chain cannot be established it will fall back to the ` + "`" + `PrimeActualWeight` + "`" + ` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations.\nThe lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found.\n\nA service item can be on several payment requests in the case of partial payment requests and payments.\n\nIn the request, if no params are necessary, then just the ` + "`" + `serviceItem` + "`" + ` ` + "`" + `id` + "`" + ` is required. For example:\n` + "`" + `` + "`" + `` + "`" + `json\n{\n \"isFinal\": false,\n \"moveTaskOrderID\": \"uuid\",\n \"serviceItems\": [\n {\n \"id\": \"uuid\",\n },\n {\n \"id\": \"uuid\",\n \"params\": [\n {\n \"key\": \"Service Item Parameter Name\",\n \"value\": \"Service Item Parameter Value\"\n }\n ]\n }\n ],\n \"pointOfContact\": \"string\"\n}\n` + "`" + `` + "`" + `` + "`" + `\n\nDomestic Basic Service Items \u0026 Accepted Payment Request Parameters:\n---\n\n**DLH - Domestic Linehaul**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DSH - Domestic Shorthaul**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**FSC - Fuel Surcharge**\n**NOTE**: FSC requires ` + "`" + `ActualPickupDate` + "`" + ` to be updated on the shipment.\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DUPK - Domestic Unpacking**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DPK - Domestic Packing**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DNPK - Domestic NTS Packing**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DPK - Domestic Packing**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOP - Domestic Origin Price**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDP - Domestic Destination Price**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\nDomestic SIT Service Items \u0026 Accepted Payment Request Parameters:\n---\n\n**DOFSIT - Domestic origin 1st day SIT**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOASIT - Domestic origin add'l SIT** *(SITPaymentRequestStart \u0026 SITPaymentRequestEnd are **REQUIRED**)*\n*To create a paymentRequest for this service item, the ` + "`" + `SITPaymentRequestStart` + "`" + ` and ` + "`" + `SITPaymentRequestEnd` + "`" + ` dates must not overlap previously requested SIT dates.*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n },\n {\n \"key\": \"SITPaymentRequestStart\",\n \"value\": \"date\"\n },\n {\n \"key\": \"SITPaymentRequestEnd\",\n \"value\": \"date\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOPSIT - Domestic origin SIT pickup**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DOSHUT - Domestic origin shuttle service**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDFSIT - Domestic destination 1st day SIT**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDASIT - Domestic destination add'l SIT** *(SITPaymentRequestStart \u0026 SITPaymentRequestEnd are **REQUIRED**)*\n*To create a paymentRequest for this service item, the ` + "`" + `SITPaymentRequestStart` + "`" + ` and ` + "`" + `SITPaymentRequestEnd` + "`" + ` dates must not overlap previously requested SIT dates.*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n },\n {\n \"key\": \"SITPaymentRequestStart\",\n \"value\": \"date\"\n },\n {\n \"key\": \"SITPaymentRequestEnd\",\n \"value\": \"date\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDDSIT - Domestic destination SIT delivery**\n*To create a paymentRequest for this service item, it must first have a final address set via [updateMTOServiceItem](#operation/updateMTOServiceItem).*\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**DDSHUT - Domestic destination shuttle service**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n---\n\nInternational Basic Service Items \u0026 Accepted Payment Request Parameters:\n---\nJust like domestic shipments \u0026 service items, if ` + "`" + `WeightBilled` + "`" + ` is not provided then the full shipment weight (` + "`" + `PrimeActualWeight` + "`" + `) will be considered in the calculation.\n**NOTE**: ` + "`" + `POEFSC` + "`" + ` \u0026 ` + "`" + `PODFSC` + "`" + ` service items must have a port associated on the service item in order to successfully add it to a payment request. To update the port of a service item, you must use the (#operation/updateMTOServiceItem) endpoint.\n\n**ISLH - International Shipping \u0026 Linehaul**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**IHPK - International HHG Pack**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**IHUPK - International HHG Unpack**\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**POEFSC - International Port of Embarkation Fuel Surcharge**\n **NOTE**: POEFSC requires ` + "`" + `ActualPickupDate` + "`" + ` to be updated on the shipment \u0026 ` + "`" + `POELocation` + "`" + ` on the service item.\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n\n**PODFSC - International Port of Debarkation Fuel Surcharge**\n**NOTE**: PODFSC requires ` + "`" + `ActualPickupDate` + "`" + ` to be updated on the shipment \u0026 ` + "`" + `PODLocation` + "`" + ` on the service item.\n` + "`" + `` + "`" + `` + "`" + `json\n \"params\": [\n {\n \"key\": \"WeightBilled\",\n \"value\": \"integer\"\n }\n ]\n` + "`" + `` + "`" + `` + "`" + `\n---\n", "consumes": [ "application/json" ], diff --git a/pkg/gen/primeapi/primeoperations/payment_request/create_payment_request.go b/pkg/gen/primeapi/primeoperations/payment_request/create_payment_request.go index 1e98d93abe7..d1ca6a38ab8 100644 --- a/pkg/gen/primeapi/primeoperations/payment_request/create_payment_request.go +++ b/pkg/gen/primeapi/primeoperations/payment_request/create_payment_request.go @@ -45,7 +45,17 @@ marked with the status `DEPRECATED`. **NOTE**: In order to create a payment request for most service items, the shipment *must* be updated with the `PrimeActualWeight` value via [updateMTOShipment](#operation/updateMTOShipment). -**FSC - Fuel Surcharge** service items require `ActualPickupDate` to be updated on the shipment. +If `WeightBilled` is not provided then the full shipment weight (`PrimeActualWeight`) will be considered in the calculation. + +**NOTE**: Diversions have a unique calcuation for payment requests without a `WeightBilled` parameter. + +If you created a payment request for a diversion and `WeightBilled` is not provided, then the following will be used in the calculation: +- The lowest shipment weight (`PrimeActualWeight`) found in the diverted shipment chain. +- The lowest reweigh weight found in the diverted shipment chain. + +The diverted shipment chain is created by referencing the `diversion` boolean, `divertedFromShipmentId` UUID, and matching destination to pickup addresses. +If the chain cannot be established it will fall back to the `PrimeActualWeight` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations. +The lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found. A service item can be on several payment requests in the case of partial payment requests and payments. @@ -74,19 +84,120 @@ In the request, if no params are necessary, then just the `serviceItem` `id` is ``` -SIT Service Items & Accepted Payment Request Parameters: +Domestic Basic Service Items & Accepted Payment Request Parameters: --- -If `WeightBilled` is not provided then the full shipment weight (`PrimeActualWeight`) will be considered in the calculation. -**NOTE**: Diversions have a unique calcuation for payment requests without a `WeightBilled` parameter. +**DLH - Domestic Linehaul** +```json -If you created a payment request for a diversion and `WeightBilled` is not provided, then the following will be used in the calculation: -- The lowest shipment weight (`PrimeActualWeight`) found in the diverted shipment chain. -- The lowest reweigh weight found in the diverted shipment chain. + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] -The diverted shipment chain is created by referencing the `diversion` boolean, `divertedFromShipmentId` UUID, and matching destination to pickup addresses. -If the chain cannot be established it will fall back to the `PrimeActualWeight` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations. -The lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found. +``` + +**DSH - Domestic Shorthaul** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**FSC - Fuel Surcharge** +**NOTE**: FSC requires `ActualPickupDate` to be updated on the shipment. +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DUPK - Domestic Unpacking** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DPK - Domestic Packing** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DNPK - Domestic NTS Packing** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DPK - Domestic Packing** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DOP - Domestic Origin Price** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DDP - Domestic Destination Price** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +Domestic SIT Service Items & Accepted Payment Request Parameters: +--- **DOFSIT - Domestic origin 1st day SIT** ```json @@ -201,6 +312,76 @@ The lowest weight found is the true shipment weight, and thus we search the chai } ] +``` +--- + +International Basic Service Items & Accepted Payment Request Parameters: +--- +Just like domestic shipments & service items, if `WeightBilled` is not provided then the full shipment weight (`PrimeActualWeight`) will be considered in the calculation. +**NOTE**: `POEFSC` & `PODFSC` service items must have a port associated on the service item in order to successfully add it to a payment request. To update the port of a service item, you must use the (#operation/updateMTOServiceItem) endpoint. + +**ISLH - International Shipping & Linehaul** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**IHPK - International HHG Pack** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**IHUPK - International HHG Unpack** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**POEFSC - International Port of Embarkation Fuel Surcharge** + + **NOTE**: POEFSC requires `ActualPickupDate` to be updated on the shipment & `POELocation` on the service item. + +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**PODFSC - International Port of Debarkation Fuel Surcharge** +**NOTE**: PODFSC requires `ActualPickupDate` to be updated on the shipment & `PODLocation` on the service item. +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` --- */ diff --git a/pkg/gen/primeclient/payment_request/payment_request_client.go b/pkg/gen/primeclient/payment_request/payment_request_client.go index 82eae72610e..b10b46d87cf 100644 --- a/pkg/gen/primeclient/payment_request/payment_request_client.go +++ b/pkg/gen/primeclient/payment_request/payment_request_client.go @@ -52,7 +52,17 @@ marked with the status `DEPRECATED`. **NOTE**: In order to create a payment request for most service items, the shipment *must* be updated with the `PrimeActualWeight` value via [updateMTOShipment](#operation/updateMTOShipment). -**FSC - Fuel Surcharge** service items require `ActualPickupDate` to be updated on the shipment. +If `WeightBilled` is not provided then the full shipment weight (`PrimeActualWeight`) will be considered in the calculation. + +**NOTE**: Diversions have a unique calcuation for payment requests without a `WeightBilled` parameter. + +If you created a payment request for a diversion and `WeightBilled` is not provided, then the following will be used in the calculation: +- The lowest shipment weight (`PrimeActualWeight`) found in the diverted shipment chain. +- The lowest reweigh weight found in the diverted shipment chain. + +The diverted shipment chain is created by referencing the `diversion` boolean, `divertedFromShipmentId` UUID, and matching destination to pickup addresses. +If the chain cannot be established it will fall back to the `PrimeActualWeight` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations. +The lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found. A service item can be on several payment requests in the case of partial payment requests and payments. @@ -81,19 +91,120 @@ In the request, if no params are necessary, then just the `serviceItem` `id` is ``` -SIT Service Items & Accepted Payment Request Parameters: +Domestic Basic Service Items & Accepted Payment Request Parameters: --- -If `WeightBilled` is not provided then the full shipment weight (`PrimeActualWeight`) will be considered in the calculation. -**NOTE**: Diversions have a unique calcuation for payment requests without a `WeightBilled` parameter. +**DLH - Domestic Linehaul** +```json -If you created a payment request for a diversion and `WeightBilled` is not provided, then the following will be used in the calculation: -- The lowest shipment weight (`PrimeActualWeight`) found in the diverted shipment chain. -- The lowest reweigh weight found in the diverted shipment chain. + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] -The diverted shipment chain is created by referencing the `diversion` boolean, `divertedFromShipmentId` UUID, and matching destination to pickup addresses. -If the chain cannot be established it will fall back to the `PrimeActualWeight` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations. -The lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found. +``` + +**DSH - Domestic Shorthaul** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**FSC - Fuel Surcharge** +**NOTE**: FSC requires `ActualPickupDate` to be updated on the shipment. +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DUPK - Domestic Unpacking** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DPK - Domestic Packing** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DNPK - Domestic NTS Packing** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DPK - Domestic Packing** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DOP - Domestic Origin Price** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**DDP - Domestic Destination Price** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +Domestic SIT Service Items & Accepted Payment Request Parameters: +--- **DOFSIT - Domestic origin 1st day SIT** ```json @@ -208,6 +319,76 @@ The lowest weight found is the true shipment weight, and thus we search the chai } ] +``` +--- + +International Basic Service Items & Accepted Payment Request Parameters: +--- +Just like domestic shipments & service items, if `WeightBilled` is not provided then the full shipment weight (`PrimeActualWeight`) will be considered in the calculation. +**NOTE**: `POEFSC` & `PODFSC` service items must have a port associated on the service item in order to successfully add it to a payment request. To update the port of a service item, you must use the (#operation/updateMTOServiceItem) endpoint. + +**ISLH - International Shipping & Linehaul** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**IHPK - International HHG Pack** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**IHUPK - International HHG Unpack** +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**POEFSC - International Port of Embarkation Fuel Surcharge** + + **NOTE**: POEFSC requires `ActualPickupDate` to be updated on the shipment & `POELocation` on the service item. + +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + +``` + +**PODFSC - International Port of Debarkation Fuel Surcharge** +**NOTE**: PODFSC requires `ActualPickupDate` to be updated on the shipment & `PODLocation` on the service item. +```json + + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` --- */ diff --git a/swagger-def/prime.yaml b/swagger-def/prime.yaml index 5fcb6cdd5e3..61f19f25a93 100644 --- a/swagger-def/prime.yaml +++ b/swagger-def/prime.yaml @@ -952,7 +952,17 @@ paths: **NOTE**: In order to create a payment request for most service items, the shipment *must* be updated with the `PrimeActualWeight` value via [updateMTOShipment](#operation/updateMTOShipment). - **FSC - Fuel Surcharge** service items require `ActualPickupDate` to be updated on the shipment. + If `WeightBilled` is not provided then the full shipment weight (`PrimeActualWeight`) will be considered in the calculation. + + **NOTE**: Diversions have a unique calcuation for payment requests without a `WeightBilled` parameter. + + If you created a payment request for a diversion and `WeightBilled` is not provided, then the following will be used in the calculation: + - The lowest shipment weight (`PrimeActualWeight`) found in the diverted shipment chain. + - The lowest reweigh weight found in the diverted shipment chain. + + The diverted shipment chain is created by referencing the `diversion` boolean, `divertedFromShipmentId` UUID, and matching destination to pickup addresses. + If the chain cannot be established it will fall back to the `PrimeActualWeight` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations. + The lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found. A service item can be on several payment requests in the case of partial payment requests and payments. @@ -979,19 +989,102 @@ paths: } ``` - SIT Service Items & Accepted Payment Request Parameters: + Domestic Basic Service Items & Accepted Payment Request Parameters: --- - If `WeightBilled` is not provided then the full shipment weight (`PrimeActualWeight`) will be considered in the calculation. - **NOTE**: Diversions have a unique calcuation for payment requests without a `WeightBilled` parameter. + **DLH - Domestic Linehaul** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` - If you created a payment request for a diversion and `WeightBilled` is not provided, then the following will be used in the calculation: - - The lowest shipment weight (`PrimeActualWeight`) found in the diverted shipment chain. - - The lowest reweigh weight found in the diverted shipment chain. + **DSH - Domestic Shorthaul** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` - The diverted shipment chain is created by referencing the `diversion` boolean, `divertedFromShipmentId` UUID, and matching destination to pickup addresses. - If the chain cannot be established it will fall back to the `PrimeActualWeight` of the current shipment. This is utilized because diverted shipments are all one single shipment, but going to different locations. - The lowest weight found is the true shipment weight, and thus we search the chain of shipments for the lowest weight found. + **FSC - Fuel Surcharge** + **NOTE**: FSC requires `ActualPickupDate` to be updated on the shipment. + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **DUPK - Domestic Unpacking** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **DPK - Domestic Packing** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **DNPK - Domestic NTS Packing** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **DPK - Domestic Packing** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **DOP - Domestic Origin Price** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **DDP - Domestic Destination Price** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + Domestic SIT Service Items & Accepted Payment Request Parameters: + --- **DOFSIT - Domestic origin 1st day SIT** ```json @@ -1092,6 +1185,64 @@ paths: ] ``` --- + + International Basic Service Items & Accepted Payment Request Parameters: + --- + Just like domestic shipments & service items, if `WeightBilled` is not provided then the full shipment weight (`PrimeActualWeight`) will be considered in the calculation. + **NOTE**: `POEFSC` & `PODFSC` service items must have a port associated on the service item in order to successfully add it to a payment request. To update the port of a service item, you must use the (#operation/updateMTOServiceItem) endpoint. + + **ISLH - International Shipping & Linehaul** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **IHPK - International HHG Pack** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **IHUPK - International HHG Unpack** + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **POEFSC - International Port of Embarkation Fuel Surcharge** + **NOTE**: POEFSC requires `ActualPickupDate` to be updated on the shipment & `POELocation` on the service item. + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + **PODFSC - International Port of Debarkation Fuel Surcharge** + **NOTE**: PODFSC requires `ActualPickupDate` to be updated on the shipment & `PODLocation` on the service item. + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + --- operationId: createPaymentRequest tags: - paymentRequest diff --git a/swagger/prime.yaml b/swagger/prime.yaml index 83ef52b06c7..2f7082e307b 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -1214,8 +1214,34 @@ paths: [updateMTOShipment](#operation/updateMTOShipment). - **FSC - Fuel Surcharge** service items require `ActualPickupDate` to be - updated on the shipment. + If `WeightBilled` is not provided then the full shipment weight + (`PrimeActualWeight`) will be considered in the calculation. + + + **NOTE**: Diversions have a unique calcuation for payment requests + without a `WeightBilled` parameter. + + + If you created a payment request for a diversion and `WeightBilled` is + not provided, then the following will be used in the calculation: + + - The lowest shipment weight (`PrimeActualWeight`) found in the diverted + shipment chain. + + - The lowest reweigh weight found in the diverted shipment chain. + + + The diverted shipment chain is created by referencing the `diversion` + boolean, `divertedFromShipmentId` UUID, and matching destination to + pickup addresses. + + If the chain cannot be established it will fall back to the + `PrimeActualWeight` of the current shipment. This is utilized because + diverted shipments are all one single shipment, but going to different + locations. + + The lowest weight found is the true shipment weight, and thus we search + the chain of shipments for the lowest weight found. A service item can be on several payment requests in the case of partial @@ -1250,38 +1276,124 @@ paths: ``` - SIT Service Items & Accepted Payment Request Parameters: + Domestic Basic Service Items & Accepted Payment Request Parameters: --- - If `WeightBilled` is not provided then the full shipment weight - (`PrimeActualWeight`) will be considered in the calculation. + **DLH - Domestic Linehaul** - **NOTE**: Diversions have a unique calcuation for payment requests - without a `WeightBilled` parameter. + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` - If you created a payment request for a diversion and `WeightBilled` is - not provided, then the following will be used in the calculation: + **DSH - Domestic Shorthaul** - - The lowest shipment weight (`PrimeActualWeight`) found in the diverted - shipment chain. + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` - - The lowest reweigh weight found in the diverted shipment chain. + **FSC - Fuel Surcharge** - The diverted shipment chain is created by referencing the `diversion` - boolean, `divertedFromShipmentId` UUID, and matching destination to - pickup addresses. + **NOTE**: FSC requires `ActualPickupDate` to be updated on the shipment. - If the chain cannot be established it will fall back to the - `PrimeActualWeight` of the current shipment. This is utilized because - diverted shipments are all one single shipment, but going to different - locations. + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + **DUPK - Domestic Unpacking** + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + **DPK - Domestic Packing** + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + **DNPK - Domestic NTS Packing** + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + **DPK - Domestic Packing** + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` - The lowest weight found is the true shipment weight, and thus we search - the chain of shipments for the lowest weight found. + + **DOP - Domestic Origin Price** + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + **DDP - Domestic Destination Price** + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + Domestic SIT Service Items & Accepted Payment Request Parameters: + + --- **DOFSIT - Domestic origin 1st day SIT** @@ -1410,6 +1522,85 @@ paths: ``` --- + + + International Basic Service Items & Accepted Payment Request Parameters: + + --- + + Just like domestic shipments & service items, if `WeightBilled` is not + provided then the full shipment weight (`PrimeActualWeight`) will be + considered in the calculation. + + **NOTE**: `POEFSC` & `PODFSC` service items must have a port associated + on the service item in order to successfully add it to a payment + request. To update the port of a service item, you must use the + (#operation/updateMTOServiceItem) endpoint. + + + **ISLH - International Shipping & Linehaul** + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + **IHPK - International HHG Pack** + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + **IHUPK - International HHG Unpack** + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + **POEFSC - International Port of Embarkation Fuel Surcharge** + **NOTE**: POEFSC requires `ActualPickupDate` to be updated on the shipment & `POELocation` on the service item. + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + + **PODFSC - International Port of Debarkation Fuel Surcharge** + + **NOTE**: PODFSC requires `ActualPickupDate` to be updated on the + shipment & `PODLocation` on the service item. + + ```json + "params": [ + { + "key": "WeightBilled", + "value": "integer" + } + ] + ``` + + --- operationId: createPaymentRequest tags: - paymentRequest From 4890576da4e3179d2e4073491ef8ca81e2869374 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Mon, 6 Jan 2025 16:56:13 +0000 Subject: [PATCH 082/113] adding logic to use port ZIP for international shipments and ZIP lookups --- ...aram_values_to_service_params_table.up.sql | 2 +- pkg/models/service_item_param_key.go | 8 +-- .../distance_zip_lookup.go | 24 ++++++- .../distance_zip_lookup_test.go | 62 +++++++++++++++++++ ...port_name_lookup.go => port_zip_lookup.go} | 15 ++--- ...lookup_test.go => port_zip_lookup_test.go} | 18 +++--- .../service_param_value_lookups.go | 4 +- .../service_param_value_lookups_test.go | 8 +++ .../intl_port_fuel_surcharge_pricer.go | 2 +- .../intl_port_fuel_surcharge_pricer_test.go | 6 +- 10 files changed, 120 insertions(+), 29 deletions(-) rename pkg/payment_request/service_param_value_lookups/{port_name_lookup.go => port_zip_lookup.go} (50%) rename pkg/payment_request/service_param_value_lookups/{port_name_lookup_test.go => port_zip_lookup_test.go} (83%) diff --git a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql index 859bbcb47b6..1186b75a7b2 100644 --- a/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql +++ b/migrations/app/schema/20241226173330_add_intl_param_values_to_service_params_table.up.sql @@ -3,7 +3,7 @@ DROP FUNCTION IF EXISTS get_rate_area_id(UUID, UUID); -- need to add in param keys for international shipments, this will be used to show breakdowns to the TIO INSERT INTO service_item_param_keys (id, key,description,type,origin,created_at,updated_at) VALUES - ('d9ad3878-4b94-4722-bbaf-d4b8080f339d','PortName','Name of the port for an international shipment','STRING','SYSTEM','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957'), + ('d9ad3878-4b94-4722-bbaf-d4b8080f339d','PortZip','ZIP of the port for an international shipment pickup or destination port','STRING','SYSTEM','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957'), ('597bb77e-0ce7-4ba2-9624-24300962625f','PerUnitCents','Per unit cents for a service item','INTEGER','SYSTEM','2024-12-26 15:55:50.041957','2024-12-26 15:55:50.041957'); -- inserting params for PODFSC diff --git a/pkg/models/service_item_param_key.go b/pkg/models/service_item_param_key.go index 868c6785b55..0c637cc7d92 100644 --- a/pkg/models/service_item_param_key.go +++ b/pkg/models/service_item_param_key.go @@ -63,8 +63,8 @@ const ( ServiceItemParamNameNumberDaysSIT ServiceItemParamName = "NumberDaysSIT" // ServiceItemParamNamePerUnitCents is the param key name PerUnitCents ServiceItemParamNamePerUnitCents ServiceItemParamName = "PerUnitCents" - // ServiceItemParamNamePortName is the param key name PortName - ServiceItemParamNamePortName ServiceItemParamName = "PortName" + // ServiceItemParamNamePortZip is the param key name PortZip + ServiceItemParamNamePortZip ServiceItemParamName = "PortZip" // ServiceItemParamNamePriceAreaDest is the param key name PriceAreaDest ServiceItemParamNamePriceAreaDest ServiceItemParamName = "PriceAreaDest" // ServiceItemParamNamePriceAreaIntlDest is the param key name PriceAreaIntlDest @@ -280,7 +280,7 @@ var ValidServiceItemParamNames = []ServiceItemParamName{ ServiceItemParamNameUncappedRequestTotal, ServiceItemParamNameLockedPriceCents, ServiceItemParamNamePerUnitCents, - ServiceItemParamNamePortName, + ServiceItemParamNamePortZip, } // ValidServiceItemParamNameStrings lists all valid service item param key names @@ -356,7 +356,7 @@ var ValidServiceItemParamNameStrings = []string{ string(ServiceItemParamNameUncappedRequestTotal), string(ServiceItemParamNameLockedPriceCents), string(ServiceItemParamNamePerUnitCents), - string(ServiceItemParamNamePortName), + string(ServiceItemParamNamePortZip), } // ValidServiceItemParamTypes lists all valid service item param types diff --git a/pkg/payment_request/service_param_value_lookups/distance_zip_lookup.go b/pkg/payment_request/service_param_value_lookups/distance_zip_lookup.go index 7b70c559d1d..7f05aa59ee9 100644 --- a/pkg/payment_request/service_param_value_lookups/distance_zip_lookup.go +++ b/pkg/payment_request/service_param_value_lookups/distance_zip_lookup.go @@ -49,6 +49,25 @@ func (r DistanceZipLookup) lookup(appCtx appcontext.AppContext, keyData *Service // Now calculate the distance between zips pickupZip := r.PickupAddress.PostalCode destinationZip := r.DestinationAddress.PostalCode + + // if the shipment is international, we need to change the respective ZIP to use the port ZIP and not the address ZIP + if mtoShipment.MarketCode == models.MarketCodeInternational { + portZip, portType, err := models.GetPortLocationInfoForShipment(appCtx.DB(), *mtoShipmentID) + if err != nil { + return "", err + } + if portZip != nil && portType != nil { + // if the port type is POEFSC this means the shipment is CONUS -> OCONUS (pickup -> port) + // if the port type is PODFSC this means the shipment is OCONUS -> CONUS (port -> destination) + if *portType == models.ReServiceCodePOEFSC.String() { + destinationZip = *portZip + } else if *portType == models.ReServiceCodePODFSC.String() { + pickupZip = *portZip + } + } else { + return "", apperror.NewNotFoundError(*mtoShipmentID, "looking for port ZIP for shipment") + } + } errorMsgForPickupZip := fmt.Sprintf("Shipment must have valid pickup zipcode. Received: %s", pickupZip) errorMsgForDestinationZip := fmt.Sprintf("Shipment must have valid destination zipcode. Received: %s", destinationZip) if len(pickupZip) < 5 { @@ -91,14 +110,15 @@ func (r DistanceZipLookup) lookup(appCtx appcontext.AppContext, keyData *Service } } - if mtoShipment.Distance != nil && mtoShipment.ShipmentType != models.MTOShipmentTypePPM { + internationalShipment := mtoShipment.MarketCode == models.MarketCodeInternational + if mtoShipment.Distance != nil && mtoShipment.ShipmentType != models.MTOShipmentTypePPM && !internationalShipment { return strconv.Itoa(mtoShipment.Distance.Int()), nil } if pickupZip == destinationZip { distanceMiles = 1 } else { - distanceMiles, err = planner.ZipTransitDistance(appCtx, pickupZip, destinationZip, false, false) + distanceMiles, err = planner.ZipTransitDistance(appCtx, pickupZip, destinationZip, false, internationalShipment) if err != nil { return "", err } diff --git a/pkg/payment_request/service_param_value_lookups/distance_zip_lookup_test.go b/pkg/payment_request/service_param_value_lookups/distance_zip_lookup_test.go index f40692f2f1c..f4fa18c8790 100644 --- a/pkg/payment_request/service_param_value_lookups/distance_zip_lookup_test.go +++ b/pkg/payment_request/service_param_value_lookups/distance_zip_lookup_test.go @@ -63,6 +63,68 @@ func (suite *ServiceParamValueLookupsSuite) TestDistanceLookup() { suite.Equal(unit.Miles(defaultZipDistance), *mtoShipment.Distance) }) + suite.Run("Calculate transit zip distance for international shipment with port data", func() { + testdatagen.MakeReContractYear(suite.DB(), testdatagen.Assertions{ + ReContractYear: models.ReContractYear{ + StartDate: time.Now().Add(-24 * time.Hour), + EndDate: time.Now().Add(24 * time.Hour), + }, + }) + portLocation := factory.FetchPortLocation(suite.DB(), []factory.Customization{ + { + Model: models.Port{ + PortCode: "SEA", + }, + }, + }, nil) + mtoServiceItem := factory.BuildMTOServiceItem(suite.DB(), []factory.Customization{ + { + Model: models.ReService{ + Code: models.ReServiceCodePOEFSC, + }, + }, + { + Model: models.Address{ + PostalCode: "74133", + }, + Type: &factory.Addresses.PickupAddress, + }, + { + Model: models.MTOServiceItem{ + POELocationID: &portLocation.ID, + }, + }, + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, + }, []factory.Trait{ + factory.GetTraitAvailableToPrimeMove, + }) + + paymentRequest := factory.BuildPaymentRequest(suite.DB(), []factory.Customization{ + { + Model: mtoServiceItem.MoveTaskOrder, + LinkOnly: true, + }, + }, nil) + + paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, paymentRequest.ID, paymentRequest.MoveTaskOrderID, nil) + suite.FatalNoError(err) + + distanceStr, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + suite.FatalNoError(err) + expected := strconv.Itoa(defaultInternationalZipDistance) + suite.Equal(expected, distanceStr) + + var mtoShipment models.MTOShipment + err = suite.DB().Find(&mtoShipment, mtoServiceItem.MTOShipmentID) + suite.NoError(err) + + suite.Equal(unit.Miles(defaultInternationalZipDistance), *mtoShipment.Distance) + }) + suite.Run("Calculate zip distance lookup without a saved service item", func() { ppmShipment := factory.BuildPPMShipment(suite.DB(), nil, nil) diff --git a/pkg/payment_request/service_param_value_lookups/port_name_lookup.go b/pkg/payment_request/service_param_value_lookups/port_zip_lookup.go similarity index 50% rename from pkg/payment_request/service_param_value_lookups/port_name_lookup.go rename to pkg/payment_request/service_param_value_lookups/port_zip_lookup.go index a925ceaa099..3ea8be94315 100644 --- a/pkg/payment_request/service_param_value_lookups/port_name_lookup.go +++ b/pkg/payment_request/service_param_value_lookups/port_zip_lookup.go @@ -9,26 +9,27 @@ import ( "github.com/transcom/mymove/pkg/models" ) -// PortNameLookup does lookup on the shipment and finds the port name -type PortNameLookup struct { +// PortZipLookup does lookup on the shipment and finds the port zip +// The mileage calculated is from port <-> pickup/destination so this value is important +type PortZipLookup struct { ServiceItem models.MTOServiceItem } -func (p PortNameLookup) lookup(appCtx appcontext.AppContext, _ *ServiceItemParamKeyData) (string, error) { +func (p PortZipLookup) lookup(appCtx appcontext.AppContext, _ *ServiceItemParamKeyData) (string, error) { var portLocationID *uuid.UUID if p.ServiceItem.PODLocationID != nil { portLocationID = p.ServiceItem.PODLocationID } else if p.ServiceItem.POELocationID != nil { portLocationID = p.ServiceItem.POELocationID } else { - return "", fmt.Errorf("unable to find port location for service item id: %s", p.ServiceItem.ID) + return "", fmt.Errorf("unable to find port zip for service item id: %s", p.ServiceItem.ID) } var portLocation models.PortLocation err := appCtx.DB().Q(). - EagerPreload("Port"). + EagerPreload("UsPostRegionCity"). Where("id = $1", portLocationID).First(&portLocation) if err != nil { - return "", fmt.Errorf("unable to find port location with id %s", portLocationID) + return "", fmt.Errorf("unable to find port zip with id %s", portLocationID) } - return portLocation.Port.PortName, nil + return portLocation.UsPostRegionCity.UsprZipID, nil } diff --git a/pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go b/pkg/payment_request/service_param_value_lookups/port_zip_lookup_test.go similarity index 83% rename from pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go rename to pkg/payment_request/service_param_value_lookups/port_zip_lookup_test.go index a16b174dc1c..4410ba8e198 100644 --- a/pkg/payment_request/service_param_value_lookups/port_name_lookup_test.go +++ b/pkg/payment_request/service_param_value_lookups/port_zip_lookup_test.go @@ -10,8 +10,8 @@ import ( "github.com/transcom/mymove/pkg/testdatagen" ) -func (suite *ServiceParamValueLookupsSuite) TestPortNameLookup() { - key := models.ServiceItemParamNamePortName +func (suite *ServiceParamValueLookupsSuite) TestPortZipLookup() { + key := models.ServiceItemParamNamePortZip var mtoServiceItem models.MTOServiceItem setupTestData := func(serviceCode models.ReServiceCode, portID uuid.UUID) { testdatagen.MakeReContractYear(suite.DB(), testdatagen.Assertions{ @@ -49,7 +49,7 @@ func (suite *ServiceParamValueLookupsSuite) TestPortNameLookup() { } } - suite.Run("success - returns PortName value for POEFSC", func() { + suite.Run("success - returns PortZip value for POEFSC", func() { port := factory.FetchPortLocation(suite.DB(), []factory.Customization{ { Model: models.Port{ @@ -62,12 +62,12 @@ func (suite *ServiceParamValueLookupsSuite) TestPortNameLookup() { paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) suite.FatalNoError(err) - portName, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + portZip, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) suite.FatalNoError(err) - suite.Equal(portName, port.Port.PortName) + suite.Equal(portZip, port.UsPostRegionCity.UsprZipID) }) - suite.Run("success - returns PortName value for PODFSC", func() { + suite.Run("success - returns PortZip value for PODFSC", func() { port := factory.FetchPortLocation(suite.DB(), []factory.Customization{ { Model: models.Port{ @@ -80,12 +80,12 @@ func (suite *ServiceParamValueLookupsSuite) TestPortNameLookup() { paramLookup, err := ServiceParamLookupInitialize(suite.AppContextForTest(), suite.planner, mtoServiceItem, uuid.Must(uuid.NewV4()), mtoServiceItem.MoveTaskOrderID, nil) suite.FatalNoError(err) - portName, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) + portZip, err := paramLookup.ServiceParamValue(suite.AppContextForTest(), key) suite.FatalNoError(err) - suite.Equal(portName, port.Port.PortName) + suite.Equal(portZip, port.UsPostRegionCity.UsprZipID) }) - suite.Run("failure - no port value on service item", func() { + suite.Run("failure - no port zip on service item", func() { testdatagen.MakeReContractYear(suite.DB(), testdatagen.Assertions{ ReContractYear: models.ReContractYear{ StartDate: time.Now().Add(-24 * time.Hour), diff --git a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go index c3669e5cb41..33775af842b 100644 --- a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go +++ b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups.go @@ -87,7 +87,7 @@ var ServiceItemParamsWithLookups = []models.ServiceItemParamName{ models.ServiceItemParamNameStandaloneCrateCap, models.ServiceItemParamNameLockedPriceCents, models.ServiceItemParamNamePerUnitCents, - models.ServiceItemParamNamePortName, + models.ServiceItemParamNamePortZip, } // ServiceParamLookupInitialize initializes service parameter lookup @@ -439,7 +439,7 @@ func InitializeLookups(appCtx appcontext.AppContext, shipment models.MTOShipment MTOShipment: shipment, } - lookups[models.ServiceItemParamNamePortName] = PortNameLookup{ + lookups[models.ServiceItemParamNamePortZip] = PortZipLookup{ ServiceItem: serviceItem, } diff --git a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups_test.go b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups_test.go index 1c9138e51ee..7b8307f147c 100644 --- a/pkg/payment_request/service_param_value_lookups/service_param_value_lookups_test.go +++ b/pkg/payment_request/service_param_value_lookups/service_param_value_lookups_test.go @@ -29,6 +29,7 @@ import ( ) const defaultZipDistance = 1234 +const defaultInternationalZipDistance = 1800 type ServiceParamValueLookupsSuite struct { *testingsuite.PopTestSuite @@ -49,6 +50,13 @@ func TestServiceParamValueLookupsSuite(t *testing.T) { false, false, ).Return(defaultZipDistance, nil) + planner.On("ZipTransitDistance", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + mock.Anything, + false, + true, + ).Return(defaultInternationalZipDistance, nil) ts := &ServiceParamValueLookupsSuite{ PopTestSuite: testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), testingsuite.WithPerTestTransaction()), diff --git a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go index 3e6dcfe1bdd..e970f29a1c0 100644 --- a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go +++ b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer.go @@ -96,7 +96,7 @@ func (p portFuelSurchargePricer) PriceUsingParams(appCtx appcontext.AppContext, return unit.Cents(0), nil, err } - _, err = getParamString(params, models.ServiceItemParamNamePortName) + _, err = getParamString(params, models.ServiceItemParamNamePortZip) if err != nil { return unit.Cents(0), nil, err } diff --git a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go index fa660bd796d..ce64f248c22 100644 --- a/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go +++ b/pkg/services/ghcrateengine/intl_port_fuel_surcharge_pricer_test.go @@ -20,7 +20,7 @@ const ( intlPortFscWeightDistanceMultiplier = float64(0.000417) intlPortFscFuelPrice = unit.Millicents(281400) intlPortFscPriceCents = unit.Cents(2980) - intlPortFscPortName = "PORTLAND INTL" + intlPortFscPortZip = "99505" ) var intlPortFscActualPickupDate = time.Date(testdatagen.TestYear, time.June, 5, 7, 33, 11, 456, time.UTC) @@ -186,9 +186,9 @@ func (suite *GHCRateEngineServiceSuite) setupPortFuelSurchargeServiceItem() mode Value: fmt.Sprintf("%d", int(intlPortFscFuelPrice)), }, { - Key: models.ServiceItemParamNamePortName, + Key: models.ServiceItemParamNamePortZip, KeyType: models.ServiceItemParamTypeString, - Value: intlPortFscPortName, + Value: intlPortFscPortZip, }, }, nil, nil, ) From 24cf97188d8e910911fa109cc57712a1a2a3657e Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 20 Dec 2024 22:39:44 +0000 Subject: [PATCH 083/113] initial commit, functonality added, tests added --- migrations/app/migrations_manifest.txt | 1 + ...4_add_destination_gbloc_db_function.up.sql | 85 ++++++++++++++++ pkg/gen/ghcapi/embedded_spec.go | 10 ++ pkg/gen/ghcmessages/address.go | 20 ++++ pkg/gen/internalapi/embedded_spec.go | 10 ++ pkg/gen/internalmessages/address.go | 20 ++++ pkg/gen/pptasapi/embedded_spec.go | 10 ++ pkg/gen/pptasmessages/address.go | 20 ++++ pkg/gen/primeapi/embedded_spec.go | 10 ++ pkg/gen/primemessages/address.go | 20 ++++ pkg/gen/primev2api/embedded_spec.go | 10 ++ pkg/gen/primev2messages/address.go | 20 ++++ pkg/gen/primev3api/embedded_spec.go | 10 ++ pkg/gen/primev3messages/address.go | 20 ++++ .../primeapiv3/payloads/model_to_payload.go | 21 ++-- .../payloads/model_to_payload_test.go | 53 +++++----- pkg/models/address.go | 1 + pkg/models/mto_shipments.go | 20 ++++ pkg/models/mto_shipments_test.go | 97 +++++++++++++++++++ .../move_task_order_fetcher.go | 12 +++ .../move_task_order_fetcher_test.go | 57 +++++++++++ swagger-def/definitions/Address.yaml | 4 + swagger/ghc.yaml | 4 + swagger/internal.yaml | 4 + swagger/pptas.yaml | 4 + swagger/prime.yaml | 4 + swagger/prime_v2.yaml | 4 + swagger/prime_v3.yaml | 4 + 28 files changed, 521 insertions(+), 34 deletions(-) create mode 100644 migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index 32afaa072da..222544bddb1 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1058,6 +1058,7 @@ 20241217180136_add_AK_zips_to_zip3_distances.up.sql 20241218201833_add_PPPO_BASE_ELIZABETH.up.sql 20241220171035_add_additional_AK_zips_to_zip3_distances.up.sql +20241220213134_add_destination_gbloc_db_function.up.sql 20241227153723_remove_empty_string_emplid_values.up.sql 20241227202424_insert_transportation_offices_camp_pendelton.up.sql 20241230190638_remove_AK_zips_from_zip3.up.sql diff --git a/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql b/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql new file mode 100644 index 00000000000..bd7d9b1db5d --- /dev/null +++ b/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql @@ -0,0 +1,85 @@ +-- this function will handle getting the destination GBLOC associated with a shipment's destination address +-- this only applies to OCONUS destination addresses on a shipment, but this also checks domestic shipments +CREATE OR REPLACE FUNCTION get_destination_gbloc_for_shipment(shipment_id UUID) +RETURNS TEXT AS $$ +DECLARE + service_member_affiliation TEXT; + zip TEXT; + gbloc_result TEXT; + alaska_zone_ii BOOLEAN; + market_code TEXT; +BEGIN + -- get the shipment's market_code + SELECT ms.market_code + INTO market_code + FROM mto_shipments ms + WHERE ms.id = shipment_id; + + -- if it's a domestic shipment, use postal_code_to_gblocs + IF market_code = 'd' THEN + SELECT upc.uspr_zip_id + INTO zip + FROM addresses a + JOIN us_post_region_cities upc ON a.us_post_region_cities_id = upc.id + WHERE a.id = (SELECT destination_address_id FROM mto_shipments WHERE id = shipment_id); + + SELECT gbloc + INTO gbloc_result + FROM postal_code_to_gblocs + WHERE postal_code = zip + LIMIT 1; + + IF gbloc_result IS NULL THEN + RETURN NULL; + END IF; + + RETURN gbloc_result; + + ELSEIF market_code = 'i' THEN + -- if it's 'i' then we need to check for some exceptions + SELECT sm.affiliation + INTO service_member_affiliation + FROM service_members sm + JOIN orders o ON o.service_member_id = sm.id + JOIN moves m ON m.orders_id = o.id + JOIN mto_shipments ms ON ms.move_id = m.id + WHERE ms.id = shipment_id; + + SELECT upc.uspr_zip_id + INTO zip + FROM addresses a + JOIN us_post_region_cities upc ON a.us_post_region_cities_id = upc.id + WHERE a.id = (SELECT destination_address_id FROM mto_shipments WHERE id = shipment_id); + + -- check if the postal code (uspr_zip_id) is in Alaska Zone II + SELECT EXISTS ( + SELECT 1 + FROM re_oconus_rate_areas ro + JOIN re_rate_areas ra ON ro.rate_area_id = ra.id + JOIN us_post_region_cities upc ON upc.id = ro.us_post_region_cities_id + WHERE upc.uspr_zip_id = zip + AND ra.code = 'US8190100' -- Alaska Zone II Code + ) + INTO alaska_zone_ii; + + -- if the service member is USAF or USSF and the address is in Alaska Zone II, return 'MBFL' + IF (service_member_affiliation = 'AIR_FORCE' OR service_member_affiliation = 'SPACE_FORCE') AND alaska_zone_ii THEN + RETURN 'MBFL'; + END IF; + + -- for all other branches except USMC, return the gbloc from the postal_code_to_gbloc table based on the zip + SELECT gbloc + INTO gbloc_result + FROM postal_code_to_gblocs + WHERE postal_code = zip + LIMIT 1; + + IF gbloc_result IS NULL THEN + RETURN NULL; + END IF; + + RETURN gbloc_result; + END IF; + +END; +$$ LANGUAGE plpgsql; diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index 361f666c222..3b8e14d3e66 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -6403,6 +6403,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true @@ -23143,6 +23148,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true diff --git a/pkg/gen/ghcmessages/address.go b/pkg/gen/ghcmessages/address.go index 47148e32cf7..42bd1d8d69e 100644 --- a/pkg/gen/ghcmessages/address.go +++ b/pkg/gen/ghcmessages/address.go @@ -36,6 +36,10 @@ type Address struct { // Example: LOS ANGELES County *string `json:"county,omitempty"` + // destination gbloc + // Pattern: ^[A-Z]{4}$ + DestinationGbloc *string `json:"destinationGbloc,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` @@ -91,6 +95,10 @@ func (m *Address) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateDestinationGbloc(formats); err != nil { + res = append(res, err) + } + if err := m.validateID(formats); err != nil { res = append(res, err) } @@ -138,6 +146,18 @@ func (m *Address) validateCountry(formats strfmt.Registry) error { return nil } +func (m *Address) validateDestinationGbloc(formats strfmt.Registry) error { + if swag.IsZero(m.DestinationGbloc) { // not required + return nil + } + + if err := validate.Pattern("destinationGbloc", "body", *m.DestinationGbloc, `^[A-Z]{4}$`); err != nil { + return err + } + + return nil +} + func (m *Address) validateID(formats strfmt.Registry) error { if swag.IsZero(m.ID) { // not required return nil diff --git a/pkg/gen/internalapi/embedded_spec.go b/pkg/gen/internalapi/embedded_spec.go index c1351734062..53aee4aa8cf 100644 --- a/pkg/gen/internalapi/embedded_spec.go +++ b/pkg/gen/internalapi/embedded_spec.go @@ -3363,6 +3363,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true @@ -12482,6 +12487,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true diff --git a/pkg/gen/internalmessages/address.go b/pkg/gen/internalmessages/address.go index 529cc0d7110..733df1c0680 100644 --- a/pkg/gen/internalmessages/address.go +++ b/pkg/gen/internalmessages/address.go @@ -36,6 +36,10 @@ type Address struct { // Example: LOS ANGELES County *string `json:"county,omitempty"` + // destination gbloc + // Pattern: ^[A-Z]{4}$ + DestinationGbloc *string `json:"destinationGbloc,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` @@ -91,6 +95,10 @@ func (m *Address) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateDestinationGbloc(formats); err != nil { + res = append(res, err) + } + if err := m.validateID(formats); err != nil { res = append(res, err) } @@ -138,6 +146,18 @@ func (m *Address) validateCountry(formats strfmt.Registry) error { return nil } +func (m *Address) validateDestinationGbloc(formats strfmt.Registry) error { + if swag.IsZero(m.DestinationGbloc) { // not required + return nil + } + + if err := validate.Pattern("destinationGbloc", "body", *m.DestinationGbloc, `^[A-Z]{4}$`); err != nil { + return err + } + + return nil +} + func (m *Address) validateID(formats strfmt.Registry) error { if swag.IsZero(m.ID) { // not required return nil diff --git a/pkg/gen/pptasapi/embedded_spec.go b/pkg/gen/pptasapi/embedded_spec.go index 1757ac556cc..fc54f37df09 100644 --- a/pkg/gen/pptasapi/embedded_spec.go +++ b/pkg/gen/pptasapi/embedded_spec.go @@ -114,6 +114,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true @@ -1008,6 +1013,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true diff --git a/pkg/gen/pptasmessages/address.go b/pkg/gen/pptasmessages/address.go index 1e53ba6d230..0e5a9af985a 100644 --- a/pkg/gen/pptasmessages/address.go +++ b/pkg/gen/pptasmessages/address.go @@ -36,6 +36,10 @@ type Address struct { // Example: LOS ANGELES County *string `json:"county,omitempty"` + // destination gbloc + // Pattern: ^[A-Z]{4}$ + DestinationGbloc *string `json:"destinationGbloc,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` @@ -91,6 +95,10 @@ func (m *Address) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateDestinationGbloc(formats); err != nil { + res = append(res, err) + } + if err := m.validateID(formats); err != nil { res = append(res, err) } @@ -138,6 +146,18 @@ func (m *Address) validateCountry(formats strfmt.Registry) error { return nil } +func (m *Address) validateDestinationGbloc(formats strfmt.Registry) error { + if swag.IsZero(m.DestinationGbloc) { // not required + return nil + } + + if err := validate.Pattern("destinationGbloc", "body", *m.DestinationGbloc, `^[A-Z]{4}$`); err != nil { + return err + } + + return nil +} + func (m *Address) validateID(formats strfmt.Registry) error { if swag.IsZero(m.ID) { // not required return nil diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index 52039d783cc..9076c994d00 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -1214,6 +1214,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true @@ -6108,6 +6113,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true diff --git a/pkg/gen/primemessages/address.go b/pkg/gen/primemessages/address.go index 2fe5ba87adb..4ff5b6f7932 100644 --- a/pkg/gen/primemessages/address.go +++ b/pkg/gen/primemessages/address.go @@ -36,6 +36,10 @@ type Address struct { // Example: LOS ANGELES County *string `json:"county,omitempty"` + // destination gbloc + // Pattern: ^[A-Z]{4}$ + DestinationGbloc *string `json:"destinationGbloc,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` @@ -91,6 +95,10 @@ func (m *Address) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateDestinationGbloc(formats); err != nil { + res = append(res, err) + } + if err := m.validateID(formats); err != nil { res = append(res, err) } @@ -138,6 +146,18 @@ func (m *Address) validateCountry(formats strfmt.Registry) error { return nil } +func (m *Address) validateDestinationGbloc(formats strfmt.Registry) error { + if swag.IsZero(m.DestinationGbloc) { // not required + return nil + } + + if err := validate.Pattern("destinationGbloc", "body", *m.DestinationGbloc, `^[A-Z]{4}$`); err != nil { + return err + } + + return nil +} + func (m *Address) validateID(formats strfmt.Registry) error { if swag.IsZero(m.ID) { // not required return nil diff --git a/pkg/gen/primev2api/embedded_spec.go b/pkg/gen/primev2api/embedded_spec.go index f0468e10884..f6ffd9298ba 100644 --- a/pkg/gen/primev2api/embedded_spec.go +++ b/pkg/gen/primev2api/embedded_spec.go @@ -399,6 +399,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true @@ -4006,6 +4011,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true diff --git a/pkg/gen/primev2messages/address.go b/pkg/gen/primev2messages/address.go index 631419ea719..2f1631a297c 100644 --- a/pkg/gen/primev2messages/address.go +++ b/pkg/gen/primev2messages/address.go @@ -36,6 +36,10 @@ type Address struct { // Example: LOS ANGELES County *string `json:"county,omitempty"` + // destination gbloc + // Pattern: ^[A-Z]{4}$ + DestinationGbloc *string `json:"destinationGbloc,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` @@ -91,6 +95,10 @@ func (m *Address) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateDestinationGbloc(formats); err != nil { + res = append(res, err) + } + if err := m.validateID(formats); err != nil { res = append(res, err) } @@ -138,6 +146,18 @@ func (m *Address) validateCountry(formats strfmt.Registry) error { return nil } +func (m *Address) validateDestinationGbloc(formats strfmt.Registry) error { + if swag.IsZero(m.DestinationGbloc) { // not required + return nil + } + + if err := validate.Pattern("destinationGbloc", "body", *m.DestinationGbloc, `^[A-Z]{4}$`); err != nil { + return err + } + + return nil +} + func (m *Address) validateID(formats strfmt.Registry) error { if swag.IsZero(m.ID) { // not required return nil diff --git a/pkg/gen/primev3api/embedded_spec.go b/pkg/gen/primev3api/embedded_spec.go index 22f6c879b42..e788d625932 100644 --- a/pkg/gen/primev3api/embedded_spec.go +++ b/pkg/gen/primev3api/embedded_spec.go @@ -405,6 +405,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true @@ -4697,6 +4702,11 @@ func init() { "x-nullable": true, "example": "LOS ANGELES" }, + "destinationGbloc": { + "type": "string", + "pattern": "^[A-Z]{4}$", + "x-nullable": true + }, "eTag": { "type": "string", "readOnly": true diff --git a/pkg/gen/primev3messages/address.go b/pkg/gen/primev3messages/address.go index edffd06b01c..43fbf3bc550 100644 --- a/pkg/gen/primev3messages/address.go +++ b/pkg/gen/primev3messages/address.go @@ -36,6 +36,10 @@ type Address struct { // Example: LOS ANGELES County *string `json:"county,omitempty"` + // destination gbloc + // Pattern: ^[A-Z]{4}$ + DestinationGbloc *string `json:"destinationGbloc,omitempty"` + // e tag // Read Only: true ETag string `json:"eTag,omitempty"` @@ -91,6 +95,10 @@ func (m *Address) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateDestinationGbloc(formats); err != nil { + res = append(res, err) + } + if err := m.validateID(formats); err != nil { res = append(res, err) } @@ -138,6 +146,18 @@ func (m *Address) validateCountry(formats strfmt.Registry) error { return nil } +func (m *Address) validateDestinationGbloc(formats strfmt.Registry) error { + if swag.IsZero(m.DestinationGbloc) { // not required + return nil + } + + if err := validate.Pattern("destinationGbloc", "body", *m.DestinationGbloc, `^[A-Z]{4}$`); err != nil { + return err + } + + return nil +} + func (m *Address) validateID(formats strfmt.Registry) error { if swag.IsZero(m.ID) { // not required return nil diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload.go b/pkg/handlers/primeapiv3/payloads/model_to_payload.go index 4676e9b3d47..2b93dd3480f 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload.go @@ -231,16 +231,17 @@ func Address(address *models.Address) *primev3messages.Address { return nil } return &primev3messages.Address{ - ID: strfmt.UUID(address.ID.String()), - StreetAddress1: &address.StreetAddress1, - StreetAddress2: address.StreetAddress2, - StreetAddress3: address.StreetAddress3, - City: &address.City, - State: &address.State, - PostalCode: &address.PostalCode, - Country: Country(address.Country), - ETag: etag.GenerateEtag(address.UpdatedAt), - County: address.County, + ID: strfmt.UUID(address.ID.String()), + StreetAddress1: &address.StreetAddress1, + StreetAddress2: address.StreetAddress2, + StreetAddress3: address.StreetAddress3, + City: &address.City, + State: &address.State, + PostalCode: &address.PostalCode, + Country: Country(address.Country), + ETag: etag.GenerateEtag(address.UpdatedAt), + County: address.County, + DestinationGbloc: address.DestinationGbloc, } } diff --git a/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go b/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go index e6de847b908..8ef4396aa1d 100644 --- a/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapiv3/payloads/model_to_payload_test.go @@ -144,12 +144,13 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { PostalCode: fairbanksAlaskaPostalCode, }, DestinationAddress: &models.Address{ - StreetAddress1: "123 Main St", - StreetAddress2: &streetAddress2, - StreetAddress3: &streetAddress3, - City: "Anchorage", - State: "AK", - PostalCode: anchorageAlaskaPostalCode, + StreetAddress1: "123 Main St", + StreetAddress2: &streetAddress2, + StreetAddress3: &streetAddress3, + City: "Anchorage", + State: "AK", + PostalCode: anchorageAlaskaPostalCode, + DestinationGbloc: models.StringPointer("JEAT"), }, }) newMove.MTOShipments = append(newMove.MTOShipments, models.MTOShipment{ @@ -162,12 +163,13 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { PostalCode: wasillaAlaskaPostalCode, }, DestinationAddress: &models.Address{ - StreetAddress1: "123 Main St", - StreetAddress2: &streetAddress2, - StreetAddress3: &streetAddress3, - City: "Wasilla", - State: "AK", - PostalCode: wasillaAlaskaPostalCode, + StreetAddress1: "123 Main St", + StreetAddress2: &streetAddress2, + StreetAddress3: &streetAddress3, + City: "Wasilla", + State: "AK", + PostalCode: wasillaAlaskaPostalCode, + DestinationGbloc: models.StringPointer("JEAT"), }, }) newMove.MTOShipments = append(newMove.MTOShipments, models.MTOShipment{ @@ -231,20 +233,22 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { }) newMove.MTOShipments = append(newMove.MTOShipments, models.MTOShipment{ PickupAddress: &models.Address{ - StreetAddress1: "123 Main St", - StreetAddress2: &streetAddress2, - StreetAddress3: &streetAddress3, - City: "Beverly Hills", - State: "CA", - PostalCode: "90210", + StreetAddress1: "123 Main St", + StreetAddress2: &streetAddress2, + StreetAddress3: &streetAddress3, + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + DestinationGbloc: models.StringPointer("JEAT"), }, DestinationAddress: &models.Address{ - StreetAddress1: "123 Main St", - StreetAddress2: &streetAddress2, - StreetAddress3: &streetAddress3, - City: "Beverly Hills", - State: "CA", - PostalCode: "90210", + StreetAddress1: "123 Main St", + StreetAddress2: &streetAddress2, + StreetAddress3: &streetAddress3, + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + DestinationGbloc: models.StringPointer("JEAT"), }, }) @@ -351,6 +355,7 @@ func (suite *PayloadsSuite) TestMoveTaskOrder() { } else { suite.NotNil(shipment.PickupAddress) suite.NotNil(shipment.DestinationAddress) + suite.NotNil(shipment.DestinationAddress.DestinationGbloc) if slices.Contains(expectedAlaskaPostalCodes, *shipment.PickupAddress.PostalCode) { ra, contains := shipmentPostalCodeRateAreaLookupMap[*shipment.PickupAddress.PostalCode] suite.True(contains) diff --git a/pkg/models/address.go b/pkg/models/address.go index e683f7771ab..6042ae53960 100644 --- a/pkg/models/address.go +++ b/pkg/models/address.go @@ -34,6 +34,7 @@ type Address struct { IsOconus *bool `json:"is_oconus" db:"is_oconus"` UsPostRegionCityID *uuid.UUID `json:"us_post_region_cities_id" db:"us_post_region_cities_id"` UsPostRegionCity *UsPostRegionCity `belongs_to:"us_post_region_cities" fk_id:"us_post_region_cities_id"` + DestinationGbloc *string `db:"-"` } // TableName overrides the table name used by Pop. diff --git a/pkg/models/mto_shipments.go b/pkg/models/mto_shipments.go index 9b914f98198..783188addaa 100644 --- a/pkg/models/mto_shipments.go +++ b/pkg/models/mto_shipments.go @@ -415,6 +415,26 @@ func UpdateEstimatedPricingForShipmentBasicServiceItems(db *pop.Connection, ship return nil } +// GetDestinationGblocForShipment gets the GBLOC associated with the shipment's destination address +// there are certain exceptions for OCONUS addresses in Alaska Zone II based on affiliation +func GetDestinationGblocForShipment(db *pop.Connection, shipmentID uuid.UUID) (*string, error) { + var gbloc *string + + err := db.RawQuery("SELECT * FROM get_destination_gbloc_for_shipment($1)", shipmentID). + First(&gbloc) + + if err != nil && err != sql.ErrNoRows { + return nil, fmt.Errorf("error fetching destination gbloc for shipment ID: %s with error %w", shipmentID, err) + } + + // return the ZIP code and port type, or nil if not found + if gbloc != nil { + return gbloc, nil + } + + return nil, nil +} + // Returns a Shipment for a given id func FetchShipmentByID(db *pop.Connection, shipmentID uuid.UUID) (*MTOShipment, error) { var mtoShipment MTOShipment diff --git a/pkg/models/mto_shipments_test.go b/pkg/models/mto_shipments_test.go index 0af9121e013..1b92fbb7035 100644 --- a/pkg/models/mto_shipments_test.go +++ b/pkg/models/mto_shipments_test.go @@ -339,3 +339,100 @@ func (suite *ModelSuite) TestFindShipmentByID() { suite.Equal(models.ErrFetchNotFound, err) }) } + +func (suite *ModelSuite) TestGetDestinationGblocForShipment() { + suite.Run("success - get GBLOC for USAF in AK Zone II", func() { + // Create a USAF move in Alaska Zone II + // this is a hard coded uuid that is a us_post_region_cities_id within AK Zone II + // this should always return MBFL + zone2UUID, err := uuid.FromString("66768964-e0de-41f3-b9be-7ef32e4ae2b4") + suite.FatalNoError(err) + airForce := models.AffiliationAIRFORCE + postalCode := "99501" + + destinationAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: postalCode, + UsPostRegionCityID: &zone2UUID, + }, + }, + }, nil) + + move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.ServiceMember{ + Affiliation: &airForce, + }, + }, + }, nil) + + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: destinationAddress, + LinkOnly: true, + }, + }, nil) + + gbloc, err := models.GetDestinationGblocForShipment(suite.DB(), shipment.ID) + suite.NoError(err) + suite.NotNil(gbloc) + suite.Equal(*gbloc, "MBFL") + }) + suite.Run("success - get GBLOC for Army in AK Zone II", func() { + // Create an ARMY move in Alaska Zone II + zone2UUID, err := uuid.FromString("66768964-e0de-41f3-b9be-7ef32e4ae2b4") + suite.FatalNoError(err) + army := models.AffiliationARMY + postalCode := "99501" + // since we truncate the test db, we need to add the postal_code_to_gbloc value + factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), "99744", "JEAT") + + destinationAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: postalCode, + UsPostRegionCityID: &zone2UUID, + }, + }, + }, nil) + + move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.ServiceMember{ + Affiliation: &army, + }, + }, + }, nil) + + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: destinationAddress, + LinkOnly: true, + }, + }, nil) + + gbloc, err := models.GetDestinationGblocForShipment(suite.DB(), shipment.ID) + suite.NoError(err) + suite.NotNil(gbloc) + suite.Equal(*gbloc, "JEAT") + }) +} diff --git a/pkg/services/move_task_order/move_task_order_fetcher.go b/pkg/services/move_task_order/move_task_order_fetcher.go index b63f5960290..b41d6bc9176 100644 --- a/pkg/services/move_task_order/move_task_order_fetcher.go +++ b/pkg/services/move_task_order/move_task_order_fetcher.go @@ -278,6 +278,18 @@ func (f moveTaskOrderFetcher) FetchMoveTaskOrder(appCtx appcontext.AppContext, s return &models.Move{}, apperror.NewQueryError("MobileHomeShipment", loadErrMH, "") } } + // we need to get the destination GBLOC associated with a shipment's destination address + // USMC always goes to the USMC GBLOC + if mto.MTOShipments[i].DestinationAddress != nil { + if *mto.Orders.ServiceMember.Affiliation == models.AffiliationMARINES { + *mto.MTOShipments[i].DestinationAddress.DestinationGbloc = "USMC" + } else { + mto.MTOShipments[i].DestinationAddress.DestinationGbloc, err = models.GetDestinationGblocForShipment(appCtx.DB(), mto.MTOShipments[i].ID) + if err != nil { + return &models.Move{}, apperror.NewQueryError("Error getting shipment GBLOC", err, "") + } + } + } filteredShipments = append(filteredShipments, mto.MTOShipments[i]) } mto.MTOShipments = filteredShipments diff --git a/pkg/services/move_task_order/move_task_order_fetcher_test.go b/pkg/services/move_task_order/move_task_order_fetcher_test.go index 9ba9f7a8ba2..fb59f8209f3 100644 --- a/pkg/services/move_task_order/move_task_order_fetcher_test.go +++ b/pkg/services/move_task_order/move_task_order_fetcher_test.go @@ -344,6 +344,63 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderFetcher() { } }) + suite.Run("Success with Prime available move, returns destination GBLOC in shipment dest address", func() { + zone2UUID, err := uuid.FromString("66768964-e0de-41f3-b9be-7ef32e4ae2b4") + suite.FatalNoError(err) + army := models.AffiliationARMY + postalCode := "99501" + // since we truncate the test db, we need to add the postal_code_to_gbloc value + factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), "99744", "JEAT") + + destinationAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: postalCode, + UsPostRegionCityID: &zone2UUID, + }, + }, + }, nil) + + move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.ServiceMember{ + Affiliation: &army, + }, + }, + }, nil) + + factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: destinationAddress, + LinkOnly: true, + }, + }, nil) + searchParams := services.MoveTaskOrderFetcherParams{ + IncludeHidden: false, + Locator: move.Locator, + ExcludeExternalShipments: true, + } + + actualMTO, err := mtoFetcher.FetchMoveTaskOrder(suite.AppContextForTest(), &searchParams) + suite.NoError(err) + suite.NotNil(actualMTO) + + if suite.Len(actualMTO.MTOShipments, 1) { + suite.Equal(move.ID.String(), actualMTO.ID.String()) + // the shipment should have a destination GBLOC value + suite.NotNil(actualMTO.MTOShipments[0].DestinationAddress.DestinationGbloc) + } + }) + suite.Run("Success with move that has only deleted shipments", func() { mtoWithAllShipmentsDeleted := factory.BuildMove(suite.DB(), nil, nil) factory.BuildMTOShipmentMinimal(suite.DB(), []factory.Customization{ diff --git a/swagger-def/definitions/Address.yaml b/swagger-def/definitions/Address.yaml index baa869f59b3..0c018795ee2 100644 --- a/swagger-def/definitions/Address.yaml +++ b/swagger-def/definitions/Address.yaml @@ -161,6 +161,10 @@ properties: type: string format: uuid example: c56a4180-65aa-42ec-a945-5fd21dec0538 + destinationGbloc: + type: string + pattern: '^[A-Z]{4}$' + x-nullable: true required: - streetAddress1 - city diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index ab568b434c8..2456656b1c6 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -8301,6 +8301,10 @@ definitions: type: string format: uuid example: c56a4180-65aa-42ec-a945-5fd21dec0538 + destinationGbloc: + type: string + pattern: ^[A-Z]{4}$ + x-nullable: true required: - streetAddress1 - city diff --git a/swagger/internal.yaml b/swagger/internal.yaml index 84097cd100a..e6026ff442e 100644 --- a/swagger/internal.yaml +++ b/swagger/internal.yaml @@ -2758,6 +2758,10 @@ definitions: type: string format: uuid example: c56a4180-65aa-42ec-a945-5fd21dec0538 + destinationGbloc: + type: string + pattern: ^[A-Z]{4}$ + x-nullable: true required: - streetAddress1 - city diff --git a/swagger/pptas.yaml b/swagger/pptas.yaml index e2aa9b3c525..6b4223b52f7 100644 --- a/swagger/pptas.yaml +++ b/swagger/pptas.yaml @@ -245,6 +245,10 @@ definitions: type: string format: uuid example: c56a4180-65aa-42ec-a945-5fd21dec0538 + destinationGbloc: + type: string + pattern: ^[A-Z]{4}$ + x-nullable: true required: - streetAddress1 - city diff --git a/swagger/prime.yaml b/swagger/prime.yaml index 83ef52b06c7..c42c5608559 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -2955,6 +2955,10 @@ definitions: type: string format: uuid example: c56a4180-65aa-42ec-a945-5fd21dec0538 + destinationGbloc: + type: string + pattern: ^[A-Z]{4}$ + x-nullable: true required: - streetAddress1 - city diff --git a/swagger/prime_v2.yaml b/swagger/prime_v2.yaml index 00c4e8d169b..815a6e87ad5 100644 --- a/swagger/prime_v2.yaml +++ b/swagger/prime_v2.yaml @@ -1566,6 +1566,10 @@ definitions: type: string format: uuid example: c56a4180-65aa-42ec-a945-5fd21dec0538 + destinationGbloc: + type: string + pattern: ^[A-Z]{4}$ + x-nullable: true required: - streetAddress1 - city diff --git a/swagger/prime_v3.yaml b/swagger/prime_v3.yaml index c478289f287..fcb9b1a0d40 100644 --- a/swagger/prime_v3.yaml +++ b/swagger/prime_v3.yaml @@ -1654,6 +1654,10 @@ definitions: type: string format: uuid example: c56a4180-65aa-42ec-a945-5fd21dec0538 + destinationGbloc: + type: string + pattern: ^[A-Z]{4}$ + x-nullable: true required: - streetAddress1 - city From 509f3fc29e37aecbe9fbed5372daee29c01406c9 Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 20 Dec 2024 22:55:00 +0000 Subject: [PATCH 084/113] comments --- .../20241220213134_add_destination_gbloc_db_function.up.sql | 4 ++-- pkg/models/address.go | 2 +- pkg/models/mto_shipments.go | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql b/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql index bd7d9b1db5d..4b679eb8d48 100644 --- a/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql +++ b/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql @@ -1,5 +1,5 @@ -- this function will handle getting the destination GBLOC associated with a shipment's destination address --- this only applies to OCONUS destination addresses on a shipment, but this also checks domestic shipments +-- this only applies to OCONUS destination addresses on a shipment, but this can also checks domestic shipments CREATE OR REPLACE FUNCTION get_destination_gbloc_for_shipment(shipment_id UUID) RETURNS TEXT AS $$ DECLARE @@ -9,7 +9,7 @@ DECLARE alaska_zone_ii BOOLEAN; market_code TEXT; BEGIN - -- get the shipment's market_code + -- get the shipment's market code to determine conditionals SELECT ms.market_code INTO market_code FROM mto_shipments ms diff --git a/pkg/models/address.go b/pkg/models/address.go index 6042ae53960..d89a163c9aa 100644 --- a/pkg/models/address.go +++ b/pkg/models/address.go @@ -34,7 +34,7 @@ type Address struct { IsOconus *bool `json:"is_oconus" db:"is_oconus"` UsPostRegionCityID *uuid.UUID `json:"us_post_region_cities_id" db:"us_post_region_cities_id"` UsPostRegionCity *UsPostRegionCity `belongs_to:"us_post_region_cities" fk_id:"us_post_region_cities_id"` - DestinationGbloc *string `db:"-"` + DestinationGbloc *string `db:"-"` // this tells Pop not to look in the db for this value } // TableName overrides the table name used by Pop. diff --git a/pkg/models/mto_shipments.go b/pkg/models/mto_shipments.go index 783188addaa..9c2506e3292 100644 --- a/pkg/models/mto_shipments.go +++ b/pkg/models/mto_shipments.go @@ -427,7 +427,6 @@ func GetDestinationGblocForShipment(db *pop.Connection, shipmentID uuid.UUID) (* return nil, fmt.Errorf("error fetching destination gbloc for shipment ID: %s with error %w", shipmentID, err) } - // return the ZIP code and port type, or nil if not found if gbloc != nil { return gbloc, nil } From f6408b9868670883466e64a1238b5973088a338e Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 20 Dec 2024 23:05:01 +0000 Subject: [PATCH 085/113] added usmc test --- .../move_task_order_fetcher.go | 2 +- .../move_task_order_fetcher_test.go | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/pkg/services/move_task_order/move_task_order_fetcher.go b/pkg/services/move_task_order/move_task_order_fetcher.go index b41d6bc9176..1d30aef5ceb 100644 --- a/pkg/services/move_task_order/move_task_order_fetcher.go +++ b/pkg/services/move_task_order/move_task_order_fetcher.go @@ -282,7 +282,7 @@ func (f moveTaskOrderFetcher) FetchMoveTaskOrder(appCtx appcontext.AppContext, s // USMC always goes to the USMC GBLOC if mto.MTOShipments[i].DestinationAddress != nil { if *mto.Orders.ServiceMember.Affiliation == models.AffiliationMARINES { - *mto.MTOShipments[i].DestinationAddress.DestinationGbloc = "USMC" + mto.MTOShipments[i].DestinationAddress.DestinationGbloc = models.StringPointer("USMC") } else { mto.MTOShipments[i].DestinationAddress.DestinationGbloc, err = models.GetDestinationGblocForShipment(appCtx.DB(), mto.MTOShipments[i].ID) if err != nil { diff --git a/pkg/services/move_task_order/move_task_order_fetcher_test.go b/pkg/services/move_task_order/move_task_order_fetcher_test.go index fb59f8209f3..a80be4aa524 100644 --- a/pkg/services/move_task_order/move_task_order_fetcher_test.go +++ b/pkg/services/move_task_order/move_task_order_fetcher_test.go @@ -401,6 +401,45 @@ func (suite *MoveTaskOrderServiceSuite) TestMoveTaskOrderFetcher() { } }) + suite.Run("Success with Prime available move, returns USMC destination GBLOC for USMC move", func() { + usmc := models.AffiliationMARINES + + destinationAddress := factory.BuildAddress(suite.DB(), nil, nil) + move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.ServiceMember{ + Affiliation: &usmc, + }, + }, + }, nil) + + factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: destinationAddress, + LinkOnly: true, + }, + }, nil) + searchParams := services.MoveTaskOrderFetcherParams{ + IncludeHidden: false, + Locator: move.Locator, + ExcludeExternalShipments: true, + } + + actualMTO, err := mtoFetcher.FetchMoveTaskOrder(suite.AppContextForTest(), &searchParams) + suite.NoError(err) + suite.NotNil(actualMTO) + + if suite.Len(actualMTO.MTOShipments, 1) { + suite.Equal(move.ID.String(), actualMTO.ID.String()) + suite.NotNil(actualMTO.MTOShipments[0].DestinationAddress.DestinationGbloc) + suite.Equal(*actualMTO.MTOShipments[0].DestinationAddress.DestinationGbloc, "USMC") + } + }) + suite.Run("Success with move that has only deleted shipments", func() { mtoWithAllShipmentsDeleted := factory.BuildMove(suite.DB(), nil, nil) factory.BuildMTOShipmentMinimal(suite.DB(), []factory.Customization{ From 55c862950a184157476aa5296504e16d80afa02c Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Thu, 26 Dec 2024 13:47:23 +0000 Subject: [PATCH 086/113] updated db func to include USMC check, added test --- ...4_add_destination_gbloc_db_function.up.sql | 5 ++ pkg/models/mto_shipments_test.go | 49 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql b/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql index 4b679eb8d48..0375f64d044 100644 --- a/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql +++ b/migrations/app/schema/20241220213134_add_destination_gbloc_db_function.up.sql @@ -45,6 +45,11 @@ BEGIN JOIN mto_shipments ms ON ms.move_id = m.id WHERE ms.id = shipment_id; + -- if the service member is USMC, return 'USMC' + IF service_member_affiliation = 'MARINES' THEN + RETURN 'USMC'; + END IF; + SELECT upc.uspr_zip_id INTO zip FROM addresses a diff --git a/pkg/models/mto_shipments_test.go b/pkg/models/mto_shipments_test.go index 1b92fbb7035..4014aa90d12 100644 --- a/pkg/models/mto_shipments_test.go +++ b/pkg/models/mto_shipments_test.go @@ -435,4 +435,53 @@ func (suite *ModelSuite) TestGetDestinationGblocForShipment() { suite.NotNil(gbloc) suite.Equal(*gbloc, "JEAT") }) + suite.Run("success - get GBLOC for USMC in AK Zone II", func() { + // Create a USMC move in Alaska Zone II + // this should always return USMC + zone2UUID, err := uuid.FromString("66768964-e0de-41f3-b9be-7ef32e4ae2b4") + suite.FatalNoError(err) + usmc := models.AffiliationMARINES + postalCode := "99501" + // since we truncate the test db, we need to add the postal_code_to_gbloc value + // this doesn't matter to the db function because it will check for USMC but we are just verifying it won't be JEAT despite the zip matching + factory.FetchOrBuildPostalCodeToGBLOC(suite.DB(), "99744", "JEAT") + + destinationAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: postalCode, + UsPostRegionCityID: &zone2UUID, + }, + }, + }, nil) + + move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{ + { + Model: models.ServiceMember{ + Affiliation: &usmc, + }, + }, + }, nil) + + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.MTOShipment{ + MarketCode: models.MarketCodeInternational, + }, + }, + { + Model: move, + LinkOnly: true, + }, + { + Model: destinationAddress, + LinkOnly: true, + }, + }, nil) + + gbloc, err := models.GetDestinationGblocForShipment(suite.DB(), shipment.ID) + suite.NoError(err) + suite.NotNil(gbloc) + suite.Equal(*gbloc, "USMC") + }) } From 1ac32cdfad43652c60b0da90184b1d256a137ebf Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Fri, 20 Dec 2024 23:10:03 +0000 Subject: [PATCH 087/113] if it broke, ya gotta fix From 2e06c81ff9dfa2aadeb4be2ded798079813ad968 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Wed, 8 Jan 2025 20:52:24 +0000 Subject: [PATCH 088/113] add pay grade and allowances to db truncate --- scripts/db-truncate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/db-truncate b/scripts/db-truncate index a0f15bc3450..ee7739bc9fc 100755 --- a/scripts/db-truncate +++ b/scripts/db-truncate @@ -9,7 +9,7 @@ DO \$\$ DECLARE r RECORD; BEGIN FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema() - AND tablename NOT IN ('us_post_region_cities', 're_countries', 're_states', 're_cities', 're_us_post_regions', 're_oconus_rate_areas', 're_rate_areas', 're_intl_transit_times', 'ub_allowances','re_services','re_service_items', 'ports', 'port_locations', 're_fsc_multipliers')) LOOP + AND tablename NOT IN ('us_post_region_cities', 're_countries', 're_states', 're_cities', 're_us_post_regions', 're_oconus_rate_areas', 're_rate_areas', 're_intl_transit_times', 'ub_allowances','re_services','re_service_items', 'ports', 'port_locations', 're_fsc_multipliers', 'pay_grades', 'hhg_allowances')) LOOP EXECUTE 'TRUNCATE TABLE ' || quote_ident(r.tablename) || ' CASCADE'; END LOOP; END \$\$; From 1155ac24343f996a0809ad70c8f6741425cb3925 Mon Sep 17 00:00:00 2001 From: Jon Spight Date: Wed, 8 Jan 2025 21:56:00 +0000 Subject: [PATCH 089/113] removed tests --- .../payloads/model_to_payload_test.go | 62 ------------------- 1 file changed, 62 deletions(-) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go index 7537a5a01b4..0e6b225889e 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go @@ -13,7 +13,6 @@ import ( "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/models/roles" "github.com/transcom/mymove/pkg/storage/test" - "github.com/transcom/mymove/pkg/testdatagen" "github.com/transcom/mymove/pkg/unit" ) @@ -717,8 +716,6 @@ func (suite *PayloadsSuite) TestSearchMoves() { appCtx := suite.AppContextForTest() marines := models.AffiliationMARINES - spaceForce := models.AffiliationSPACEFORCE - army := models.AffiliationARMY moveUSMC := factory.BuildMove(suite.DB(), []factory.Customization{ { Model: models.ServiceMember{ @@ -726,73 +723,14 @@ func (suite *PayloadsSuite) TestSearchMoves() { }, }, }, nil) - moveSF := factory.BuildMove(suite.DB(), []factory.Customization{ - { - Model: models.ServiceMember{ - Affiliation: &spaceForce, - }, - }, - }, nil) - moveA := factory.BuildMove(suite.DB(), []factory.Customization{ - { - Model: models.ServiceMember{ - Affiliation: &army, - }, - }, - }, nil) - moveUSMC.Status = models.MoveStatusNeedsServiceCounseling - scheduledPickupDate := time.Date(testdatagen.GHCTestYear, time.September, 20, 0, 0, 0, 0, time.UTC) - scheduledDeliveryDate := time.Date(testdatagen.GHCTestYear, time.September, 20, 0, 0, 0, 0, time.UTC) - sitAllowance := int(90) - gbloc := "LKNQ" - storageFacility := factory.BuildStorageFacility(suite.DB(), nil, nil) - mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ - { - Model: moveSF, - LinkOnly: true, - }, - { - Model: models.MTOShipment{ - Status: models.MTOShipmentStatusApproved, - ShipmentType: models.MTOShipmentTypeHHGIntoNTSDom, - CounselorRemarks: handlers.FmtString("counselor remark"), - SITDaysAllowance: &sitAllowance, - ScheduledPickupDate: &scheduledPickupDate, - ScheduledDeliveryDate: &scheduledDeliveryDate, - }, - }, - { - Model: storageFacility, - LinkOnly: true, - }, - }, nil) - - moveSF.MTOShipments = append(moveSF.MTOShipments, mtoShipment) - moveSF.ShipmentGBLOC = append(moveSF.ShipmentGBLOC, models.MoveToGBLOC{GBLOC: &gbloc}) moves := models.Moves{moveUSMC} - moveSpaceForce := models.Moves{moveSF} - moveArmy := models.Moves{moveA} suite.Run("Success - Returns a ghcmessages Upload payload from Upload Struct Marine move with no shipments", func() { payload := SearchMoves(appCtx, moves) suite.IsType(payload, &ghcmessages.SearchMoves{}) suite.NotNil(payload) }) - suite.Run("Success - Returns a ghcmessages Upload payload from Upload Struct Non-Marine move, a shipment, and delivery/pickup time. ", func() { - payload := SearchMoves(appCtx, moveSpaceForce) - suite.IsType(payload, &ghcmessages.SearchMoves{}) - suite.NotNil(payload) - suite.NotNil(mtoShipment) - - suite.NotNil(moveA) - }) - suite.Run("Success - Returns a ghcmessages Upload payload from Upload Struct Army move, with no shipments. ", func() { - payload := SearchMoves(appCtx, moveArmy) - suite.IsType(payload, &ghcmessages.SearchMoves{}) - suite.NotNil(payload) - - }) } func (suite *PayloadsSuite) TestMarketCode() { From 9713b2cc944e11b5832b678603c53a7a6976f328 Mon Sep 17 00:00:00 2001 From: Jon Spight Date: Wed, 8 Jan 2025 21:59:33 +0000 Subject: [PATCH 090/113] PR comments --- pkg/factory/shared.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/factory/shared.go b/pkg/factory/shared.go index 2ff8e0dea9b..d7f5b069776 100644 --- a/pkg/factory/shared.go +++ b/pkg/factory/shared.go @@ -273,14 +273,14 @@ var TransportationOffices = transportationOfficeGroup{ type officeUserGroup struct { SCAssignedUser CustomType - TOOAssignedUser CustomType TIOAssignedUser CustomType + TOOAssignedUser CustomType } var OfficeUsers = officeUserGroup{ SCAssignedUser: "SCAssignedUser", - TOOAssignedUser: "TOOAssignedUser", TIOAssignedUser: "TIOAssignedUser", + TOOAssignedUser: "TOOAssignedUser", } // uploadGroup is a grouping of all the upload related fields From 5ba4026689a5fbcf24380c1be7d0d6a563bd014c Mon Sep 17 00:00:00 2001 From: josiahzimmerman-caci Date: Thu, 9 Jan 2025 17:33:30 +0000 Subject: [PATCH 091/113] add test ignore rules --- .gitlab-ci.yml | 985 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 727 insertions(+), 258 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index da9f9399d77..803b008bb71 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -57,9 +57,11 @@ stages: - export REACT_APP_ERROR_LOGGING=otel .announce_failure: &announce_failure - - if [[ "$CI_COMMIT_BRANCH" == "main" && "$CI_JOB_STATUS" == "failed" ]]; then - echo "Announcing broken branch in GitLab CI" - fi + #- if [[ "$CI_COMMIT_BRANCH" == "main" && "$CI_JOB_STATUS" == "failed" ]]; then + - echo $CI_COMMIT_BRANCH + - echo $CI_JOB_STATUS + - echo "Announcing broken branch in GitLab CI" + # fi .setup_aws_vars_dp3: &setup_aws_vars_dp3 - if [[ "$DP3_ENV" == "exp" OR "$DP3_ENV" == "loadtest" OR "$DP3_ENV" == "demo" ]]; then @@ -141,6 +143,89 @@ stages: .check_debug: &check_debug - if: '$debug == "true"' +.check_integration_ignore_branch: &check_integration_ignore_branch + - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == $INTEGRATION_IGNORE_BRANCH' + +.check_integration_mtls_ignore_branch: &check_integration_mtls_ignore_branch + - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == $INTEGRATION_MTLS_IGNORE_BRANCH' + +.check_client_ignore_branch: &check_client_ignore_branch + - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == $CLIENT_IGNORE_BRANCH' + +.check_server_ignore_branch: &check_server_ignore_branch + - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == $SERVER_IGNORE_BRANCH' + + +.install_yarn: &install_yarn + - | + mkdir -p /builds/milmove/mymove/.cache + mkdir -p /builds/milmove/mymove/.cache/yarn + yarn install --frozen-lockfile --cache-folder /builds/milmove/mymove/.cache/yarn + scripts/check-generated-code yarn.lock + echo "yarn check dependencies" + ./scripts/rebuild-dependencies-without-binaries + +.yarn_cache: &yarn_cache + cache: + key: + files: + - yarn.lock + paths: + - .cache/yarn + +.gosum_cache: &gosum_cache + cache: + key: + files: + - go.sum + paths: + - .cache/yarn + +.setup_generic_app_env_variables: &setup_generic_app_env_variables + - | + export APPLICATION=app + export DB_PASSWORD=mysecretpassword + export DB_USER_LOW_PRIV=crud + export DB_PASSWORD_LOW_PRIV=mysecretpassword + export DB_USER=postgres + export DB_HOST=localhost + export DB_PORT=5432 + export MIGRATION_MANIFEST='/builds/milmove/mymove/migrations/app/migrations_manifest.txt' + export MIGRATION_PATH='file:///builds/milmove/mymove/migrations/app/schema;file:///builds/milmove/mymove/migrations/app/secure' + export EIA_KEY=db2522a43820268a41a802a16ae9fd26 + +.setup_devseed_env_variables: &setup_devseed_env_variables + - | + export DB_NAME=dev_db + export DB_NAME_DEV=dev_db + export ENVIRONMENT=development + export DOD_CA_PACKAGE=/builds/milmove/mymove/config/tls/milmove-cert-bundle.p7b + +.setup_server_env_variables: &setup_server_env_variables + - | + echo "make server_test_build for app" + export LOGIN_GOV_SECRET_KEY=$(echo $E2E_LOGIN_GOV_SECRET_KEY | base64 --decode) + export OKTA_CUST_CLIENT_ID=notrealkey + export OKTA_CUSTOMER_SECRET_KEY=notrealkey + export OKTA_OFFICE_SECRET_KEY=notrealkey1 + export OKTA_ADMIN_SECRET_KEY=notrealkey2 + export OKTA_TENANT_ORG_URL=test-milmove.okta.mil + export GOTEST_PARALLEL=8 + export DB_PORT_TEST=5433 + export DB_NAME=test_db + export DB_NAME_TEST=test_db + export DTOD_USE_MOCK='true' + export ENV=test + export ENVIRONMENT=test + export SERVER_REPORT=1 + export COVERAGE=1 + export SERVE_API_INTERNAL='true' + export OKTA_CUSTOMER_CLIENT_ID=1q2w3e4r5t6y7u8i9o + export OKTA_ADMIN_CLIENT_ID=AQ1SW2DE3FR4G5 + export OKTA_OFFICE_CLIENT_ID=9f9f9s8s90gig9 + export OKTA_API_KEY=notrealapikey8675309 + export OKTA_OFFICE_GROUP_ID=notrealgroupId + export OKTA_CUSTOMER_GROUP_ID=notrealcustomergroupId sast: stage: pre_checks @@ -149,6 +234,19 @@ include: - template: Jobs/Dependency-Scanning.gitlab-ci.yml - template: Jobs/Secret-Detection.gitlab-ci.yml +testing_services: + stage: pre_checks + image: alpine:3.7 + allow_failure: true + services: + - php:7 + - node:latest + - golang:1.10 + script: + - php -v + - node -v + - go version + anti_virus: stage: pre_checks image: milmove/clamav-ci # Custom image with ClamAV pre-installed @@ -176,13 +274,29 @@ anti_virus: rules: - *check_main +# Prep the public folder for frontend dependency serving +# This is needed for things like pdfjs-dist +prep_server_hosted_client_deps: + stage: pre_checks + image: $DOCKER_APP_IMAGE + before_script: + - *setup_milmove_env + script: | + echo "Running prep_server_hosted_client_deps" + ./scripts/fetch-react-file-viewer-from-yarn + after_script: + - *announce_failure + artifacts: + paths: + - /builds/milmove/mymove/public + pre_deps_golang: stage: pre_checks image: $DOCKER_APP_IMAGE before_script: - *setup_milmove_env variables: - KUBERNETES_CPU_REQUEST: "2" + KUBERNETES_CPU_REQUEST: "4" KUBERNETES_MEMORY_REQUEST: "4Gi" KUBERNETES_MEMORY_LIMIT: "4Gi" script: @@ -193,8 +307,8 @@ pre_deps_golang: - *announce_failure artifacts: paths: - - bin/ - - swagger/ + - /builds/milmove/mymove/bin/ + - /builds/milmove/mymove/swagger/ #TODO: Optimization potential # cache: # key: "$CI_COMMIT_REF_SLUG-go" @@ -211,17 +325,10 @@ pre_deps_yarn: before_script: - *setup_milmove_env script: - - pwd - - ls bin - - yarn config set "strict-ssl" false - - yarn install --frozen-lockfile --cache-folder ~/.cache/yarn - - scripts/check-generated-code yarn.lock - - echo "Temporarily skipping yarn installation and code checks." - artifacts: - paths: - - ~/.cache/yarn + - *install_yarn + <<: *yarn_cache after_script: - - *announce_failure + - *announce_failure check_generated_code: stage: pre_checks @@ -285,28 +392,71 @@ check_tls_certificate_prd: after_script: - *announce_failure +build_storybook: + stage: build + image: $DOCKER_APP_IMAGE + variables: + KUBERNETES_CPU_REQUEST: "4" + KUBERNETES_MEMORY_REQUEST: "8Gi" + KUBERNETES_MEMORY_LIMIT: "8Gi" + needs: + - pre_deps_yarn + - anti_virus + <<: *yarn_cache + before_script: + - *setup_milmove_env + - *install_yarn + script: + - yarn build-storybook + after_script: + - *announce_failure + artifacts: + paths: + - /builds/milmove/mymove/storybook-static + rules: + - *check_main + +deploy_storybook_dp3: + stage: deploy + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_yarn + - build_storybook + before_script: + - *setup_milmove_env + script: + - echo "TODO Add steps" + - echo "deploy_storybook_dp3" + after_script: + - *announce_failure + artifacts: + paths: + - /builds/milmove/mymove/storybook-static + # rules: + # - *check_main + compile_app_client: stage: build image: $DOCKER_APP_IMAGE + <<: *yarn_cache variables: - KUBERNETES_CPU_REQUEST: "2" + KUBERNETES_CPU_REQUEST: "6" KUBERNETES_MEMORY_REQUEST: "8Gi" KUBERNETES_MEMORY_LIMIT: "8Gi" - before_script: *setup_milmove_env + before_script: + - *setup_milmove_env + - *install_yarn needs: - pre_deps_yarn script: - make client_build - - echo "Skipping actual build steps." artifacts: paths: - - ~/.cache/yarn - /builds/milmove/mymove/bin - /builds/milmove/mymove/build - playwright - playwright.config.js - package.json - - yarn.lock - eslint-plugin-ato expire_in: 1 week after_script: @@ -316,14 +466,17 @@ compile_app_client: compile_app_server: stage: build image: $DOCKER_APP_IMAGE + <<: *yarn_cache variables: - KUBERNETES_CPU_REQUEST: "2" - KUBERNETES_MEMORY_REQUEST: "4Gi" - KUBERNETES_MEMORY_LIMIT: "4Gi" + KUBERNETES_CPU_REQUEST: "6" + KUBERNETES_MEMORY_REQUEST: "6Gi" + KUBERNETES_MEMORY_LIMIT: "8Gi" needs: - pre_deps_golang - pre_deps_yarn - before_script: *setup_milmove_env + before_script: + - *setup_milmove_env + - *install_yarn script: - make -j 4 server_build build_tools - echo "Skipping server and tools compilation." @@ -347,6 +500,549 @@ compile_app_server: after_script: - *announce_failure + +##################################### +## Test stages various conditions ## +##################################### + +pre_test: + stage: test + image: $DOCKER_APP_IMAGE + <<: *yarn_cache + needs: + - pre_deps_golang + - pre_deps_yarn + - check_tls_certificate_stg + - check_tls_certificate_prd + variables: + KUBERNETES_CPU_REQUEST: "4" + KUBERNETES_MEMORY_REQUEST: "6Gi" + KUBERNETES_MEMORY_LIMIT: "6Gi" + before_script: *setup_milmove_env + script: + - export GODEBUG=asyncpreemptoff=1 + - echo "Save Baseline Spectral Lint" + - | + [ -d ~/transcom/mymove/spectral ] && cp -r ~/transcom/mymove/spectral /tmp/spectral_baseline || echo "Skipping saving baseline" + - rm -rf ~/transcom/mymove/spectral + - *install_yarn + - ./scripts/pre-commit-go-mod || exit 0 + - echo "Run pre-commit tests without golangci-lint, eslint, or prettier" + - SKIP=golangci-lint,eslint,prettier,ato-go-linter,gomod,appcontext-linter pre-commit run --all-files + - | + echo "Run pre-commit tests with ato-go-linter only" + pre-commit run -v --all-files ato-go-linter + - | + echo "Run pre-commit tests with gomod only" + pre-commit run -v --all-files gomod,appcontext-linter + - | + echo "Run pre-commit tests with appcontext-linter only" + pre-commit run -v --all-files appcontext-linter + - echo "Run pre-commit tests with golangci-lint only" + - | + echo 'export GOLANGCI_LINT_CONCURRENCY=4' >> $BASH_ENV + echo 'export GOLANGCI_LINT_VERBOSE=-v' >> $BASH_ENV + source $BASH_ENV + mkdir -p tmp/test-results/pretest + pre-commit run -v --all-files golangci-lint | tee tmp/test-results/pretest/golangci-lint.out + - echo "Run prettier, eslint, danger checks" + - yarn prettier-ci + - yarn lint + - yarn danger ci --failOnErrors + - echo "Run spectral linter on all files" + - ./scripts/ensure-spectral-lint /tmp/spectral_baseline spectral + after_script: + - *announce_failure + rules: + - *check_server_ignore_branch + +server_test: + stage: test + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_golang + before_script: + - *setup_milmove_env + - *setup_generic_app_env_variables + - *setup_server_env_variables + services: + - postgres:latest + - docker:dind + variables: + APPLICATION: app + # 8 since this runs on xlarge with 8 CPUs + GOTEST_PARALLEL: 8 + DB_PASSWORD: mysecretpassword + DB_USER_LOW_PRIV: crud + DB_PASSWORD_LOW_PRIV: mysecretpassword + DB_USER: postgres + DB_HOST: localhost + DB_PORT_TEST: 5433 + DB_PORT: 5432 + DB_NAME: test_db + DB_NAME_TEST: test_db + DTOD_USE_MOCK: 'true' + MIGRATION_MANIFEST: '/builds/milmove/mymove/migrations/app/migrations_manifest.txt' + MIGRATION_PATH: 'file:///builds/milmove/mymove/migrations/app/schema;file:///builds/milmove/mymove/migrations/app/secure' + EIA_KEY: db2522a43820268a41a802a16ae9fd26 # dummy key generated with openssl rand -hex 16 + ENV: test + ENVIRONMENT: test + SERVER_REPORT: 1 + COVERAGE: 1 + SERVE_API_INTERNAL: 'true' + OKTA_CUSTOMER_CLIENT_ID: 1q2w3e4r5t6y7u8i9o + OKTA_ADMIN_CLIENT_ID: AQ1SW2DE3FR4G5 + OKTA_OFFICE_CLIENT_ID: 9f9f9s8s90gig9 + OKTA_API_KEY: notrealapikey8675309 + OKTA_OFFICE_GROUP_ID: notrealgroupId + OKTA_CUSTOMER_GROUP_ID: notrealcustomergroupId + script: + - psql --version + - for i in $(seq 1 5); do go mod download && break || s=$? && sleep 5; done; (exit $s) + - scripts/check-generated-code go.sum + - make bin/swagger + - echo "server test -- TODO Add steps need to potentially pass job id to file and persist" + - make -j 2 bin/milmove bin/gotestsum + - make server_test for app + # - go install gotest.tools/gotestsum@latest + # - go mod tidy + #- bin/gotestsum --junitfile server_test_report.xml --format server_test + artifacts: + paths: + - /builds/milmove/mymove/bin/gotestsum + - /builds/milmove/mymove/tmp/test-results + when: always + reports: + junit: /builds/milmove/mymove/tmp/test-results/gotest/app/go-test-report.xml + after_script: + - *announce_failure + rules: + - *check_server_ignore_branch + +server_test_coverage: + stage: test + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_golang + - server_test + before_script: *setup_milmove_env + script: + - echo "TODO understand recording stats and PR interaction" + - echo "server test coverage" + - | + echo "Ensure Test Coverage Increasing" + ./scripts/ensure-go-test-coverage \ + tmp/baseline-go-coverage/go-coverage.txt \ + tmp/test-results/gotest/app/go-coverage.txt + after_script: + - *announce_failure + rules: + - *check_server_ignore_branch + ###may need to rethink the logic and intent of this they save per the following and do some PR interaction + # only save the cache on default branch builds because we only want to + # change the baseline of test results on main builds + # + # Save the new baseline regardless of if the coverage succeeds + # or fails as a merge to main means we have a new baseline. We + # will use other means to measure if our coverage is increasing + +client_test: + stage: test + image: $DOCKER_APP_IMAGE + variables: + KUBERNETES_CPU_REQUEST: "4" + KUBERNETES_MEMORY_REQUEST: "8Gi" + KUBERNETES_MEMORY_LIMIT: "8Gi" + needs: + - pre_deps_yarn + <<: *yarn_cache + before_script: + - *setup_milmove_env + - *install_yarn + coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/ + dependencies: + - pre_deps_yarn + script: + - echo "client test coverage" + - JEST_JUNIT_OUTPUT_DIR=jest-junit-reports yarn test:coverage -results=false >> $CI_PROJECT_DIR/coverage.output + artifacts: + when: always + reports: + junit: + - jest-junit-reports/junit.xml + paths: + - /builds/milmove/mymove/coverage + - /builds/milmove/mymove/jest-junit-reports + after_script: + - *announce_failure + rules: + - *check_client_ignore_branch + +client_test_coverage: + stage: test + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_yarn + - client_test + before_script: *setup_milmove_env + # TODO: need to add cache for max coverage increase similar to this + # https://stackoverflow.com/questions/54542922/force-coverage-increase-in-gitlab-prs + script: + - echo "TODO understand recording stats and PR interaction" + - | + echo "Ensure Test Coverage Increasing" + ./scripts/ensure-js-test-coverage \ + tmp/baseline-jest-coverage/clover.xml \ + coverage/clover.xml + after_script: + - *announce_failure + rules: + - *check_client_ignore_branch + +integration_test_devseed: + stage: test + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_golang + - prep_server_hosted_client_deps + before_script: + - *setup_milmove_env + - *setup_generic_app_env_variables + - *setup_devseed_env_variables + script: + - | + echo "integration_test_devseed" + echo 'export MOVE_MIL_DOD_CA_CERT=$(cat config/tls/devlocal-ca.pem)' >> $BASH_ENV + echo 'export MOVE_MIL_DOD_TLS_CERT=$(cat config/tls/devlocal-https.pem)' >> $BASH_ENV + echo 'export MOVE_MIL_DOD_TLS_KEY=$(cat config/tls/devlocal-https.key)' >> $BASH_ENV + source $BASH_ENV + make db_dev_fresh + after_script: + - *announce_failure + rules: + - *check_integration_ignore_branch + +integration_tests: + stage: test + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_yarn + - pre_deps_golang + - compile_app_client + - compile_app_server + - integration_test_my + - integration_test_office + - integration_test_admin + - integration_test_devseed + before_script: *setup_milmove_env + script: + - echo "TODO Add steps" + - echo "integration_tests" + after_script: + - *announce_failure + rules: + - *check_integration_ignore_branch + +integration_test_mtls: + stage: test + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_yarn + - compile_app_server + before_script: *setup_milmove_env + script: + - echo "TODO Add steps" + - echo "integration_test_mtls" + after_script: + - *announce_failure + rules: + - *check_integration_mtls_ignore_branch + +integration_test_admin: + stage: test + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_yarn + - pre_deps_golang + - compile_app_client + - compile_app_server + before_script: *setup_milmove_env + script: + - echo "TODO Add steps" + - echo "integration_test_admin" + after_script: + - *announce_failure + rules: + - *check_integration_ignore_branch + +integration_test_my: + stage: test + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_yarn + - pre_deps_golang + - compile_app_client + - compile_app_server + before_script: *setup_milmove_env + script: + - echo "TODO Add steps" + - echo "integration_test_my" + after_script: + - *announce_failure + rules: + - *check_integration_ignore_branch + +integration_test_office: + stage: test + image: $DOCKER_APP_IMAGE + needs: + - pre_deps_yarn + - pre_deps_golang + - compile_app_client + - compile_app_server + before_script: *setup_milmove_env + script: + - echo "TODO Add steps" + - echo "integration_test_office" + after_script: + - *announce_failure + rules: + - *check_integration_ignore_branch + + +############################################################### +## DP3 Env push and deploy stages all off of setting dp3 env ## +############################################################### +build_push_app_dp3: + stage: push + environment: DP3_ENV + image: + name: gcr.io/kaniko-project/executor:v1.14.0-debug + entrypoint: [""] + needs: + - compile_app_client + - compile_app_server + before_script: + - *setup_aws_vars_dp3 + - *setup_release_dp3 + - *kaniko_before_setup + script: + - echo "Building and Pushing app Docker image..." + - /kaniko/executor --context "${CI_PROJECT_DIR}/" --dockerfile "${CI_PROJECT_DIR}/${APP_DOCKER_FILE}" --destination "${ECR_REPOSITORY_URI}/app:$CI_COMMIT_SHORT_SHA" + after_script: + - *announce_failure + rules: + - *check_dp3 + +build_push_migrations_dp3: + stage: push + environment: DP3_ENV + image: + name: gcr.io/kaniko-project/executor:v1.14.0-debug + entrypoint: [""] + needs: + - compile_app_server + - compile_app_client + before_script: + - *setup_aws_vars_dp3 + - *setup_release_dp3 + - *kaniko_before_setup + script: + - echo "Building and Pushing migrations Docker image..." + - /kaniko/executor --context "${CI_PROJECT_DIR}/" --dockerfile "${CI_PROJECT_DIR}/Dockerfile.migrations" --destination "${ECR_REPOSITORY_URI}/app-migrations:$CI_COMMIT_SHORT_SHA" + after_script: + - *announce_failure + rules: + - *check_dp3 + +build_push_tasks_dp3: + stage: push + environment: DP3_ENV + image: + name: gcr.io/kaniko-project/executor:v1.14.0-debug + entrypoint: [""] + needs: + - compile_app_server + - compile_app_client + before_script: + - *setup_aws_vars_dp3 + - *setup_release_dp3 + - *kaniko_before_setup + script: + - echo "Building tasks Docker image..." + - /kaniko/executor --context "${CI_PROJECT_DIR}/" --dockerfile "${CI_PROJECT_DIR}/${TASK_DOCKER_FILE}" --destination "${ECR_REPOSITORY_URI}/app-tasks:$CI_COMMIT_SHORT_SHA" + after_script: + - *announce_failure + rules: + - *check_dp3 + +push_otel_collector_image_dp3: + stage: push + environment: DP3_ENV + image: + name: $DOCKER_BASE_IMAGE + entrypoint: [""] + needs: + - compile_app_server + - compile_app_client + script: + - echo "Logging in to Amazon ECR with Crane..." + - aws ecr get-login-password --region us-gov-west-1 | crane auth login ${ECR_REPOSITORY_URI} -u AWS --password-stdin + + - echo "Pulling the AWS OTel Collector image from the public registry with Crane..." + - crane pull --insecure public.ecr.aws/aws-observability/aws-otel-collector:v0.31.0 image.tar + + - echo "Pushing the image to our private ECR using Crane..." + - crane push --insecure image.tar ${ECR_REPOSITORY_URI}/otel-collector:${CI_COMMIT_SHORT_SHA} + + - echo "Cleaning up the temporary image file..." + - rm image.tar + allow_failure: false + after_script: + - *announce_failure + rules: + - *check_dp3 + +deploy_migrations_dp3: + stage: deploy + environment: DP3_ENV + image: + name: $DOCKER_APP_IMAGE + entrypoint: [""] + needs: + - build_push_migrations_dp3 + - compile_app_server + - compile_app_client + before_script: + - *setup_aws_vars_dp3 + - *setup_release_dp3 + script: + # Step 1: Get the Digest + - echo "Getting Digest from AWS" + - export ECR_DIGEST=$(aws ecr describe-images --repository-name app-migrations --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) + # Step 2: Ensure exclusive execution and Snapshot + - echo "Snapshotting database" + - ./scripts/rds-snapshot-app-db "$APP_ENVIRONMENT" + # Step 3: Run migrations + - echo "Running migrations" + - ./scripts/ecs-run-app-migrations-container "${ECR_REPOSITORY_URI}/app-migrations@${ECR_DIGEST}" "${APP_ENVIRONMENT}" + after_script: + - *announce_failure + rules: + - *check_dp3 + +deploy_tasks_dp3: + stage: deploy + image: + name: $DOCKER_APP_IMAGE + entrypoint: [""] + needs: + - build_push_tasks_dp3 + - compile_app_server + - compile_app_client + before_script: + - *setup_release_dp3 + script: + - echo "Getting Digest from AWS" + - export ECR_DIGEST=$(aws ecr describe-images --repository-name app-tasks --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) + - echo "Deploying GHC fuel price data task service" + - ./scripts/ecs-deploy-task-container save-ghc-fuel-price-data "${ECR_REPOSITORY_URI}/app-tasks@${ECR_DIGEST}" "${APP_ENVIRONMENT}" + - echo "Deploying payment reminder email task service" + - ./scripts/ecs-deploy-task-container send-payment-reminder "${ECR_REPOSITORY_URI}/app-tasks@${ECR_DIGEST}" "${APP_ENVIRONMENT}" + after_script: + - *announce_failure + rules: + - *check_dp3 + +deploy_app_client_tls_dp3: + stage: deploy + environment: DP3_ENV + image: + name: $DOCKER_APP_IMAGE + entrypoint: [""] + needs: + - deploy_migrations_dp3 + - push_otel_collector_image_dp3 + - compile_app_server + - compile_app_client + variables: + OPEN_TELEMETRY_SIDECAR: "true" + HEALTH_CHECK: "true" + before_script: + - *setup_aws_vars_dp3 + - *setup_release_dp3 + script: + # - echo "Comparing against deployed commit" + # - ./scripts/compare-deployed-commit "" $CI_COMMIT_SHA ${TLS_KEY} ${TLS_CERT} ${TLS_CA} + - echo "Getting Digest from AWS" + - export ECR_DIGEST=$(aws ecr describe-images --repository-name app --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) + - echo "Getting otel collector Digest from AWS" + - export OTEL_ECR_DIGEST=$(aws ecr describe-images --repository-name otel-collector --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) + - export OTEL_COLLECTOR_IMAGE="${ECR_REPOSITORY_URI}/otel-collector@${OTEL_ECR_DIGEST}" + - echo "Deploying app-client-tls service" + - ./scripts/ecs-deploy-service-container app-client-tls "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" + - echo "Running Health Check" + # - bin/health-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --tries 10 --backoff 3 --log-level info --timeout 5m + # - echo "Running TLS Check" + # - bin/tls-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --log-level info --timeout 15m + # - echo "Checking deployed commits" + # - ./scripts/check-deployed-commit "api.demo.dp3.us" "$CI_COMMIT_SHA" ${TLS_KEY} ${TLS_CERT} ${TLS_CA} + after_script: + - *announce_failure + rules: + - *check_dp3 + +deploy_app_dp3: + stage: deploy + environment: DP3_ENV + image: + name: $DOCKER_APP_IMAGE + entrypoint: [""] + needs: + - build_push_app_dp3 + - deploy_migrations_dp3 + - compile_app_server + - compile_app_client + variables: + OPEN_TELEMETRY_SIDECAR: "true" + HEALTH_CHECK: "true" + before_script: + - *setup_aws_vars_dp3 + - *setup_release_dp3 + script: + - echo "Comparing against deployed commit" + # - ./scripts/compare-deployed-commit "" "$CI_COMMIT_SHA" "$TLS_KEY" "$TLS_CERT" "$TLS_CA" + - echo "Creating .go-version file if not already present" + - | + if [ -f ".go-version" ]; then + echo ".go-version already exists, no need to re-create" + else + GO_VERSION=$(awk '/golang/ { print $2 }' .tool-versions) + echo "Creating .go-version using version ${GO_VERSION}" + echo $GO_VERSION > .go-version + fi + - echo "Getting Digest from AWS" + - export ECR_DIGEST=$(aws ecr describe-images --repository-name app --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) + - echo "Getting otel collector digest from AWS" + - export OTEL_ECR_DIGEST=$(aws ecr describe-images --repository-name otel-collector --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) + - export OTEL_COLLECTOR_IMAGE="${ECR_REPOSITORY_URI}/otel-collector@${OTEL_ECR_DIGEST}" + - echo "Deploying app service" + - ./scripts/ecs-deploy-service-container app "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" + - echo "Running Health Check" + # - bin/health-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --tries 10 --backoff 3 --log-level info --timeout 5m + # - echo "Running TLS Check" + # - bin/tls-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --log-level info --timeout 15m + # - echo "Checking deployed commits" + - ./scripts/check-deployed-commit "my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us" "$CI_COMMIT_SHA" + after_script: + - *announce_failure + rules: + - *check_dp3 + +######################################################## +## STG push and deploy stages all off of main only ## +######################################################## + build_push_app_stg: stage: push environment: stg @@ -578,12 +1274,12 @@ deploy_app_stg: rules: - *check_main +############################################################################## +## PROD push and deploy stages all dependent on prod_approval manual stage ## +############################################################################## prod_approval: stage: prod_approval environment: prd_approval - image: - name: gcr.io/kaniko-project/executor:v1.14.0-debug - entrypoint: [""] needs: - compile_app_client - compile_app_server @@ -638,7 +1334,7 @@ build_push_migrations_prd: after_script: - *announce_failure rules: - - if: '$CI_COMMIT_BRANCH == "main"' + - *check_main build_push_tasks_prd: stage: push_prd @@ -691,7 +1387,7 @@ push_otel_collector_image_prd: after_script: - *announce_failure rules: - - if: '$CI_COMMIT_BRANCH == "main"' + - *check_main deploy_migrations_prd: stage: deploy_prd @@ -829,231 +1525,4 @@ deploy_app_prd: after_script: - *announce_failure rules: - - *check_main - - -build_push_app_dp3: - stage: push - environment: DP3_ENV - image: - name: gcr.io/kaniko-project/executor:v1.14.0-debug - entrypoint: [""] - needs: - - compile_app_client - - compile_app_server - before_script: - - *setup_aws_vars_dp3 - - *setup_release_dp3 - - *kaniko_before_setup - script: - - echo "Building and Pushing app Docker image..." - - /kaniko/executor --context "${CI_PROJECT_DIR}/" --dockerfile "${CI_PROJECT_DIR}/${APP_DOCKER_FILE}" --destination "${ECR_REPOSITORY_URI}/app:$CI_COMMIT_SHORT_SHA" - after_script: - - *announce_failure - rules: - - *check_dp3 - -build_push_migrations_dp3: - stage: push - environment: DP3_ENV - image: - name: gcr.io/kaniko-project/executor:v1.14.0-debug - entrypoint: [""] - needs: - - compile_app_server - - compile_app_client - before_script: - - *setup_aws_vars_dp3 - - *setup_release_dp3 - - *kaniko_before_setup - script: - - echo "Building and Pushing migrations Docker image..." - - /kaniko/executor --context "${CI_PROJECT_DIR}/" --dockerfile "${CI_PROJECT_DIR}/Dockerfile.migrations" --destination "${ECR_REPOSITORY_URI}/app-migrations:$CI_COMMIT_SHORT_SHA" - after_script: - - *announce_failure - rules: - - *check_dp3 - -build_push_tasks_dp3: - stage: push - environment: DP3_ENV - image: - name: gcr.io/kaniko-project/executor:v1.14.0-debug - entrypoint: [""] - needs: - - compile_app_server - - compile_app_client - before_script: - - *setup_aws_vars_dp3 - - *setup_release_dp3 - - *kaniko_before_setup - script: - - echo "Building tasks Docker image..." - - /kaniko/executor --context "${CI_PROJECT_DIR}/" --dockerfile "${CI_PROJECT_DIR}/${TASK_DOCKER_FILE}" --destination "${ECR_REPOSITORY_URI}/app-tasks:$CI_COMMIT_SHORT_SHA" - after_script: - - *announce_failure - rules: - - *check_dp3 - -push_otel_collector_image_dp3: - stage: push - environment: DP3_ENV - image: - name: $DOCKER_BASE_IMAGE - entrypoint: [""] - needs: - - compile_app_server - - compile_app_client - script: - - echo "Logging in to Amazon ECR with Crane..." - - aws ecr get-login-password --region us-gov-west-1 | crane auth login ${ECR_REPOSITORY_URI} -u AWS --password-stdin - - - echo "Pulling the AWS OTel Collector image from the public registry with Crane..." - - crane pull --insecure public.ecr.aws/aws-observability/aws-otel-collector:v0.31.0 image.tar - - - echo "Pushing the image to our private ECR using Crane..." - - crane push --insecure image.tar ${ECR_REPOSITORY_URI}/otel-collector:${CI_COMMIT_SHORT_SHA} - - - echo "Cleaning up the temporary image file..." - - rm image.tar - allow_failure: false - after_script: - - *announce_failure - rules: - - *check_dp3 - -deploy_migrations_dp3: - stage: deploy - environment: DP3_ENV - image: - name: $DOCKER_APP_IMAGE - entrypoint: [""] - needs: - - build_push_migrations_dp3 - - compile_app_server - - compile_app_client - before_script: - - *setup_aws_vars_dp3 - - *setup_release_dp3 - script: - # Step 1: Get the Digest - - echo "Getting Digest from AWS" - - export ECR_DIGEST=$(aws ecr describe-images --repository-name app-migrations --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) - # Step 2: Ensure exclusive execution and Snapshot - - echo "Snapshotting database" - - ./scripts/rds-snapshot-app-db "$APP_ENVIRONMENT" - # Step 3: Run migrations - - echo "Running migrations" - - ./scripts/ecs-run-app-migrations-container "${ECR_REPOSITORY_URI}/app-migrations@${ECR_DIGEST}" "${APP_ENVIRONMENT}" - after_script: - - *announce_failure - rules: - - *check_dp3 - -deploy_tasks_dp3: - stage: deploy - image: - name: $DOCKER_APP_IMAGE - entrypoint: [""] - needs: - - build_push_tasks_dp3 - - compile_app_server - - compile_app_client - before_script: - - *setup_release_dp3 - script: - - echo "Getting Digest from AWS" - - export ECR_DIGEST=$(aws ecr describe-images --repository-name app-tasks --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) - - echo "Deploying GHC fuel price data task service" - - ./scripts/ecs-deploy-task-container save-ghc-fuel-price-data "${ECR_REPOSITORY_URI}/app-tasks@${ECR_DIGEST}" "${APP_ENVIRONMENT}" - - echo "Deploying payment reminder email task service" - - ./scripts/ecs-deploy-task-container send-payment-reminder "${ECR_REPOSITORY_URI}/app-tasks@${ECR_DIGEST}" "${APP_ENVIRONMENT}" - after_script: - - *announce_failure - rules: - - *check_dp3 - -deploy_app_client_tls_dp3: - stage: deploy - environment: DP3_ENV - image: - name: $DOCKER_APP_IMAGE - entrypoint: [""] - needs: - - deploy_migrations_dp3 - - push_otel_collector_image_dp3 - - compile_app_server - - compile_app_client - variables: - OPEN_TELEMETRY_SIDECAR: "true" - HEALTH_CHECK: "true" - before_script: - - *setup_aws_vars_dp3 - - *setup_release_dp3 - script: - # - echo "Comparing against deployed commit" - # - ./scripts/compare-deployed-commit "" $CI_COMMIT_SHA ${TLS_KEY} ${TLS_CERT} ${TLS_CA} - - echo "Getting Digest from AWS" - - export ECR_DIGEST=$(aws ecr describe-images --repository-name app --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) - - echo "Getting otel collector Digest from AWS" - - export OTEL_ECR_DIGEST=$(aws ecr describe-images --repository-name otel-collector --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) - - export OTEL_COLLECTOR_IMAGE="${ECR_REPOSITORY_URI}/otel-collector@${OTEL_ECR_DIGEST}" - - echo "Deploying app-client-tls service" - - ./scripts/ecs-deploy-service-container app-client-tls "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" - - echo "Running Health Check" - # - bin/health-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --tries 10 --backoff 3 --log-level info --timeout 5m - # - echo "Running TLS Check" - # - bin/tls-checker --schemes https --hosts api.demo.dp3.us --key ${TLS_KEY} --cert ${TLS_CERT} --ca ${TLS_CA} --log-level info --timeout 15m - # - echo "Checking deployed commits" - # - ./scripts/check-deployed-commit "api.demo.dp3.us" "$CI_COMMIT_SHA" ${TLS_KEY} ${TLS_CERT} ${TLS_CA} - after_script: - - *announce_failure - rules: - - *check_dp3 - -deploy_app_dp3: - stage: deploy - environment: DP3_ENV - image: - name: $DOCKER_APP_IMAGE - entrypoint: [""] - needs: - - build_push_app_dp3 - - deploy_migrations_dp3 - - compile_app_server - - compile_app_client - variables: - OPEN_TELEMETRY_SIDECAR: "true" - HEALTH_CHECK: "true" - before_script: - - *setup_aws_vars_dp3 - - *setup_release_dp3 - script: - - echo "Comparing against deployed commit" - # - ./scripts/compare-deployed-commit "" "$CI_COMMIT_SHA" "$TLS_KEY" "$TLS_CERT" "$TLS_CA" - - echo "Creating .go-version file if not already present" - - | - if [ -f ".go-version" ]; then - echo ".go-version already exists, no need to re-create" - else - GO_VERSION=$(awk '/golang/ { print $2 }' .tool-versions) - echo "Creating .go-version using version ${GO_VERSION}" - echo $GO_VERSION > .go-version - fi - - echo "Getting Digest from AWS" - - export ECR_DIGEST=$(aws ecr describe-images --repository-name app --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) - - echo "Getting otel collector digest from AWS" - - export OTEL_ECR_DIGEST=$(aws ecr describe-images --repository-name otel-collector --image-ids imageTag=$CI_COMMIT_SHORT_SHA --query 'imageDetails[0].imageDigest' --output text) - - export OTEL_COLLECTOR_IMAGE="${ECR_REPOSITORY_URI}/otel-collector@${OTEL_ECR_DIGEST}" - - echo "Deploying app service" - - ./scripts/ecs-deploy-service-container app "${ECR_REPOSITORY_URI}/app@${ECR_DIGEST}" "${APP_ENVIRONMENT}" "/bin/milmove serve" - - echo "Running Health Check" - # - bin/health-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --tries 10 --backoff 3 --log-level info --timeout 5m - # - echo "Running TLS Check" - # - bin/tls-checker --schemes https --hosts my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us --log-level info --timeout 15m - # - echo "Checking deployed commits" - - ./scripts/check-deployed-commit "my.demo.dp3.us,office.demo.dp3.us,admin.demo.dp3.us" "$CI_COMMIT_SHA" - after_script: - - *announce_failure - rules: - - *check_dp3 \ No newline at end of file + - *check_main \ No newline at end of file From b4cd9199c6f073bb46d05e6c4755ca8a79dbbcd2 Mon Sep 17 00:00:00 2001 From: josiahzimmerman-caci Date: Thu, 9 Jan 2025 17:40:19 +0000 Subject: [PATCH 092/113] add test ignore rules --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 803b008bb71..63b5b379577 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -432,8 +432,8 @@ deploy_storybook_dp3: artifacts: paths: - /builds/milmove/mymove/storybook-static - # rules: - # - *check_main + rules: + - *check_main compile_app_client: stage: build From a9a849699e08f1e1490d11870b012eebb9c828a8 Mon Sep 17 00:00:00 2001 From: josiahzimmerman-caci Date: Thu, 9 Jan 2025 18:27:30 +0000 Subject: [PATCH 093/113] add tags --- .gitlab-ci.yml | 108 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 11 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 63b5b379577..0c625258232 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,6 @@ variables: DOCKERHUB_PASSWORD: DOCKERHUB_PASSWORD DOCKER_TOKEN: DOCKER_TOKEN registry: https://registry.hub.docker.com/ - #Circle CI need to replace #CIRCLE_PROJECT_USERNAME: "my-username" # Replace with the actual namespace CIRCLE_PROJECT_REPONAME: "mymove" # Replace with your GitLab project name @@ -22,23 +21,19 @@ variables: CIRCLE_BRANCH: "$CI_COMMIT_BRANCH" # Map to GitLab's branch variable #CIRCLE_TOKEN: "$GITLAB_API_TOKEN" # GitLab API token for querying pipelines CIRCLE_BUILD_NUM: "$CI_PIPELINE_ID" - GOPATH: "$CI_PROJECT_DIR/go" GOLANGCI_LINT_CONCURRENCY: "4" GOLANGCI_LINT_VERBOSE: "-v" - # Specify the environment: loadtest, demo, exp DP3_ENV: &dp3_env placeholder_env - # Specify the branch to deploy DP3_BRANCH: &dp3_branch placeholder_branch_name - # Ignore branches for integration tests INTEGRATION_IGNORE_BRANCH: &integration_ignore_branch placeholder_branch_name INTEGRATION_MTLS_IGNORE_BRANCH: &integration_mtls_ignore_branch placeholder_branch_name CLIENT_IGNORE_BRANCH: &client_ignore_branch placeholder_branch_name SERVER_IGNORE_BRANCH: &server_ignore_branch placeholder_branch_name - + RUNNER_TAG: &runner_tag milmove stages: - pre_checks - build @@ -48,21 +43,18 @@ stages: - prod_approval - push_prd - deploy_prd - #anchors #set safe directory and path .setup_milmove_env: &setup_milmove_env - git config --global --add safe.directory /builds/milmove/mymove - export PATH=${PATH}:${GOPATH}/bin:~/transcom/mymove/builds/milmove/mymove - export REACT_APP_ERROR_LOGGING=otel - .announce_failure: &announce_failure #- if [[ "$CI_COMMIT_BRANCH" == "main" && "$CI_JOB_STATUS" == "failed" ]]; then - echo $CI_COMMIT_BRANCH - echo $CI_JOB_STATUS - echo "Announcing broken branch in GitLab CI" # fi - .setup_aws_vars_dp3: &setup_aws_vars_dp3 - if [[ "$DP3_ENV" == "exp" OR "$DP3_ENV" == "loadtest" OR "$DP3_ENV" == "demo" ]]; then export AWS_DEFAULT_REGION=$(eval echo \$${DP3_ENV^^}_REGION) @@ -229,6 +221,8 @@ stages: sast: stage: pre_checks + tags: + - $RUNNER_TAG include: - template: Jobs/SAST.gitlab-ci.yml - template: Jobs/Dependency-Scanning.gitlab-ci.yml @@ -237,6 +231,8 @@ include: testing_services: stage: pre_checks image: alpine:3.7 + tags: + - $RUNNER_TAG allow_failure: true services: - php:7 @@ -246,9 +242,13 @@ testing_services: - php -v - node -v - go version + rules: + - *check_integration_ignore_branch anti_virus: stage: pre_checks + tags: + - $RUNNER_TAG image: milmove/clamav-ci # Custom image with ClamAV pre-installed script: - pwd @@ -278,6 +278,8 @@ anti_virus: # This is needed for things like pdfjs-dist prep_server_hosted_client_deps: stage: pre_checks + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE before_script: - *setup_milmove_env @@ -292,6 +294,8 @@ prep_server_hosted_client_deps: pre_deps_golang: stage: pre_checks + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE before_script: - *setup_milmove_env @@ -319,6 +323,8 @@ pre_deps_golang: pre_deps_yarn: stage: pre_checks + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_golang @@ -348,6 +354,8 @@ check_generated_code: check_tls_certificate_dp3: stage: pre_checks + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE # Replace with your appropriate Docker image. before_script: - *setup_aws_vars_dp3 @@ -370,6 +378,8 @@ check_tls_certificate_dp3: check_tls_certificate_stg: stage: pre_checks + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE # This can reB-18585-gitlab-pipeline-work unchanged, or you can use a lightweight image since no real work is done. before_script: - *setup_aws_vars_stg @@ -382,6 +392,8 @@ check_tls_certificate_stg: check_tls_certificate_prd: stage: pre_checks + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE before_script: - *setup_tls_vars_prd @@ -394,6 +406,8 @@ check_tls_certificate_prd: build_storybook: stage: build + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE variables: KUBERNETES_CPU_REQUEST: "4" @@ -418,6 +432,8 @@ build_storybook: deploy_storybook_dp3: stage: deploy + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_yarn @@ -437,6 +453,8 @@ deploy_storybook_dp3: compile_app_client: stage: build + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE <<: *yarn_cache variables: @@ -465,6 +483,8 @@ compile_app_client: compile_app_server: stage: build + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE <<: *yarn_cache variables: @@ -507,6 +527,8 @@ compile_app_server: pre_test: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE <<: *yarn_cache needs: @@ -558,6 +580,8 @@ pre_test: server_test: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_golang @@ -621,6 +645,8 @@ server_test: server_test_coverage: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_golang @@ -648,6 +674,8 @@ server_test_coverage: client_test: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE variables: KUBERNETES_CPU_REQUEST: "4" @@ -680,6 +708,8 @@ client_test: client_test_coverage: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_yarn @@ -701,6 +731,8 @@ client_test_coverage: integration_test_devseed: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_golang @@ -724,6 +756,8 @@ integration_test_devseed: integration_tests: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_yarn @@ -745,6 +779,8 @@ integration_tests: integration_test_mtls: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_yarn @@ -760,6 +796,8 @@ integration_test_mtls: integration_test_admin: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_yarn @@ -777,6 +815,8 @@ integration_test_admin: integration_test_my: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_yarn @@ -794,6 +834,8 @@ integration_test_my: integration_test_office: stage: test + tags: + - $RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_yarn @@ -815,6 +857,8 @@ integration_test_office: ############################################################### build_push_app_dp3: stage: push + tags: + - $RUNNER_TAG environment: DP3_ENV image: name: gcr.io/kaniko-project/executor:v1.14.0-debug @@ -836,6 +880,8 @@ build_push_app_dp3: build_push_migrations_dp3: stage: push + tags: + - $RUNNER_TAG environment: DP3_ENV image: name: gcr.io/kaniko-project/executor:v1.14.0-debug @@ -857,6 +903,8 @@ build_push_migrations_dp3: build_push_tasks_dp3: stage: push + tags: + - $RUNNER_TAG environment: DP3_ENV image: name: gcr.io/kaniko-project/executor:v1.14.0-debug @@ -878,6 +926,8 @@ build_push_tasks_dp3: push_otel_collector_image_dp3: stage: push + tags: + - $RUNNER_TAG environment: DP3_ENV image: name: $DOCKER_BASE_IMAGE @@ -905,6 +955,8 @@ push_otel_collector_image_dp3: deploy_migrations_dp3: stage: deploy + tags: + - $RUNNER_TAG environment: DP3_ENV image: name: $DOCKER_APP_IMAGE @@ -933,6 +985,8 @@ deploy_migrations_dp3: deploy_tasks_dp3: stage: deploy + tags: + - $RUNNER_TAG image: name: $DOCKER_APP_IMAGE entrypoint: [""] @@ -956,6 +1010,8 @@ deploy_tasks_dp3: deploy_app_client_tls_dp3: stage: deploy + tags: + - $RUNNER_TAG environment: DP3_ENV image: name: $DOCKER_APP_IMAGE @@ -994,6 +1050,8 @@ deploy_app_client_tls_dp3: deploy_app_dp3: stage: deploy + tags: + - $RUNNER_TAG environment: DP3_ENV image: name: $DOCKER_APP_IMAGE @@ -1045,6 +1103,8 @@ deploy_app_dp3: build_push_app_stg: stage: push + tags: + - $RUNNER_TAG environment: stg image: name: gcr.io/kaniko-project/executor:v1.14.0-debug @@ -1066,6 +1126,8 @@ build_push_app_stg: build_push_migrations_stg: stage: push + tags: + - $RUNNER_TAG environment: stg image: name: gcr.io/kaniko-project/executor:v1.14.0-debug @@ -1087,6 +1149,8 @@ build_push_migrations_stg: build_push_tasks_stg: stage: push + tags: + - $RUNNER_TAG environment: stg image: name: gcr.io/kaniko-project/executor:v1.14.0-debug @@ -1108,6 +1172,8 @@ build_push_tasks_stg: push_otel_collector_image_stg: stage: push + tags: + - $RUNNER_TAG environment: stg image: name: $DOCKER_BASE_IMAGE @@ -1138,6 +1204,8 @@ push_otel_collector_image_stg: deploy_migrations_stg: stage: deploy + tags: + - $RUNNER_TAG environment: stg image: name: $DOCKER_APP_IMAGE @@ -1166,6 +1234,8 @@ deploy_migrations_stg: deploy_tasks_stg: stage: deploy + tags: + - $RUNNER_TAG environment: stg image: name: $DOCKER_APP_IMAGE @@ -1229,6 +1299,8 @@ deploy_app_client_tls_stg: deploy_app_stg: stage: deploy + tags: + - $RUNNER_TAG environment: stg image: name: $DOCKER_APP_IMAGE @@ -1279,6 +1351,8 @@ deploy_app_stg: ############################################################################## prod_approval: stage: prod_approval + tags: + - $RUNNER_TAG environment: prd_approval needs: - compile_app_client @@ -1313,9 +1387,10 @@ build_push_app_prd: - *announce_failure rules: - *check_main - build_push_migrations_prd: stage: push_prd + tags: + - $RUNNER_TAG environment: prd image: name: gcr.io/kaniko-project/executor:v1.14.0-debug @@ -1339,6 +1414,8 @@ build_push_migrations_prd: build_push_tasks_prd: stage: push_prd environment: prd + tags: + - $RUNNER_TAG image: name: gcr.io/kaniko-project/executor:v1.14.0-debug entrypoint: [""] @@ -1360,6 +1437,8 @@ build_push_tasks_prd: push_otel_collector_image_prd: stage: push_prd + tags: + - $RUNNER_TAG environment: prd image: name: $DOCKER_BASE_IMAGE @@ -1377,7 +1456,6 @@ push_otel_collector_image_prd: - echo "Pulling the AWS OTel Collector image from the public registry with Crane..." - crane pull --insecure public.ecr.aws/aws-observability/aws-otel-collector:v0.31.0 image.tar - - echo "Pushing the image to our private ECR using Crane..." - crane push --insecure image.tar ${ECR_REPOSITORY_URI}/otel-collector:${CI_COMMIT_SHORT_SHA} @@ -1392,6 +1470,8 @@ push_otel_collector_image_prd: deploy_migrations_prd: stage: deploy_prd environment: prd + tags: + - $RUNNER_TAG image: name: $DOCKER_APP_IMAGE entrypoint: [""] @@ -1420,6 +1500,8 @@ deploy_migrations_prd: deploy_tasks_prd: stage: deploy_prd environment: prd + tags: + - $RUNNER_TAG image: name: $DOCKER_APP_IMAGE entrypoint: [""] @@ -1445,6 +1527,8 @@ deploy_tasks_prd: deploy_app_client_tls_prd: stage: deploy_prd environment: prd + tags: + - $RUNNER_TAG image: name: $DOCKER_APP_IMAGE entrypoint: [""] @@ -1482,6 +1566,8 @@ deploy_app_client_tls_prd: deploy_app_prd: stage: deploy_prd + tags: + - $RUNNER_TAG environment: prd image: name: $DOCKER_APP_IMAGE From 93b4fbb74ea0bf76e8f81620d3265ea5890cc4e2 Mon Sep 17 00:00:00 2001 From: AaronW Date: Thu, 9 Jan 2025 20:37:04 +0000 Subject: [PATCH 094/113] brought in changes again since an auto-merge issue happened --- .../payloads/model_to_payload_test.go | 88 +++++++++++++ pkg/handlers/internalapi/orders.go | 6 + pkg/handlers/internalapi/orders_test.go | 124 ++++++++++++++++++ pkg/models/order.go | 43 ++++++ pkg/testdatagen/scenario/shared.go | 54 ++++++++ pkg/testdatagen/testharness/dispatch.go | 3 + pkg/testdatagen/testharness/make_move.go | 16 +++ .../servicesCounselingFlows.spec.js | 29 ++++ playwright/tests/utils/testharness.js | 11 ++ 9 files changed, 374 insertions(+) diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go index ec3072d2ca0..40fb6e67ebf 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go @@ -9,6 +9,7 @@ import ( "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/gen/ghcmessages" + "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/models/roles" @@ -315,6 +316,93 @@ func (suite *PayloadsSuite) TestShipmentAddressUpdate() { }) } +func (suite *PayloadsSuite) TestMoveWithGBLOC() { + defaultOrdersNumber := "ORDER3" + defaultTACNumber := "F8E1" + defaultDepartmentIndicator := "AIR_AND_SPACE_FORCE" + defaultGrade := "E_1" + defaultHasDependents := false + defaultSpouseHasProGear := false + defaultOrdersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION + defaultOrdersTypeDetail := internalmessages.OrdersTypeDetail("HHG_PERMITTED") + defaultStatus := models.OrderStatusDRAFT + testYear := 2018 + defaultIssueDate := time.Date(testYear, time.March, 15, 0, 0, 0, 0, time.UTC) + defaultReportByDate := time.Date(testYear, time.August, 1, 0, 0, 0, 0, time.UTC) + defaultGBLOC := "KKFA" + + originDutyLocation := models.DutyLocation{ + Name: "Custom Origin", + } + originDutyLocationTOName := "origin duty location transportation office" + firstName := "customFirst" + lastName := "customLast" + serviceMember := models.ServiceMember{ + FirstName: &firstName, + LastName: &lastName, + } + uploadedOrders := models.Document{ + ID: uuid.Must(uuid.NewV4()), + } + dependents := 7 + entitlement := models.Entitlement{ + TotalDependents: &dependents, + } + amendedOrders := models.Document{ + ID: uuid.Must(uuid.NewV4()), + } + // Create order + order := factory.BuildOrder(suite.DB(), []factory.Customization{ + { + Model: originDutyLocation, + Type: &factory.DutyLocations.OriginDutyLocation, + }, + { + Model: models.TransportationOffice{ + Name: originDutyLocationTOName, + }, + Type: &factory.TransportationOffices.OriginDutyLocation, + }, + { + Model: serviceMember, + }, + { + Model: uploadedOrders, + Type: &factory.Documents.UploadedOrders, + }, + { + Model: entitlement, + }, + { + Model: amendedOrders, + Type: &factory.Documents.UploadedAmendedOrders, + }, + }, nil) + + suite.Equal(defaultOrdersNumber, *order.OrdersNumber) + suite.Equal(defaultTACNumber, *order.TAC) + suite.Equal(defaultDepartmentIndicator, *order.DepartmentIndicator) + suite.Equal(defaultGrade, string(*order.Grade)) + suite.Equal(defaultHasDependents, order.HasDependents) + suite.Equal(defaultSpouseHasProGear, order.SpouseHasProGear) + suite.Equal(defaultOrdersType, order.OrdersType) + suite.Equal(defaultOrdersTypeDetail, *order.OrdersTypeDetail) + suite.Equal(defaultStatus, order.Status) + suite.Equal(defaultIssueDate, order.IssueDate) + suite.Equal(defaultReportByDate, order.ReportByDate) + suite.Equal(defaultGBLOC, *order.OriginDutyLocationGBLOC) + + suite.Equal(originDutyLocation.Name, order.OriginDutyLocation.Name) + suite.Equal(originDutyLocationTOName, order.OriginDutyLocation.TransportationOffice.Name) + suite.Equal(*serviceMember.FirstName, *order.ServiceMember.FirstName) + suite.Equal(*serviceMember.LastName, *order.ServiceMember.LastName) + suite.Equal(uploadedOrders.ID, order.UploadedOrdersID) + suite.Equal(uploadedOrders.ID, order.UploadedOrders.ID) + suite.Equal(*entitlement.TotalDependents, *order.Entitlement.TotalDependents) + suite.Equal(amendedOrders.ID, *order.UploadedAmendedOrdersID) + suite.Equal(amendedOrders.ID, order.UploadedAmendedOrders.ID) +} + func (suite *PayloadsSuite) TestWeightTicketUpload() { uploadID, _ := uuid.NewV4() testURL := "https://testurl.com" diff --git a/pkg/handlers/internalapi/orders.go b/pkg/handlers/internalapi/orders.go index c9b5125c827..3936dcb39e2 100644 --- a/pkg/handlers/internalapi/orders.go +++ b/pkg/handlers/internalapi/orders.go @@ -382,6 +382,12 @@ func (h UpdateOrdersHandler) Handle(params ordersop.UpdateOrdersParams) middlewa order.OriginDutyLocation = &originDutyLocation order.OriginDutyLocationID = &originDutyLocationID + originGBLOC, originGBLOCerr := models.FetchGBLOCForPostalCode(appCtx.DB(), originDutyLocation.Address.PostalCode) + if originGBLOCerr != nil { + return handlers.ResponseForError(appCtx.Logger(), originGBLOCerr), originGBLOCerr + } + order.OriginDutyLocationGBLOC = &originGBLOC.GBLOC + if payload.MoveID != "" { moveID, err := uuid.FromString(payload.MoveID.String()) diff --git a/pkg/handlers/internalapi/orders_test.go b/pkg/handlers/internalapi/orders_test.go index b2ad7897f8a..59df8daf475 100644 --- a/pkg/handlers/internalapi/orders_test.go +++ b/pkg/handlers/internalapi/orders_test.go @@ -723,6 +723,130 @@ func (suite *HandlerSuite) TestUpdateOrdersHandler() { }) } +func (suite *HandlerSuite) TestUpdateOrdersHandlerOriginPostalCodeAndGBLOC() { + factory.BuildPostalCodeToGBLOC(suite.DB(), []factory.Customization{ + { + Model: models.PostalCodeToGBLOC{ + PostalCode: "90210", + GBLOC: "KKFA", + }, + }, + }, nil) + factory.BuildPostalCodeToGBLOC(suite.DB(), []factory.Customization{ + { + Model: models.PostalCodeToGBLOC{ + PostalCode: "35023", + GBLOC: "CNNQ", + }, + }, + }, nil) + + firstAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: "90210", + }, + }, + }, nil) + updatedAddress := factory.BuildAddress(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + PostalCode: "35023", + }, + }, + }, nil) + dutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + { + Model: models.DutyLocation{ + AddressID: firstAddress.ID, + }, + }, + }, nil) + updatedDutyLocation := factory.BuildDutyLocation(suite.DB(), []factory.Customization{ + { + Model: models.DutyLocation{ + AddressID: updatedAddress.ID, + }, + }, + }, nil) + newDutyLocation := factory.BuildDutyLocation(suite.DB(), nil, nil) + + order := factory.BuildOrder(suite.DB(), []factory.Customization{ + { + Model: models.Order{ + OriginDutyLocationID: &dutyLocation.ID, + NewDutyLocationID: newDutyLocation.ID, + }, + }, + }, nil) + + fetchedOrder, err := models.FetchOrder(suite.DB(), order.ID) + suite.NoError(err) + + var fetchedPostalCode, fetchedGBLOC string + fetchedPostalCode, err = fetchedOrder.GetOriginPostalCode(suite.DB()) + suite.NoError(err) + fetchedGBLOC, err = fetchedOrder.GetOriginGBLOC(suite.DB()) + suite.NoError(err) + + suite.Equal("90210", fetchedPostalCode) + suite.Equal("KKFA", fetchedGBLOC) + + newOrdersType := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION + issueDate := time.Date(2018, time.March, 10, 0, 0, 0, 0, time.UTC) + reportByDate := time.Date(2018, time.August, 1, 0, 0, 0, 0, time.UTC) + deptIndicator := internalmessages.DeptIndicatorAIRANDSPACEFORCE + + payload := &internalmessages.CreateUpdateOrders{ + OrdersType: &order.OrdersType, + NewDutyLocationID: handlers.FmtUUID(order.NewDutyLocationID), + OriginDutyLocationID: *handlers.FmtUUID(updatedDutyLocation.ID), + IssueDate: handlers.FmtDate(issueDate), + ReportByDate: handlers.FmtDate(reportByDate), + DepartmentIndicator: &deptIndicator, + HasDependents: handlers.FmtBool(false), + SpouseHasProGear: handlers.FmtBool(false), + ServiceMemberID: handlers.FmtUUID(order.ServiceMemberID), + Grade: models.ServiceMemberGradeE4.Pointer(), + } + + path := fmt.Sprintf("/orders/%v", order.ID.String()) + req := httptest.NewRequest("PUT", path, nil) + req = suite.AuthenticateRequest(req, order.ServiceMember) + + params := ordersop.UpdateOrdersParams{ + HTTPRequest: req, + OrdersID: *handlers.FmtUUID(order.ID), + UpdateOrders: payload, + } + + fakeS3 := storageTest.NewFakeS3Storage(true) + handlerConfig := suite.HandlerConfig() + handlerConfig.SetFileStorer(fakeS3) + + handler := UpdateOrdersHandler{handlerConfig} + + response := handler.Handle(params) + + suite.IsType(&ordersop.UpdateOrdersOK{}, response) + + okResponse := response.(*ordersop.UpdateOrdersOK) + + suite.NoError(okResponse.Payload.Validate(strfmt.Default)) + suite.Equal(string(newOrdersType), string(*okResponse.Payload.OrdersType)) + + fetchedOrder, err = models.FetchOrder(suite.DB(), order.ID) + suite.NoError(err) + + fetchedPostalCode, err = fetchedOrder.GetOriginPostalCode(suite.DB()) + suite.NoError(err) + fetchedGBLOC = *fetchedOrder.OriginDutyLocationGBLOC + suite.NoError(err) + + suite.Equal("35023", fetchedPostalCode) + suite.Equal("CNNQ", fetchedGBLOC) +} + func (suite *HandlerSuite) TestEntitlementHelperFunc() { orderGrade := internalmessages.OrderPayGrade("O-3") int64Dependents := int64(2) diff --git a/pkg/models/order.go b/pkg/models/order.go index 9898e7f27e3..f5cc022e239 100644 --- a/pkg/models/order.go +++ b/pkg/models/order.go @@ -309,6 +309,49 @@ func (o *Order) CreateNewMove(db *pop.Connection, moveOptions MoveOptions) (*Mov return createNewMove(db, *o, moveOptions) } +/* + * GetOriginPostalCode returns the GBLOC for the postal code of the the origin duty location of the order. + */ +func (o Order) GetOriginPostalCode(db *pop.Connection) (string, error) { + // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. + if uuid.UUID.IsNil(o.ID) { + return "", errors.WithMessage(ErrInvalidOrderID, "You must create the order in the DB before getting the origin GBLOC.") + } + + err := db.Load(&o, "OriginDutyLocation.Address") + if err != nil { + if err.Error() == RecordNotFoundErrorString { + return "", errors.WithMessage(err, "No Origin Duty Location was found for the order ID "+o.ID.String()) + } + return "", err + } + + return o.OriginDutyLocation.Address.PostalCode, nil +} + +/* + * GetOriginGBLOC returns the GBLOC for the postal code of the the origin duty location of the order. + */ +func (o Order) GetOriginGBLOC(db *pop.Connection) (string, error) { + // Since this requires looking up the order in the DB, the order must have an ID. This means, the order has to have been created first. + if uuid.UUID.IsNil(o.ID) { + return "", errors.WithMessage(ErrInvalidOrderID, "You must created the order in the DB before getting the destination GBLOC.") + } + + originPostalCode, err := o.GetOriginPostalCode(db) + if err != nil { + return "", err + } + + var originGBLOC PostalCodeToGBLOC + originGBLOC, err = FetchGBLOCForPostalCode(db, originPostalCode) + if err != nil { + return "", err + } + + return originGBLOC.GBLOC, nil +} + // IsComplete checks if orders have all fields necessary to approve a move func (o *Order) IsComplete() bool { diff --git a/pkg/testdatagen/scenario/shared.go b/pkg/testdatagen/scenario/shared.go index 15e8836bc1d..2b2990d46bc 100644 --- a/pkg/testdatagen/scenario/shared.go +++ b/pkg/testdatagen/scenario/shared.go @@ -10519,6 +10519,60 @@ func CreateNeedsServicesCounseling(appCtx appcontext.AppContext, ordersType inte return move } +/* +Create Needs Service Counseling in a non-default GBLOC - pass in orders with all required information, shipment type, destination type, locator +*/ +func CreateNeedsServicesCounselingInOtherGBLOC(appCtx appcontext.AppContext, ordersType internalmessages.OrdersType, shipmentType models.MTOShipmentType, destinationType *models.DestinationType, locator string) models.Move { + db := appCtx.DB() + originDutyLocationAddress := factory.BuildAddress(db, []factory.Customization{ + { + Model: models.Address{ + PostalCode: "35023", + }, + }, + }, nil) + originDutyLocation := factory.BuildDutyLocation(db, []factory.Customization{ + { + Model: models.DutyLocation{ + Name: "Test Location", + AddressID: originDutyLocationAddress.ID, + }, + }, + }, nil) + order := factory.BuildOrder(db, []factory.Customization{ + { + Model: originDutyLocation, + }, + }, nil) + move := factory.BuildMove(db, []factory.Customization{ + { + Model: order, + LinkOnly: true, + }, + { + Model: models.Move{ + Locator: locator, + Status: models.MoveStatusNeedsServiceCounseling, + }, + }, + }, nil) + + factory.BuildMTOShipment(db, []factory.Customization{ + { + Model: move, + LinkOnly: true, + }, + { + Model: models.MTOShipment{ + ShipmentType: shipmentType, + Status: models.MTOShipmentStatusSubmitted, + }, + }, + }, nil) + + return move +} + func CreateNeedsServicesCounselingWithAmendedOrders(appCtx appcontext.AppContext, userUploader *uploader.UserUploader, ordersType internalmessages.OrdersType, shipmentType models.MTOShipmentType, destinationType *models.DestinationType, locator string) models.Move { db := appCtx.DB() submittedAt := time.Now() diff --git a/pkg/testdatagen/testharness/dispatch.go b/pkg/testdatagen/testharness/dispatch.go index e9cf4c27d5f..dd4b421cc2c 100644 --- a/pkg/testdatagen/testharness/dispatch.go +++ b/pkg/testdatagen/testharness/dispatch.go @@ -50,6 +50,9 @@ var actionDispatcher = map[string]actionFunc{ "HHGMoveNeedsSC": func(appCtx appcontext.AppContext) testHarnessResponse { return MakeHHGMoveNeedsSC(appCtx) }, + "HHGMoveNeedsSCOtherGBLOC": func(appCtx appcontext.AppContext) testHarnessResponse { + return MakeHHGMoveNeedsSCOtherGBLOC(appCtx) + }, "HHGMoveAsUSMCNeedsSC": func(appCtx appcontext.AppContext) testHarnessResponse { return MakeHHGMoveNeedsServicesCounselingUSMC(appCtx) }, diff --git a/pkg/testdatagen/testharness/make_move.go b/pkg/testdatagen/testharness/make_move.go index ddd96329c38..aae7c83ecfa 100644 --- a/pkg/testdatagen/testharness/make_move.go +++ b/pkg/testdatagen/testharness/make_move.go @@ -4013,6 +4013,22 @@ func MakeHHGMoveNeedsSC(appCtx appcontext.AppContext) models.Move { return *newmove } +// MakeHHGMoveNeedsSCOtherGBLOC creates an fully ready move needing SC approval in a non-default GBLOC +func MakeHHGMoveNeedsSCOtherGBLOC(appCtx appcontext.AppContext) models.Move { + pcos := internalmessages.OrdersTypePERMANENTCHANGEOFSTATION + hhg := models.MTOShipmentTypeHHG + locator := models.GenerateLocator() + move := scenario.CreateNeedsServicesCounselingInOtherGBLOC(appCtx, pcos, hhg, nil, locator) + + // re-fetch the move so that we ensure we have exactly what is in + // the db + newmove, err := models.FetchMove(appCtx.DB(), &auth.Session{}, move.ID) + if err != nil { + log.Panic(fmt.Errorf("failed to fetch move: %w", err)) + } + return *newmove +} + // MakeBoatHaulAwayMoveNeedsSC creates an fully ready move with a boat haul-away shipment needing SC approval func MakeBoatHaulAwayMoveNeedsSC(appCtx appcontext.AppContext) models.Move { userUploader := newUserUploader(appCtx) diff --git a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js index 6ab07e9026c..5aa4db714c3 100644 --- a/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js +++ b/playwright/tests/office/servicescounseling/servicesCounselingFlows.spec.js @@ -13,6 +13,33 @@ const supportingDocsEnabled = process.env.FEATURE_FLAG_MANAGE_SUPPORTING_DOCS; const LocationLookup = 'BEVERLY HILLS, CA 90210 (LOS ANGELES)'; test.describe('Services counselor user', () => { + test.describe('GBLOC tests', () => { + test.describe('Origin Duty Location', () => { + let moveLocatorKKFA = ''; + let moveLocatorCNNQ = ''; + test.beforeEach(async ({ scPage }) => { + const moveKKFA = await scPage.testHarness.buildHHGMoveNeedsSC(); + moveLocatorKKFA = moveKKFA.locator; + const moveCNNQ = await scPage.testHarness.buildHHGMoveNeedsSC(); + moveLocatorCNNQ = moveCNNQ.locator; + }); + + test('when origin duty location GBLOC matches services counselor GBLOC', async ({ page }) => { + const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); + await locatorFilter.fill(moveLocatorKKFA); + await locatorFilter.blur(); + await expect(page.getByTestId('locator-0')).toBeVisible(); + }); + + test('when origin duty location GBLOC does not match services counselor GBLOC', async ({ page }) => { + const locatorFilter = await page.getByTestId('locator').getByTestId('TextBoxFilter'); + await locatorFilter.fill(moveLocatorCNNQ); + await locatorFilter.blur(); + await expect(page.getByTestId('locator-0')).not.toBeVisible(); + }); + }); + }); + test.describe('with basic HHG move', () => { test.beforeEach(async ({ scPage }) => { const move = await scPage.testHarness.buildHHGMoveNeedsSC(); @@ -351,6 +378,8 @@ test.describe('Services counselor user', () => { await expect(page.getByText(LocationLookup, { exact: true })).toBeVisible(); await page.keyboard.press('Enter'); await page.locator('select[name="destinationType"]').selectOption({ label: 'Home of selection (HOS)' }); + await page.getByLabel('Requested pickup date').fill('16 Mar 2022'); + await page.locator('[data-testid="submitForm"]').click(); await scPage.waitForLoading(); diff --git a/playwright/tests/utils/testharness.js b/playwright/tests/utils/testharness.js index a01dac3461e..74feee4ffef 100644 --- a/playwright/tests/utils/testharness.js +++ b/playwright/tests/utils/testharness.js @@ -28,6 +28,9 @@ export class TestHarness { * @property {string} id * @property {string} locator * @property {Object} Orders + * @property {Object} OriginDutyLocation + * @property {Object} OriginDutyLocation.Address + * @property {string} OriginDutyLocation.Address.PostalCode * @property {Object} Orders.NewDutyLocation * @property {string} Orders.NewDutyLocation.name * @property {Object} Orders.ServiceMember @@ -392,6 +395,14 @@ export class TestHarness { return this.buildDefault('HHGMoveNeedsSC'); } + /** + * Use testharness to build hhg move needing SC approval in a non-default GBLOC + * @returns {Promise} + */ + async buildHHGMoveNeedsSCInOtherGBLOC() { + return this.buildDefault('HHGMoveNeedsSCOtherGBLOC'); + } + /** * Use testharness to build hhg move as USMC needing SC approval * @returns {Promise} From 4c2068d366552b95a3c8f7becf6d83382d185247 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Fri, 10 Jan 2025 17:26:57 +0000 Subject: [PATCH 095/113] suite setup changes --- pkg/factory/entitlement_factory.go | 14 ++++++++++++++ pkg/factory/entitlement_factory_test.go | 10 +++++++--- pkg/factory/shared_test.go | 8 ++++++++ pkg/handlers/ghcapi/api_test.go | 13 +++++++++++++ pkg/handlers/ghcapi/move_task_order_test.go | 2 +- pkg/handlers/ghcapi/move_test.go | 2 +- pkg/handlers/ghcapi/moving_expense_test.go | 18 ++++++++++++++---- pkg/handlers/ghcapi/ppm_document_test.go | 10 ++++++---- pkg/handlers/ghcapi/ppm_shipment_test.go | 5 +++-- pkg/handlers/internalapi/api_test.go | 8 ++++++++ .../internal/payloads/payloads_test.go | 8 ++++++++ pkg/handlers/primeapi/api_test.go | 2 ++ .../primeapi/payloads/payloads_test.go | 2 ++ pkg/handlers/primeapiv2/api_test.go | 2 ++ .../primeapiv2/payloads/payloads_test.go | 2 ++ pkg/handlers/primeapiv3/api_test.go | 2 ++ .../primeapiv3/payloads/payloads_test.go | 2 ++ .../entitlements/entitlements_service_test.go | 7 +++++++ pkg/services/event/event_test.go | 2 ++ pkg/services/move/move_service_test.go | 2 ++ .../mto_shipment/mto_shipment_service_test.go | 2 ++ .../pptas_report/pptas_report_service_test.go | 7 ------- .../shipment_summary_worksheet_service_test.go | 7 ------- 23 files changed, 108 insertions(+), 29 deletions(-) diff --git a/pkg/factory/entitlement_factory.go b/pkg/factory/entitlement_factory.go index 80a27ba5e61..3d6a374bf9d 100644 --- a/pkg/factory/entitlement_factory.go +++ b/pkg/factory/entitlement_factory.go @@ -162,6 +162,20 @@ func BuildHHGAllowance(db *pop.Connection, customs []Customization, traits []Tra return hhgAllowance } +func DeleteAllotmentsFromDatabase(db *pop.Connection) error { + if db != nil { + err := db.RawQuery("DELETE FROM hhg_allowances").Exec() + if err != nil { + return err + } + err = db.RawQuery("DELETE FROM pay_grades").Exec() + if err != nil { + return err + } + } + return nil +} + // Helper function to retrieve default weight data by grade func getDefaultWeightData(grade string) struct { TotalWeightSelf int diff --git a/pkg/factory/entitlement_factory_test.go b/pkg/factory/entitlement_factory_test.go index 01b65b7cbb8..1e865f500df 100644 --- a/pkg/factory/entitlement_factory_test.go +++ b/pkg/factory/entitlement_factory_test.go @@ -10,7 +10,6 @@ import ( func (suite *FactorySuite) TestBuildEntitlement() { fetcher := entitlements.NewWeightAllotmentFetcher() - setupE1Allotment := func() { pg := BuildPayGrade(suite.DB(), []Customization{ { @@ -301,10 +300,15 @@ func (suite *FactorySuite) TestBuildHHGAllowance() { }) } +func (suite *FactorySuite) deleteAllotmentsFromDatabase() { + err := suite.DB().RawQuery("DELETE FROM hhg_allowances").Exec() + suite.FatalNoError(err) + err = suite.DB().RawQuery("DELETE FROM pay_grades").Exec() + suite.FatalNoError(err) +} + func (suite *FactorySuite) TestSetupAllAllotments() { suite.Run("Successful creation of allotments for all known grades", func() { - err := suite.DB().TruncateAll() - suite.NoError(err) SetupDefaultAllotments(suite.DB()) diff --git a/pkg/factory/shared_test.go b/pkg/factory/shared_test.go index 3e9dffffd11..35c951b834e 100644 --- a/pkg/factory/shared_test.go +++ b/pkg/factory/shared_test.go @@ -15,6 +15,14 @@ type FactorySuite struct { *testingsuite.PopTestSuite } +func (suite *FactorySuite) SetupSuite() { + suite.PreloadData(func() { + // The factory suite creates allotments and allowances, make sure they're wiped + // This is because pay grades and hhg allowances are excluded from truncation, meaning they have static data built in + suite.deleteAllotmentsFromDatabase() + }) +} + func TestFactorySuite(t *testing.T) { ts := &FactorySuite{ diff --git a/pkg/handlers/ghcapi/api_test.go b/pkg/handlers/ghcapi/api_test.go index c608e5de951..5050123528d 100644 --- a/pkg/handlers/ghcapi/api_test.go +++ b/pkg/handlers/ghcapi/api_test.go @@ -16,6 +16,19 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } +func (suite *HandlerSuite) deleteAllotmentsFromDatabase() { + err := suite.DB().RawQuery("DELETE FROM hhg_allowances").Exec() + suite.FatalNoError(err) + err = suite.DB().RawQuery("DELETE FROM pay_grades").Exec() + suite.FatalNoError(err) +} + +func (suite *HandlerSuite) SetupSuite() { + suite.PreloadData(func() { + suite.deleteAllotmentsFromDatabase() + }) +} + // AfterTest completes tests by trying to close open files func (suite *HandlerSuite) AfterTest() { for _, file := range suite.TestFilesToClose() { diff --git a/pkg/handlers/ghcapi/move_task_order_test.go b/pkg/handlers/ghcapi/move_task_order_test.go index a156eb19251..132821069c1 100644 --- a/pkg/handlers/ghcapi/move_task_order_test.go +++ b/pkg/handlers/ghcapi/move_task_order_test.go @@ -108,7 +108,7 @@ func (suite *HandlerSuite) TestUpdateMoveTaskOrderHandlerIntegrationSuccess() { suite.MustSave(&csTaskOrderFee) } - suite.PreloadData(setupPricerData) + setupPricerData() setUpSignedCertificationCreatorMock := func(returnValue ...interface{}) services.SignedCertificationCreator { mockCreator := &mocks.SignedCertificationCreator{} diff --git a/pkg/handlers/ghcapi/move_test.go b/pkg/handlers/ghcapi/move_test.go index f345c7cfb85..05e3ee43a55 100644 --- a/pkg/handlers/ghcapi/move_test.go +++ b/pkg/handlers/ghcapi/move_test.go @@ -927,7 +927,7 @@ func (suite *HandlerSuite) TestCheckForLockedMovesAndUnlockHandler() { return req, handler } - suite.PreloadData(setupLockedMove) + setupLockedMove() suite.Run("Successful unlocking of move", func() { req, handler := setupTestData() diff --git a/pkg/handlers/ghcapi/moving_expense_test.go b/pkg/handlers/ghcapi/moving_expense_test.go index 06c8d03cfc6..b95a42ef201 100644 --- a/pkg/handlers/ghcapi/moving_expense_test.go +++ b/pkg/handlers/ghcapi/moving_expense_test.go @@ -27,7 +27,7 @@ import ( func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerUnit() { var ppmShipment models.PPMShipment - suite.PreloadData(func() { + setupData := func() { userUploader, err := uploader.NewUserUploader(suite.createS3HandlerConfig().FileStorer(), uploader.MaxCustomerUserUploadFileSizeLimit) suite.FatalNoError(err) @@ -54,7 +54,8 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerUnit() { }, }, nil), ) - }) + } + setupData() setUpRequestAndParams := func() movingexpenseops.UpdateMovingExpenseParams { movingExpense := ppmShipment.MovingExpenses[0] @@ -236,7 +237,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { var ppmShipment models.PPMShipment var movingExpense models.MovingExpense - suite.PreloadData(func() { + setupData := func() { var err error userUploader, err = uploader.NewUserUploader(suite.createS3HandlerConfig().FileStorer(), uploader.MaxCustomerUserUploadFileSizeLimit) @@ -270,7 +271,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { }, nil) ppmShipment.MovingExpenses = append(ppmShipment.MovingExpenses, movingExpense) - }) + } setUpRequestAndParams := func(movingExpense models.MovingExpense) movingexpenseops.UpdateMovingExpenseParams { endpoint := fmt.Sprintf("/ppm-shipments/%s/moving-expense/%s", ppmShipment.ID.String(), movingExpense.ID.String()) @@ -330,6 +331,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { suite.Run("Success", func() { suite.Run("Can approve a moving expense", func() { + setupData() params := setUpRequestAndParams(ppmShipment.MovingExpenses[0]) params.UpdateMovingExpense.Status = ghcmessages.PPMDocumentStatusAPPROVED @@ -352,6 +354,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { }) suite.Run("Can exclude a moving expense", func() { + setupData() params := setUpRequestAndParams(ppmShipment.MovingExpenses[0]) reason := "Not a valid receipt" @@ -380,6 +383,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { }) suite.Run("Can reject a moving expense", func() { + setupData() params := setUpRequestAndParams(ppmShipment.MovingExpenses[0]) reason := "Over budget!" @@ -408,6 +412,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { }) suite.Run("Can update a non-storage moving expense", func() { + setupData() params := setUpRequestAndParams(ppmShipment.MovingExpenses[0]) newAmount := movingExpense.Amount.AddCents(1000) @@ -435,6 +440,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { }) suite.Run("Can update a storage moving expense", func() { + setupData() storageExpenseType := models.MovingExpenseReceiptTypeStorage storageMovingExpense := factory.BuildMovingExpense(suite.DB(), []factory.Customization{ { @@ -503,6 +509,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { suite.Run("Failure", func() { suite.Run("Returns a Forbidden response if the request doesn't come from the office app", func() { + setupData() params := setUpRequestAndParams(ppmShipment.MovingExpenses[0]) params.HTTPRequest = suite.AuthenticateRequest(params.HTTPRequest, ppmShipment.Shipment.MoveTaskOrder.Orders.ServiceMember) @@ -515,6 +522,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { }) suite.Run("Returns a NotFound response when the moving expense is not found", func() { + setupData() params := setUpRequestAndParams(ppmShipment.MovingExpenses[0]) params.MovingExpenseID = handlers.FmtUUIDValue(uuid.Must(uuid.NewV4())) @@ -527,6 +535,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { }) suite.Run("Returns a PreconditionFailed response when the eTag doesn't match the expected eTag", func() { + setupData() params := setUpRequestAndParams(ppmShipment.MovingExpenses[0]) params.IfMatch = "wrong eTag" @@ -547,6 +556,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerIntegration() { }) suite.Run("Returns an UnprocessableEntity response when the requested updates aren't valid", func() { + setupData() params := setUpRequestAndParams(ppmShipment.MovingExpenses[0]) params.UpdateMovingExpense = &ghcmessages.UpdateMovingExpense{ diff --git a/pkg/handlers/ghcapi/ppm_document_test.go b/pkg/handlers/ghcapi/ppm_document_test.go index 576a792f43c..aaea0745f34 100644 --- a/pkg/handlers/ghcapi/ppm_document_test.go +++ b/pkg/handlers/ghcapi/ppm_document_test.go @@ -28,7 +28,7 @@ import ( func (suite *HandlerSuite) TestGetPPMDocumentsHandlerUnit() { var ppmShipment models.PPMShipment - suite.PreloadData(func() { + setupData := func() { userUploader, err := uploader.NewUserUploader(suite.createS3HandlerConfig().FileStorer(), uploader.MaxCustomerUserUploadFileSizeLimit) suite.FatalNoError(err) @@ -103,7 +103,8 @@ func (suite *HandlerSuite) TestGetPPMDocumentsHandlerUnit() { ) } - }) + } + setupData() setUpRequestAndParams := func() ppmdocumentops.GetPPMDocumentsParams { endpoint := fmt.Sprintf("/shipments/%s/ppm-documents", ppmShipment.Shipment.ID.String()) @@ -233,7 +234,7 @@ func (suite *HandlerSuite) TestGetPPMDocumentsHandlerIntegration() { var ppmShipment models.PPMShipment - suite.PreloadData(func() { + setupData := func() { userUploader, err := uploader.NewUserUploader(suite.createS3HandlerConfig().FileStorer(), uploader.MaxCustomerUserUploadFileSizeLimit) suite.FatalNoError(err) @@ -308,7 +309,8 @@ func (suite *HandlerSuite) TestGetPPMDocumentsHandlerIntegration() { ) } - }) + } + setupData() setUpParamsAndHandler := func() (ppmdocumentops.GetPPMDocumentsParams, GetPPMDocumentsHandler) { endpoint := fmt.Sprintf("/shipments/%s/ppm-documents", ppmShipment.Shipment.ID.String()) diff --git a/pkg/handlers/ghcapi/ppm_shipment_test.go b/pkg/handlers/ghcapi/ppm_shipment_test.go index d5e358feb4a..d1ddd182159 100644 --- a/pkg/handlers/ghcapi/ppm_shipment_test.go +++ b/pkg/handlers/ghcapi/ppm_shipment_test.go @@ -344,7 +344,7 @@ func (suite *HandlerSuite) TestGetPPMSITEstimatedCostHandler() { }) } - suite.PreloadData(func() { + setupData := func() { setupPricerData() sitLocationDestination := models.SITLocationTypeDestination entryDate := time.Date(2020, time.March, 15, 0, 0, 0, 0, time.UTC) @@ -389,7 +389,8 @@ func (suite *HandlerSuite) TestGetPPMSITEstimatedCostHandler() { mockedPlanner := &routemocks.Planner{} mockedPlanner.On("ZipTransitDistance", mock.AnythingOfType("*appcontext.appContext"), "90210", "30813", false, false).Return(2294, nil) - }) + } + setupData() setUpGetCostRequestAndParams := func() ppmsitops.GetPPMSITEstimatedCostParams { endpoint := fmt.Sprintf("/ppm-shipments/%s/sit_location/%s/sit-estimated-cost", ppmShipment.ID.String(), *ppmShipment.SITLocation) diff --git a/pkg/handlers/internalapi/api_test.go b/pkg/handlers/internalapi/api_test.go index 0f56159a405..5776e66fce2 100644 --- a/pkg/handlers/internalapi/api_test.go +++ b/pkg/handlers/internalapi/api_test.go @@ -27,8 +27,16 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } +func (suite *HandlerSuite) deleteAllotmentsFromDatabase() { + err := suite.DB().RawQuery("DELETE FROM hhg_allowances").Exec() + suite.FatalNoError(err) + err = suite.DB().RawQuery("DELETE FROM pay_grades").Exec() + suite.FatalNoError(err) +} + func (suite *HandlerSuite) SetupSuite() { suite.PreloadData(func() { + suite.deleteAllotmentsFromDatabase() factory.SetupDefaultAllotments(suite.DB()) factory.FetchOrBuildCountry(suite.DB(), []factory.Customization{ { diff --git a/pkg/handlers/internalapi/internal/payloads/payloads_test.go b/pkg/handlers/internalapi/internal/payloads/payloads_test.go index 6913c01afb7..6e152f3b6b9 100644 --- a/pkg/handlers/internalapi/internal/payloads/payloads_test.go +++ b/pkg/handlers/internalapi/internal/payloads/payloads_test.go @@ -19,8 +19,16 @@ type PayloadsSuite struct { storer storage.FileStorer } +func (suite *PayloadsSuite) deleteAllotmentsFromDatabase() { + err := suite.DB().RawQuery("DELETE FROM hhg_allowances").Exec() + suite.FatalNoError(err) + err = suite.DB().RawQuery("DELETE FROM pay_grades").Exec() + suite.FatalNoError(err) +} + func (suite *PayloadsSuite) SetupSuite() { suite.PreloadData(func() { + suite.deleteAllotmentsFromDatabase() factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/handlers/primeapi/api_test.go b/pkg/handlers/primeapi/api_test.go index 4ab9b83e4e5..2e982e8cdd6 100644 --- a/pkg/handlers/primeapi/api_test.go +++ b/pkg/handlers/primeapi/api_test.go @@ -20,6 +20,8 @@ type HandlerSuite struct { func (suite *HandlerSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/handlers/primeapi/payloads/payloads_test.go b/pkg/handlers/primeapi/payloads/payloads_test.go index 8f9d7410aa3..d49184bbc27 100644 --- a/pkg/handlers/primeapi/payloads/payloads_test.go +++ b/pkg/handlers/primeapi/payloads/payloads_test.go @@ -18,6 +18,8 @@ type PayloadsSuite struct { func (suite *PayloadsSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/handlers/primeapiv2/api_test.go b/pkg/handlers/primeapiv2/api_test.go index c9ffc043d29..537e8592109 100644 --- a/pkg/handlers/primeapiv2/api_test.go +++ b/pkg/handlers/primeapiv2/api_test.go @@ -18,6 +18,8 @@ type HandlerSuite struct { func (suite *HandlerSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/handlers/primeapiv2/payloads/payloads_test.go b/pkg/handlers/primeapiv2/payloads/payloads_test.go index 8f9d7410aa3..d49184bbc27 100644 --- a/pkg/handlers/primeapiv2/payloads/payloads_test.go +++ b/pkg/handlers/primeapiv2/payloads/payloads_test.go @@ -18,6 +18,8 @@ type PayloadsSuite struct { func (suite *PayloadsSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/handlers/primeapiv3/api_test.go b/pkg/handlers/primeapiv3/api_test.go index 0c425c90948..070b3b80056 100644 --- a/pkg/handlers/primeapiv3/api_test.go +++ b/pkg/handlers/primeapiv3/api_test.go @@ -25,6 +25,8 @@ func (suite *HandlerSuite) AfterTest() { func (suite *HandlerSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/handlers/primeapiv3/payloads/payloads_test.go b/pkg/handlers/primeapiv3/payloads/payloads_test.go index 8f9d7410aa3..d49184bbc27 100644 --- a/pkg/handlers/primeapiv3/payloads/payloads_test.go +++ b/pkg/handlers/primeapiv3/payloads/payloads_test.go @@ -18,6 +18,8 @@ type PayloadsSuite struct { func (suite *PayloadsSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/services/entitlements/entitlements_service_test.go b/pkg/services/entitlements/entitlements_service_test.go index 241ebd258cd..fb3da88399f 100644 --- a/pkg/services/entitlements/entitlements_service_test.go +++ b/pkg/services/entitlements/entitlements_service_test.go @@ -6,6 +6,7 @@ import ( "github.com/spf13/afero" "github.com/stretchr/testify/suite" + "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -15,6 +16,12 @@ type EntitlementsServiceSuite struct { fs *afero.Afero } +func (suite *EntitlementsServiceSuite) SetupSuite() { + suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) + }) +} func TestEntitlementsServiceSuite(t *testing.T) { var f = afero.NewMemMapFs() file := &afero.Afero{Fs: f} diff --git a/pkg/services/event/event_test.go b/pkg/services/event/event_test.go index d50ba34a524..1d1e6d2ea87 100644 --- a/pkg/services/event/event_test.go +++ b/pkg/services/event/event_test.go @@ -22,6 +22,8 @@ type EventServiceSuite struct { func (suite *EventServiceSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/services/move/move_service_test.go b/pkg/services/move/move_service_test.go index 197c48f4c93..b40530b87cb 100644 --- a/pkg/services/move/move_service_test.go +++ b/pkg/services/move/move_service_test.go @@ -15,6 +15,8 @@ type MoveServiceSuite struct { func (suite *MoveServiceSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/services/mto_shipment/mto_shipment_service_test.go b/pkg/services/mto_shipment/mto_shipment_service_test.go index 3cba0276e4e..57e41e8191d 100644 --- a/pkg/services/mto_shipment/mto_shipment_service_test.go +++ b/pkg/services/mto_shipment/mto_shipment_service_test.go @@ -15,6 +15,8 @@ type MTOShipmentServiceSuite struct { func (suite *MTOShipmentServiceSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/services/pptas_report/pptas_report_service_test.go b/pkg/services/pptas_report/pptas_report_service_test.go index 8b8aa416896..11a857a646e 100644 --- a/pkg/services/pptas_report/pptas_report_service_test.go +++ b/pkg/services/pptas_report/pptas_report_service_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -13,12 +12,6 @@ type ReportServiceSuite struct { *testingsuite.PopTestSuite } -func (suite *ReportServiceSuite) SetupSuite() { - suite.PreloadData(func() { - factory.SetupDefaultAllotments(suite.DB()) - }) -} - func TestReportServiceSuite(t *testing.T) { ts := &ReportServiceSuite{ PopTestSuite: testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), diff --git a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_service_test.go b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_service_test.go index a6ae372873a..86e6cd7f457 100644 --- a/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_service_test.go +++ b/pkg/services/shipment_summary_worksheet/shipment_summary_worksheet_service_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -13,12 +12,6 @@ type ShipmentSummaryWorksheetServiceSuite struct { *testingsuite.PopTestSuite } -func (suite *ShipmentSummaryWorksheetServiceSuite) SetupSuite() { - suite.PreloadData(func() { - factory.SetupDefaultAllotments(suite.DB()) - }) -} - func TestShipmentSummaryWorksheetServiceSuite(t *testing.T) { ts := &ShipmentSummaryWorksheetServiceSuite{ testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), testingsuite.WithPerTestTransaction()), From 18fb4df000384c66ddbd64007992461bfb2ce88e Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Fri, 10 Jan 2025 13:15:06 -0500 Subject: [PATCH 096/113] should be the last of the suite adjustments --- .../supportapi/internal/payloads/model_to_payload_test.go | 2 ++ pkg/services/order/order_service_test.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go index cbf3fb8e66f..85003478d7b 100644 --- a/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go @@ -25,6 +25,8 @@ type PayloadsSuite struct { func (suite *PayloadsSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } diff --git a/pkg/services/order/order_service_test.go b/pkg/services/order/order_service_test.go index f2c2a57c8e1..ace19077dd7 100644 --- a/pkg/services/order/order_service_test.go +++ b/pkg/services/order/order_service_test.go @@ -15,6 +15,8 @@ type OrderServiceSuite struct { func (suite *OrderServiceSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } From fece94954c369273b691609f1725844ea2634ce7 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Fri, 10 Jan 2025 18:48:02 +0000 Subject: [PATCH 097/113] fix suite test --- pkg/handlers/supportapi/api_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/handlers/supportapi/api_test.go b/pkg/handlers/supportapi/api_test.go index 688581e3c3b..38303355669 100644 --- a/pkg/handlers/supportapi/api_test.go +++ b/pkg/handlers/supportapi/api_test.go @@ -18,6 +18,8 @@ type HandlerSuite struct { func (suite *HandlerSuite) SetupSuite() { suite.PreloadData(func() { + err := factory.DeleteAllotmentsFromDatabase(suite.DB()) + suite.FatalNoError(err) factory.SetupDefaultAllotments(suite.DB()) }) } From f4fe0a2102f777d7f54ec5bf3363eb879aee2d0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 19:41:33 +0000 Subject: [PATCH 098/113] Bump swagger-ui-dist from 5.2.0 to 5.18.2 Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.2.0 to 5.18.2. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.2.0...v5.18.2) --- updated-dependencies: - dependency-name: swagger-ui-dist dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index c3bff3b6b4c..222b9519685 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "reselect": "^4.1.8", "sass": "^1.77.6", "swagger-client": "^3.18.5", - "swagger-ui-dist": "^5.2.0", + "swagger-ui-dist": "^5.18.2", "uswds": "2.13.3", "uuid": "^9.0.0", "webpack": "5", diff --git a/yarn.lock b/yarn.lock index dd46ddf509b..13023153627 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2947,6 +2947,11 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz#6801033be7ff87a6b7cadaf5b337c9f366a3c4b0" integrity sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw== +"@scarf/scarf@=1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.4.0.tgz#3bbb984085dbd6d982494538b523be1ce6562972" + integrity sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ== + "@sinclair/typebox@^0.23.3": version "0.23.5" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d" @@ -16650,10 +16655,12 @@ swagger-client@^3.18.5: traverse "~0.6.6" url "~0.11.0" -swagger-ui-dist@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.2.0.tgz#175e112b3aea756fdbbbb035d4cffef26ac579d1" - integrity sha512-rLvJBgualxNZcwKOmTFzy4zF1nHy+3S0pUDDR/ageDRZgi8aITSe7pVYiAy03xGQZtqEifjwEtHQE+eF14gveg== +swagger-ui-dist@^5.18.2: + version "5.18.2" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz#62013074374d272c04ed3030704b88db5aa8c0b7" + integrity sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw== + dependencies: + "@scarf/scarf" "=1.4.0" swc-loader@^0.2.3: version "0.2.3" From 894581cd2aadd9ade45a7a8f1ba762421d744981 Mon Sep 17 00:00:00 2001 From: brenna-everett-caci Date: Fri, 10 Jan 2025 22:06:07 +0000 Subject: [PATCH 099/113] update homesafe certificate in staging --- config/tls/milmove-cert-bundle.p7b | Bin 61660 -> 63876 bytes migrations/app/migrations_manifest.txt | 1 + ...20250110214012_homesafeconnect_cert.up.sql | 4 ++++ 3 files changed, 5 insertions(+) create mode 100644 migrations/app/secure/20250110214012_homesafeconnect_cert.up.sql diff --git a/config/tls/milmove-cert-bundle.p7b b/config/tls/milmove-cert-bundle.p7b index 9a53bf6c2e917566b25c549713cf89bf0e3728ff..85eb6a72d7f30320f641ac1d1827f3ef6a25185f 100644 GIT binary patch delta 930 zcmV;T16};w;RA&811&Iu`G5uqDuzgg_YDCD0-%EVaWI1UZ2|!SF#s?N1_>&LNQUha z$npPf$ue$iXaUfvPq;A%O?DSkwuSkP7@Cu%@Ym0w=w?4^(a2JfYl|fg9Uo(&PG_dY zJujFVDaAdzp(UAQYIsWi%_Z0iCO3rlFXhq8aJmD^jm^_n0wSYUB<<{{DFqZ~OnuS-2)|UEO+FeQDN;U@_ttlUpQ7C5|RPGGOObUiu}wqb=dfl zb``0c$t4d$ugE+&okZ=9%D_{y(>iq@zeB(b=lPM7H%?z=cik^?`J!D(9hoe9ebtSs z=jjcIOeN`fK3PYD5b`% z&U5OUW_^#WGyas{sCU)PAAQLITJ;=|NC2m+Lt?N+WBg&;TWt5=R=Ail>&u~>A2u=O z)$a_bV?i1y8LLKE-^wnYeh^kjwZf~M=TcR8Zhee(rg5>d{WU&0SE2%WZ-2&qy|2B+ z-@g}610bDC_peZgeBGE;8nj`Y*?(XWZWw!g^> zgJ2;QW>uvC_SUyr;dJpJ*_*Qi1@c$?#dU=x+s9hR@cBC3(hgcD%P{XI6Syf3Sf}?r zU*WI!g`a8RpIHuz{@56jO}G`;Sv=k|lmF&3 zf1$%D&Kxc!xmVIrbA{zHI46d_p4R1;0yLENpYPjFa1-O@Hx`(1eB65~$3naKgYg{& z{;&D!^iN#z1)G)BSYR+)4(HP0Vv7cNs*{p+mA6wa3Y4PLBN)e2bh2Gt9J)k3frB}v ztjRQv#!pdF&+_BWVi=MiaB5$y7sN|0e}3NqnFb&Vg!nCfKygdnuPZK92Q?mO6jH=4wC|Og%~iH;Hfs~4SRIW+ zM8^cj{*c%(9b&;GwwcB=v_sP>n)PQtH7-chXJ`c8>B2Y%#;cpQfDLhV5PplL1icn9 E0QGaWM*si- delta 59 zcmZqq%zWn|v#vqYhZ}61T5TR}-+39?nHDsCI0+Ow%EZWM$Y8*YkYNPMtlg-)_5+h4 F0|2~a7B&C? diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index 0270c9e14cb..d51ffc3c7c0 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1067,3 +1067,4 @@ 20241230190638_remove_AK_zips_from_zip3.up.sql 20241230190647_add_missing_AK_zips_to_zip3_distances.up.sql 20250103180420_update_pricing_proc_to_use_local_price_variable.up.sql +20250110214012_homesafeconnect_cert.up.sql diff --git a/migrations/app/secure/20250110214012_homesafeconnect_cert.up.sql b/migrations/app/secure/20250110214012_homesafeconnect_cert.up.sql new file mode 100644 index 00000000000..f9862f58a7c --- /dev/null +++ b/migrations/app/secure/20250110214012_homesafeconnect_cert.up.sql @@ -0,0 +1,4 @@ +-- Local test migration. +-- This will be run on development environments. +-- It should mirror what you intend to apply on prd/stg/exp/demo +-- DO NOT include any sensitive data. From 0f99b857906770cbb3a00b8aa72cd9074623abb7 Mon Sep 17 00:00:00 2001 From: Paul Stonebraker Date: Mon, 13 Jan 2025 16:27:26 +0000 Subject: [PATCH 100/113] add test for queries --- src/hooks/queries.test.jsx | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/hooks/queries.test.jsx b/src/hooks/queries.test.jsx index 38bf1cbd08b..4136a50f825 100644 --- a/src/hooks/queries.test.jsx +++ b/src/hooks/queries.test.jsx @@ -17,6 +17,7 @@ import { useEvaluationReportQueries, useServicesCounselingQueuePPMQueries, useReviewShipmentWeightsQuery, + useBulkAssignmentQueries, } from './queries'; import { serviceItemCodes } from 'content/serviceItems'; @@ -311,6 +312,33 @@ jest.mock('services/ghcApi', () => ({ ], }); }, + getBulkAssignmentData: () => + Promise.resolve({ + availableOfficeUsers: [ + { + firstName: 'Dan', + lastName: 'Quinn', + officeUserId: '0567dc9d-d88e-4c8d-94b0-00483e769058', + workload: 2, + }, + { + firstName: 'Brian', + lastName: 'Robinson', + officeUserId: '10fa3a2b-436a-4cc9-8c7c-c2a9604b0d41', + }, + { + firstName: 'Jayden', + lastName: 'Daniels', + officeUserId: '1be7530a-362b-4f84-87c7-e076a1d9873d', + workload: 2, + }, + ], + bulkAssignmentMoveIDs: [ + 'd63b5a39-c47e-4855-a39d-5f6b156d0421', + 'a5d3d748-bdc0-4439-af45-2a95e545fa8c', + '72b98287-641e-4da8-a6e2-6c9eb3373bbb', + ], + }), })); jest.mock('services/internalApi', () => ({ @@ -1004,6 +1032,46 @@ describe('useServicesCounselingQueuePPMQueries', () => { }); }); +describe('useBulkAssignmentQueries', () => { + it('loads data', async () => { + const { result, waitFor } = renderHook(() => useBulkAssignmentQueries('COUNSELING'), { wrapper }); + + await waitFor(() => result.current.isSuccess); + + expect(result.current).toEqual({ + bulkAssignmentData: { + availableOfficeUsers: [ + { + firstName: 'Dan', + lastName: 'Quinn', + officeUserId: '0567dc9d-d88e-4c8d-94b0-00483e769058', + workload: 2, + }, + { + firstName: 'Brian', + lastName: 'Robinson', + officeUserId: '10fa3a2b-436a-4cc9-8c7c-c2a9604b0d41', + }, + { + firstName: 'Jayden', + lastName: 'Daniels', + officeUserId: '1be7530a-362b-4f84-87c7-e076a1d9873d', + workload: 2, + }, + ], + bulkAssignmentMoveIDs: [ + 'd63b5a39-c47e-4855-a39d-5f6b156d0421', + 'a5d3d748-bdc0-4439-af45-2a95e545fa8c', + '72b98287-641e-4da8-a6e2-6c9eb3373bbb', + ], + }, + isLoading: false, + isError: false, + isSuccess: true, + }); + }); +}); + describe('useReviewShipmentWeightsQuery', () => { it('loads data', async () => { const { result, waitFor } = renderHook(() => useReviewShipmentWeightsQuery('ABCDEF'), { wrapper }); From 7031b2484aa52aaab356d52e5a9d6c899690ce38 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 13 Jan 2025 17:26:04 +0000 Subject: [PATCH 101/113] add student orders case to entitlement fetch --- pkg/models/order.go | 60 +++++++++++++++++++++++++++++++++------- pkg/models/order_test.go | 46 ++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 10 deletions(-) diff --git a/pkg/models/order.go b/pkg/models/order.go index 8302ad634e0..970466c226a 100644 --- a/pkg/models/order.go +++ b/pkg/models/order.go @@ -1,6 +1,9 @@ package models import ( + "bytes" + "encoding/json" + "fmt" "time" "github.com/gobuffalo/pop/v6" @@ -184,6 +187,18 @@ func (o *Order) Cancel() error { return nil } +var ordersTypeToAllotmentAppParamName = map[internalmessages.OrdersType]string{ + internalmessages.OrdersTypeSTUDENTTRAVEL: "studentTravelHhgAllowance", +} + +// Helper func to enforce strict unmarshal of application param values into a given interface +func strictUnmarshal(data []byte, v interface{}) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + // Fail on unknown fields + decoder.DisallowUnknownFields() + return decoder.Decode(v) +} + // FetchOrderForUser returns orders only if it is allowed for the given user to access those orders. func FetchOrderForUser(db *pop.Connection, session *auth.Session, id uuid.UUID) (Order, error) { var order Order @@ -209,23 +224,48 @@ func FetchOrderForUser(db *pop.Connection, session *auth.Session, id uuid.UUID) } // Conduct allotment lookup - // TODO: OCONUS case orders type if order.Entitlement != nil && order.Grade != nil { - var hhgAllowance HHGAllowance - err = db.RawQuery(` - SELECT hhg_allowances.* - FROM hhg_allowances - INNER JOIN pay_grades ON hhg_allowances.pay_grade_id = pay_grades.id - WHERE pay_grades.grade = $1 - LIMIT 1 - `, order.Grade).First(&hhgAllowance) - if err == nil { + switch order.OrdersType { + case internalmessages.OrdersTypeSTUDENTTRAVEL: + // We currently store orders allotment overrides as an application parameter + // as it is a current one-off use case introduced by E-06189 + var jsonData json.RawMessage + if paramName, ok := ordersTypeToAllotmentAppParamName[order.OrdersType]; ok { + err := db.RawQuery(` + SELECT parameter_json + FROM application_parameters + WHERE parameter_name = $1 + `, paramName).First(&jsonData) + + if err != nil { + return Order{}, fmt.Errorf("failed to fetch weight allotment for orders type %s: %w", order.OrdersType, err) + } + + // Convert the JSON data to the WeightAllotment struct + err = strictUnmarshal(jsonData, &order.Entitlement.WeightAllotted) + if err != nil { + return Order{}, fmt.Errorf("failed to parse weight allotment JSON for orders type %s: %w", order.OrdersType, err) + } + } + default: + var hhgAllowance HHGAllowance + err = db.RawQuery(` + SELECT hhg_allowances.* + FROM hhg_allowances + INNER JOIN pay_grades ON hhg_allowances.pay_grade_id = pay_grades.id + WHERE pay_grades.grade = $1 + LIMIT 1 + `, order.Grade).First(&hhgAllowance) + if err != nil { + return Order{}, fmt.Errorf("failed to parse weight allotment JSON for orders type %s: %w", order.OrdersType, err) + } order.Entitlement.WeightAllotted = &WeightAllotment{ TotalWeightSelf: hhgAllowance.TotalWeightSelf, TotalWeightSelfPlusDependents: hhgAllowance.TotalWeightSelfPlusDependents, ProGearWeight: hhgAllowance.ProGearWeight, ProGearWeightSpouse: hhgAllowance.ProGearWeightSpouse, } + } } diff --git a/pkg/models/order_test.go b/pkg/models/order_test.go index 9e3e84f50f8..16368d59768 100644 --- a/pkg/models/order_test.go +++ b/pkg/models/order_test.go @@ -1,6 +1,7 @@ package models_test import ( + "encoding/json" "time" "github.com/gofrs/uuid" @@ -115,6 +116,22 @@ func (suite *ModelSuite) TestTacFormat() { } func (suite *ModelSuite) TestFetchOrderForUser() { + setupHhgStudentAllowanceParameter := func() { + paramJSON := `{ + "TotalWeightSelf": 350, + "TotalWeightSelfPlusDependents": 350, + "ProGearWeight": 0, + "ProGearWeightSpouse": 0, + "UnaccompaniedBaggageAllowance": 100 + }` + rawMessage := json.RawMessage(paramJSON) + + parameter := models.ApplicationParameters{ + ParameterName: models.StringPointer("studentTravelHhgAllowance"), + ParameterJson: &rawMessage, + } + suite.MustCreate(¶meter) + } suite.Run("successful fetch by authorized user", func() { order := factory.BuildOrder(suite.DB(), nil, nil) @@ -139,6 +156,35 @@ func (suite *ModelSuite) TestFetchOrderForUser() { suite.Equal(order.UploadedOrdersID, goodOrder.UploadedOrdersID) }) + suite.Run("retrieves student entitlement properly for user", func() { + setupHhgStudentAllowanceParameter() + order := factory.BuildOrder(suite.DB(), []factory.Customization{ + { + Model: models.Order{ + OrdersType: internalmessages.OrdersTypeSTUDENTTRAVEL, + }, + }, + }, nil) + + // User is authorized to fetch order + session := &auth.Session{ + ApplicationName: auth.MilApp, + UserID: order.ServiceMember.UserID, + ServiceMemberID: order.ServiceMemberID, + } + goodOrder, err := m.FetchOrderForUser(suite.DB(), session, order.ID) + + suite.NoError(err) + suite.FatalNotNil(goodOrder.Entitlement) + suite.FatalNotNil(goodOrder.Entitlement.WeightAllotted) + suite.Equal(goodOrder.Entitlement.WeightAllotted.TotalWeightSelf, 350) + suite.Equal(goodOrder.Entitlement.WeightAllotted.TotalWeightSelfPlusDependents, 350) + suite.Equal(goodOrder.Entitlement.WeightAllotted.ProGearWeight, 0) + suite.Equal(goodOrder.Entitlement.WeightAllotted.ProGearWeightSpouse, 0) + suite.Equal(goodOrder.Entitlement.WeightAllotted.UnaccompaniedBaggageAllowance, 100) + + }) + suite.Run("check for closeout office", func() { closeoutOffice := factory.BuildTransportationOffice(suite.DB(), nil, nil) move := factory.BuildMove(suite.DB(), []factory.Customization{ From 34ce331d38affaa87a67bafcde7bae771e117fed Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 13 Jan 2025 12:32:01 -0500 Subject: [PATCH 102/113] fix some goofs --- pkg/services/entitlements/weight_restrictor.go | 2 +- pkg/services/move_task_order/move_task_order_fetcher.go | 2 +- scripts/run-server-test | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/services/entitlements/weight_restrictor.go b/pkg/services/entitlements/weight_restrictor.go index 8f68f993d92..62dd6c1d4e3 100644 --- a/pkg/services/entitlements/weight_restrictor.go +++ b/pkg/services/entitlements/weight_restrictor.go @@ -37,7 +37,7 @@ func (wr *weightRestrictor) ApplyWeightRestrictionToEntitlement(appCtx appcontex return nil, err } - // Don't allow applying a weight restriction above teh max allowance, that's silly + // Don't allow applying a weight restriction above the max allowance, that's silly if weightRestriction > maxHhgAllowance { return nil, apperror.NewInvalidInputError(entitlement.ID, fmt.Errorf("weight restriction %d exceeds max HHG allowance %d", weightRestriction, maxHhgAllowance), diff --git a/pkg/services/move_task_order/move_task_order_fetcher.go b/pkg/services/move_task_order/move_task_order_fetcher.go index a53bd4f62c2..2c05a193163 100644 --- a/pkg/services/move_task_order/move_task_order_fetcher.go +++ b/pkg/services/move_task_order/move_task_order_fetcher.go @@ -200,7 +200,7 @@ func (f moveTaskOrderFetcher) FetchMoveTaskOrder(appCtx appcontext.AppContext, s } // Now that we have the move and order, construct the allotment (hhg allowance) - // Only fetch if grade us not nil + // Only fetch if grade is not nil if mto.Orders.Grade != nil { allotment, err := f.waf.GetWeightAllotment(appCtx, string(*mto.Orders.Grade), mto.Orders.OrdersType) if err != nil { 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 7dfef82968c70d3fa04585bcc3c760ce599d13d8 Mon Sep 17 00:00:00 2001 From: Jon Spight Date: Mon, 13 Jan 2025 18:59:12 +0000 Subject: [PATCH 103/113] fixced merge conflicts --- pkg/factory/move_factory.go | 4 ---- pkg/factory/shared.go | 8 -------- 2 files changed, 12 deletions(-) diff --git a/pkg/factory/move_factory.go b/pkg/factory/move_factory.go index f496c1dc5f1..4564f893657 100644 --- a/pkg/factory/move_factory.go +++ b/pkg/factory/move_factory.go @@ -35,7 +35,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode closeoutOffice = BuildTransportationOffice(db, tempCloseoutOfficeCustoms, nil) } - var counselingOffice models.TransportationOffice tempCounselingOfficeCustoms := customs counselingOfficeResult := findValidCustomization(customs, TransportationOffices.CounselingOffice) @@ -52,7 +51,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode scAssignedUser = BuildOfficeUser(db, tempSCAssignedUserCustoms, nil) } - var tooAssignedUser models.OfficeUser tempTOOAssignedUserCustoms := customs tooAssignedUserResult := findValidCustomization(customs, OfficeUsers.TOOAssignedUser) @@ -107,7 +105,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.CloseoutOfficeID = &closeoutOffice.ID } - if counselingOfficeResult != nil { move.CounselingOffice = &counselingOffice move.CounselingOfficeID = &counselingOffice.ID @@ -118,7 +115,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.SCAssignedID = &scAssignedUser.ID } - if tooAssignedUserResult != nil { move.TOOAssignedUser = &tooAssignedUser move.TOOAssignedID = &tooAssignedUser.ID diff --git a/pkg/factory/shared.go b/pkg/factory/shared.go index 8d72b513a7d..037f2d352d5 100644 --- a/pkg/factory/shared.go +++ b/pkg/factory/shared.go @@ -273,14 +273,6 @@ var TransportationOffices = transportationOfficeGroup{ CounselingOffice: "CounselingOffice", } -type officeUserGroup struct { - SCAssignedUser CustomType -} - -var OfficeUsers = officeUserGroup{ - SCAssignedUser: "SCAssignedUser", -} - type officeUserGroup struct { SCAssignedUser CustomType TIOAssignedUser CustomType From 09cef7361f6c843aa8b0e21140e2fffa0e1233e1 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Mon, 13 Jan 2025 19:16:10 +0000 Subject: [PATCH 104/113] add db lookup for entitlement testdatagen --- pkg/testdatagen/make_entitlement.go | 48 +++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/pkg/testdatagen/make_entitlement.go b/pkg/testdatagen/make_entitlement.go index 2f502f59e3e..442cf8f94b6 100644 --- a/pkg/testdatagen/make_entitlement.go +++ b/pkg/testdatagen/make_entitlement.go @@ -3,6 +3,7 @@ package testdatagen import ( "github.com/gobuffalo/pop/v6" + "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" ) @@ -33,16 +34,45 @@ func makeEntitlement(db *pop.Connection, assertions Assertions) models.Entitleme OrganizationalClothingAndIndividualEquipment: ocie, } - weightData := getDefaultWeightData(string(*grade)) - allotment := models.WeightAllotment{ - TotalWeightSelf: weightData.TotalWeightSelf, - TotalWeightSelfPlusDependents: weightData.TotalWeightSelfPlusDependents, - ProGearWeight: weightData.ProGearWeight, - ProGearWeightSpouse: weightData.ProGearWeightSpouse, + var hhgAllowance models.HHGAllowance + if assertions.Order.OrdersType == internalmessages.OrdersTypeSTUDENTTRAVEL { + // Set to student travel allotment + entitlement.WeightAllotted = &models.WeightAllotment{ + TotalWeightSelf: 350, + TotalWeightSelfPlusDependents: 350, + ProGearWeight: 0, + ProGearWeightSpouse: 0, + UnaccompaniedBaggageAllowance: 100, + } + } + err := db.RawQuery(` + SELECT hhg_allowances.* + FROM hhg_allowances + INNER JOIN pay_grades ON hhg_allowances.pay_grade_id = pay_grades.id + WHERE pay_grades.grade = $1 + LIMIT 1 + `, grade).First(&hhgAllowance) + if err != nil { + // Resort to defaults, for some reason it must've been truncated + weightData := getDefaultWeightData(string(*grade)) + allotment := models.WeightAllotment{ + TotalWeightSelf: weightData.TotalWeightSelf, + TotalWeightSelfPlusDependents: weightData.TotalWeightSelfPlusDependents, + ProGearWeight: weightData.ProGearWeight, + ProGearWeightSpouse: weightData.ProGearWeightSpouse, + } + entitlement.WeightAllotted = &allotment + dBAuthorizedWeight := entitlement.AuthorizedWeight() + entitlement.DBAuthorizedWeight = dBAuthorizedWeight + } else { + // The db data was found + entitlement.WeightAllotted = &models.WeightAllotment{ + TotalWeightSelf: hhgAllowance.TotalWeightSelf, + TotalWeightSelfPlusDependents: hhgAllowance.TotalWeightSelfPlusDependents, + ProGearWeight: hhgAllowance.ProGearWeight, + ProGearWeightSpouse: hhgAllowance.ProGearWeightSpouse, + } } - entitlement.WeightAllotted = &allotment - dBAuthorizedWeight := entitlement.AuthorizedWeight() - entitlement.DBAuthorizedWeight = dBAuthorizedWeight // Overwrite values with those from assertions mergeModels(&entitlement, assertions.Entitlement) From 28ce85b4a886815bcf8ab04aef1fdf39108d099b Mon Sep 17 00:00:00 2001 From: Jon Spight Date: Mon, 13 Jan 2025 19:49:21 +0000 Subject: [PATCH 105/113] PR Fixes --- pkg/factory/move_factory.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/factory/move_factory.go b/pkg/factory/move_factory.go index 4564f893657..3f923f95204 100644 --- a/pkg/factory/move_factory.go +++ b/pkg/factory/move_factory.go @@ -35,14 +35,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode closeoutOffice = BuildTransportationOffice(db, tempCloseoutOfficeCustoms, nil) } - var counselingOffice models.TransportationOffice - tempCounselingOfficeCustoms := customs - counselingOfficeResult := findValidCustomization(customs, TransportationOffices.CounselingOffice) - if counselingOfficeResult != nil { - tempCounselingOfficeCustoms = convertCustomizationInList(tempCounselingOfficeCustoms, TransportationOffices.CounselingOffice, TransportationOffice) - counselingOffice = BuildTransportationOffice(db, tempCounselingOfficeCustoms, nil) - } - var scAssignedUser models.OfficeUser tempSCAssignedUserCustoms := customs scAssignedUserResult := findValidCustomization(customs, OfficeUsers.SCAssignedUser) @@ -67,6 +59,14 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode tioAssignedUser = BuildOfficeUser(db, tempTIOAssignedUserCustoms, nil) } + var counselingOffice models.TransportationOffice + tempCounselingOfficeCustoms := customs + counselingOfficeResult := findValidCustomization(customs, TransportationOffices.CounselingOffice) + if counselingOfficeResult != nil { + tempCounselingOfficeCustoms = convertCustomizationInList(tempCounselingOfficeCustoms, TransportationOffices.CounselingOffice, TransportationOffice) + counselingOffice = BuildTransportationOffice(db, tempCounselingOfficeCustoms, nil) + } + var defaultReferenceID string var err error if db != nil { @@ -105,11 +105,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.CloseoutOfficeID = &closeoutOffice.ID } - if counselingOfficeResult != nil { - move.CounselingOffice = &counselingOffice - move.CounselingOfficeID = &counselingOffice.ID - } - if scAssignedUserResult != nil { move.SCAssignedUser = &scAssignedUser move.SCAssignedID = &scAssignedUser.ID @@ -125,6 +120,11 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.TIOAssignedID = &tioAssignedUser.ID } + if counselingOfficeResult != nil { + move.CounselingOffice = &counselingOffice + move.CounselingOfficeID = &counselingOffice.ID + } + // Overwrite values with those from assertions testdatagen.MergeModels(&move, cMove) From a7757af972ceb12cbc181979606af04e6bd156b4 Mon Sep 17 00:00:00 2001 From: Jon Spight Date: Mon, 13 Jan 2025 21:25:52 +0000 Subject: [PATCH 106/113] Fixed merge conflicts --- pkg/factory/move_factory.go | 28 ++++----- pkg/factory/shared.go | 8 --- .../payloads/model_to_payload_test.go | 63 +------------------ 3 files changed, 14 insertions(+), 85 deletions(-) diff --git a/pkg/factory/move_factory.go b/pkg/factory/move_factory.go index d147fab8d6b..3f923f95204 100644 --- a/pkg/factory/move_factory.go +++ b/pkg/factory/move_factory.go @@ -35,14 +35,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode closeoutOffice = BuildTransportationOffice(db, tempCloseoutOfficeCustoms, nil) } - var counselingOffice models.TransportationOffice - tempCounselingOfficeCustoms := customs - counselingOfficeResult := findValidCustomization(customs, TransportationOffices.CounselingOffice) - if counselingOfficeResult != nil { - tempCounselingOfficeCustoms = convertCustomizationInList(tempCounselingOfficeCustoms, TransportationOffices.CounselingOffice, TransportationOffice) - counselingOffice = BuildTransportationOffice(db, tempCounselingOfficeCustoms, nil) - } - var scAssignedUser models.OfficeUser tempSCAssignedUserCustoms := customs scAssignedUserResult := findValidCustomization(customs, OfficeUsers.SCAssignedUser) @@ -67,6 +59,14 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode tioAssignedUser = BuildOfficeUser(db, tempTIOAssignedUserCustoms, nil) } + var counselingOffice models.TransportationOffice + tempCounselingOfficeCustoms := customs + counselingOfficeResult := findValidCustomization(customs, TransportationOffices.CounselingOffice) + if counselingOfficeResult != nil { + tempCounselingOfficeCustoms = convertCustomizationInList(tempCounselingOfficeCustoms, TransportationOffices.CounselingOffice, TransportationOffice) + counselingOffice = BuildTransportationOffice(db, tempCounselingOfficeCustoms, nil) + } + var defaultReferenceID string var err error if db != nil { @@ -105,18 +105,11 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.CloseoutOfficeID = &closeoutOffice.ID } - if counselingOfficeResult != nil { - move.CounselingOffice = &counselingOffice - move.CounselingOfficeID = &counselingOffice.ID - } - - if scAssignedUserResult != nil { move.SCAssignedUser = &scAssignedUser move.SCAssignedID = &scAssignedUser.ID } - if tooAssignedUserResult != nil { move.TOOAssignedUser = &tooAssignedUser move.TOOAssignedID = &tooAssignedUser.ID @@ -127,6 +120,11 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.TIOAssignedID = &tioAssignedUser.ID } + if counselingOfficeResult != nil { + move.CounselingOffice = &counselingOffice + move.CounselingOfficeID = &counselingOffice.ID + } + // Overwrite values with those from assertions testdatagen.MergeModels(&move, cMove) diff --git a/pkg/factory/shared.go b/pkg/factory/shared.go index 1c0e38146db..207a8cdb86c 100644 --- a/pkg/factory/shared.go +++ b/pkg/factory/shared.go @@ -273,14 +273,6 @@ var TransportationOffices = transportationOfficeGroup{ CounselingOffice: "CounselingOffice", } -type officeUserGroup struct { - SCAssignedUser CustomType -} - -var OfficeUsers = officeUserGroup{ - SCAssignedUser: "SCAssignedUser", -} - type officeUserGroup struct { SCAssignedUser CustomType TOOAssignedUser CustomType diff --git a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go index 7481b293662..d4e330f09b2 100644 --- a/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/ghcapi/internal/payloads/model_to_payload_test.go @@ -14,7 +14,6 @@ import ( "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/models/roles" "github.com/transcom/mymove/pkg/storage/test" - "github.com/transcom/mymove/pkg/testdatagen" "github.com/transcom/mymove/pkg/unit" ) @@ -805,8 +804,6 @@ func (suite *PayloadsSuite) TestSearchMoves() { appCtx := suite.AppContextForTest() marines := models.AffiliationMARINES - spaceForce := models.AffiliationSPACEFORCE - army := models.AffiliationARMY moveUSMC := factory.BuildMove(suite.DB(), []factory.Customization{ { Model: models.ServiceMember{ @@ -814,73 +811,15 @@ func (suite *PayloadsSuite) TestSearchMoves() { }, }, }, nil) - moveSF := factory.BuildMove(suite.DB(), []factory.Customization{ - { - Model: models.ServiceMember{ - Affiliation: &spaceForce, - }, - }, - }, nil) - moveA := factory.BuildMove(suite.DB(), []factory.Customization{ - { - Model: models.ServiceMember{ - Affiliation: &army, - }, - }, - }, nil) - moveUSMC.Status = models.MoveStatusNeedsServiceCounseling - scheduledPickupDate := time.Date(testdatagen.GHCTestYear, time.September, 20, 0, 0, 0, 0, time.UTC) - scheduledDeliveryDate := time.Date(testdatagen.GHCTestYear, time.September, 20, 0, 0, 0, 0, time.UTC) - sitAllowance := int(90) - gbloc := "LKNQ" - storageFacility := factory.BuildStorageFacility(suite.DB(), nil, nil) - mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ - { - Model: moveSF, - LinkOnly: true, - }, - { - Model: models.MTOShipment{ - Status: models.MTOShipmentStatusApproved, - ShipmentType: models.MTOShipmentTypeHHGIntoNTSDom, - CounselorRemarks: handlers.FmtString("counselor remark"), - SITDaysAllowance: &sitAllowance, - ScheduledPickupDate: &scheduledPickupDate, - ScheduledDeliveryDate: &scheduledDeliveryDate, - }, - }, - { - Model: storageFacility, - LinkOnly: true, - }, - }, nil) - - moveSF.MTOShipments = append(moveSF.MTOShipments, mtoShipment) - moveSF.ShipmentGBLOC = append(moveSF.ShipmentGBLOC, models.MoveToGBLOC{GBLOC: &gbloc}) moves := models.Moves{moveUSMC} - moveSpaceForce := models.Moves{moveSF} - moveArmy := models.Moves{moveA} + suite.Run("Success - Returns a ghcmessages Upload payload from Upload Struct Marine move with no shipments", func() { payload := SearchMoves(appCtx, moves) suite.IsType(payload, &ghcmessages.SearchMoves{}) suite.NotNil(payload) }) - suite.Run("Success - Returns a ghcmessages Upload payload from Upload Struct Non-Marine move, a shipment, and delivery/pickup time. ", func() { - payload := SearchMoves(appCtx, moveSpaceForce) - suite.IsType(payload, &ghcmessages.SearchMoves{}) - suite.NotNil(payload) - suite.NotNil(mtoShipment) - - suite.NotNil(moveA) - }) - suite.Run("Success - Returns a ghcmessages Upload payload from Upload Struct Army move, with no shipments. ", func() { - payload := SearchMoves(appCtx, moveArmy) - suite.IsType(payload, &ghcmessages.SearchMoves{}) - suite.NotNil(payload) - - }) } func (suite *PayloadsSuite) TestMarketCode() { From a3eada6c8b3f2886c0a1ade06df1378e2d0f66ff Mon Sep 17 00:00:00 2001 From: Jon Spight Date: Mon, 13 Jan 2025 21:30:20 +0000 Subject: [PATCH 107/113] moved SC to match INT placement --- pkg/factory/move_factory.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/factory/move_factory.go b/pkg/factory/move_factory.go index 3f923f95204..8561df50127 100644 --- a/pkg/factory/move_factory.go +++ b/pkg/factory/move_factory.go @@ -105,11 +105,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.CloseoutOfficeID = &closeoutOffice.ID } - if scAssignedUserResult != nil { - move.SCAssignedUser = &scAssignedUser - move.SCAssignedID = &scAssignedUser.ID - } - if tooAssignedUserResult != nil { move.TOOAssignedUser = &tooAssignedUser move.TOOAssignedID = &tooAssignedUser.ID @@ -125,6 +120,11 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.CounselingOfficeID = &counselingOffice.ID } + if scAssignedUserResult != nil { + move.SCAssignedUser = &scAssignedUser + move.SCAssignedID = &scAssignedUser.ID + } + // Overwrite values with those from assertions testdatagen.MergeModels(&move, cMove) From 37784db97a8de7658b6ce7860ce86c638338f1b5 Mon Sep 17 00:00:00 2001 From: Jon Spight Date: Mon, 13 Jan 2025 22:26:55 +0000 Subject: [PATCH 108/113] PR Fixes --- pkg/factory/move_factory.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/factory/move_factory.go b/pkg/factory/move_factory.go index 3f923f95204..8561df50127 100644 --- a/pkg/factory/move_factory.go +++ b/pkg/factory/move_factory.go @@ -105,11 +105,6 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.CloseoutOfficeID = &closeoutOffice.ID } - if scAssignedUserResult != nil { - move.SCAssignedUser = &scAssignedUser - move.SCAssignedID = &scAssignedUser.ID - } - if tooAssignedUserResult != nil { move.TOOAssignedUser = &tooAssignedUser move.TOOAssignedID = &tooAssignedUser.ID @@ -125,6 +120,11 @@ func BuildMove(db *pop.Connection, customs []Customization, traits []Trait) mode move.CounselingOfficeID = &counselingOffice.ID } + if scAssignedUserResult != nil { + move.SCAssignedUser = &scAssignedUser + move.SCAssignedID = &scAssignedUser.ID + } + // Overwrite values with those from assertions testdatagen.MergeModels(&move, cMove) From d204e63311f04a3aed84c91c46875f9206bf3939 Mon Sep 17 00:00:00 2001 From: josiahzimmerman-caci Date: Tue, 14 Jan 2025 15:55:34 +0000 Subject: [PATCH 109/113] work deploy anchors/cleanup cache --- .gitlab-ci.yml | 167 ++++++++++++++++++++++++++++++------------------- 1 file changed, 104 insertions(+), 63 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0c625258232..5909d3e55c9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,6 +14,7 @@ variables: DOCKERHUB_PASSWORD: DOCKERHUB_PASSWORD DOCKER_TOKEN: DOCKER_TOKEN registry: https://registry.hub.docker.com/ + #Circle CI need to replace #CIRCLE_PROJECT_USERNAME: "my-username" # Replace with the actual namespace CIRCLE_PROJECT_REPONAME: "mymove" # Replace with your GitLab project name @@ -21,19 +22,31 @@ variables: CIRCLE_BRANCH: "$CI_COMMIT_BRANCH" # Map to GitLab's branch variable #CIRCLE_TOKEN: "$GITLAB_API_TOKEN" # GitLab API token for querying pipelines CIRCLE_BUILD_NUM: "$CI_PIPELINE_ID" + GOPATH: "$CI_PROJECT_DIR/go" GOLANGCI_LINT_CONCURRENCY: "4" GOLANGCI_LINT_VERBOSE: "-v" + # Specify the environment: loadtest, demo, exp DP3_ENV: &dp3_env placeholder_env - # Specify the branch to deploy + + # Specify the branch to deploy TODO: this might be not needed. So far useless DP3_BRANCH: &dp3_branch placeholder_branch_name + # Ignore branches for integration tests INTEGRATION_IGNORE_BRANCH: &integration_ignore_branch placeholder_branch_name INTEGRATION_MTLS_IGNORE_BRANCH: &integration_mtls_ignore_branch placeholder_branch_name CLIENT_IGNORE_BRANCH: &client_ignore_branch placeholder_branch_name SERVER_IGNORE_BRANCH: &server_ignore_branch placeholder_branch_name + + #RUNNER_TAG: &runner_tag milmove RUNNER_TAG: &runner_tag milmove + DOCKER_RUNNER_TAG: &docker_runner_tag eks_cluster_runner + + postgres: &postgres postgres:16.4 + #postgres: &postgres postgres:16.4 + redis: &redis redis:5.0.6 + stages: - pre_checks - build @@ -43,32 +56,51 @@ stages: - prod_approval - push_prd - deploy_prd + #anchors #set safe directory and path .setup_milmove_env: &setup_milmove_env - git config --global --add safe.directory /builds/milmove/mymove - export PATH=${PATH}:${GOPATH}/bin:~/transcom/mymove/builds/milmove/mymove - export REACT_APP_ERROR_LOGGING=otel + .announce_failure: &announce_failure #- if [[ "$CI_COMMIT_BRANCH" == "main" && "$CI_JOB_STATUS" == "failed" ]]; then - echo $CI_COMMIT_BRANCH - echo $CI_JOB_STATUS - echo "Announcing broken branch in GitLab CI" # fi + +.setup_tls_vars_dp3: &setup_tls_vars_dp3 + - | + if [[ "$DP3_ENV" == "exp" || "$DP3_ENV" == "loadtest" || "$DP3_ENV" == "demo" ]]; then + export ENV=$(echo ${DP3_ENV} | tr '[:lower:]' '[:upper:]'); + export TLS_CERT=$(eval echo \$${ENV^^}_DP3_CERT); + export TLS_KEY=$(eval echo \$${ENV^^}_DP3_KEY); + export TLS_CA=$(eval echo \$${ENV^^}_DP3_CA); + fi + .setup_aws_vars_dp3: &setup_aws_vars_dp3 - - if [[ "$DP3_ENV" == "exp" OR "$DP3_ENV" == "loadtest" OR "$DP3_ENV" == "demo" ]]; then - export AWS_DEFAULT_REGION=$(eval echo \$${DP3_ENV^^}_REGION) - export AWS_ACCOUNT_ID=$(eval echo \$${DP3_ENV^^}_ACCOUNT_ID) - export AWS_ACCESS_KEY_ID=$(eval echo \$${DP3_ENV^^}_ACCESS_KEY_ID) - export AWS_SECRET_ACCESS_KEY=$(eval echo \$${DP3_ENV^^}_SECRET_ACCESS_KEY) + - | + if [[ "$DP3_ENV" == "exp" || "$DP3_ENV" == "loadtest" || "$DP3_ENV" == "demo" ]]; then + export ENV=$(echo ${DP3_ENV} | tr '[:lower:]' '[:upper:]'); + export AWS_DEFAULT_REGION=$(eval echo \$${ENV^^}_REGION); + export AWS_ACCOUNT_ID=$(eval echo \$${ENV^^}_ACCOUNT_ID); + export AWS_ACCESS_KEY_ID=$(eval echo \$${ENV^^}_ACCESS_KEY_ID); + export AWS_SECRET_ACCESS_KEY=$(eval echo \$${ENV^^}_SECRET_ACCESS_KEY); fi -.setup_tls_vars_dp3: &setup_tls_vars_dp3 - - if [[ "$DP3_ENV" == "exp" OR "$DP3_ENV" == "loadtest" OR "$DP3_ENV" == "demo" ]]; then - export TLS_CERT=$(eval echo \$${DP3_ENV^^}_DP3_CERT) - export TLS_KEY=$(eval echo \$${DP3_ENV^^}_DP3_KEY) - export TLS_CA=$(eval echo \$${DP3_ENV^^}_DP3_CA) - fi +.setup_release_dp3: &setup_release_dp3 + - | + if [[ "$DP3_ENV" == "exp" || "$DP3_ENV" == "loadtest" || "$DP3_ENV" == "demo" ]]; then + export ENV=$(echo ${DP3_ENV} | tr '[:lower:]' '[:upper:]'); + export AWS_REGION=$(eval echo \$${ENV}_REGION); + export AWS_ACCOUNT_ID=$(eval echo \$${ENV}_ACCOUNT_ID); + export ECR_REPOSITORY_URI=$(echo ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com) + export APP_DOCKER_FILE=Dockerfile.dp3 + export TASK_DOCKER_FILE=Dockerfile.tasks_dp3 + export APP_ENVIRONMENT=$ENV + fi .setup_aws_vars_stg: &setup_aws_vars_stg - export AWS_DEFAULT_REGION=$STG_REGION @@ -94,14 +126,6 @@ stages: - export TLS_KEY=$PRD_MOVE_MIL_DOD_TLS_KEY - export TLS_CA=$PRD_MOVE_MIL_DOD_TLS_CA -.setup_release_dp3: &setup_release_dp3 - #if demo/loadtest/exp - - export ECR_REPOSITORY_URI=$(eval echo \$${DP3_ENV^^}_ACCOUNT_ID).dkr.ecr.$(eval echo \$${DP3_ENV^^}_REGION).amazonaws.com - - export APP_DOCKER_FILE=Dockerfile.dp3 - - export TASK_DOCKER_FILE=Dockerfile.tasks_dp3 - - export APP_ENVIRONMENT=$DPS_ENV - - echo ${ECR_REPOSITORY_URI} - .setup_release_stg: &setup_release_stg #if main - export ECR_REPOSITORY_URI=${STG_ACCOUNT_ID}.dkr.ecr.${STG_REGION}.amazonaws.com @@ -109,7 +133,6 @@ stages: - export TASK_DOCKER_FILE=Dockerfile.tasks_dp3 #TODO: update demo to stg - export APP_ENVIRONMENT=demo - - echo ${ECR_REPOSITORY_URI} .setup_release_prd: &setup_release_prd #build off prd variables @@ -118,7 +141,6 @@ stages: - export TASK_DOCKER_FILE=Dockerfile.tasks_dp3 #TODO: update exp to prod - export APP_ENVIRONMENT=exp - - echo ${ECR_REPOSITORY_URI} .kaniko_before_setup: &kaniko_before_setup # prep login for kaniko @@ -158,20 +180,19 @@ stages: ./scripts/rebuild-dependencies-without-binaries .yarn_cache: &yarn_cache - cache: key: files: - yarn.lock paths: - .cache/yarn -.gosum_cache: &gosum_cache - cache: +.go_cache: &go_cache key: files: - go.sum paths: - - .cache/yarn + - $GOPATH/pkg/mod + - /builds/milmove/mymove/bin .setup_generic_app_env_variables: &setup_generic_app_env_variables - | @@ -228,23 +249,6 @@ include: - template: Jobs/Dependency-Scanning.gitlab-ci.yml - template: Jobs/Secret-Detection.gitlab-ci.yml -testing_services: - stage: pre_checks - image: alpine:3.7 - tags: - - $RUNNER_TAG - allow_failure: true - services: - - php:7 - - node:latest - - golang:1.10 - script: - - php -v - - node -v - - go version - rules: - - *check_integration_ignore_branch - anti_virus: stage: pre_checks tags: @@ -309,6 +313,8 @@ pre_deps_golang: - make bin/swagger after_script: - *announce_failure + cache: + - <<: *go_cache artifacts: paths: - /builds/milmove/mymove/bin/ @@ -326,13 +332,12 @@ pre_deps_yarn: tags: - $RUNNER_TAG image: $DOCKER_APP_IMAGE - needs: - - pre_deps_golang before_script: - *setup_milmove_env script: - *install_yarn - <<: *yarn_cache + cache: + - <<: *yarn_cache after_script: - *announce_failure @@ -380,7 +385,7 @@ check_tls_certificate_stg: stage: pre_checks tags: - $RUNNER_TAG - image: $DOCKER_APP_IMAGE # This can reB-18585-gitlab-pipeline-work unchanged, or you can use a lightweight image since no real work is done. + image: $DOCKER_APP_IMAGE before_script: - *setup_aws_vars_stg - *setup_tls_vars_stg @@ -416,7 +421,8 @@ build_storybook: needs: - pre_deps_yarn - anti_virus - <<: *yarn_cache + cache: + - <<: *yarn_cache before_script: - *setup_milmove_env - *install_yarn @@ -456,7 +462,8 @@ compile_app_client: tags: - $RUNNER_TAG image: $DOCKER_APP_IMAGE - <<: *yarn_cache + cache: + - <<: *yarn_cache variables: KUBERNETES_CPU_REQUEST: "6" KUBERNETES_MEMORY_REQUEST: "8Gi" @@ -486,7 +493,9 @@ compile_app_server: tags: - $RUNNER_TAG image: $DOCKER_APP_IMAGE - <<: *yarn_cache + cache: + - <<: *go_cache + - <<: *yarn_cache variables: KUBERNETES_CPU_REQUEST: "6" KUBERNETES_MEMORY_REQUEST: "6Gi" @@ -530,7 +539,9 @@ pre_test: tags: - $RUNNER_TAG image: $DOCKER_APP_IMAGE - <<: *yarn_cache + cache: + - <<: *go_cache + - <<: *yarn_cache needs: - pre_deps_golang - pre_deps_yarn @@ -548,7 +559,6 @@ pre_test: [ -d ~/transcom/mymove/spectral ] && cp -r ~/transcom/mymove/spectral /tmp/spectral_baseline || echo "Skipping saving baseline" - rm -rf ~/transcom/mymove/spectral - *install_yarn - - ./scripts/pre-commit-go-mod || exit 0 - echo "Run pre-commit tests without golangci-lint, eslint, or prettier" - SKIP=golangci-lint,eslint,prettier,ato-go-linter,gomod,appcontext-linter pre-commit run --all-files - | @@ -573,6 +583,7 @@ pre_test: - yarn danger ci --failOnErrors - echo "Run spectral linter on all files" - ./scripts/ensure-spectral-lint /tmp/spectral_baseline spectral + - ./scripts/pre-commit-go-mod || exit 0 after_script: - *announce_failure rules: @@ -581,7 +592,7 @@ pre_test: server_test: stage: test tags: - - $RUNNER_TAG + - $DOCKER_RUNNER_TAG image: $DOCKER_APP_IMAGE needs: - pre_deps_golang @@ -590,9 +601,13 @@ server_test: - *setup_generic_app_env_variables - *setup_server_env_variables services: - - postgres:latest - - docker:dind + - name: docker:dind + alias: docker + - name: $postgres + - name: $redis variables: + DOCKER_HOST: "tcp://docker-backend.gitlab-runner.svc.cluster.local:2375" + DOCKER_TLS_CERTDIR: "" APPLICATION: app # 8 since this runs on xlarge with 8 CPUs GOTEST_PARALLEL: 8 @@ -683,7 +698,8 @@ client_test: KUBERNETES_MEMORY_LIMIT: "8Gi" needs: - pre_deps_yarn - <<: *yarn_cache + cache: + - <<: *yarn_cache before_script: - *setup_milmove_env - *install_yarn @@ -732,8 +748,32 @@ client_test_coverage: integration_test_devseed: stage: test tags: - - $RUNNER_TAG + - $DOCKER_RUNNER_TAG image: $DOCKER_APP_IMAGE + services: + - name: docker:dind + alias: docker + - name: $postgres + - name: $redis + variables: + DOCKER_HOST: "tcp://docker-backend.gitlab-runner.svc.cluster.local:2375" + DOCKER_TLS_CERTDIR: "" + APPLICATION: app + DB_PASSWORD: mysecretpassword + DB_USER_LOW_PRIV: crud + DB_PASSWORD_LOW_PRIV: mysecretpassword + DB_USER: postgres + DB_HOST: localhost + DB_PORT: 5432 + DB_NAME: dev_db + DB_NAME_DEV: dev_db + MIGRATION_MANIFEST: '/builds/milmove/mymove/migrations/app/migrations_manifest.txt' + MIGRATION_PATH: 'file:///builds/milmove/mymove/migrations/app/schema;file:///builds/milmove/mymove/migrations/app/secure' + EIA_KEY: db2522a43820268a41a802a16ae9fd26 # dummy key generated with openssl rand -hex 16 + ENVIRONMENT: development + DOD_CA_PACKAGE: /builds/milmove/mymove/config/tls/milmove-cert-bundle.p7b + POSTGRES_PASSWORD: mysecretpassword + POSTGRES_DB: test_db needs: - pre_deps_golang - prep_server_hosted_client_deps @@ -742,13 +782,12 @@ integration_test_devseed: - *setup_generic_app_env_variables - *setup_devseed_env_variables script: + - echo "integration_test_devseed" - | - echo "integration_test_devseed" - echo 'export MOVE_MIL_DOD_CA_CERT=$(cat config/tls/devlocal-ca.pem)' >> $BASH_ENV - echo 'export MOVE_MIL_DOD_TLS_CERT=$(cat config/tls/devlocal-https.pem)' >> $BASH_ENV - echo 'export MOVE_MIL_DOD_TLS_KEY=$(cat config/tls/devlocal-https.key)' >> $BASH_ENV - source $BASH_ENV - make db_dev_fresh + export MOVE_MIL_DOD_CA_CERT=$(cat config/tls/devlocal-ca.pem) + export MOVE_MIL_DOD_TLS_CERT=$(cat config/tls/devlocal-https.pem) + export MOVE_MIL_DOD_TLS_KEY=$(cat config/tls/devlocal-https.key) + - make db_dev_fresh after_script: - *announce_failure rules: @@ -1387,6 +1426,7 @@ build_push_app_prd: - *announce_failure rules: - *check_main + build_push_migrations_prd: stage: push_prd tags: @@ -1456,6 +1496,7 @@ push_otel_collector_image_prd: - echo "Pulling the AWS OTel Collector image from the public registry with Crane..." - crane pull --insecure public.ecr.aws/aws-observability/aws-otel-collector:v0.31.0 image.tar + - echo "Pushing the image to our private ECR using Crane..." - crane push --insecure image.tar ${ECR_REPOSITORY_URI}/otel-collector:${CI_COMMIT_SHORT_SHA} From 7092e1ccff40c08c3e524b00c015c54755d8733f Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Tue, 14 Jan 2025 16:45:53 +0000 Subject: [PATCH 110/113] entitlements refactoring removing factories --- pkg/factory/entitlement_factory.go | 210 ++---------------- pkg/factory/entitlement_factory_test.go | 175 +-------------- pkg/factory/order_factory.go | 18 +- pkg/factory/shared_test.go | 8 - pkg/handlers/ghcapi/orders_test.go | 68 +----- pkg/handlers/internalapi/api_test.go | 9 - .../internal/payloads/payloads_test.go | 15 -- pkg/handlers/primeapi/api_test.go | 9 - .../primeapi/payloads/payloads_test.go | 9 - pkg/handlers/primeapiv2/api_test.go | 9 - .../primeapiv2/payloads/payloads_test.go | 9 - pkg/handlers/primeapiv3/api_test.go | 9 - .../primeapiv3/payloads/payloads_test.go | 9 - pkg/handlers/supportapi/api_test.go | 9 - .../payloads/model_to_payload_test.go | 9 - .../entitlements/entitlements_service_test.go | 7 - .../weight_allotment_fetcher_test.go | 57 +---- .../entitlements/weight_restrictor.go | 8 +- pkg/services/event/event_test.go | 8 - pkg/services/move/move_service_test.go | 9 - .../mto_shipment/mto_shipment_service_test.go | 9 - pkg/services/order/order_service_test.go | 9 - 22 files changed, 35 insertions(+), 647 deletions(-) diff --git a/pkg/factory/entitlement_factory.go b/pkg/factory/entitlement_factory.go index 3d6a374bf9d..0c450c2c52c 100644 --- a/pkg/factory/entitlement_factory.go +++ b/pkg/factory/entitlement_factory.go @@ -1,6 +1,9 @@ package factory import ( + "fmt" + "log" + "github.com/gobuffalo/pop/v6" "github.com/transcom/mymove/pkg/gen/internalmessages" @@ -60,12 +63,28 @@ func BuildEntitlement(db *pop.Connection, customs []Customization, traits []Trai OrganizationalClothingAndIndividualEquipment: ocie, } // Set default calculated values - weightData := getDefaultWeightData(string(*grade)) + var hhgAllowance models.HHGAllowance + if db != nil { + err := db. + RawQuery(` + SELECT hhg_allowances.* + FROM hhg_allowances + INNER JOIN pay_grades ON hhg_allowances.pay_grade_id = pay_grades.id + WHERE pay_grades.grade = $1 + LIMIT 1 + `, grade). + First(&hhgAllowance) + if err != nil { + // The database must not be running or the data was truncated + log.Panic(fmt.Errorf("database is not configured properly and is missing static hhg allowance and pay grade data %w", err)) + } + } + allotment := models.WeightAllotment{ - TotalWeightSelf: weightData.TotalWeightSelf, - TotalWeightSelfPlusDependents: weightData.TotalWeightSelfPlusDependents, - ProGearWeight: weightData.ProGearWeight, - ProGearWeightSpouse: weightData.ProGearWeightSpouse, + TotalWeightSelf: hhgAllowance.TotalWeightSelf, + TotalWeightSelfPlusDependents: hhgAllowance.TotalWeightSelfPlusDependents, + ProGearWeight: hhgAllowance.ProGearWeight, + ProGearWeightSpouse: hhgAllowance.ProGearWeightSpouse, } entitlement.WeightAllotted = &allotment entitlement.DBAuthorizedWeight = entitlement.AuthorizedWeight() @@ -80,184 +99,3 @@ func BuildEntitlement(db *pop.Connection, customs []Customization, traits []Trai return entitlement } - -func BuildPayGrade(db *pop.Connection, customs []Customization, traits []Trait) models.PayGrade { - customs = setupCustomizations(customs, traits) - - // Find Pay Grade Customization and extract the custom Pay Grade - var cPayGrade models.PayGrade - if result := findValidCustomization(customs, PayGrade); result != nil { - cPayGrade = result.Model.(models.PayGrade) - if result.LinkOnly { - return cPayGrade - } - } - - // Check if the Grade already exists - var existingPayGrade models.PayGrade - if db != nil { - err := db.Where("grade = ?", cPayGrade.Grade).First(&existingPayGrade) - if err == nil { - return existingPayGrade - } - } - - // Create default Pay Grade - payGrade := models.PayGrade{ - Grade: "E_5", - GradeDescription: models.StringPointer("Enlisted Grade E-5"), - } - - // Overwrite default values with those from custom Pay Grade - testdatagen.MergeModels(&payGrade, cPayGrade) - - if db != nil { - mustCreate(db, &payGrade) - } - - return payGrade -} - -func BuildHHGAllowance(db *pop.Connection, customs []Customization, traits []Trait) models.HHGAllowance { - customs = setupCustomizations(customs, traits) - - // Find HHG Allowance Customization and extract the custom HHG Allowance - var cHHGAllowance models.HHGAllowance - if result := findValidCustomization(customs, HHGAllowance); result != nil { - cHHGAllowance = result.Model.(models.HHGAllowance) - if result.LinkOnly { - return cHHGAllowance - } - } - - // Check if Allowance with this Grade already exists - var existingHHGAllowance models.HHGAllowance - if db != nil { - err := db.Where("pay_grade_id = ?", cHHGAllowance.PayGradeID).First(&existingHHGAllowance) - if err == nil { - return existingHHGAllowance - } - } - - // Create a default HHG Allowance with default pay grade - payGrade := BuildPayGrade(db, customs, traits) - defaultWeightData := getDefaultWeightData(payGrade.Grade) - - hhgAllowance := models.HHGAllowance{ - PayGradeID: payGrade.ID, - PayGrade: payGrade, - TotalWeightSelf: defaultWeightData.TotalWeightSelf, - TotalWeightSelfPlusDependents: defaultWeightData.TotalWeightSelfPlusDependents, - ProGearWeight: defaultWeightData.ProGearWeight, - ProGearWeightSpouse: defaultWeightData.ProGearWeightSpouse, - } - - // Overwrite default values with those from custom HHG Allowance - testdatagen.MergeModels(&hhgAllowance, cHHGAllowance) - - if db != nil { - mustCreate(db, &hhgAllowance) - } - - return hhgAllowance -} - -func DeleteAllotmentsFromDatabase(db *pop.Connection) error { - if db != nil { - err := db.RawQuery("DELETE FROM hhg_allowances").Exec() - if err != nil { - return err - } - err = db.RawQuery("DELETE FROM pay_grades").Exec() - if err != nil { - return err - } - } - return nil -} - -// Helper function to retrieve default weight data by grade -func getDefaultWeightData(grade string) struct { - TotalWeightSelf int - TotalWeightSelfPlusDependents int - ProGearWeight int - ProGearWeightSpouse int -} { - if data, ok := knownAllowances[grade]; ok { - return data - } - return knownAllowances["EMPTY"] // Default to EMPTY if grade not found. This is just dummy default data -} - -// Make the life easier for test suites by creating all -// known allotments and their expected outcomes -func SetupDefaultAllotments(db *pop.Connection) { - // Wrap in case of stub - if db != nil { - // Iterate over all known allowances - for grade, allowance := range knownAllowances { - // Build the pay grade and HHG allowance - pg := BuildPayGrade(db, []Customization{ - { - Model: models.PayGrade{ - Grade: grade, - }, - }, - }, nil) - BuildHHGAllowance(db, []Customization{ - { - Model: pg, - LinkOnly: true, - }, - { - Model: models.HHGAllowance{ - TotalWeightSelf: allowance.TotalWeightSelf, - TotalWeightSelfPlusDependents: allowance.TotalWeightSelfPlusDependents, - ProGearWeight: allowance.ProGearWeight, - ProGearWeightSpouse: allowance.ProGearWeightSpouse, - }, - }, - }, nil) - } - } -} - -// Default allowances CAO December 2024 -// Note that the testdatagen package has its own default allowance -var knownAllowances = map[string]struct { - TotalWeightSelf int - TotalWeightSelfPlusDependents int - ProGearWeight int - ProGearWeightSpouse int -}{ - "EMPTY": {0, 0, 0, 0}, - "ACADEMY_CADET": {350, 350, 0, 0}, - "MIDSHIPMAN": {350, 350, 0, 0}, - "AVIATION_CADET": {7000, 8000, 2000, 500}, - "E_1": {5000, 8000, 2000, 500}, - "E_2": {5000, 8000, 2000, 500}, - "E_3": {5000, 8000, 2000, 500}, - "E_4": {7000, 8000, 2000, 500}, - "E_5": {7000, 9000, 2000, 500}, - "E_6": {8000, 11000, 2000, 500}, - "E_7": {11000, 13000, 2000, 500}, - "E_8": {12000, 14000, 2000, 500}, - "E_9": {13000, 15000, 2000, 500}, - "E_9SPECIALSENIORENLISTED": {14000, 17000, 2000, 500}, - "O_1ACADEMYGRADUATE": {10000, 12000, 2000, 500}, - "O_2": {12500, 13500, 2000, 500}, - "O_3": {13000, 14500, 2000, 500}, - "O_4": {14000, 17000, 2000, 500}, - "O_5": {16000, 17500, 2000, 500}, - "O_6": {18000, 18000, 2000, 500}, - "O_7": {18000, 18000, 2000, 500}, - "O_8": {18000, 18000, 2000, 500}, - "O_9": {18000, 18000, 2000, 500}, - "O_10": {18000, 18000, 2000, 500}, - "W_1": {10000, 12000, 2000, 500}, - "W_2": {12500, 13500, 2000, 500}, - "W_3": {13000, 14500, 2000, 500}, - "W_4": {14000, 17000, 2000, 500}, - "W_5": {16000, 17500, 2000, 500}, - "CIVILIAN_EMPLOYEE": {18000, 18000, 2000, 500}, -} diff --git a/pkg/factory/entitlement_factory_test.go b/pkg/factory/entitlement_factory_test.go index 1e865f500df..3ece040f039 100644 --- a/pkg/factory/entitlement_factory_test.go +++ b/pkg/factory/entitlement_factory_test.go @@ -10,36 +10,7 @@ import ( func (suite *FactorySuite) TestBuildEntitlement() { fetcher := entitlements.NewWeightAllotmentFetcher() - setupE1Allotment := func() { - pg := BuildPayGrade(suite.DB(), []Customization{ - { - Model: models.PayGrade{ - Grade: "E_1", - }, - }, - }, nil) - BuildHHGAllowance(suite.DB(), []Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - } - setupO9Allotment := func() { - pg := BuildPayGrade(suite.DB(), []Customization{ - { - Model: models.PayGrade{ - Grade: "O_9", - }, - }, - }, nil) - BuildHHGAllowance(suite.DB(), []Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - } + suite.Run("Successful creation of default entitlement", func() { // Under test: BuildEntitlement // Mocked: None @@ -48,7 +19,6 @@ func (suite *FactorySuite) TestBuildEntitlement() { // SETUP // Create a default entitlement to compare values - setupE1Allotment() defEnt := models.Entitlement{ DependentsAuthorized: models.BoolPointer(true), TotalDependents: models.IntPointer(0), @@ -91,7 +61,6 @@ func (suite *FactorySuite) TestBuildEntitlement() { // SETUP // Create a default entitlement to compare values - setupE1Allotment() custEnt := models.Entitlement{ DependentsAuthorized: models.BoolPointer(false), TotalDependents: models.IntPointer(0), @@ -165,7 +134,6 @@ func (suite *FactorySuite) TestBuildEntitlement() { // SETUP // Create a default stubbed entitlement to compare values - setupO9Allotment() testEnt := BuildEntitlement(nil, nil, nil) // Set the weight allotment on the custom object to O_9 testEnt.DBAuthorizedWeight = nil // clear original value @@ -190,144 +158,3 @@ func (suite *FactorySuite) TestBuildEntitlement() { }) } - -func (suite *FactorySuite) TestBuildPayGrade() { - suite.Run("Successful creation of PayGrade with default values", func() { - // Default grade should be "E_5" - payGrade := BuildPayGrade(suite.DB(), nil, nil) - - suite.NotNil(payGrade.ID) - suite.Equal("E_5", payGrade.Grade) - suite.Equal("Enlisted Grade E-5", *payGrade.GradeDescription) - - pgCount, err := suite.DB().Count(models.PayGrade{}) - suite.NoError(err) - suite.True(pgCount > 0) - }) - - suite.Run("BuildPayGrade with customization", func() { - customGrade := "X-5" - customDescription := "Custom Grade X-5" - customPayGrade := models.PayGrade{ - Grade: customGrade, - GradeDescription: &customDescription, - } - - payGrade := BuildPayGrade( - suite.DB(), - []Customization{ - {Model: customPayGrade}, - }, - nil, - ) - - suite.Equal(customGrade, payGrade.Grade) - suite.Equal(customDescription, *payGrade.GradeDescription) - }) - - suite.Run("Finds existing record", func() { - - persistedPayGrade := BuildPayGrade(suite.DB(), nil, nil) - - pg := BuildPayGrade(suite.DB(), []Customization{ - { - Model: persistedPayGrade, - LinkOnly: true, - }, - }, nil) - - suite.Equal(persistedPayGrade.ID, pg.ID) - suite.Equal(persistedPayGrade.Grade, pg.Grade) - - }) -} - -func (suite *FactorySuite) TestBuildHHGAllowance() { - suite.Run("Successful creation of HHGAllowance with default values", func() { - // Default allowance and grade of E_5 - hhgAllowance := BuildHHGAllowance(suite.DB(), nil, nil) - suite.NotNil(hhgAllowance.PayGradeID) - suite.NotEmpty(hhgAllowance.PayGrade) - suite.NotEmpty(hhgAllowance.ProGearWeight) - suite.NotEmpty(hhgAllowance.ProGearWeightSpouse) - suite.NotEmpty(hhgAllowance.TotalWeightSelf) - suite.NotEmpty(hhgAllowance.TotalWeightSelfPlusDependents) - }) - - suite.Run("BuildHHGAllowance with customization", func() { - hhgAllowance := BuildHHGAllowance( - suite.DB(), - []Customization{ - {Model: models.HHGAllowance{ - TotalWeightSelf: 8000, - TotalWeightSelfPlusDependents: 12000, - ProGearWeight: 3000, - ProGearWeightSpouse: 600, - }}, - }, - nil, - ) - - // E_5 default allowances - suite.Equal(8000, hhgAllowance.TotalWeightSelf) - suite.Equal(12000, hhgAllowance.TotalWeightSelfPlusDependents) - suite.Equal(3000, hhgAllowance.ProGearWeight) - suite.Equal(600, hhgAllowance.ProGearWeightSpouse) - }) - - suite.Run("Finds existing record", func() { - pg := BuildPayGrade(suite.DB(), nil, nil) - - existingHhg := models.HHGAllowance{ - PayGradeID: pg.ID, - TotalWeightSelf: 8000, - TotalWeightSelfPlusDependents: 12000, - ProGearWeight: 3000, - ProGearWeightSpouse: 600, - } - suite.MustCreate(&existingHhg) - - newHhg := BuildHHGAllowance( - suite.DB(), - []Customization{ - {Model: models.HHGAllowance{PayGradeID: pg.ID}}, - }, - nil, - ) - - suite.Equal(existingHhg.ID, newHhg.ID) - suite.Equal(3000, newHhg.ProGearWeight) - }) -} - -func (suite *FactorySuite) deleteAllotmentsFromDatabase() { - err := suite.DB().RawQuery("DELETE FROM hhg_allowances").Exec() - suite.FatalNoError(err) - err = suite.DB().RawQuery("DELETE FROM pay_grades").Exec() - suite.FatalNoError(err) -} - -func (suite *FactorySuite) TestSetupAllAllotments() { - suite.Run("Successful creation of allotments for all known grades", func() { - - SetupDefaultAllotments(suite.DB()) - - // Validate the allotments - for grade, allowance := range knownAllowances { - // Ensure pay grade exists - pg := &models.PayGrade{} - err := suite.DB().Where("grade = ?", grade).First(pg) - suite.NoError(err, grade) - suite.NotNil(pg.ID, grade) - - // Ensure HHGAllowance was created and matches the expected values - hhgAllowance := &models.HHGAllowance{} - err = suite.DB().Where("pay_grade_id = ?", pg.ID).First(hhgAllowance) - suite.NoError(err, grade) - suite.Equal(allowance.TotalWeightSelf, hhgAllowance.TotalWeightSelf, grade) - suite.Equal(allowance.TotalWeightSelfPlusDependents, hhgAllowance.TotalWeightSelfPlusDependents, grade) - suite.Equal(allowance.ProGearWeight, hhgAllowance.ProGearWeight, grade) - suite.Equal(allowance.ProGearWeightSpouse, hhgAllowance.ProGearWeightSpouse, grade) - } - }) -} diff --git a/pkg/factory/order_factory.go b/pkg/factory/order_factory.go index 771dd5b2f3f..d64a2449e1c 100644 --- a/pkg/factory/order_factory.go +++ b/pkg/factory/order_factory.go @@ -1,6 +1,7 @@ package factory import ( + "fmt" "log" "time" @@ -301,14 +302,7 @@ func buildOrderWithBuildType(db *pop.Connection, customs []Customization, traits grade := internalmessages.OrderPayGrade(existingPayGrade.Grade) order.Grade = internalmessages.NewOrderPayGrade(grade) } else { - // Create a new PayGrade - existingPayGrade = BuildPayGrade(db, []Customization{ - { - Model: models.PayGrade{ - Grade: string(*order.Grade), - }, - }, - }, nil) + log.Panic(fmt.Errorf("database is not configured properly and is missing static hhg allowance and pay grade data %w", err)) } } @@ -316,13 +310,7 @@ func buildOrderWithBuildType(db *pop.Connection, customs []Customization, traits var existingHHGAllowance models.HHGAllowance err := db.Where("pay_grade_id = ?", existingPayGrade.ID).First(&existingHHGAllowance) if err != nil { - // Create a new HHGAllowance - BuildHHGAllowance(db, []Customization{ - { - Model: existingPayGrade, - LinkOnly: true, - }, - }, nil) + log.Panic(fmt.Errorf("database is not configured properly and is missing static hhg allowance and pay grade data %w", err)) } mustCreate(db, &order) diff --git a/pkg/factory/shared_test.go b/pkg/factory/shared_test.go index 35c951b834e..3e9dffffd11 100644 --- a/pkg/factory/shared_test.go +++ b/pkg/factory/shared_test.go @@ -15,14 +15,6 @@ type FactorySuite struct { *testingsuite.PopTestSuite } -func (suite *FactorySuite) SetupSuite() { - suite.PreloadData(func() { - // The factory suite creates allotments and allowances, make sure they're wiped - // This is because pay grades and hhg allowances are excluded from truncation, meaning they have static data built in - suite.deleteAllotmentsFromDatabase() - }) -} - func TestFactorySuite(t *testing.T) { ts := &FactorySuite{ diff --git a/pkg/handlers/ghcapi/orders_test.go b/pkg/handlers/ghcapi/orders_test.go index e7b63650aac..8ecbdc05318 100644 --- a/pkg/handlers/ghcapi/orders_test.go +++ b/pkg/handlers/ghcapi/orders_test.go @@ -40,22 +40,7 @@ func (suite *HandlerSuite) TestCreateOrder() { waf := entitlements.NewWeightAllotmentFetcher() sm := factory.BuildExtendedServiceMember(suite.AppContextForTest().DB(), nil, nil) officeUser := factory.BuildOfficeUserWithRoles(suite.AppContextForTest().DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) - setupE1Allotment := func() { - pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ - { - Model: models.PayGrade{ - Grade: "E_1", - }, - }, - }, nil) - factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - } - setupE1Allotment() + originDutyLocation := factory.BuildDutyLocation(suite.AppContextForTest().DB(), []factory.Customization{ { Model: models.DutyLocation{ @@ -124,22 +109,7 @@ func (suite *HandlerSuite) TestCreateOrder() { func (suite *HandlerSuite) TestCreateOrderWithOCONUSValues() { waf := entitlements.NewWeightAllotmentFetcher() - setupE1Allotment := func() { - pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ - { - Model: models.PayGrade{ - Grade: "E_1", - }, - }, - }, nil) - factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - } - setupE1Allotment() + sm := factory.BuildExtendedServiceMember(suite.AppContextForTest().DB(), nil, nil) officeUser := factory.BuildOfficeUserWithRoles(suite.AppContextForTest().DB(), nil, []roles.RoleType{roles.RoleTypeTOO}) @@ -1284,28 +1254,12 @@ func (suite *HandlerSuite) makeUpdateBillableWeightHandlerSubtestData() (subtest func (suite *HandlerSuite) TestUpdateAllowanceHandler() { request := httptest.NewRequest("PATCH", "/orders/{orderID}/allowances", nil) - setupGrade := func(grade string) { - pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ - { - Model: models.PayGrade{ - Grade: grade, - }, - }, - }, nil) - factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - } + suite.Run("Returns 200 when all validations pass", func() { handlerConfig := suite.HandlerConfig() subtestData := suite.makeUpdateAllowanceHandlerSubtestData() order := subtestData.order body := subtestData.body - // We're going to update from E_1 to O_5. Make sure O_5 has an allowance - setupGrade(string(*subtestData.body.Grade)) requestUser := factory.BuildOfficeUserWithRoles(nil, nil, []roles.RoleType{roles.RoleTypeTOO, roles.RoleTypeTIO, roles.RoleTypeServicesCounselor}) request = suite.AuthenticateOfficeRequest(request, requestUser) @@ -1511,21 +1465,6 @@ func (suite *HandlerSuite) TestUpdateAllowanceEventTrigger() { func (suite *HandlerSuite) TestCounselingUpdateAllowanceHandler() { grade := ghcmessages.GradeO5 - setupGrade := func(grade string) { - pg := factory.BuildPayGrade(suite.DB(), []factory.Customization{ - { - Model: models.PayGrade{ - Grade: grade, - }, - }, - }, nil) - factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - } affiliation := ghcmessages.AffiliationAIRFORCE ocie := false proGearWeight := models.Int64Pointer(100) @@ -1546,7 +1485,6 @@ func (suite *HandlerSuite) TestCounselingUpdateAllowanceHandler() { request := httptest.NewRequest("PATCH", "/counseling/orders/{orderID}/allowances", nil) suite.Run("Returns 200 when all validations pass", func() { - setupGrade(string(grade)) handlerConfig := suite.HandlerConfig() move := factory.BuildNeedsServiceCounselingMove(suite.DB(), nil, nil) order := move.Orders diff --git a/pkg/handlers/internalapi/api_test.go b/pkg/handlers/internalapi/api_test.go index 5776e66fce2..e4c243a6dd2 100644 --- a/pkg/handlers/internalapi/api_test.go +++ b/pkg/handlers/internalapi/api_test.go @@ -27,17 +27,8 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } -func (suite *HandlerSuite) deleteAllotmentsFromDatabase() { - err := suite.DB().RawQuery("DELETE FROM hhg_allowances").Exec() - suite.FatalNoError(err) - err = suite.DB().RawQuery("DELETE FROM pay_grades").Exec() - suite.FatalNoError(err) -} - func (suite *HandlerSuite) SetupSuite() { suite.PreloadData(func() { - suite.deleteAllotmentsFromDatabase() - factory.SetupDefaultAllotments(suite.DB()) factory.FetchOrBuildCountry(suite.DB(), []factory.Customization{ { Model: models.Country{ diff --git a/pkg/handlers/internalapi/internal/payloads/payloads_test.go b/pkg/handlers/internalapi/internal/payloads/payloads_test.go index 6e152f3b6b9..c17b8a85256 100644 --- a/pkg/handlers/internalapi/internal/payloads/payloads_test.go +++ b/pkg/handlers/internalapi/internal/payloads/payloads_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/storage" @@ -19,20 +18,6 @@ type PayloadsSuite struct { storer storage.FileStorer } -func (suite *PayloadsSuite) deleteAllotmentsFromDatabase() { - err := suite.DB().RawQuery("DELETE FROM hhg_allowances").Exec() - suite.FatalNoError(err) - err = suite.DB().RawQuery("DELETE FROM pay_grades").Exec() - suite.FatalNoError(err) -} - -func (suite *PayloadsSuite) SetupSuite() { - suite.PreloadData(func() { - suite.deleteAllotmentsFromDatabase() - factory.SetupDefaultAllotments(suite.DB()) - }) -} - // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/handlers/primeapi/api_test.go b/pkg/handlers/primeapi/api_test.go index 2e982e8cdd6..971fa44c0f2 100644 --- a/pkg/handlers/primeapi/api_test.go +++ b/pkg/handlers/primeapi/api_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/gen/primemessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" @@ -18,14 +17,6 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } -func (suite *HandlerSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - // AfterTest completes tests by trying to close open files func (suite *HandlerSuite) AfterTest() { for _, file := range suite.TestFilesToClose() { diff --git a/pkg/handlers/primeapi/payloads/payloads_test.go b/pkg/handlers/primeapi/payloads/payloads_test.go index d49184bbc27..3ec8f8386dd 100644 --- a/pkg/handlers/primeapi/payloads/payloads_test.go +++ b/pkg/handlers/primeapi/payloads/payloads_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -16,14 +15,6 @@ type PayloadsSuite struct { handlers.BaseHandlerTestSuite } -func (suite *PayloadsSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/handlers/primeapiv2/api_test.go b/pkg/handlers/primeapiv2/api_test.go index 537e8592109..f1697c293f9 100644 --- a/pkg/handlers/primeapiv2/api_test.go +++ b/pkg/handlers/primeapiv2/api_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -16,14 +15,6 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } -func (suite *HandlerSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - // AfterTest completes tests by trying to close open files func (suite *HandlerSuite) AfterTest() { for _, file := range suite.TestFilesToClose() { diff --git a/pkg/handlers/primeapiv2/payloads/payloads_test.go b/pkg/handlers/primeapiv2/payloads/payloads_test.go index d49184bbc27..3ec8f8386dd 100644 --- a/pkg/handlers/primeapiv2/payloads/payloads_test.go +++ b/pkg/handlers/primeapiv2/payloads/payloads_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -16,14 +15,6 @@ type PayloadsSuite struct { handlers.BaseHandlerTestSuite } -func (suite *PayloadsSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/handlers/primeapiv3/api_test.go b/pkg/handlers/primeapiv3/api_test.go index 070b3b80056..19aa406fdae 100644 --- a/pkg/handlers/primeapiv3/api_test.go +++ b/pkg/handlers/primeapiv3/api_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -23,14 +22,6 @@ func (suite *HandlerSuite) AfterTest() { } } -func (suite *HandlerSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &HandlerSuite{ diff --git a/pkg/handlers/primeapiv3/payloads/payloads_test.go b/pkg/handlers/primeapiv3/payloads/payloads_test.go index d49184bbc27..3ec8f8386dd 100644 --- a/pkg/handlers/primeapiv3/payloads/payloads_test.go +++ b/pkg/handlers/primeapiv3/payloads/payloads_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -16,14 +15,6 @@ type PayloadsSuite struct { handlers.BaseHandlerTestSuite } -func (suite *PayloadsSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/handlers/supportapi/api_test.go b/pkg/handlers/supportapi/api_test.go index 38303355669..580dc4c4081 100644 --- a/pkg/handlers/supportapi/api_test.go +++ b/pkg/handlers/supportapi/api_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/notifications" "github.com/transcom/mymove/pkg/testingsuite" @@ -16,14 +15,6 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } -func (suite *HandlerSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - // AfterTest completes tests by trying to close open files func (hs *HandlerSuite) AfterTest() { for _, file := range hs.TestFilesToClose() { diff --git a/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go b/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go index 85003478d7b..eeb93bc0394 100644 --- a/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go +++ b/pkg/handlers/supportapi/internal/payloads/model_to_payload_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/suite" "github.com/transcom/mymove/pkg/etag" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/handlers" "github.com/transcom/mymove/pkg/models" @@ -23,14 +22,6 @@ type PayloadsSuite struct { handlers.BaseHandlerTestSuite } -func (suite *PayloadsSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - // TestHandlerSuite creates our test suite func TestHandlerSuite(t *testing.T) { hs := &PayloadsSuite{ diff --git a/pkg/services/entitlements/entitlements_service_test.go b/pkg/services/entitlements/entitlements_service_test.go index fb3da88399f..241ebd258cd 100644 --- a/pkg/services/entitlements/entitlements_service_test.go +++ b/pkg/services/entitlements/entitlements_service_test.go @@ -6,7 +6,6 @@ import ( "github.com/spf13/afero" "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -16,12 +15,6 @@ type EntitlementsServiceSuite struct { fs *afero.Afero } -func (suite *EntitlementsServiceSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - }) -} func TestEntitlementsServiceSuite(t *testing.T) { var f = afero.NewMemMapFs() file := &afero.Afero{Fs: f} diff --git a/pkg/services/entitlements/weight_allotment_fetcher_test.go b/pkg/services/entitlements/weight_allotment_fetcher_test.go index dd6bc994693..43330be5c09 100644 --- a/pkg/services/entitlements/weight_allotment_fetcher_test.go +++ b/pkg/services/entitlements/weight_allotment_fetcher_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/transcom/mymove/pkg/apperror" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/gen/internalmessages" "github.com/transcom/mymove/pkg/models" ) @@ -13,22 +12,10 @@ func (suite *EntitlementsServiceSuite) TestGetWeightAllotment() { suite.Run("If a weight allotment is fetched by grade, it should be returned", func() { fetcher := NewWeightAllotmentFetcher() - pg := factory.BuildPayGrade(suite.DB(), nil, nil) - hhgAllowance := factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: pg, - LinkOnly: true, - }, - }, nil) - - allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), pg.Grade, internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) + allotment, err := fetcher.GetWeightAllotment(suite.AppContextForTest(), "E_1", internalmessages.OrdersTypePERMANENTCHANGEOFSTATION) suite.NoError(err) - suite.Equal(hhgAllowance.TotalWeightSelf, allotment.TotalWeightSelf) - suite.Equal(hhgAllowance.TotalWeightSelfPlusDependents, allotment.TotalWeightSelfPlusDependents) - suite.Equal(hhgAllowance.ProGearWeight, allotment.ProGearWeight) - suite.Equal(hhgAllowance.ProGearWeightSpouse, allotment.ProGearWeightSpouse) - suite.Equal(hhgAllowance.PayGrade.Grade, pg.Grade) + suite.NotEmpty(allotment) }) suite.Run("If pay grade does not exist, return an error", func() { @@ -45,47 +32,9 @@ func (suite *EntitlementsServiceSuite) TestGetAllWeightAllotments() { suite.Run("Successfully fetch all weight allotments", func() { fetcher := NewWeightAllotmentFetcher() - // Build E-5 - e5 := factory.BuildPayGrade(suite.DB(), nil, nil) - e5Allowance := factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: e5, // Link the pay grade - LinkOnly: true, - }, - }, nil) - - // Build E-6 - e6 := factory.BuildPayGrade(suite.DB(), []factory.Customization{ - { - Model: models.PayGrade{ - Grade: "E-6", - }, - }, - }, nil) - e6Allowance := factory.BuildHHGAllowance(suite.DB(), []factory.Customization{ - { - Model: e6, - LinkOnly: true, - }, - }, nil) - allotments, err := fetcher.GetAllWeightAllotments(suite.AppContextForTest()) suite.NoError(err) - suite.Len(allotments, 2) - - // Check E-5 allotment by its map key - e5Key := internalmessages.OrderPayGrade(e5.Grade) - suite.Equal(e5Allowance.TotalWeightSelf, allotments[e5Key].TotalWeightSelf) - suite.Equal(e5Allowance.TotalWeightSelfPlusDependents, allotments[e5Key].TotalWeightSelfPlusDependents) - suite.Equal(e5Allowance.ProGearWeight, allotments[e5Key].ProGearWeight) - suite.Equal(e5Allowance.ProGearWeightSpouse, allotments[e5Key].ProGearWeightSpouse) - - // Check E-6 allotment by its map key - e6Key := internalmessages.OrderPayGrade(e6.Grade) - suite.Equal(e6Allowance.TotalWeightSelf, allotments[e6Key].TotalWeightSelf) - suite.Equal(e6Allowance.TotalWeightSelfPlusDependents, allotments[e6Key].TotalWeightSelfPlusDependents) - suite.Equal(e6Allowance.ProGearWeight, allotments[e6Key].ProGearWeight) - suite.Equal(e6Allowance.ProGearWeightSpouse, allotments[e6Key].ProGearWeightSpouse) + suite.Greater(len(allotments), 0) }) } diff --git a/pkg/services/entitlements/weight_restrictor.go b/pkg/services/entitlements/weight_restrictor.go index 62dd6c1d4e3..deae4a57c19 100644 --- a/pkg/services/entitlements/weight_restrictor.go +++ b/pkg/services/entitlements/weight_restrictor.go @@ -93,14 +93,8 @@ func (wr *weightRestrictor) RemoveWeightRestrictionFromEntitlement(appCtx appcon func (wr *weightRestrictor) fetchMaxHhgAllowance(appCtx appcontext.AppContext) (int, error) { var maxHhgAllowance int err := appCtx.DB(). - RawQuery(` - SELECT parameter_value::int - FROM application_parameters - WHERE parameter_name = $1 - LIMIT 1 - `, "maxHhgAllowance"). + RawQuery(`SELECT parameter_value::int FROM application_parameters WHERE parameter_name = 'maxHhgAllowance' LIMIT 1`). First(&maxHhgAllowance) - if err != nil { return maxHhgAllowance, apperror.NewQueryError("ApplicationParameters", err, "error fetching max HHG allowance") } diff --git a/pkg/services/event/event_test.go b/pkg/services/event/event_test.go index 1d1e6d2ea87..3de1e255036 100644 --- a/pkg/services/event/event_test.go +++ b/pkg/services/event/event_test.go @@ -20,14 +20,6 @@ type EventServiceSuite struct { *testingsuite.PopTestSuite } -func (suite *EventServiceSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - func TestEventServiceSuite(t *testing.T) { ts := &EventServiceSuite{ PopTestSuite: testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), testingsuite.WithPerTestTransaction()), diff --git a/pkg/services/move/move_service_test.go b/pkg/services/move/move_service_test.go index b40530b87cb..1a16c967e81 100644 --- a/pkg/services/move/move_service_test.go +++ b/pkg/services/move/move_service_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -13,14 +12,6 @@ type MoveServiceSuite struct { *testingsuite.PopTestSuite } -func (suite *MoveServiceSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - func TestMoveServiceSuite(t *testing.T) { hs := &MoveServiceSuite{ diff --git a/pkg/services/mto_shipment/mto_shipment_service_test.go b/pkg/services/mto_shipment/mto_shipment_service_test.go index 57e41e8191d..e5696befda7 100644 --- a/pkg/services/mto_shipment/mto_shipment_service_test.go +++ b/pkg/services/mto_shipment/mto_shipment_service_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -13,14 +12,6 @@ type MTOShipmentServiceSuite struct { *testingsuite.PopTestSuite } -func (suite *MTOShipmentServiceSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - func TestMTOShipmentServiceSuite(t *testing.T) { ts := &MTOShipmentServiceSuite{ diff --git a/pkg/services/order/order_service_test.go b/pkg/services/order/order_service_test.go index ace19077dd7..92313ed9635 100644 --- a/pkg/services/order/order_service_test.go +++ b/pkg/services/order/order_service_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/transcom/mymove/pkg/factory" "github.com/transcom/mymove/pkg/testingsuite" ) @@ -13,14 +12,6 @@ type OrderServiceSuite struct { *testingsuite.PopTestSuite } -func (suite *OrderServiceSuite) SetupSuite() { - suite.PreloadData(func() { - err := factory.DeleteAllotmentsFromDatabase(suite.DB()) - suite.FatalNoError(err) - factory.SetupDefaultAllotments(suite.DB()) - }) -} - func TestOrderServiceSuite(t *testing.T) { ts := &OrderServiceSuite{ PopTestSuite: testingsuite.NewPopTestSuite(testingsuite.CurrentPackage(), testingsuite.WithPerTestTransaction()), From eaec912ae6f2c9814a724a34a2cfbfb48e79584d Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Tue, 14 Jan 2025 16:51:19 +0000 Subject: [PATCH 111/113] remove is_weight_restricted bool --- .../app/schema/20241209121924_entitlements_refactor.up.sql | 3 +-- pkg/models/ghc_entitlements.go | 1 - pkg/services/entitlements/weight_restrictor.go | 6 ++---- pkg/services/entitlements/weight_restrictor_test.go | 7 ++----- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/migrations/app/schema/20241209121924_entitlements_refactor.up.sql b/migrations/app/schema/20241209121924_entitlements_refactor.up.sql index 3620e24bef0..a8438074776 100644 --- a/migrations/app/schema/20241209121924_entitlements_refactor.up.sql +++ b/migrations/app/schema/20241209121924_entitlements_refactor.up.sql @@ -1,8 +1,7 @@ -- See https://dp3.atlassian.net/wiki/spaces/MT/pages/2738716677/HHG+and+UB+Entitlements -- Prep entitlements table for holding weight restricted ALTER TABLE entitlements -ADD COLUMN IF NOT EXISTS is_weight_restricted boolean NOT NULL DEFAULT false, - ADD COLUMN IF NOT EXISTS weight_restriction int; +ADD COLUMN IF NOT EXISTS weight_restriction int; -- Create pay grades table to get our static entitlements.go file to be db based CREATE TABLE IF NOT EXISTS pay_grades ( id uuid PRIMARY KEY NOT NULL, diff --git a/pkg/models/ghc_entitlements.go b/pkg/models/ghc_entitlements.go index eade163db45..f56c915ad19 100644 --- a/pkg/models/ghc_entitlements.go +++ b/pkg/models/ghc_entitlements.go @@ -34,7 +34,6 @@ type Entitlement struct { OrganizationalClothingAndIndividualEquipment bool `db:"organizational_clothing_and_individual_equipment"` ProGearWeight int `db:"pro_gear_weight"` ProGearWeightSpouse int `db:"pro_gear_weight_spouse"` - IsWeightRestricted bool `db:"is_weight_restricted"` WeightRestriction *int `db:"weight_restriction"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` diff --git a/pkg/services/entitlements/weight_restrictor.go b/pkg/services/entitlements/weight_restrictor.go index deae4a57c19..a8255aa51cf 100644 --- a/pkg/services/entitlements/weight_restrictor.go +++ b/pkg/services/entitlements/weight_restrictor.go @@ -44,8 +44,7 @@ func (wr *weightRestrictor) ApplyWeightRestrictionToEntitlement(appCtx appcontex nil, "error applying weight restriction") } - // Update the restriction fields - originalEntitlement.IsWeightRestricted = true + // Update the restriction field originalEntitlement.WeightRestriction = &weightRestriction verrs, err := appCtx.DB().ValidateAndUpdate(&originalEntitlement) @@ -75,8 +74,7 @@ func (wr *weightRestrictor) RemoveWeightRestrictionFromEntitlement(appCtx appcon return nil, apperror.NewPreconditionFailedError(originalEntitlement.ID, nil) } - // Update the restriction fields - originalEntitlement.IsWeightRestricted = false + // Update the restriction field originalEntitlement.WeightRestriction = nil verrs, err := appCtx.DB().ValidateAndUpdate(&originalEntitlement) diff --git a/pkg/services/entitlements/weight_restrictor_test.go b/pkg/services/entitlements/weight_restrictor_test.go index 19168e7c55a..b9b2b534156 100644 --- a/pkg/services/entitlements/weight_restrictor_test.go +++ b/pkg/services/entitlements/weight_restrictor_test.go @@ -30,7 +30,6 @@ func (suite *EntitlementsServiceSuite) TestWeightRestrictor() { updatedEntitlement, err := restrictor.ApplyWeightRestrictionToEntitlement(suite.AppContextForTest(), entitlement, 10000, etag.GenerateEtag(entitlement.UpdatedAt)) suite.NoError(err) suite.NotNil(updatedEntitlement) - suite.True(updatedEntitlement.IsWeightRestricted) suite.NotNil(updatedEntitlement.WeightRestriction) suite.Equal(10000, *updatedEntitlement.WeightRestriction) }) @@ -70,9 +69,8 @@ func (suite *EntitlementsServiceSuite) TestWeightRestrictor() { // Create an entitlement with a restriction already applied weightRestriction := 5000 entitlement := models.Entitlement{ - ID: uuid.Must(uuid.NewV4()), - IsWeightRestricted: true, - WeightRestriction: &weightRestriction, + ID: uuid.Must(uuid.NewV4()), + WeightRestriction: &weightRestriction, } suite.MustCreate(&entitlement) @@ -80,7 +78,6 @@ func (suite *EntitlementsServiceSuite) TestWeightRestrictor() { updatedEntitlement, err := restrictor.RemoveWeightRestrictionFromEntitlement(suite.AppContextForTest(), entitlement, etag.GenerateEtag(entitlement.UpdatedAt)) suite.NoError(err) suite.NotNil(updatedEntitlement) - suite.False(updatedEntitlement.IsWeightRestricted) suite.Nil(updatedEntitlement.WeightRestriction) }) From 840bb3a2e6f8b6d60e778773fa20c6740b279d0a Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Tue, 14 Jan 2025 18:19:52 +0000 Subject: [PATCH 112/113] test fixes --- pkg/factory/entitlement_factory.go | 4 ++-- pkg/factory/order_factory.go | 22 ++++++++++----------- pkg/handlers/ghcapi/api_test.go | 13 ------------ pkg/handlers/ghcapi/move_task_order_test.go | 2 +- pkg/handlers/ghcapi/move_test.go | 2 +- pkg/handlers/ghcapi/moving_expense_test.go | 5 ++--- pkg/handlers/ghcapi/ppm_document_test.go | 10 ++++------ pkg/handlers/ghcapi/ppm_shipment_test.go | 5 ++--- 8 files changed, 22 insertions(+), 41 deletions(-) diff --git a/pkg/factory/entitlement_factory.go b/pkg/factory/entitlement_factory.go index 0c450c2c52c..17aff299990 100644 --- a/pkg/factory/entitlement_factory.go +++ b/pkg/factory/entitlement_factory.go @@ -64,7 +64,7 @@ func BuildEntitlement(db *pop.Connection, customs []Customization, traits []Trai } // Set default calculated values var hhgAllowance models.HHGAllowance - if db != nil { + if db != nil && grade != nil { err := db. RawQuery(` SELECT hhg_allowances.* @@ -76,7 +76,7 @@ func BuildEntitlement(db *pop.Connection, customs []Customization, traits []Trai First(&hhgAllowance) if err != nil { // The database must not be running or the data was truncated - log.Panic(fmt.Errorf("database is not configured properly and is missing static hhg allowance and pay grade data %w", err)) + log.Panic(fmt.Errorf("database is not configured properly and is missing static hhg allowance and pay grade data. pay grade: %s err: %w", *order.Grade, err)) } } diff --git a/pkg/factory/order_factory.go b/pkg/factory/order_factory.go index d64a2449e1c..fa5e6b23a82 100644 --- a/pkg/factory/order_factory.go +++ b/pkg/factory/order_factory.go @@ -292,25 +292,23 @@ func buildOrderWithBuildType(db *pop.Connection, customs []Customization, traits testdatagen.MergeModels(&order, cOrder) // If db is false, it's a stub. No need to create in database - if db != nil { + if db != nil && order.Grade != nil { // Check if PayGrade already exists var existingPayGrade models.PayGrade - if order.Grade != nil { - err := db.Where("grade = ?", string(*order.Grade)).First(&existingPayGrade) - if err == nil { - // PayGrade exists - grade := internalmessages.OrderPayGrade(existingPayGrade.Grade) - order.Grade = internalmessages.NewOrderPayGrade(grade) - } else { - log.Panic(fmt.Errorf("database is not configured properly and is missing static hhg allowance and pay grade data %w", err)) - } + err := db.Where("grade = ?", string(*order.Grade)).First(&existingPayGrade) + if err == nil { + // PayGrade exists + grade := internalmessages.OrderPayGrade(existingPayGrade.Grade) + order.Grade = internalmessages.NewOrderPayGrade(grade) + } else { + log.Panic(fmt.Errorf("database is not configured properly and is missing static hhg allowance and pay grade data. pay grade: %s err: %w", *order.Grade, err)) } // Check if HHGAllowance already exists for this PayGrade var existingHHGAllowance models.HHGAllowance - err := db.Where("pay_grade_id = ?", existingPayGrade.ID).First(&existingHHGAllowance) + err = db.Where("pay_grade_id = ?", existingPayGrade.ID).First(&existingHHGAllowance) if err != nil { - log.Panic(fmt.Errorf("database is not configured properly and is missing static hhg allowance and pay grade data %w", err)) + log.Panic(fmt.Errorf("database is not configured properly and is missing static hhg allowance and pay grade data. pay grade: %s err: %w", *order.Grade, err)) } mustCreate(db, &order) diff --git a/pkg/handlers/ghcapi/api_test.go b/pkg/handlers/ghcapi/api_test.go index 5050123528d..c608e5de951 100644 --- a/pkg/handlers/ghcapi/api_test.go +++ b/pkg/handlers/ghcapi/api_test.go @@ -16,19 +16,6 @@ type HandlerSuite struct { handlers.BaseHandlerTestSuite } -func (suite *HandlerSuite) deleteAllotmentsFromDatabase() { - err := suite.DB().RawQuery("DELETE FROM hhg_allowances").Exec() - suite.FatalNoError(err) - err = suite.DB().RawQuery("DELETE FROM pay_grades").Exec() - suite.FatalNoError(err) -} - -func (suite *HandlerSuite) SetupSuite() { - suite.PreloadData(func() { - suite.deleteAllotmentsFromDatabase() - }) -} - // AfterTest completes tests by trying to close open files func (suite *HandlerSuite) AfterTest() { for _, file := range suite.TestFilesToClose() { diff --git a/pkg/handlers/ghcapi/move_task_order_test.go b/pkg/handlers/ghcapi/move_task_order_test.go index 132821069c1..a156eb19251 100644 --- a/pkg/handlers/ghcapi/move_task_order_test.go +++ b/pkg/handlers/ghcapi/move_task_order_test.go @@ -108,7 +108,7 @@ func (suite *HandlerSuite) TestUpdateMoveTaskOrderHandlerIntegrationSuccess() { suite.MustSave(&csTaskOrderFee) } - setupPricerData() + suite.PreloadData(setupPricerData) setUpSignedCertificationCreatorMock := func(returnValue ...interface{}) services.SignedCertificationCreator { mockCreator := &mocks.SignedCertificationCreator{} diff --git a/pkg/handlers/ghcapi/move_test.go b/pkg/handlers/ghcapi/move_test.go index 05e3ee43a55..f345c7cfb85 100644 --- a/pkg/handlers/ghcapi/move_test.go +++ b/pkg/handlers/ghcapi/move_test.go @@ -927,7 +927,7 @@ func (suite *HandlerSuite) TestCheckForLockedMovesAndUnlockHandler() { return req, handler } - setupLockedMove() + suite.PreloadData(setupLockedMove) suite.Run("Successful unlocking of move", func() { req, handler := setupTestData() diff --git a/pkg/handlers/ghcapi/moving_expense_test.go b/pkg/handlers/ghcapi/moving_expense_test.go index b95a42ef201..5d7e1b8f3a9 100644 --- a/pkg/handlers/ghcapi/moving_expense_test.go +++ b/pkg/handlers/ghcapi/moving_expense_test.go @@ -27,7 +27,7 @@ import ( func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerUnit() { var ppmShipment models.PPMShipment - setupData := func() { + suite.PreloadData(func() { userUploader, err := uploader.NewUserUploader(suite.createS3HandlerConfig().FileStorer(), uploader.MaxCustomerUserUploadFileSizeLimit) suite.FatalNoError(err) @@ -54,8 +54,7 @@ func (suite *HandlerSuite) TestUpdateMovingExpenseHandlerUnit() { }, }, nil), ) - } - setupData() + }) setUpRequestAndParams := func() movingexpenseops.UpdateMovingExpenseParams { movingExpense := ppmShipment.MovingExpenses[0] diff --git a/pkg/handlers/ghcapi/ppm_document_test.go b/pkg/handlers/ghcapi/ppm_document_test.go index aaea0745f34..576a792f43c 100644 --- a/pkg/handlers/ghcapi/ppm_document_test.go +++ b/pkg/handlers/ghcapi/ppm_document_test.go @@ -28,7 +28,7 @@ import ( func (suite *HandlerSuite) TestGetPPMDocumentsHandlerUnit() { var ppmShipment models.PPMShipment - setupData := func() { + suite.PreloadData(func() { userUploader, err := uploader.NewUserUploader(suite.createS3HandlerConfig().FileStorer(), uploader.MaxCustomerUserUploadFileSizeLimit) suite.FatalNoError(err) @@ -103,8 +103,7 @@ func (suite *HandlerSuite) TestGetPPMDocumentsHandlerUnit() { ) } - } - setupData() + }) setUpRequestAndParams := func() ppmdocumentops.GetPPMDocumentsParams { endpoint := fmt.Sprintf("/shipments/%s/ppm-documents", ppmShipment.Shipment.ID.String()) @@ -234,7 +233,7 @@ func (suite *HandlerSuite) TestGetPPMDocumentsHandlerIntegration() { var ppmShipment models.PPMShipment - setupData := func() { + suite.PreloadData(func() { userUploader, err := uploader.NewUserUploader(suite.createS3HandlerConfig().FileStorer(), uploader.MaxCustomerUserUploadFileSizeLimit) suite.FatalNoError(err) @@ -309,8 +308,7 @@ func (suite *HandlerSuite) TestGetPPMDocumentsHandlerIntegration() { ) } - } - setupData() + }) setUpParamsAndHandler := func() (ppmdocumentops.GetPPMDocumentsParams, GetPPMDocumentsHandler) { endpoint := fmt.Sprintf("/shipments/%s/ppm-documents", ppmShipment.Shipment.ID.String()) diff --git a/pkg/handlers/ghcapi/ppm_shipment_test.go b/pkg/handlers/ghcapi/ppm_shipment_test.go index d1ddd182159..bc8093032b5 100644 --- a/pkg/handlers/ghcapi/ppm_shipment_test.go +++ b/pkg/handlers/ghcapi/ppm_shipment_test.go @@ -26,7 +26,7 @@ func (suite *HandlerSuite) TestGetPPMSITEstimatedCostHandler() { var ppmShipment models.PPMShipment newFakeSITEstimatedCost := models.CentPointer(unit.Cents(25500)) - setupPricerData := func() { + suite.PreloadData(func() { testdatagen.FetchOrMakeGHCDieselFuelPrice(suite.DB(), testdatagen.Assertions{ GHCDieselFuelPrice: models.GHCDieselFuelPrice{ FuelPriceInMillicents: unit.Millicents(281400), @@ -342,10 +342,9 @@ func (suite *HandlerSuite) TestGetPPMSITEstimatedCostHandler() { PriceCents: 63, }, }) - } + }) setupData := func() { - setupPricerData() sitLocationDestination := models.SITLocationTypeDestination entryDate := time.Date(2020, time.March, 15, 0, 0, 0, 0, time.UTC) mtoShipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ From 8f5340c0020bc60929c2d62e1409f28d5ce52f46 Mon Sep 17 00:00:00 2001 From: cameroncaci Date: Tue, 14 Jan 2025 14:22:55 -0500 Subject: [PATCH 113/113] final test fix --- pkg/handlers/ghcapi/orders_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/handlers/ghcapi/orders_test.go b/pkg/handlers/ghcapi/orders_test.go index 8ecbdc05318..8a8d9c09bbf 100644 --- a/pkg/handlers/ghcapi/orders_test.go +++ b/pkg/handlers/ghcapi/orders_test.go @@ -238,7 +238,7 @@ func (suite *HandlerSuite) TestGetOrderHandlerIntegration() { func (suite *HandlerSuite) TestWeightAllowances() { suite.Run("With E-1 rank and no dependents", func() { - order := factory.BuildOrder(nil, []factory.Customization{ + order := factory.BuildOrder(suite.DB(), []factory.Customization{ { Model: models.Order{ ID: uuid.Must(uuid.NewV4()), @@ -292,7 +292,7 @@ func (suite *HandlerSuite) TestWeightAllowances() { }) suite.Run("With E-1 rank and dependents", func() { - order := factory.BuildOrder(nil, []factory.Customization{ + order := factory.BuildOrder(suite.DB(), []factory.Customization{ { Model: models.Order{ ID: uuid.Must(uuid.NewV4()),