Skip to content

Commit

Permalink
fix(transport-authority): Handle all warnSever (E, L, W) + dummy mile…
Browse files Browse the repository at this point in the history
…age in vehicle validation (#16662)

* Fix typo

* Fix mileageReading vs isRequired
Stop using mileageRequired from answers in getSelectedVehicle and always look at the value from externalData (it should not change)

* Make sure to always use requiresMileageRegistration from currentvehicleswithmileageandinsp (not basicVehicleInformation)
Cleanup make+vehcom (basicVehicleInformation) vs make (currentvehicles)

* Cleanup

* cleanup

* cleanup

* cleanup

* Use shared function to extract messages from error body
Display always E, L and W messages (but E and L first)

* Use dummy mileage value when validating per vehicle (owner/operator change)

* Catch error from mileage api

* Fix the way validation errors are displayed in SGS ownerchange
Allow all users to retry submit application if all approvals are finished

* Apply same change to change co-owner + change operator
Display ValidationErrorMessages always in overview, no matter who is reviewing

* Cleanup

* Cleanup in LicensePlateRenewal + OrderVehicleLicensePlate
Add validation per plate if user has more than 5

* Fix the way vehicle subModel is displayed

* Fixes after review

* Fix the way errors are displayed for RenewLicensePlate
Add MocablePayment

* Add validation for OrderVehicleLicensePlate

* Cleanup

* Fix comment

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
johannaagma and kodiakhq[bot] authored Nov 18, 2024
1 parent b8905ab commit d89ae9c
Show file tree
Hide file tree
Showing 100 changed files with 1,902 additions and 1,007 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './ownerChangeAnswers.input'
export * from './operatorChangeAnswers.input'
export * from './checkTachoNet.input'
export * from './plateAvailability.input'
export * from './plateOrderAnswers.input'
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Field, InputType } from '@nestjs/graphql'

@InputType()
export class PlateOrderAnswersPickVehicle {
@Field(() => String, { nullable: false })
plate!: string
}

@InputType()
export class PlateOrderAnswersPlateSize {
@Field(() => [String], { nullable: true })
frontPlateSize?: string[]

@Field(() => [String], { nullable: true })
rearPlateSize?: string[]
}

@InputType()
export class OperatorChangeAnswersPlateDelivery {
@Field(() => String, { nullable: true })
deliveryMethodIsDeliveryStation?: string

@Field(() => String, { nullable: true })
deliveryStationTypeCode?: string

@Field(() => [String], { nullable: true })
includeRushFee?: string[]
}

@InputType()
export class PlateOrderAnswers {
@Field(() => PlateOrderAnswersPickVehicle, { nullable: false })
pickVehicle!: PlateOrderAnswersPickVehicle

@Field(() => PlateOrderAnswersPlateSize, { nullable: false })
plateSize!: PlateOrderAnswersPlateSize

@Field(() => OperatorChangeAnswersPlateDelivery, { nullable: false })
plateDelivery!: OperatorChangeAnswersPlateDelivery
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
CheckTachoNetInput,
OperatorChangeAnswers,
PlateAvailabilityInput,
PlateOrderAnswers,
} from './dto'
import {
CheckTachoNetExists,
Expand All @@ -25,6 +26,7 @@ import {
VehiclePlateOrderChecksByPermno,
MyPlateOwnershipChecksByRegno,
PlateAvailability,
PlateOrderValidation,
} from './models'
import { CoOwnerChangeAnswers } from './dto/coOwnerChangeAnswers.input'

Expand Down Expand Up @@ -123,6 +125,18 @@ export class MainResolver {
)
}

@Scopes(ApiScope.samgongustofaVehicles)
@Query(() => PlateOrderValidation, { nullable: true })
vehiclePlateOrderValidation(
@CurrentUser() user: User,
@Args('answers') answers: PlateOrderAnswers,
) {
return this.transportAuthorityApi.validateApplicationForPlateOrder(
user,
answers,
)
}

@Scopes(ApiScope.internal)
@Query(() => MyPlateOwnershipChecksByRegno, {
name: 'myPlateOwnershipChecksByRegno',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './operatorChangeValidation.model'
export * from './checkTachoNetExists.model'
export * from './getCurrentVehicles.model'
export * from './plateAvailability.model'
export * from './plateOrderValidation.model'
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Field, ObjectType } from '@nestjs/graphql'

@ObjectType()
export class PlateOrderValidationMessage {
@Field(() => String, { nullable: true })
errorNo?: string | null

@Field(() => String, { nullable: true })
defaultMessage?: string | null
}

@ObjectType()
export class PlateOrderValidation {
@Field(() => Boolean)
hasError!: boolean

@Field(() => [PlateOrderValidationMessage], { nullable: true })
errorMessages?: PlateOrderValidationMessage[] | null
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools'
import { VehicleOwnerChangeClient } from '@island.is/clients/transport-authority/vehicle-owner-change'
import { DigitalTachographDriversCardClient } from '@island.is/clients/transport-authority/digital-tachograph-drivers-card'
import { VehicleOperatorsClient } from '@island.is/clients/transport-authority/vehicle-operators'
import { VehiclePlateOrderingClient } from '@island.is/clients/transport-authority/vehicle-plate-ordering'
import {
SGS_DELIVERY_STATION_CODE,
SGS_DELIVERY_STATION_TYPE,
VehiclePlateOrderingClient,
} from '@island.is/clients/transport-authority/vehicle-plate-ordering'
import { VehiclePlateRenewalClient } from '@island.is/clients/transport-authority/vehicle-plate-renewal'
import { VehicleServiceFjsV1Client } from '@island.is/clients/vehicle-service-fjs-v1'
import {
Expand All @@ -14,6 +18,7 @@ import {
OwnerChangeAnswers,
OperatorChangeAnswers,
CheckTachoNetInput,
PlateOrderAnswers,
} from './graphql/dto'
import {
OwnerChangeValidation,
Expand All @@ -22,6 +27,7 @@ import {
VehicleOwnerchangeChecksByPermno,
VehicleOperatorChangeChecksByPermno,
VehiclePlateOrderChecksByPermno,
PlateOrderValidation,
} from './graphql/models'
import { ApolloError } from 'apollo-server-express'
import { CoOwnerChangeAnswers } from './graphql/dto/coOwnerChangeAnswers.input'
Expand Down Expand Up @@ -109,6 +115,7 @@ export class TransportAuthorityApi {
return {
basicVehicleInformation: {
permno: vehicle.permno,
// Note: subModel (vehcom+speccom) has already been added to this field
make: vehicle.make,
color: vehicle.colorName,
requireMileage: vehicle.requiresMileageRegistration,
Expand All @@ -134,12 +141,6 @@ export class TransportAuthorityApi {
// the current timestamp
const todayStr = new Date().toISOString()

// No need to continue with this validation in user is neither seller nor buyer
// (only time application data changes is on state change from these roles)
if (user.nationalId !== sellerSsn && user.nationalId !== buyerSsn) {
return null
}

const filteredBuyerCoOwnerAndOperator =
answers?.buyerCoOwnerAndOperator?.filter(
({ wasRemoved }) => wasRemoved !== 'true',
Expand Down Expand Up @@ -279,6 +280,7 @@ export class TransportAuthorityApi {
: null,
basicVehicleInformation: {
color: vehicle.colorName,
// Note: subModel (vehcom+speccom) has already been added to this field
make: vehicle.make,
permno: vehicle.permno,
requireMileage: vehicle.requiresMileageRegistration,
Expand All @@ -291,13 +293,6 @@ export class TransportAuthorityApi {
user: User,
answers: OperatorChangeAnswers,
): Promise<OperatorChangeValidation | null> {
// No need to continue with this validation in user is not owner
// (only time application data changes is on state change from that role)
const ownerSsn = answers?.owner?.nationalId
if (user.nationalId !== ownerSsn) {
return null
}

const permno = answers?.pickVehicle?.plate

const filteredOldOperators = answers?.oldOperators?.filter(
Expand Down Expand Up @@ -347,25 +342,71 @@ export class TransportAuthorityApi {
})

// Get validation
const validation = await this.vehiclePlateOrderingClient.validatePlateOrder(
auth,
permno,
vehicleInfo?.platetypefront || '',
vehicleInfo?.platetyperear || '',
)
const validation =
await this.vehiclePlateOrderingClient.validateVehicleForPlateOrder(
auth,
permno,
vehicleInfo?.platetypefront || '',
vehicleInfo?.platetyperear || '',
)

return {
validationErrorMessages: validation?.hasError
? validation.errorMessages
: null,
basicVehicleInformation: {
color: vehicleInfo.color,
make: `${vehicleInfo.make} ${vehicleInfo.vehcom}`,
make: `${vehicleInfo.make} ${this.getVehicleSubModel(vehicleInfo)}`,
permno: vehicleInfo.permno,
},
}
}

private getVehicleSubModel(vehicle: BasicVehicleInformationDto) {
return [vehicle.vehcom, vehicle.speccom].filter(Boolean).join(' ')
}

async validateApplicationForPlateOrder(
user: User,
answers: PlateOrderAnswers,
): Promise<PlateOrderValidation | null> {
const YES = 'yes'

const includeRushFee =
answers?.plateDelivery?.includeRushFee?.includes(YES) || false

// Check if used selected delivery method: Pick up at delivery station
const deliveryStationTypeCode =
answers?.plateDelivery?.deliveryStationTypeCode
let deliveryStationType: string
let deliveryStationCode: string
if (
answers.plateDelivery?.deliveryMethodIsDeliveryStation === YES &&
deliveryStationTypeCode
) {
// Split up code+type (was merged when we fetched that data)
deliveryStationType = deliveryStationTypeCode.split('_')[0]
deliveryStationCode = deliveryStationTypeCode.split('_')[1]
} else {
// Otherwise we will default to option "Pick up at Samgöngustofa"
deliveryStationType = SGS_DELIVERY_STATION_TYPE
deliveryStationCode = SGS_DELIVERY_STATION_CODE
}

const result =
await this.vehiclePlateOrderingClient.validateAllForPlateOrder(
user,
answers?.pickVehicle?.plate,
answers?.plateSize?.frontPlateSize?.[0] || '',
answers?.plateSize?.rearPlateSize?.[0] || '',
deliveryStationType,
deliveryStationCode,
includeRushFee,
)

return result
}

async getMyPlateOwnershipChecksByRegno(
auth: User,
regno: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class ChangeCoOwnerOfVehicleService extends BaseTemplateApiService {
)
}

// A. vehicleCount > 20
// Case: count > 20
// Display search box, validate vehicle when permno is entered
if (totalRecords > 20) {
return {
Expand All @@ -114,13 +114,13 @@ export class ChangeCoOwnerOfVehicleService extends BaseTemplateApiService {

const vehicles = await Promise.all(
resultData.map(async (vehicle) => {
// B. 20 >= vehicleCount > 5
// Case: 20 >= count > 5
// Display dropdown, validate vehicle when selected in dropdown
if (totalRecords > 5) {
return this.mapVehicle(auth, vehicle, false)
}

// C. vehicleCount <= 5
// Case: count <= 5
// Display radio buttons, validate all vehicles now
return this.mapVehicle(auth, vehicle, true)
}),
Expand Down Expand Up @@ -468,34 +468,51 @@ export class ChangeCoOwnerOfVehicleService extends BaseTemplateApiService {

const mileage = answers?.vehicleMileage?.value

await this.vehicleOwnerChangeClient.saveOwnerChange(auth, {
permno: permno,
seller: {
ssn: ownerSsn,
email: ownerEmail,
},
buyer: {
ssn: ownerSsn,
email: ownerEmail,
const submitResult = await this.vehicleOwnerChangeClient.saveOwnerChange(
auth,
{
permno: permno,
seller: {
ssn: ownerSsn,
email: ownerEmail,
},
buyer: {
ssn: ownerSsn,
email: ownerEmail,
},
dateOfPurchase: new Date(application.created),
dateOfPurchaseTimestamp: createdStr.substring(11, createdStr.length),
saleAmount: currentOwnerChange?.saleAmount,
mileage: mileage ? Number(mileage) || 0 : null,
insuranceCompanyCode: currentOwnerChange?.insuranceCompanyCode,
operators: currentOperators?.map((operator) => ({
ssn: operator.ssn || '',
// Note: It should be ok that the email we send in is empty, since we dont get
// the email when fetching current operators, and according to them (SGS), they
// are not using the operator email in their API (not being saved in their DB)
email: null,
isMainOperator: operator.isMainOperator || false,
})),
coOwners: filteredCoOwners.map((x) => ({
ssn: x.nationalId || '',
email: x.email || '',
})),
},
dateOfPurchase: new Date(application.created),
dateOfPurchaseTimestamp: createdStr.substring(11, createdStr.length),
saleAmount: currentOwnerChange?.saleAmount,
mileage: mileage ? Number(mileage) || 0 : null,
insuranceCompanyCode: currentOwnerChange?.insuranceCompanyCode,
operators: currentOperators?.map((operator) => ({
ssn: operator.ssn || '',
// Note: It should be ok that the email we send in is empty, since we dont get
// the email when fetching current operators, and according to them (SGS), they
// are not using the operator email in their API (not being saved in their DB)
email: null,
isMainOperator: operator.isMainOperator || false,
})),
coOwners: filteredCoOwners.map((x) => ({
ssn: x.nationalId || '',
email: x.email || '',
})),
})
)

if (
submitResult.hasError &&
submitResult.errorMessages &&
submitResult.errorMessages.length > 0
) {
throw new TemplateApiError(
{
title: applicationCheck.validation.alertTitle,
summary: submitResult.errorMessages,
},
400,
)
}

// 3. Notify everyone in the process that the application has successfully been submitted

Expand Down
Loading

0 comments on commit d89ae9c

Please sign in to comment.