diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index aef35c5de860..51e54f62e853 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -284,13 +284,14 @@ jobs: timeout-minutes: 5 steps: - uses: actions/checkout@v4 - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@2.0.0 + - uses: reviewdog/action-shellcheck@v1 with: - ignore_paths: >- - node_modules - apps/native/app/android - severity: warning + github_token: ${{ secrets.github_token }} + reporter: github-pr-review + fail_on_error: true + level: info + exclude: >- + */node_modules/* formatting: needs: diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 2e735bcf6367..179b3ae41d3a 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -1,4 +1,3 @@ -import { Base } from 'infra/src/dsl/xroad' import { Base64 } from 'js-base64' import { Includeable, Sequelize } from 'sequelize' import { Transaction } from 'sequelize/types' diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/case.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/case.response.ts index 7889f054a2e9..6398f668c5e7 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/case.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/case.response.ts @@ -1,7 +1,10 @@ import { ApiProperty } from '@nestjs/swagger' import { formatDate } from '@island.is/judicial-system/formatters' -import { DateType } from '@island.is/judicial-system/types' +import { + DateType, + isSuccessfulServiceStatus, +} from '@island.is/judicial-system/types' import { InternalCaseResponse } from './internal/internalCase.response' import { Groups } from './shared/groups.model' @@ -41,7 +44,10 @@ export class CaseResponse { caseId: internalCase.id, data: { caseNumber: `${t.caseNumber} ${internalCase.courtCaseNumber}`, - hasBeenServed: subpoenas.length > 0 ? subpoenas[0].acknowledged : false, + hasBeenServed: + subpoenas.length > 0 + ? isSuccessfulServiceStatus(subpoenas[0].serviceStatus) + : false, groups: [ { label: t.defendant, diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts index 72bd4ccfa847..efff1d029dd3 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts @@ -3,6 +3,7 @@ import { DefenderChoice, Gender, Institution, + ServiceStatus, User, } from '@island.is/judicial-system/types' @@ -42,5 +43,5 @@ interface DateLog { interface Subpoena { id: string subpoenaId: string - acknowledged: boolean + serviceStatus?: ServiceStatus } diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts index 342e0ed294f5..0c432b55b5b5 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts @@ -6,7 +6,11 @@ import { formatDate, normalizeAndFormatNationalId, } from '@island.is/judicial-system/formatters' -import { DateType, DefenderChoice } from '@island.is/judicial-system/types' +import { + DateType, + DefenderChoice, + isSuccessfulServiceStatus, +} from '@island.is/judicial-system/types' import { InternalCaseResponse } from './internal/internalCase.response' import { Groups } from './shared/groups.model' @@ -59,6 +63,12 @@ class SubpoenaData { @ApiProperty({ type: Boolean }) hasBeenServed?: boolean + + @ApiProperty({ type: Boolean }) + hasChosenDefender?: boolean + + @ApiProperty({ enum: DefenderChoice }) + defaultDefenderChoice?: DefenderChoice } export class SubpoenaResponse { @@ -87,9 +97,11 @@ export class SubpoenaResponse { ) const waivedRight = defendantInfo?.defenderChoice === DefenderChoice.WAIVE - const hasDefender = defendantInfo?.defenderName !== undefined - const subpoena = defendantInfo?.subpoenas ?? [] - const hasBeenServed = subpoena[0]?.acknowledged ?? false + const hasDefender = defendantInfo?.defenderName !== null + const subpoenas = defendantInfo?.subpoenas ?? [] + const hasBeenServed = + subpoenas.length > 0 && + isSuccessfulServiceStatus(subpoenas[0].serviceStatus) const canChangeDefenseChoice = !waivedRight && !hasDefender const subpoenaDateLog = internalCase.dateLogs?.find( @@ -108,6 +120,11 @@ export class SubpoenaResponse { title: t.subpoena, subtitle: courtNameAndAddress, hasBeenServed: hasBeenServed, + hasChosenDefender: Boolean( + defendantInfo?.defenderChoice && + defendantInfo.defenderChoice !== DefenderChoice.DELAY, + ), + defaultDefenderChoice: DefenderChoice.DELAY, alerts: [ ...(hasBeenServed ? [ diff --git a/apps/service-portal/src/screens/Dashboard/Dashboard.tsx b/apps/service-portal/src/screens/Dashboard/Dashboard.tsx index bd5f5a9b7c2e..4dcec472ff69 100644 --- a/apps/service-portal/src/screens/Dashboard/Dashboard.tsx +++ b/apps/service-portal/src/screens/Dashboard/Dashboard.tsx @@ -17,7 +17,7 @@ import { DocumentsPaths, DocumentLine, DocumentLineV3, - useDocumentList, + useDocumentListV3, } from '@island.is/service-portal/documents' import { LinkResolver, @@ -41,9 +41,7 @@ import { useFeatureFlagClient } from '@island.is/react/feature-flags' export const Dashboard: FC> = () => { const { userInfo } = useAuth() - const { filteredDocuments, data, loading } = useDocumentList({ - defaultPageSize: 8, - }) + const { data: organizations } = useOrganizations() const { formatMessage } = useLocale() const { width } = useWindowSize() @@ -56,6 +54,10 @@ export const Dashboard: FC> = () => { // Versioning feature flag. Remove after feature is live. const [v3Enabled, setV3Enabled] = useState() + const { filteredDocuments, data, loading } = useDocumentListV3({ + defaultPageSize: 8, + }) + const featureFlagClient = useFeatureFlagClient() useEffect(() => { const isFlagEnabled = async () => { diff --git a/apps/services/auth/admin-api/infra/auth-admin-api.ts b/apps/services/auth/admin-api/infra/auth-admin-api.ts index 376206480242..0fc25a62d5d4 100644 --- a/apps/services/auth/admin-api/infra/auth-admin-api.ts +++ b/apps/services/auth/admin-api/infra/auth-admin-api.ts @@ -74,6 +74,8 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-admin-api'> => { .secrets({ ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL', ZENDESK_CONTACT_FORM_TOKEN: '/k8s/api/ZENDESK_CONTACT_FORM_TOKEN', + ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE: + '/k8s/services-auth/ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE', CLIENT_SECRET_ENCRYPTION_KEY: '/k8s/services-auth/admin-api/CLIENT_SECRET_ENCRYPTION_KEY', IDENTITY_SERVER_CLIENT_SECRET: diff --git a/apps/services/auth/admin-api/src/app/app.module.ts b/apps/services/auth/admin-api/src/app/app.module.ts index 982ab803273f..305deb6362b6 100644 --- a/apps/services/auth/admin-api/src/app/app.module.ts +++ b/apps/services/auth/admin-api/src/app/app.module.ts @@ -3,7 +3,6 @@ import { ConfigModule } from '@nestjs/config' import { SequelizeModule } from '@nestjs/sequelize' import { - DelegationApiUserSystemNotificationConfig, DelegationConfig, SequelizeConfigService, } from '@island.is/auth-api-lib' diff --git a/apps/services/auth/admin-api/src/app/v2/delegations/delegation-admin.controller.ts b/apps/services/auth/admin-api/src/app/v2/delegations/delegation-admin.controller.ts index 4ab51563c17a..ddbd8359d273 100644 --- a/apps/services/auth/admin-api/src/app/v2/delegations/delegation-admin.controller.ts +++ b/apps/services/auth/admin-api/src/app/v2/delegations/delegation-admin.controller.ts @@ -9,30 +9,34 @@ import { UseGuards, } from '@nestjs/common' import { ApiTags } from '@nestjs/swagger' +import flatMap from 'lodash/flatMap' import { + BypassAuth, CurrentUser, IdsUserGuard, Scopes, ScopesGuard, User, + ZendeskAuthGuard, } from '@island.is/auth-nest-tools' import { CreatePaperDelegationDto, DelegationAdminCustomDto, DelegationAdminCustomService, DelegationDTO, + ZendeskWebhookInputDto, } from '@island.is/auth-api-lib' import { Documentation } from '@island.is/nest/swagger' import { Audit, AuditService } from '@island.is/nest/audit' import { DelegationAdminScopes } from '@island.is/auth/scopes' -import flatMap from 'lodash/flatMap' import { isDefined } from '@island.is/shared/utils' +import env from '../../../environments/environment' + const namespace = '@island.is/auth/delegation-admin' @UseGuards(IdsUserGuard, ScopesGuard) -@Scopes(DelegationAdminScopes.read) @ApiTags('delegation-admin') @Controller('delegation-admin') @Audit({ namespace }) @@ -43,6 +47,7 @@ export class DelegationAdminController { ) {} @Get() + @Scopes(DelegationAdminScopes.read) @Documentation({ response: { status: 200, type: DelegationAdminCustomDto }, request: { @@ -91,6 +96,18 @@ export class DelegationAdminController { ) } + @BypassAuth() + @UseGuards(new ZendeskAuthGuard(env.zendeskGeneralMandateWebhookSecret)) + @Post('/zendesk') + @Documentation({ + response: { status: 200 }, + }) + async createByZendeskId( + @Body() { id }: ZendeskWebhookInputDto, + ): Promise { + await this.delegationAdminService.createDelegationByZendeskId(id) + } + @Delete(':delegationId') @Scopes(DelegationAdminScopes.admin) @Documentation({ diff --git a/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.auth.spec.ts b/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.auth.spec.ts index f97758aaf310..6fddc5aacb23 100644 --- a/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.auth.spec.ts +++ b/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.auth.spec.ts @@ -1,4 +1,5 @@ import request from 'supertest' +import bodyParser from 'body-parser' import { getRequestMethod, @@ -11,9 +12,11 @@ import { User } from '@island.is/auth-nest-tools' import { FixtureFactory } from '@island.is/services/auth/testing' import { createCurrentUser } from '@island.is/testing/fixtures' import { DelegationAdminScopes } from '@island.is/auth/scopes' -import { SequelizeConfigService } from '@island.is/auth-api-lib' +import { DelegationDTO, SequelizeConfigService } from '@island.is/auth-api-lib' +import { DelegationAdminCustomService } from '@island.is/auth-api-lib' import { AppModule } from '../../../app.module' +import { includeRawBodyMiddleware } from '@island.is/infra-nest-server' describe('withoutAuth and permissions', () => { async function formatUrl(app: TestApp, endpoint: string, user?: User) { @@ -132,4 +135,79 @@ describe('withoutAuth and permissions', () => { app.cleanUp() }, ) + + describe('POST /delegation-admin/zendesk', () => { + let app: TestApp + let server: request.SuperTest + let delegationAdminService: DelegationAdminCustomService + + beforeEach(async () => { + app = await setupAppWithoutAuth({ + AppModule, + SequelizeConfigService, + dbType: 'postgres', + beforeServerStart: async (app) => { + await new Promise((resolve) => + resolve(app.use(includeRawBodyMiddleware())), + ) + }, + }) + + server = request(app.getHttpServer()) + + delegationAdminService = app.get(DelegationAdminCustomService) + + jest + .spyOn(delegationAdminService, 'createDelegationByZendeskId') + .mockImplementation(() => Promise.resolve()) + }) + + afterEach(() => { + app.cleanUp() + }) + + it('POST /delegation-admin/zendesk should return 403 Forbidden when request signature is invalid.', async () => { + // Act + const res = await getRequestMethod( + server, + 'POST', + )('/delegation-admin/zendesk') + .send({ + id: 'Incorrect body', + }) + .set( + 'x-zendesk-webhook-signature', + '6sUtGV8C8OdoGgCdsV2xRm3XeskZ33Bc5124RiAK4Q4=', + ) + .set('x-zendesk-webhook-signature-timestamp', '2024-10-02T14:21:04Z') + + // Assert + expect(res.status).toEqual(403) + expect(res.body).toMatchObject({ + status: 403, + type: 'https://httpstatuses.org/403', + title: 'Forbidden', + detail: 'Forbidden resource', + }) + }) + + it('POST /delegation-admin/zendesk should return 200 when signature is valid', async () => { + // Act + const res = await getRequestMethod( + server, + 'POST', + )('/delegation-admin/zendesk') + .send({ + id: 'test', + }) + .set( + 'x-zendesk-webhook-signature', + 'ntgS06VGgd4z73lHjIpC2sk9azhRNi4u1xkXF/KPKTs=', + ) + .set('x-zendesk-webhook-signature-timestamp', '2024-10-02T14:21:04Z') + + // Assert + expect(res.status).toEqual(200) + }) + }) }) diff --git a/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.spec.ts b/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.spec.ts index 718b6427b800..19cc29c1c92f 100644 --- a/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.spec.ts +++ b/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.spec.ts @@ -224,15 +224,25 @@ describe('DelegationAdmin - With authentication', () => { const mockZendeskService = ( toNationalId: string, fromNationalId: string, + info?: { + tags?: string[] + status?: TicketStatus + }, ) => { + const { tags, status } = { + tags: [DELEGATION_TAG], + status: TicketStatus.Solved, + ...info, + } + zendeskServiceApiSpy = jest .spyOn(zendeskService, 'getTicket') .mockImplementation((ticketId: string) => { return new Promise((resolve) => resolve({ id: ticketId, - tags: [DELEGATION_TAG], - status: TicketStatus.Solved, + tags: tags, + status: status, custom_fields: [ { id: ZENDESK_CUSTOM_FIELDS.DelegationToReferenceId, @@ -328,5 +338,26 @@ describe('DelegationAdmin - With authentication', () => { // Assert expect(res.status).toEqual(400) }) + + it('POST /delegation-admin should not create delegation with incorrect zendesk ticket status', async () => { + // Arrange + mockZendeskService(toNationalId, fromNationalId, { + status: TicketStatus.Open, + }) + + const delegation: CreatePaperDelegationDto = { + toNationalId, + fromNationalId, + referenceId: 'ref1', + } + + // Act + const res = await getRequestMethod( + server, + 'POST', + )('/delegation-admin').send(delegation) + + expect(res.status).toEqual(400) + }) }) }) diff --git a/apps/services/auth/admin-api/src/environments/environment.ts b/apps/services/auth/admin-api/src/environments/environment.ts index 4dd0c52c607b..a86d4bbb93fe 100644 --- a/apps/services/auth/admin-api/src/environments/environment.ts +++ b/apps/services/auth/admin-api/src/environments/environment.ts @@ -12,6 +12,11 @@ const devConfig = { port: 6333, clientSecretEncryptionKey: process.env.CLIENT_SECRET_ENCRYPTION_KEY ?? 'secret', + zendeskGeneralMandateWebhookSecret: + process.env.ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE ?? + //The static test signing secret from Zendesk as described in their docs + // https://developer.zendesk.com/documentation/webhooks/verifying/#signing-secrets-on-new-webhooks + 'dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==', } const prodConfig = { @@ -27,6 +32,8 @@ const prodConfig = { }, port: 3333, clientSecretEncryptionKey: process.env.CLIENT_SECRET_ENCRYPTION_KEY, + zendeskGeneralMandateWebhookSecret: + process.env.ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE, } export default process.env.NODE_ENV === 'production' ? prodConfig : devConfig diff --git a/apps/services/auth/admin-api/src/main.ts b/apps/services/auth/admin-api/src/main.ts index 5b9ad800db75..e93b97811c1f 100644 --- a/apps/services/auth/admin-api/src/main.ts +++ b/apps/services/auth/admin-api/src/main.ts @@ -1,4 +1,7 @@ -import { bootstrap } from '@island.is/infra-nest-server' +import { + bootstrap, + includeRawBodyMiddleware, +} from '@island.is/infra-nest-server' import { AppModule } from './app/app.module' import { environment as env } from './environments' @@ -14,4 +17,7 @@ bootstrap({ healthCheck: { database: true, }, + beforeServerStart: (app) => { + app.use(includeRawBodyMiddleware()) + }, }) diff --git a/apps/web/layouts/main.tsx b/apps/web/layouts/main.tsx index e243da70d00f..1b1f67eab40f 100644 --- a/apps/web/layouts/main.tsx +++ b/apps/web/layouts/main.tsx @@ -659,9 +659,7 @@ Layout.getProps = async ({ apolloClient, locale, req }) => { alertBannerContent: { ...alertBanner, showAlertBanner: - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error make web strict - alertBanner.showAlertBanner && + alertBanner?.showAlertBanner && (!req?.headers.cookie || req.headers.cookie?.indexOf(alertBannerId) === -1), }, diff --git a/apps/web/screens/Organization/Syslumenn/Auctions.tsx b/apps/web/screens/Organization/Syslumenn/Auctions.tsx index 31a013dd41cf..3806485f30fe 100644 --- a/apps/web/screens/Organization/Syslumenn/Auctions.tsx +++ b/apps/web/screens/Organization/Syslumenn/Auctions.tsx @@ -1100,17 +1100,23 @@ Auctions.getProps = async ({ apolloClient, locale, req, res }) => { ), ]) + const usingDefaultHeader: boolean = namespace['usingDefaultHeader'] ?? false + return { organizationPage: getOrganizationPage, subpage: getOrganizationSubpage, syslumennAuctions: getSyslumennAuctions, namespace, showSearchInHeader: false, + themeConfig: !usingDefaultHeader + ? { + headerButtonColorScheme: 'negative', + headerColorScheme: 'white', + } + : {}, } } export default withMainLayout(Auctions, { - headerButtonColorScheme: 'negative', - headerColorScheme: 'white', footerVersion: 'organization', }) diff --git a/apps/web/screens/Organization/Syslumenn/Homestay.tsx b/apps/web/screens/Organization/Syslumenn/Homestay.tsx index ff72e3e59cd4..822c0d613e63 100644 --- a/apps/web/screens/Organization/Syslumenn/Homestay.tsx +++ b/apps/web/screens/Organization/Syslumenn/Homestay.tsx @@ -372,17 +372,23 @@ Homestay.getProps = async ({ apolloClient, locale, req }) => { throw new CustomNextError(404, 'Organization subpage not found') } + const usingDefaultHeader: boolean = namespace['usingDefaultHeader'] ?? false + return { organizationPage: getOrganizationPage, subpage: getOrganizationSubpage, homestays: getHomestays, namespace, showSearchInHeader: false, + themeConfig: !usingDefaultHeader + ? { + headerButtonColorScheme: 'negative', + headerColorScheme: 'white', + } + : {}, } } export default withMainLayout(Homestay, { - headerButtonColorScheme: 'negative', - headerColorScheme: 'white', footerVersion: 'organization', }) diff --git a/apps/web/screens/Organization/Syslumenn/OperatingLicenses.tsx b/apps/web/screens/Organization/Syslumenn/OperatingLicenses.tsx index 013cf107936f..e862ea1b7a21 100644 --- a/apps/web/screens/Organization/Syslumenn/OperatingLicenses.tsx +++ b/apps/web/screens/Organization/Syslumenn/OperatingLicenses.tsx @@ -668,16 +668,22 @@ OperatingLicenses.getProps = async ({ apolloClient, locale, req }) => { throw new CustomNextError(404, 'Organization subpage not found') } + const usingDefaultHeader: boolean = namespace['usingDefaultHeader'] ?? false + return { organizationPage: getOrganizationPage, subpage: getOrganizationSubpage, namespace, showSearchInHeader: false, + themeConfig: !usingDefaultHeader + ? { + headerButtonColorScheme: 'negative', + headerColorScheme: 'white', + } + : {}, } } export default withMainLayout(OperatingLicenses, { - headerButtonColorScheme: 'negative', - headerColorScheme: 'white', footerVersion: 'organization', }) diff --git a/charts/identity-server/values.dev.yaml b/charts/identity-server/values.dev.yaml index 5f76287c7ee4..d50deba82d2a 100644 --- a/charts/identity-server/values.dev.yaml +++ b/charts/identity-server/values.dev.yaml @@ -298,6 +298,7 @@ services-auth-admin-api: SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL' ZENDESK_CONTACT_FORM_TOKEN: '/k8s/api/ZENDESK_CONTACT_FORM_TOKEN' + ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE: '/k8s/services-auth/ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE' securityContext: allowPrivilegeEscalation: false privileged: false diff --git a/charts/identity-server/values.prod.yaml b/charts/identity-server/values.prod.yaml index 077392613786..f29985ef17e7 100644 --- a/charts/identity-server/values.prod.yaml +++ b/charts/identity-server/values.prod.yaml @@ -295,6 +295,7 @@ services-auth-admin-api: SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL' ZENDESK_CONTACT_FORM_TOKEN: '/k8s/api/ZENDESK_CONTACT_FORM_TOKEN' + ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE: '/k8s/services-auth/ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE' securityContext: allowPrivilegeEscalation: false privileged: false diff --git a/charts/identity-server/values.staging.yaml b/charts/identity-server/values.staging.yaml index bee228ee9292..a52d3a746a75 100644 --- a/charts/identity-server/values.staging.yaml +++ b/charts/identity-server/values.staging.yaml @@ -298,6 +298,7 @@ services-auth-admin-api: SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL' ZENDESK_CONTACT_FORM_TOKEN: '/k8s/api/ZENDESK_CONTACT_FORM_TOKEN' + ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE: '/k8s/services-auth/ZENDESK_WEBHOOK_SECRET_GENERAL_MANDATE' securityContext: allowPrivilegeEscalation: false privileged: false diff --git a/infra/scripts/helm-diff.sh b/infra/scripts/helm-diff.sh index b856f6b13ab5..f00c992b2852 100755 --- a/infra/scripts/helm-diff.sh +++ b/infra/scripts/helm-diff.sh @@ -22,8 +22,8 @@ if [ -z "${2}" ]; then fi # curl https://api.github.com/repos/island-is/island.is/contents/charts/islandis/values.prod.yaml | jq -r ".content" | base64 --decode -curl -s -H "Accept: application/json" 'https://api.github.com/repos/island-is/island.is/contents/charts/islandis/values.prod.yaml?ref='${1}'' | jq -r ".content" | base64 --decode >current-release.json -curl -s -H "Accept: application/json" 'https://api.github.com/repos/island-is/island.is/contents/charts/islandis/values.prod.yaml?ref='${2}'' | jq -r ".content" | base64 --decode >new-release.json +curl -s -H "Accept: application/json" "https://api.github.com/repos/island-is/island.is/contents/charts/islandis/values.prod.yaml?ref=${1}" | jq -r ".content" | base64 --decode >current-release.json +curl -s -H "Accept: application/json" "https://api.github.com/repos/island-is/island.is/contents/charts/islandis/values.prod.yaml?ref=${2}" | jq -r ".content" | base64 --decode >new-release.json diff -u ./current-release.json ./new-release.json | ydiff -w 0 -s rm -f ./new-release.json rm -f ./current-release.json diff --git a/libs/api/domains/documents/src/lib/document.module.ts b/libs/api/domains/documents/src/lib/document.module.ts index ec76a2873ae2..59c7788956ca 100644 --- a/libs/api/domains/documents/src/lib/document.module.ts +++ b/libs/api/domains/documents/src/lib/document.module.ts @@ -6,9 +6,10 @@ import { DocumentServiceV2 } from './documentV2.service' import { DocumentResolverV1 } from './documentV1.resolver' import { DocumentResolverV2 } from './documentV2.resolver' import { DocumentService } from './documentV1.service' +import { FeatureFlagModule } from '@island.is/nest/feature-flags' @Module({ - imports: [DocumentsClientV2Module, DocumentsClientModule], + imports: [DocumentsClientV2Module, DocumentsClientModule, FeatureFlagModule], providers: [ DocumentResolverV2, DocumentResolverV1, diff --git a/libs/api/domains/documents/src/lib/documentV2.resolver.ts b/libs/api/domains/documents/src/lib/documentV2.resolver.ts index 3622993197f9..221a2e87225f 100644 --- a/libs/api/domains/documents/src/lib/documentV2.resolver.ts +++ b/libs/api/domains/documents/src/lib/documentV2.resolver.ts @@ -16,7 +16,11 @@ import { Document as DocumentV2, PaginatedDocuments, } from './models/v2/document.model' - +import { + FeatureFlagGuard, + FeatureFlagService, + Features, +} from '@island.is/nest/feature-flags' import { PostRequestPaperInput } from './dto/postRequestPaperInput' import { DocumentInput } from './models/v2/document.input' import { DocumentServiceV2 } from './documentV2.service' @@ -35,13 +39,14 @@ import { DocumentConfirmActions } from './models/v2/confirmActions.model' const LOG_CATEGORY = 'documents-resolver' -@UseGuards(IdsUserGuard, ScopesGuard) +@UseGuards(IdsUserGuard, ScopesGuard, FeatureFlagGuard) @Resolver(() => PaginatedDocuments) @Audit({ namespace: '@island.is/api/document-v2' }) export class DocumentResolverV2 { constructor( private documentServiceV2: DocumentServiceV2, private readonly auditService: AuditService, + private readonly featureFlagService: FeatureFlagService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -53,6 +58,7 @@ export class DocumentResolverV2 { locale: Locale = 'is', @CurrentUser() user: User, ): Promise { + const ffEnabled = await this.getFeatureFlag() try { return await this.auditService.auditPromise( { @@ -62,12 +68,14 @@ export class DocumentResolverV2 { resources: input.id, meta: { includeDocument: input.includeDocument }, }, - this.documentServiceV2.findDocumentById( - user.nationalId, - input.id, - locale, - input.includeDocument, - ), + ffEnabled + ? this.documentServiceV2.findDocumentByIdV3( + user.nationalId, + input.id, + locale, + input.includeDocument, + ) + : this.documentServiceV2.findDocumentById(user.nationalId, input.id), ) } catch (e) { this.logger.info('failed to get single document', { @@ -82,10 +90,13 @@ export class DocumentResolverV2 { @Scopes(DocumentsScope.main) @Query(() => PaginatedDocuments, { nullable: true }) @Audit() - documentsV2( + async documentsV2( @Args('input') input: DocumentsInput, @CurrentUser() user: User, ): Promise { + const ffEnabled = await this.getFeatureFlag() + if (ffEnabled) + return this.documentServiceV2.listDocumentsV3(user.nationalId, input) return this.documentServiceV2.listDocuments(user.nationalId, input) } @@ -206,4 +217,11 @@ export class DocumentResolverV2 { throw e } } + + private async getFeatureFlag(): Promise { + return await this.featureFlagService.getValue( + Features.isServicePortalDocumentsV3PageEnabled, + false, + ) + } } diff --git a/libs/api/domains/documents/src/lib/documentV2.service.ts b/libs/api/domains/documents/src/lib/documentV2.service.ts index aaf967fc8dbe..e22ced843676 100644 --- a/libs/api/domains/documents/src/lib/documentV2.service.ts +++ b/libs/api/domains/documents/src/lib/documentV2.service.ts @@ -36,6 +36,51 @@ export class DocumentServiceV2 { async findDocumentById( nationalId: string, documentId: string, + ): Promise { + const document = await this.documentService.getCustomersDocument( + nationalId, + documentId, + ) + + if (!document) { + return null // Null document logged in clients-documents-v2 + } + + let type: FileType + switch (document.fileType) { + case 'html': + type = FileType.HTML + break + case 'pdf': + type = FileType.PDF + break + case 'url': + type = FileType.URL + break + default: + type = FileType.UNKNOWN + } + + return { + ...document, + publicationDate: document.date, + id: documentId, + name: document.fileName, + downloadUrl: `${this.downloadServiceConfig.baseUrl}/download/v1/electronic-documents/${documentId}`, + sender: { + id: document.senderNationalId, + name: document.senderName, + }, + content: { + type, + value: document.content, + }, + } + } + + async findDocumentByIdV3( + nationalId: string, + documentId: string, locale?: string, includeDocument?: boolean, ): Promise { @@ -123,6 +168,69 @@ export class DocumentServiceV2 { nationalId, }) + if (typeof documents?.totalCount !== 'number') { + this.logger.warn('Document total count unavailable', { + category: LOG_CATEGORY, + totalCount: documents?.totalCount, + }) + } + + const documentData: Array = + documents?.documents + .map((d) => { + if (!d) { + return null + } + + return { + ...d, + id: d.id, + downloadUrl: `${this.downloadServiceConfig.baseUrl}/download/v1/electronic-documents/${d.id}`, + sender: { + name: d.senderName, + id: d.senderNationalId, + }, + } + }) + .filter(isDefined) ?? [] + + return { + data: documentData, + totalCount: documents?.totalCount ?? 0, + unreadCount: documents?.unreadCount, + pageInfo: { + hasNextPage: false, + }, + } + } + + async listDocumentsV3( + nationalId: string, + input: DocumentsInput, + ): Promise { + //If a delegated user is viewing the mailbox, do not return any health related data + //Category is now "1,2,3,...,n" + const { categoryIds, ...restOfInput } = input + let mutableCategoryIds = categoryIds ?? [] + + if (input.isLegalGuardian) { + if (!mutableCategoryIds.length) { + mutableCategoryIds = (await this.getCategories(nationalId, true)).map( + (c) => c.id, + ) + } else { + mutableCategoryIds = mutableCategoryIds.filter( + (c) => c === HEALTH_CATEGORY_ID, + ) + } + } + + const documents = await this.documentService.getDocumentList({ + ...restOfInput, + categoryId: mutableCategoryIds.join(), + nationalId, + }) + if (typeof documents?.totalCount !== 'number') { this.logger.warn('Document total count unavailable', { category: LOG_CATEGORY, diff --git a/libs/api/domains/documents/src/lib/models/v2/documentContent.model.ts b/libs/api/domains/documents/src/lib/models/v2/documentContent.model.ts index fac02463df0b..2fbf5c02dd09 100644 --- a/libs/api/domains/documents/src/lib/models/v2/documentContent.model.ts +++ b/libs/api/domains/documents/src/lib/models/v2/documentContent.model.ts @@ -14,8 +14,9 @@ export class DocumentContent { @Field(() => FileType) type!: FileType - @Field({ + @Field(() => String, { description: 'Either pdf base64 string, html markup string, or an url', + nullable: true, }) - value!: string + value?: string | null } diff --git a/libs/api/domains/driving-license/src/lib/drivingLicense.service.ts b/libs/api/domains/driving-license/src/lib/drivingLicense.service.ts index 13cd2086ac21..9cb539b8105c 100644 --- a/libs/api/domains/driving-license/src/lib/drivingLicense.service.ts +++ b/libs/api/domains/driving-license/src/lib/drivingLicense.service.ts @@ -355,8 +355,15 @@ export class DrivingLicenseService { } } - async canApplyFor(type: 'B-full' | 'B-temp' | 'BE', token: string) { - if (type === 'B-full') { + async canApplyFor( + type: 'B-full' | 'B-temp' | 'BE' | 'B-full-renewal-65', + token: string, + ) { + if (type === 'B-full-renewal-65') { + return this.drivingLicenseApi.getCanApplyForRenewal65({ + token, + }) + } else if (type === 'B-full') { return this.drivingLicenseApi.getCanApplyForCategoryFull({ category: 'B', token, diff --git a/libs/api/domains/driving-license/src/lib/drivingLicense.type.ts b/libs/api/domains/driving-license/src/lib/drivingLicense.type.ts index 24a6d5062b98..741ca9a09017 100644 --- a/libs/api/domains/driving-license/src/lib/drivingLicense.type.ts +++ b/libs/api/domains/driving-license/src/lib/drivingLicense.type.ts @@ -74,6 +74,7 @@ export enum RequirementKey { hasHadValidCategoryForFiveYearsOrMore = 'HasHadValidCategoryForFiveYearsOrMore', //TODO: Remove when RLS/SGS supports health certificate in BE license beRequiresHealthCertificate = 'beRequiresHealthCertificate', + noExtendedDrivingLicense = 'NoExtendedDrivingLicense', } export interface ApplicationEligibilityRequirement { diff --git a/libs/api/domains/law-and-order/src/lib/helpers/mappers.ts b/libs/api/domains/law-and-order/src/lib/helpers/mappers.ts index fc736be669e9..f6f6734471cc 100644 --- a/libs/api/domains/law-and-order/src/lib/helpers/mappers.ts +++ b/libs/api/domains/law-and-order/src/lib/helpers/mappers.ts @@ -1,6 +1,7 @@ import { DefenderInfoDefenderChoiceEnum, StateTagColorEnum, + SubpoenaDataDefaultDefenderChoiceEnum, UpdateSubpoenaDtoDefenderChoiceEnum, } from '@island.is/clients/judicial-system-sp' import { CourtCaseStateTagColorEnum } from '../../models/courtCases.model' @@ -46,6 +47,26 @@ export const mapDefenseChoiceForSubpoena = ( } } +// Maps the application's internal representation of defense choices to the judicial system's representation. +export const mapDefenseChoiceForSubpoenaDefaultChoice = ( + choice?: SubpoenaDataDefaultDefenderChoiceEnum, +): DefenseChoiceEnum => { + switch (choice) { + // Each case maps a local enum value to the corresponding value in the judicial system's enum. + case SubpoenaDataDefaultDefenderChoiceEnum.CHOOSE: + return DefenseChoiceEnum.CHOOSE + case SubpoenaDataDefaultDefenderChoiceEnum.WAIVE: + return DefenseChoiceEnum.WAIVE + case SubpoenaDataDefaultDefenderChoiceEnum.DELAY: + return DefenseChoiceEnum.DELAY + case SubpoenaDataDefaultDefenderChoiceEnum.DELEGATE: + return DefenseChoiceEnum.DELEGATE + default: + // Provides a default mapping if the input doesn't match any known value. + return DefenseChoiceEnum.DELAY + } +} + export const mapTagTypes = ( color?: StateTagColorEnum, ): CourtCaseStateTagColorEnum => { diff --git a/libs/api/domains/law-and-order/src/lib/law-and-order.service.ts b/libs/api/domains/law-and-order/src/lib/law-and-order.service.ts index 964b2c2b4f9d..ca8f4038a1e0 100644 --- a/libs/api/domains/law-and-order/src/lib/law-and-order.service.ts +++ b/libs/api/domains/law-and-order/src/lib/law-and-order.service.ts @@ -21,6 +21,7 @@ import { DefenseChoices, mapDefenseChoice, mapDefenseChoiceForSubpoena, + mapDefenseChoiceForSubpoenaDefaultChoice, mapTagTypes, } from './helpers/mappers' @@ -123,36 +124,44 @@ export class LawAndOrderService { async getSubpoena(user: User, id: string, locale: Locale) { const { formatMessage } = await this.intlService.useIntl(namespaces, locale) - const subpoena: SubpoenaResponse | undefined | null = - await this.api.getSubpoena(id, user, locale) + const subpoena: SubpoenaResponse | null = await this.api.getSubpoena( + id, + user, + locale, + ) + + if (!isDefined(subpoena)) return null + const subpoenaData = subpoena.data + if (!isDefined(subpoenaData)) return null - const defenderChoice = subpoena?.defenderInfo?.defenderChoice + const defenderInfo = subpoena.defenderInfo + const defenderChoice = defenderInfo?.defenderChoice const message = defenderChoice - ? formatMessage( - DefenseChoices[subpoena?.defenderInfo.defenderChoice].message, - ) + ? formatMessage(DefenseChoices[defenderChoice].message) : '' const data: Subpoena = { data: { - id: subpoena?.caseId ?? id, - hasBeenServed: subpoena?.data?.hasBeenServed, - chosenDefender: [message, subpoena?.defenderInfo?.defenderName] + id: subpoena.caseId ?? id, + hasBeenServed: subpoenaData.hasBeenServed, + chosenDefender: [message, defenderInfo?.defenderName] .filter(isDefined) .join(', '), - defenderChoice: mapDefenseChoiceForSubpoena( - subpoena?.defenderInfo?.defenderChoice, + defenderChoice: mapDefenseChoiceForSubpoena(defenderChoice), + defaultChoice: mapDefenseChoiceForSubpoenaDefaultChoice( + subpoenaData.defaultDefenderChoice, ), - canEditDefenderChoice: subpoena?.defenderInfo?.canEdit, - groups: subpoena?.data.groups, - courtContactInfo: subpoena?.defenderInfo?.courtContactInfo, + hasChosen: subpoenaData.hasChosenDefender, + canEditDefenderChoice: defenderInfo?.canEdit, + groups: subpoenaData.groups, + courtContactInfo: defenderInfo?.courtContactInfo, }, actions: undefined, texts: { - confirmation: subpoena?.data.alerts?.find( + confirmation: subpoenaData.alerts?.find( (alert) => alert.type === AlertMessageTypeEnum.Success, )?.message, - description: subpoena?.data.subtitle, + description: subpoenaData.subtitle, }, } return data diff --git a/libs/api/domains/law-and-order/src/models/subpoena.model.ts b/libs/api/domains/law-and-order/src/models/subpoena.model.ts index 62a46c4b61ed..c71f0f365388 100644 --- a/libs/api/domains/law-and-order/src/models/subpoena.model.ts +++ b/libs/api/domains/law-and-order/src/models/subpoena.model.ts @@ -3,14 +3,6 @@ import { Action } from './actions.model' import { DefenseChoiceEnum } from './defenseChoiceEnum.model' import { Group } from './group.model' -@ObjectType('LawAndOrderSubpoenaAlert') -export class Alert { - @Field({ nullable: true }) - type?: string - - @Field({ nullable: true }) - message?: string -} @ObjectType('LawAndOrderSubpoenaTexts') export class Text { @Field({ nullable: true }) @@ -43,14 +35,17 @@ export class Data { @Field({ nullable: true }) courtContactInfo?: string + @Field(() => DefenseChoiceEnum) + defaultChoice!: DefenseChoiceEnum + + @Field(() => Boolean, { nullable: true }) + hasChosen?: boolean + @Field(() => DefenseChoiceEnum, { nullable: true }) defenderChoice?: DefenseChoiceEnum @Field(() => [Group], { nullable: true }) groups?: Array - - @Field(() => [Alert], { nullable: true }) - alerts?: Array } @ObjectType('LawAndOrderSubpoena') diff --git a/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/constants.ts b/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/constants.ts index 0ed762beba59..9c9e3a0c39b3 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/constants.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/constants.ts @@ -36,3 +36,95 @@ export const isRunningInProduction = process.env.NODE_ENV === 'production' export const APPLICATION_ATTACHMENT_BUCKET = 'APPLICATION_ATTACHMENT_BUCKET' export const SIX_MONTHS_IN_SECONDS_EXPIRES = 6 * 30 * 24 * 60 * 60 export const df = 'yyyy-MM-dd' + +export const rightsDescriptions: { [key: string]: string } = { + ALVEIKT: 'Alv.veikt barn eftir heimk.', + 'ANDL.MEÐG': 'Andlát hins foreldris á meðgöngu', + ANDLÁT: 'Andlát maka', + ANDVANA18: 'Fósturlát eftir 18 vikur', + ANDVANA22: 'Andvana fætt eftir 22 vikur', + 'DVAL.FJÖL': 'Dvalarstyrkur, fjölburafæðing', + DVALSTYRK: 'Dvalarstyrkur', + EITTFOR: 'Eitt foreldri', + 'F-ANDV22': 'Andvana fætt eftir 22 vikur - faðir', + FANGELSI: 'Fangelsisvist', + 'F-FL-FS': 'Fæðingarst. forsjárlauss föður', + 'F-FL-FSN': 'Fæðst. forsl.föður í námi', + 'F-FL-L-GR': 'Gr.rétt. fors.lauss föður laun', + 'F-FL-L-GRS': 'Gr.rétt. fors.lauss föður laun/sjálfst.', + 'F-FL-S-GR': 'Gr.rétt. fors.lauss föður Sjál', + 'F-FÓ-FS': 'Fæðingastyrkur fósturföðurs', + 'F-FÓ-FSN': 'Fæðingastyrkur fósturf. í námi', + 'F-FÓ-GR': 'Grunnréttur fósturföðurs', + 'F-FÓ-GR-SJ': 'Grunnréttur fósturföðurs launþ/sjálfst.', + 'F-FÓ-S-GR': 'Grunnr.fósturföðurs sjálfst.', + 'F-FS': 'Fæðingastyrkur föður', + 'F-FSN': 'Fæðingarstyrkur föður í námi', + 'F-L-GR': 'Grunnréttur föður launþ.', + 'F-L-GR-SJ': 'Grunnréttur föður launþ./sjálfst.', + 'FO-ANDV22': 'Andvana fætt eftir 22 vikur - foreldri', + 'FO-FL-FS': 'Fæðingarst. forsjárlauss foreldris', + 'FO-FL-FSN': 'Fæðst. forsl.foreldris í námi', + 'FO-FL-L-GR': 'Gr.rétt. fors.lauss foreldris laun', + 'FO-FL-L-GS': 'Gr.rétt. fors.lauss foreldris laun/sjálfst.', + 'FO-FL-S-GR': 'Gr.rétt. fors.lauss foreldris Sjál', + 'FO-FÓ-FS': 'Fæðingastyrkur fósturforeldris', + 'FO-FÓ-FSN': 'Fæðingastyrkur fósturforeldris í námi', + 'FO-FÓ-GR': 'Grunnréttur fósturforeldris', + 'FO-FÓ-GR-S': 'Grunnréttur fósturforeldris launþ/sjálfst.', + 'FO-FÓ-S-GR': 'Grunnr.fósturforeldris sjálfst.', + 'FO-FS': 'Fæðingastyrkur foreldris', + 'FO-FSN': 'Fæðingarstyrkur foreldris í námi', + 'FO-L-GR': 'Grunnréttur foreldris launþ.', + 'FO-L-GR-SJ': 'Grunnréttur foreldris launþ./sjálfst.', + 'FO-S-GR': 'Grunnréttur foreldris sjálfst.', + 'FO-Æ-FS': 'Fæðingarstyrkur foreldris ættleið.', + 'FO-Æ-FSN': 'Fæðingarst. foreldris í námi ættl.', + 'FO-Æ-L-GR': 'Grunnréttur foreldris ættleiðing', + 'FO-Æ-L-GRS': 'Grunnréttur foreldris ættleiðing launþ/sjálfst.', + 'FO-Æ-S-GR': 'Grunnr. foreldris ættl. sjálfst.', + 'FSAL-GR': 'Framsal grunnréttur', + 'F-S-GR': 'Grunnréttur föður sjálfst.', + 'F-Æ-FS': 'Fæðingarstyrkur föður ættleið.', + 'F-Æ-FSN': 'Fæðingarst. föður í námi ættl.', + 'F-Æ-L-GR': 'Grunnréttur föður ættleiðing', + 'F-Æ-L-GRSJ': 'Grunnréttur föður ættleiðing launþ/sjálfst.', + 'F-Æ-S-GR': 'Grunnr. föður ættl. sjálfst.', + 'M-ANDV22': 'Andvana fætt eftir 22 vikur - móðir', + 'M-FL-FS': 'Fæðingarst. forsjárl. móður', + 'M-FL-FSN': 'Fæðst. forsl. móður í námi', + 'M-FL-L-GR': 'Grunnr. forsjárl. móður launþ.', + 'M-FL-L-GRS': 'Grunnr. forsjárl. móður launþ./sjálfst.', + 'M-FL-S-GR': 'Grunnr. forsjárl. móður sjálfs', + 'M-FÓ-FS': 'Fæðingarstyrkur fósturmóður', + 'M-FÓ-FSN': 'Fæðingarst. fósturmóður í námi', + 'M-FÓ-L-GR': 'Grunnréttur fósturmóður', + 'M-FÓ-L-GRS': 'Grunnréttur fósturmóður launþ/sjálfst.', + 'M-FÓ-S-GR': 'Grunnréttur fósturmóður sjálfs', + 'M-FS': 'Fæðingastyrkur móður', + 'M-FSN': 'Fæðingarstyrkur móður í námi', + 'M-L-GR': 'Grunnréttur móður', + 'M-L-GR-SJ': 'Grunnréttur móður launaþ./sjálfst.', + 'M-S-GR': 'Grunnréttur móður sjálfst.', + 'M-Æ-FS': 'Fæðingarstyrkur móður ættleið.', + 'M-Æ-FSN': 'Fæðingarst. móður í námi ættl.', + 'M-Æ-L-GR': 'Grunnr.móður ættleiðing', + 'M-Æ-L-GRSJ': 'Grunnr.móður ættleiðing launþ./sjálfst.', + 'M-Æ-S-GR': 'Grunnr.móður ættleiðing', + 'NÁLG.BANN': 'Nálgunarbann', + 'ORLOF-FBF': 'Fjölburafæðing (orlof)', + 'ORLOF-FBVF': 'Fjölfóstur (orlof)', + 'ORLOF-FBÆ': 'Fjölættleiðing (orlof)', + ÓFEÐRAÐ: 'Ófeðrað barn', + RÉTTLAUST: 'Réttindaleysi hins foreldris', + 'SAM-GR': 'Sameiginlegur grunnréttur', + 'SJÚK/SLYS': 'Sjúkdómar og slys', + 'ST-FBF': 'Fjölburafæðing (styrkur)', + UMGENGNI: 'Framlenging v/takm eða engrar umg forsjárl. foreld', + VEIKBARN: 'Veikindi barns', + VEIKFÆÐING: 'Veikindi móður v/fæðingar', + VEIKMEÐG: 'Veikindi móður á meðgöngu', + VEIKTBARN7: 'Alvar.veikindi barns f. heimk.', + 'ÆTTL.STYRK': 'Ættleiðingarstyrkur', + 'ÖRYGGI-L': 'Leng. M vegna öryggisást.', +} diff --git a/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.service.spec.ts b/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.service.spec.ts index 92524c696bbd..ce4c304ef7c2 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.service.spec.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.service.spec.ts @@ -247,225 +247,157 @@ describe('ParentalLeaveService', () => { }) describe('createPeriodsDTO', () => { - it('should return 2 periods, one standard and one using the right period code', async () => { + it('should return 2 periods with "M-S-GR,ORLOF-FBF" rightsCodePeriod and ratio in days', async () => { const application = createApplication() - const res = await parentalLeaveService.createPeriodsDTO( - application, - nationalId, - ) + + set(application, 'answers.periods[1]', { + ratio: '80', + useLength: 'no', + startDate: '2025-03-12', + endDate: '2025-09-11', + }) + const periods = get(application.answers, 'periods') as object as Period[] + const rights = 'M-S-GR,ORLOF-FBF' + + const res = parentalLeaveService.createPeriodsDTO(periods, false, rights) expect(res).toEqual([ { from: '2021-05-17', - to: '2021-11-16', - ratio: '100', + to: '2022-01-01', + ratio: 'D225', approved: false, paid: false, - rightsCodePeriod: 'M-L-GR', + rightsCodePeriod: rights, }, { - from: '2021-11-17', - to: '2022-01-01', - ratio: 'D45', + from: '2025-03-12', + to: '2025-09-11', + ratio: 'D144', approved: false, paid: false, - rightsCodePeriod: apiConstants.rights.receivingRightsId, + rightsCodePeriod: rights, }, ]) }) + }) - it('should return 2 periods, one standard and one using single parent right code', async () => { + describe('createRightsDTO', () => { + it('should return 2 applicationRights, basic rights, single parent rights, and multiple Birth', async () => { const application = createApplication() - set(application, 'answers.otherParent', SINGLE) + set(application, 'answers.periods[1]', { + ratio: '100', + useLength: 'no', + startDate: '2025-03-12', + endDate: '2025-09-14', + }) + set(application, 'answers.multipleBirths.hasMultipleBirths', YES) + set(application, 'answers.multipleBirthsRequestDays', 79) + set(application, 'answers.multipleBirths.multipleBirths', 2) + set(application, 'answers.otherParentObj.chooseOtherParent', SINGLE) - const res = await parentalLeaveService.createPeriodsDTO( - application, - nationalId, - ) + const res = await parentalLeaveService.createRightsDTO(application) expect(res).toEqual([ { - from: '2021-05-17', - to: '2021-11-16', - ratio: '100', - approved: false, - paid: false, - rightsCodePeriod: 'M-L-GR', + days: '180', + daysLeft: '0', + months: '6.0', + rightsDescription: 'Grunnréttur móður', + rightsUnit: 'M-L-GR', }, { - from: '2021-11-17', - to: '2022-01-01', - ratio: 'D45', - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.artificialInseminationRightsId, + days: '180', + daysLeft: '0', + months: '6.0', + rightsDescription: 'Eitt foreldri', + rightsUnit: 'EITTFOR', + }, + { + days: '90', + daysLeft: '42', + months: '3.0', + rightsDescription: 'Fjölburafæðing (orlof)', + rightsUnit: 'ORLOF-FBF', }, ]) }) - it('should return 2 periods, one standard and mark it as ActualDateOfBirth and one using the right period code', async () => { + it('should return 1 applicationRights, basic rights', async () => { const application = createApplication() - const firstPeriod = get(application.answers, 'periods[0]') as object - set( - firstPeriod, - 'firstPeriodStart', - StartDateOptions.ACTUAL_DATE_OF_BIRTH, - ) + set(application, 'answers.requestRights', {}) - const res = await parentalLeaveService.createPeriodsDTO( - application, - nationalId, - ) + const res = await parentalLeaveService.createRightsDTO(application) expect(res).toEqual([ { - from: 'date_of_birth', - to: '2021-11-16', - ratio: '100', - approved: false, - paid: false, - rightsCodePeriod: 'M-L-GR', - }, - { - from: '2021-11-17', - to: '2022-01-01', - ratio: 'D45', - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.receivingRightsId, + days: '180', + daysLeft: '0', + months: '6.0', + rightsDescription: 'Grunnréttur móður', + rightsUnit: 'M-L-GR', }, ]) }) - it('should return 3 periods, one standard, one using single parent right code and one using multiple birth right code', async () => { + it('should return 2 applicationRights, basic rights, multiple Birth', async () => { const application = createApplication() - const firstPeriod = get(application.answers, 'periods[0]') as object - set(firstPeriod, 'endDate', '2022-07-09') - set(application.answers, 'otherParent', SINGLE) - set(application.answers, 'applicationType.option', PARENTAL_LEAVE) - set(application.answers, 'multipleBirths.hasMultipleBirths', YES) - set(application.answers, 'multipleBirths.multipleBirths', 2) - set(application.answers, 'multipleBirthsRequestDays', 90) + set(application, 'answers.multipleBirths.hasMultipleBirths', YES) + set(application, 'answers.multipleBirthsRequestDays', 79) + set(application, 'answers.multipleBirths.multipleBirths', 2) - const res = await parentalLeaveService.createPeriodsDTO( - application, - nationalId, - ) + const res = await parentalLeaveService.createRightsDTO(application) expect(res).toEqual([ { - from: '2021-05-17', - to: '2021-11-16', - ratio: '100', - approved: false, - paid: false, - rightsCodePeriod: 'M-L-GR', - }, - { - from: '2021-11-17', - to: '2022-05-16', - ratio: '100', - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.artificialInseminationRightsId, + days: '180', + daysLeft: '0', + months: '6.0', + rightsDescription: 'Grunnréttur móður', + rightsUnit: 'M-L-GR', }, { - from: '2022-05-17', - to: '2022-07-09', - ratio: 'D53', - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.multipleBirthsOrlofRightsId, + days: '79', + daysLeft: '34', + months: '2.6', + rightsDescription: 'Fjölburafæðing (orlof)', + rightsUnit: 'ORLOF-FBF', }, ]) }) - it('should return 3 periods, one standard, one using multiple birth right code and one using right period code', async () => { + it('should return 2 applicationRights, one basic rights and one for rights transfer', async () => { const application = createApplication() - const firstPeriod = get(application.answers, 'periods[0]') as object - set(firstPeriod, 'endDate', '2022-04-01') - set(application.answers, 'applicationType.option', PARENTAL_GRANT) - set(application.answers, 'multipleBirths.hasMultipleBirths', YES) - set(application.answers, 'multipleBirths.multipleBirths', 2) - set(application.answers, 'multipleBirthsRequestDays', 90) + set(application, 'answers.periods[0]', { + ratio: '100', + useLength: 'no', + startDate: '2025-03-12', + endDate: '2025-09-14', + }) - const res = await parentalLeaveService.createPeriodsDTO( - application, - nationalId, - ) + const res = await parentalLeaveService.createRightsDTO(application) expect(res).toEqual([ { - from: '2021-05-17', - to: '2021-11-16', - ratio: '100', - approved: false, - paid: false, - rightsCodePeriod: 'M-FS', + days: '180', + daysLeft: '0', + months: '6.0', + rightsDescription: 'Grunnréttur móður', + rightsUnit: 'M-L-GR', }, { - from: '2021-11-17', - to: '2022-02-16', - ratio: '100', - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.multipleBirthsGrantRightsId, - }, - { - from: '2022-02-17', - to: '2022-04-01', - ratio: 'D45', - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.receivingRightsId, + days: '45', + daysLeft: '42', + months: '1.5', + rightsDescription: 'Framsal grunnréttur', + rightsUnit: 'FSAL-GR', }, ]) }) - - it('should change period ratio to D when using .daysToUse', async () => { - const application = createApplication() - - const startDate = new Date(2022, 9, 10) - const endDate = new Date(2023, 0, 9) - const ratio = 0.59 - - const originalPeriods: Period[] = [ - { - startDate: startDate.toISOString().split('T')[0], - endDate: endDate.toISOString().split('T')[0], - ratio: `${ratio * 100}`, - firstPeriodStart: StartDateOptions.ESTIMATED_DATE_OF_BIRTH, - daysToUse: calculatePeriodLength( - startDate, - endDate, - ratio, - ).toString(), - }, - ] - - set(application.answers, 'periods', originalPeriods) - - const expectedPeriods: VmstPeriod[] = [ - { - approved: false, - from: originalPeriods[0].startDate, - to: originalPeriods[0].endDate, - paid: false, - ratio: `D${originalPeriods[0].daysToUse}`, - rightsCodePeriod: 'M-L-GR', - }, - ] - const res = await parentalLeaveService.createPeriodsDTO( - application, - nationalId, - ) - - expect(res).toEqual(expectedPeriods) - }) }) describe('sendApplication', () => { diff --git a/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.service.ts index 7e84e1d6cff8..709d9423793b 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.service.ts @@ -1,8 +1,5 @@ import { Inject, Injectable } from '@nestjs/common' import { S3 } from 'aws-sdk' -import addDays from 'date-fns/addDays' -import format from 'date-fns/format' -import cloneDeep from 'lodash/cloneDeep' import { getValueViaPath } from '@island.is/application/core' import { @@ -17,27 +14,39 @@ import { PERMANENT_FOSTER_CARE, ParentalRelations, SINGLE, - StartDateOptions, States, UnEmployedBenefitTypes, YES, + calculateDaysUsedByPeriods, calculatePeriodLength, getAdditionalSingleParentRightsInDays, getApplicationAnswers, getApplicationExternalData, getAvailablePersonalRightsInDays, - getAvailableRightsInDays, getMultipleBirthsDays, + getSelectedChild, + getTransferredDays, + getTransferredDaysInMonths, getUnApprovedEmployers, isParentWithoutBirthParent, + Period as AnswerPeriod, + getPersonalDays, + getPersonalDaysInMonths, + StartDateOptions, + getAdditionalSingleParentRightsInMonths, + clamp, + getMultipleBirthsDaysInMonths, } from '@island.is/application/templates/parental-leave' import { Application, ApplicationConfigurations, ApplicationTypes, - YesOrNo, } from '@island.is/application/types' -import type { Attachment, Period } from '@island.is/clients/vmst' +import type { + ApplicationRights, + Attachment, + Period, +} from '@island.is/clients/vmst' import { ApplicationInformationApi, ParentalLeaveApi, @@ -59,7 +68,7 @@ import { APPLICATION_ATTACHMENT_BUCKET, SIX_MONTHS_IN_SECONDS_EXPIRES, apiConstants, - df, + rightsDescriptions, } from './constants' import { generateApplicationApprovedByEmployerEmail, @@ -72,10 +81,9 @@ import { import { getType, checkIfPhoneNumberIsGSM, - getFromDate, - getRatio, getRightsCode, transformApplicationToParentalLeaveDTO, + getFromDate, } from './parental-leave.utils' import { generateAssignEmployerApplicationSms, @@ -83,6 +91,7 @@ import { generateEmployerRejectedApplicationSms, generateOtherParentRejectedApplicationSms, } from './smsGenerators' +import parseISO from 'date-fns/parseISO' interface VMSTError { type: string @@ -92,17 +101,6 @@ interface VMSTError { errors: Record } -interface AnswerPeriod { - startDate: string - endDate: string - ratio: string - firstPeriodStart?: string - useLength?: YesOrNo - daysToUse?: string - rawIndex?: number - rightCodePeriod?: string -} - @Injectable() export class ParentalLeaveService extends BaseTemplateApiService { s3 = new S3() @@ -678,89 +676,16 @@ export class ParentalLeaveService extends BaseTemplateApiService { return attachments } - async getCalculatedPeriod( - nationalRegistryId: string, - startDate: Date, - startDateString: string | undefined, - periodLength: number, - period: AnswerPeriod, - rightsCodePeriod: string, - ): Promise { - const periodObj = { - from: startDateString ?? format(startDate, df), - approved: false, - paid: false, - rightsCodePeriod: rightsCodePeriod, - } - if (period.ratio === '100') { - const isUsingNumberOfDays = period.daysToUse !== undefined - const getPeriodEndDate = - await this.parentalLeaveApi.parentalLeaveGetPeriodEndDate({ - nationalRegistryId, - startDate: startDate, - length: String(periodLength), - percentage: period.ratio, - }) - - if (getPeriodEndDate.periodEndDate === undefined) { - throw new Error( - `Could not calculate end date of period starting ${period.startDate} and using ${periodLength} days of rights`, - ) - } - - return { - ...periodObj, - to: format(getPeriodEndDate.periodEndDate, df), - ratio: getRatio( - period.ratio, - periodLength.toString(), - isUsingNumberOfDays, - ), - } - } else { - const isUsingNumberOfDays = true - - // Calculate endDate from periodLength, startDate and percentage ( period.ratio ) - const actualDaysFromPercentage = Math.floor( - periodLength / (Number(period.ratio) / 100), - ) - - const endDate = addDays(startDate, actualDaysFromPercentage) - - return { - ...periodObj, - to: format(endDate, df), - ratio: getRatio( - period.ratio, - periodLength.toString(), - isUsingNumberOfDays, - ), - } - } - } - - async createPeriodsDTO( + async createRightsDTO( application: Application, - nationalRegistryId: string, - ): Promise { - const { - periods: originalPeriods, - firstPeriodStart, - applicationType, - otherParent, - } = getApplicationAnswers(application.answers) + ): Promise { + const { applicationType, otherParent, isRequestingRights, periods } = + getApplicationAnswers(application.answers) const { applicationFundId } = getApplicationExternalData( application.externalData, ) - const answers = cloneDeep(originalPeriods).sort((a, b) => { - const dateA = new Date(a.startDate) - const dateB = new Date(b.startDate) - - return dateA.getTime() - dateB.getTime() - }) - let vmstRightCodePeriod = null if (applicationFundId) { try { @@ -794,602 +719,184 @@ export class ParentalLeaveService extends BaseTemplateApiService { } } - const periods: Period[] = [] - const maximumDaysToSpend = getAvailableRightsInDays(application) const maximumPersonalDaysToSpend = getAvailablePersonalRightsInDays(application) const maximumMultipleBirthsDaysToSpend = getMultipleBirthsDays(application) const maximumAdditionalSingleParentDaysToSpend = getAdditionalSingleParentRightsInDays(application) - const maximumDaysBeforeUsingTransferRights = - maximumPersonalDaysToSpend + maximumMultipleBirthsDaysToSpend - const maximumSingleParentDaysBeforeUsingMultipleBirthsRights = - maximumPersonalDaysToSpend + maximumAdditionalSingleParentDaysToSpend + const usedDays = calculateDaysUsedByPeriods(periods) + + const selectedChild = getSelectedChild( + application.answers, + application.externalData, + ) + if (!selectedChild) { + throw new Error('Missing selected child') + } + const transferredDays = getTransferredDays(application, selectedChild) + const personalDays = getPersonalDays(application) const mulitpleBirthsRights = applicationType === PARENTAL_LEAVE ? apiConstants.rights.multipleBirthsOrlofRightsId : apiConstants.rights.multipleBirthsGrantRightsId - const isActualDateOfBirth = - firstPeriodStart === StartDateOptions.ACTUAL_DATE_OF_BIRTH - let numberOfDaysAlreadySpent = 0 - const basicRightCodePeriod = - vmstRightCodePeriod ?? getRightsCode(application) - - for (const [index, period] of answers.entries()) { - const isFirstPeriod = index === 0 - const isUsingNumberOfDays = - period.daysToUse !== undefined && period.daysToUse !== '' - - // If a period doesn't have both startDate or endDate we skip it - if (!isFirstPeriod && (!period.startDate || !period.endDate)) { - continue - } - - const startDate = new Date(period.startDate) - const endDate = new Date(period.endDate) - const useLength = period.useLength || '' - - let periodLength = 0 - - if (isUsingNumberOfDays) { - periodLength = Number(period.daysToUse) - } else if (Number(period.ratio) < 100) { - /* - * We need to calculate periodLength when ratio is not 100% - * because there could be mis-calculate betweeen island.is and VMST - * for example: - * 8 months period with 75% - * island.is calculator returns: 180 days - * VMST returns: 184 days - */ - const fullLength = calculatePeriodLength(startDate, endDate) - periodLength = Math.round(fullLength * (Number(period.ratio) / 100)) - } else { - const getPeriodLength = - await this.parentalLeaveApi.parentalLeaveGetPeriodLength({ - nationalRegistryId, - startDate, - endDate, - percentage: period.ratio, - }) - - if (getPeriodLength.periodLength === undefined) { - throw new Error( - `Could not calculate length of period from ${period.startDate} to ${period.endDate}`, - ) - } - - periodLength = Number(getPeriodLength.periodLength ?? 0) - } - - const numberOfDaysSpentAfterPeriod = - numberOfDaysAlreadySpent + periodLength + const baseRight = vmstRightCodePeriod ?? getRightsCode(application) + const rights = [ + { + rightsUnit: baseRight, + days: String(personalDays), + rightsDescription: rightsDescriptions[baseRight], + months: String(getPersonalDaysInMonths(application)), + daysLeft: String(Math.max(0, personalDays - usedDays)), + }, + ] + + const addMultipleBirthsRights = ( + rightsArray: ApplicationRights[], + totalDays: number, + usedDays: number, + ) => { + rightsArray.push({ + rightsUnit: mulitpleBirthsRights, + days: String(maximumMultipleBirthsDaysToSpend), + rightsDescription: rightsDescriptions[mulitpleBirthsRights], + months: String(getMultipleBirthsDaysInMonths(application)), + daysLeft: String( + clamp( + totalDays + maximumMultipleBirthsDaysToSpend - usedDays, + 0, + maximumMultipleBirthsDaysToSpend, + ), + ), + }) + } - if (numberOfDaysSpentAfterPeriod > maximumDaysToSpend) { - throw new Error( - `Period from ${period.startDate} to ${period.endDate} will exceed rights (${numberOfDaysSpentAfterPeriod} > ${maximumDaysToSpend})`, + if (otherParent === SINGLE) { + rights.push({ + rightsUnit: apiConstants.rights.artificialInseminationRightsId, + days: String(maximumAdditionalSingleParentDaysToSpend), + rightsDescription: + rightsDescriptions[ + apiConstants.rights.artificialInseminationRightsId + ], + months: String(getAdditionalSingleParentRightsInMonths(application)), + daysLeft: String( + clamp( + maximumPersonalDaysToSpend + + maximumAdditionalSingleParentDaysToSpend - + usedDays, + 0, + maximumAdditionalSingleParentDaysToSpend, + ), + ), + }) + if (maximumMultipleBirthsDaysToSpend > 0) { + addMultipleBirthsRights( + rights, + personalDays + maximumAdditionalSingleParentDaysToSpend, + usedDays, ) } - - const isUsingAdditionalRights = - numberOfDaysAlreadySpent >= - maximumDaysToSpend - maximumAdditionalSingleParentDaysToSpend - const willSingleParentStartToUseAdditionalRightsWithPeriod = - numberOfDaysSpentAfterPeriod > - maximumDaysToSpend - maximumAdditionalSingleParentDaysToSpend - const isSingleParentUsingMultipleBirthsRights = - numberOfDaysAlreadySpent >= - maximumSingleParentDaysBeforeUsingMultipleBirthsRights - const isSingleParentUsingPersonalRights = - numberOfDaysAlreadySpent < maximumPersonalDaysToSpend - const willSingleParentStartUsingMultipleBirthsRight = - numberOfDaysSpentAfterPeriod > - maximumPersonalDaysToSpend + maximumAdditionalSingleParentDaysToSpend - - const isUsingMultipleBirthsRights = - numberOfDaysAlreadySpent >= maximumPersonalDaysToSpend - const willStartToUseMultipleBirthsRightsWithPeriod = - numberOfDaysSpentAfterPeriod > maximumPersonalDaysToSpend - const isUsingTransferredRights = - numberOfDaysAlreadySpent >= maximumDaysBeforeUsingTransferRights - const willStartToUseTransferredRightsWithPeriod = - numberOfDaysSpentAfterPeriod > maximumDaysBeforeUsingTransferRights - - /* - ** Priority rights: - ** 1. personal rights - ** 2. single parent rights - ** 3. common rights ( from multiple births) - ** 4. transfer rights ( from other parent) - We have to finish first one before go to next and so on - */ - if ( - !isUsingTransferredRights && - !willStartToUseTransferredRightsWithPeriod && - !isUsingMultipleBirthsRights && - !willStartToUseMultipleBirthsRightsWithPeriod && - !isUsingAdditionalRights && - !willSingleParentStartToUseAdditionalRightsWithPeriod - ) { - // We know its a normal period and it will not exceed personal rights - periods.push({ - from: getFromDate( - isFirstPeriod, - isActualDateOfBirth, - useLength, - period, - ), - to: period.endDate, - ratio: getRatio( - period.ratio, - periodLength.toString(), - period.ratio === '100' ? isUsingNumberOfDays : true, + } else { + if (maximumMultipleBirthsDaysToSpend > 0) { + addMultipleBirthsRights(rights, personalDays, usedDays) + } + if (isRequestingRights === YES) { + rights.push({ + rightsUnit: apiConstants.rights.receivingRightsId, + days: String(transferredDays), + rightsDescription: + rightsDescriptions[apiConstants.rights.receivingRightsId], + months: String( + getTransferredDaysInMonths(application, selectedChild), ), - approved: false, - paid: false, - rightsCodePeriod: basicRightCodePeriod, - }) - } else if (otherParent === SINGLE) { - // single parent - // Only using multiple births right - if (isSingleParentUsingMultipleBirthsRights) { - periods.push({ - from: period.startDate, - to: period.endDate, - ratio: getRatio( - period.ratio, - periodLength.toString(), - period.ratio === '100' ? isUsingNumberOfDays : true, - ), - approved: false, - paid: false, - rightsCodePeriod: mulitpleBirthsRights, - }) - } else { - /* - ** If we reach here, we have a period that will have: - ** 1: Personal rights and additional rights - ** 2: Personal, additional and multiplebirths rights - ** 3: Additional rights and multipleBirths rights - ** 4: Addtitonal rights - */ - if (maximumMultipleBirthsDaysToSpend === 0) { - if (isSingleParentUsingPersonalRights) { - // 1. Personal rights and additional rights - // Personal rights - const daysLeftOfPersonalRights = - maximumPersonalDaysToSpend - numberOfDaysAlreadySpent - const fromDate = getFromDate( - isFirstPeriod, - isActualDateOfBirth, - useLength, - period, - ) - - const personalPeriod = await this.getCalculatedPeriod( - nationalRegistryId, - startDate, - fromDate, - daysLeftOfPersonalRights, - period, - basicRightCodePeriod, - ) - - periods.push(personalPeriod) - - // Additional rights - const additionalSingleParentPeriodStartDate = addDays( - new Date(personalPeriod.to), - 1, - ) - const lengthOfPeriodUsingAdditionalSingleParentDays = - periodLength - daysLeftOfPersonalRights - - periods.push({ - from: format(additionalSingleParentPeriodStartDate, df), - to: period.endDate, - ratio: getRatio( - period.ratio, - lengthOfPeriodUsingAdditionalSingleParentDays.toString(), - true, - ), - approved: false, - paid: false, - rightsCodePeriod: - apiConstants.rights.artificialInseminationRightsId, - }) - } else { - // 4. Additional rights - periods.push({ - from: period.startDate, - to: period.endDate, - ratio: getRatio( - period.ratio, - periodLength.toString(), - period.ratio === '100' ? isUsingNumberOfDays : true, - ), - approved: false, - paid: false, - rightsCodePeriod: - apiConstants.rights.artificialInseminationRightsId, - }) - } - } else { - if (isSingleParentUsingPersonalRights) { - // 2. Personal, additional and multipleBirths rights - // Personal rights - const daysLeftOfPersonalRights = - maximumPersonalDaysToSpend - numberOfDaysAlreadySpent - const fromDate = getFromDate( - isFirstPeriod, - isActualDateOfBirth, - useLength, - period, - ) - - const personalPeriod = await this.getCalculatedPeriod( - nationalRegistryId, - startDate, - fromDate, - daysLeftOfPersonalRights, - period, - basicRightCodePeriod, - ) - - periods.push(personalPeriod) - - const additionalSingleParentPeriodStartDate = addDays( - new Date(personalPeriod.to), - 1, - ) - - // Additional rights - if (willSingleParentStartUsingMultipleBirthsRight) { - const additionalPeriod = await this.getCalculatedPeriod( - nationalRegistryId, - additionalSingleParentPeriodStartDate, - undefined, - maximumAdditionalSingleParentDaysToSpend, - period, - apiConstants.rights.artificialInseminationRightsId, - ) - - periods.push(additionalPeriod) - - // Common rights (multiple births) - const commonPeriodStartDate = addDays( - new Date(additionalPeriod.to), - 1, - ) - const lengthOfPeriodUsingCommonDays = - periodLength - - daysLeftOfPersonalRights - - maximumAdditionalSingleParentDaysToSpend - - periods.push({ - from: format(commonPeriodStartDate, df), - to: period.endDate, - ratio: getRatio( - period.ratio, - lengthOfPeriodUsingCommonDays.toString(), - true, - ), - approved: false, - paid: false, - rightsCodePeriod: mulitpleBirthsRights, - }) - } else { - // Additional rights - const lengthOfPeriodUsingAdditionalDays = - periodLength - daysLeftOfPersonalRights - - periods.push({ - from: format(additionalSingleParentPeriodStartDate, df), - to: period.endDate, - ratio: getRatio( - period.ratio, - lengthOfPeriodUsingAdditionalDays.toString(), - true, - ), - approved: false, - paid: false, - rightsCodePeriod: - apiConstants.rights.artificialInseminationRightsId, - }) - } - } else { - // 3. Additional rights and multipleBirths rights - if (willSingleParentStartUsingMultipleBirthsRight) { - // Additional rights - const lengthOfPeriodUsingAdditionalSingleParentDays = - maximumPersonalDaysToSpend + - maximumAdditionalSingleParentDaysToSpend - - numberOfDaysAlreadySpent - - const additionalPeriod = await this.getCalculatedPeriod( - nationalRegistryId, - startDate, - undefined, - lengthOfPeriodUsingAdditionalSingleParentDays, - period, - apiConstants.rights.artificialInseminationRightsId, - ) - - periods.push(additionalPeriod) - - // Common rights (multiple births) - const commonPeriodStartDate = addDays( - new Date(additionalPeriod.to), - 1, - ) - const lengthOfPeriodUsingCommonDays = - periodLength - lengthOfPeriodUsingAdditionalSingleParentDays - - periods.push({ - from: format(commonPeriodStartDate, df), - to: period.endDate, - ratio: getRatio( - period.ratio, - lengthOfPeriodUsingCommonDays.toString(), - true, - ), - approved: false, - paid: false, - rightsCodePeriod: mulitpleBirthsRights, - }) - } else { - // Only additional rights - periods.push({ - from: period.startDate, - to: period.endDate, - ratio: getRatio( - period.ratio, - periodLength.toString(), - period.ratio === '100' ? isUsingNumberOfDays : true, - ), - approved: false, - paid: false, - rightsCodePeriod: - apiConstants.rights.artificialInseminationRightsId, - }) - } - } - } - } - } else { - // has other parent - // We know all of the period will be using transferred rights - if (isUsingTransferredRights) { - periods.push({ - from: period.startDate, - to: period.endDate, - ratio: getRatio( - period.ratio, - periodLength.toString(), - period.ratio === '100' ? isUsingNumberOfDays : true, - ), - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.receivingRightsId, - }) - } else if (willStartToUseTransferredRightsWithPeriod) { - /* - ** If we reach here, we have a period that will have to be split into - ** two, a part of it will be using personal/personal rights and the other part - ** will be using transferred rights - ** Case: - ** 1. Period includes personal rights and transfer rights - ** 2. Period includes common rights and transfer rights - ** 3. Period includes personal rights, common rights and transfer rights - */ - - // 1. Period includes personal and transfer rights - if (maximumMultipleBirthsDaysToSpend === 0) { - const fromDate = getFromDate( - isFirstPeriod, - isActualDateOfBirth, - useLength, - period, - ) - - // Personal - const daysLeftOfPersonalRights = - maximumPersonalDaysToSpend - numberOfDaysAlreadySpent - const personalPeriod = await this.getCalculatedPeriod( - nationalRegistryId, - startDate, - fromDate, - daysLeftOfPersonalRights, - period, - basicRightCodePeriod, - ) - - periods.push(personalPeriod) - - // Transferred - const transferredPeriodStartDate = addDays( - new Date(personalPeriod.to), - 1, - ) - const lengthOfPeriodUsingTransferredDays = - periodLength - daysLeftOfPersonalRights - - periods.push({ - from: format(transferredPeriodStartDate, df), - to: period.endDate, - ratio: getRatio( - period.ratio, - lengthOfPeriodUsingTransferredDays.toString(), - true, - ), - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.receivingRightsId, - }) - } - // 2. Period includes common and transfer rights - else if (maximumPersonalDaysToSpend < numberOfDaysAlreadySpent) { - // Common (multiple births) - const daysLeftOfCommonRights = - maximumDaysBeforeUsingTransferRights - numberOfDaysAlreadySpent - const commonPeriod = await this.getCalculatedPeriod( - nationalRegistryId, - startDate, - undefined, - daysLeftOfCommonRights, - period, - mulitpleBirthsRights, - ) - - periods.push(commonPeriod) - - // Transferred - const transferredPeriodStartDate = addDays( - new Date(commonPeriod.to), - 1, - ) - const lengthOfPeriodUsingTransferredDays = - periodLength - daysLeftOfCommonRights - - periods.push({ - from: format(transferredPeriodStartDate, df), - to: period.endDate, - ratio: getRatio( - period.ratio, - lengthOfPeriodUsingTransferredDays.toString(), - true, - ), - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.receivingRightsId, - }) - } - // 3. Period includes personal, common and transfer rights - else { - // Personal - const daysLeftOfPersonalRights = - maximumPersonalDaysToSpend - numberOfDaysAlreadySpent - const fromDate = getFromDate( - isFirstPeriod, - isActualDateOfBirth, - useLength, - period, - ) - - const personalPeriod = await this.getCalculatedPeriod( - nationalRegistryId, - startDate, - fromDate, - daysLeftOfPersonalRights, - period, - basicRightCodePeriod, - ) - - periods.push(personalPeriod) - - // Common - const commonPeriodStartDate = addDays( - new Date(personalPeriod.to), - 1, - ) - const commonPeriod = await this.getCalculatedPeriod( - nationalRegistryId, - commonPeriodStartDate, - undefined, - maximumMultipleBirthsDaysToSpend, - period, - mulitpleBirthsRights, - ) - - periods.push(commonPeriod) - // Transferred - const transferredPeriodStartDate = addDays( - new Date(commonPeriod.to), - 1, - ) - const lengthOfPeriodUsingTransferredDays = - periodLength - - daysLeftOfPersonalRights - - maximumMultipleBirthsDaysToSpend - - periods.push({ - from: format(transferredPeriodStartDate, df), - to: period.endDate, - ratio: getRatio( - period.ratio, - lengthOfPeriodUsingTransferredDays.toString(), - true, - ), - approved: false, - paid: false, - rightsCodePeriod: apiConstants.rights.receivingRightsId, - }) - } - } else if (isUsingMultipleBirthsRights) { - // Applicant used upp his/her basic rights and started to use 'common' rights - // and has not reach transfer rights - periods.push({ - from: period.startDate, - to: period.endDate, - ratio: getRatio( - period.ratio, - periodLength.toString(), - period.ratio === '100' ? isUsingNumberOfDays : true, + daysLeft: String( + clamp( + maximumPersonalDaysToSpend + + maximumMultipleBirthsDaysToSpend + + transferredDays - + usedDays, + 0, + transferredDays, ), - approved: false, - paid: false, - rightsCodePeriod: mulitpleBirthsRights, - }) - } else { - // If we reach here then there is personal rights mix with common rights - // Personal - const daysLeftOfPersonalRights = - maximumPersonalDaysToSpend - numberOfDaysAlreadySpent - const fromDate = getFromDate( - isFirstPeriod, - isActualDateOfBirth, - useLength, - period, - ) - const personalPeriod = await this.getCalculatedPeriod( - nationalRegistryId, - startDate, - fromDate, - daysLeftOfPersonalRights, - period, - basicRightCodePeriod, - ) + ), + }) + } + } - periods.push(personalPeriod) + return rights + } - // Common (multiple births) - const commonPeriodStartDate = addDays(new Date(personalPeriod.to), 1) - const lengthOfPeriodUsingCommonDays = - periodLength - daysLeftOfPersonalRights + calculatePeriodDays( + startDate: string, + endDate: string, + ratio: string, + daysToUse?: string, + months?: number, + ) { + if (daysToUse) { + return daysToUse + } + const start = parseISO(startDate) + const end = parseISO(endDate) + if (isNaN(start.getTime()) || isNaN(end.getTime())) { + throw new Error('Invalid startDate or endDate') + } + const ratioNumber = Number(ratio) + if (isNaN(ratioNumber) || ratioNumber <= 0) { + throw new Error('Invalid ratio value') + } + const percentage = ratioNumber / 100 + const periodLength = calculatePeriodLength(start, end, undefined, months) + return Math.round(periodLength * percentage) + } - periods.push({ - from: format(commonPeriodStartDate, df), - to: period.endDate, - ratio: getRatio( - period.ratio, - lengthOfPeriodUsingCommonDays.toString(), - true, - ), - approved: false, - paid: false, - rightsCodePeriod: mulitpleBirthsRights, - }) - } + createPeriodsDTO( + periods: AnswerPeriod[], + isActualDateOfBirth: boolean, + rights: string, + ): Period[] { + return periods.map((period, index) => { + const isFirstPeriod = index === 0 + return { + rightsCodePeriod: rights, + from: getFromDate( + isFirstPeriod, + isActualDateOfBirth, + period.useLength || '', + period, + ), + to: period.endDate, + ratio: `D${this.calculatePeriodDays( + period.startDate, + period.endDate, + period.ratio, + period.daysToUse, + period.months, + )}`, + approved: !!period.approved, + paid: !!period.paid, } + }) + } - // Add each period to the total number of days spent when an iteration is finished - numberOfDaysAlreadySpent += periodLength - } - - return periods + async preparePeriodsAndRightsDTO( + application: Application, + periods: AnswerPeriod[], + firstPeriodStart: string | undefined, + ): Promise<{ rightsDTO: ApplicationRights[]; periodsDTO: Period[] }> { + const rightsDTO = await this.createRightsDTO(application) + const rights = rightsDTO.map(({ rightsUnit }) => rightsUnit).join(',') + const isActualDateOfBirth = + firstPeriodStart === StartDateOptions.ACTUAL_DATE_OF_BIRTH + const periodsDTO = this.createPeriodsDTO( + periods, + isActualDateOfBirth, + rights, + ) + return { rightsDTO, periodsDTO } } async sendApplication({ @@ -1402,6 +909,8 @@ export class ParentalLeaveService extends BaseTemplateApiService { applicationType, employerLastSixMonths, employers, + periods, + firstPeriodStart, } = getApplicationAnswers(application.answers) // if ( // previousState === States.VINNUMALASTOFNUN_APPROVE_EDITS || @@ -1414,18 +923,20 @@ export class ParentalLeaveService extends BaseTemplateApiService { const attachments = await this.getAttachments(application) const type = getType(application) - try { - const periods = await this.createPeriodsDTO( - application, - nationalRegistryId, - ) + const { periodsDTO, rightsDTO } = await this.preparePeriodsAndRightsDTO( + application, + periods, + firstPeriodStart, + ) + try { const parentalLeaveDTO = transformApplicationToParentalLeaveDTO( application, - periods, + periodsDTO, attachments, - false, // put false in testData as this is not dummy request - type, + false, + getType(application), + rightsDTO, ) const response = @@ -1499,25 +1010,30 @@ export class ParentalLeaveService extends BaseTemplateApiService { async validateApplication({ application }: TemplateApiModuleActionProps) { const nationalRegistryId = application.applicant - const { previousState } = getApplicationAnswers(application.answers) + const { previousState, periods, firstPeriodStart } = getApplicationAnswers( + application.answers, + ) /* This is to avoid calling the api every time the user leaves the residenceGrantApplicationNoBirthDate state or residenceGrantApplication state */ // Reject from if (previousState === States.RESIDENCE_GRANT_APPLICATION_NO_BIRTH_DATE) { return } const attachments = await this.getAttachments(application) - try { - const periods = await this.createPeriodsDTO( - application, - nationalRegistryId, - ) + const { periodsDTO, rightsDTO } = await this.preparePeriodsAndRightsDTO( + application, + periods, + firstPeriodStart, + ) + + try { const parentalLeaveDTO = transformApplicationToParentalLeaveDTO( application, - periods, + periodsDTO, attachments, true, getType(application), + rightsDTO, ) // call SetParentalLeave API with testData: TRUE as this is a dummy request diff --git a/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.utils.ts b/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.utils.ts index 2e536a4f19cd..cda5b2b83eb6 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.utils.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/parental-leave/parental-leave.utils.ts @@ -28,6 +28,7 @@ import { ApplicationWithAttachments, } from '@island.is/application/types' import { + ApplicationRights, Attachment, Employer, ParentalLeave, @@ -371,6 +372,7 @@ export const transformApplicationToParentalLeaveDTO = ( | 'empdoc' | 'empdocper' | undefined, + applicationRights?: ApplicationRights[], ): ParentalLeave => { const selectedChild = getSelectedChild( application.answers, @@ -463,6 +465,7 @@ export const transformApplicationToParentalLeaveDTO = ( type, language: language === Languages.EN ? language : undefined, // Only send language if EN otherParentBlocked: otherParentRightOfAccess === NO ? true : false, + applicationRights, } } diff --git a/libs/application/templates/driving-license/src/fields/EligibilitySummary/extractReasons.ts b/libs/application/templates/driving-license/src/fields/EligibilitySummary/extractReasons.ts index cc3009187f2c..d0a48764228d 100644 --- a/libs/application/templates/driving-license/src/fields/EligibilitySummary/extractReasons.ts +++ b/libs/application/templates/driving-license/src/fields/EligibilitySummary/extractReasons.ts @@ -25,6 +25,8 @@ const getDeniedByServiceMessageDescription = ( case RequirementKey.HasDeprivation: case RequirementKey.HasPoints: return requirementsMessages.hasPointsOrDeprivation + case RequirementKey.NoExtendedDrivingLicense: + return requirementsMessages.noExtendedDrivingLicenseTitle default: return requirementsMessages.rlsDefaultDeniedDescription } @@ -85,6 +87,11 @@ const requirementKeyToStep = ( title: requirementsMessages.beLicenseQualityPhotoTitle, description: requirementsMessages.beLicenseQualityPhotoDescription, } + case RequirementKey.NoExtendedDrivingLicense: + return { + title: requirementsMessages.noExtendedDrivingLicenseTitle, + description: requirementsMessages.noExtendedDrivingLicenseDescription, + } default: throw new Error('Unknown requirement reason - should not happen') } diff --git a/libs/application/templates/driving-license/src/fields/EligibilitySummary/useEligibility.ts b/libs/application/templates/driving-license/src/fields/EligibilitySummary/useEligibility.ts index 5a0b3eabb96e..5c474ce38154 100644 --- a/libs/application/templates/driving-license/src/fields/EligibilitySummary/useEligibility.ts +++ b/libs/application/templates/driving-license/src/fields/EligibilitySummary/useEligibility.ts @@ -1,14 +1,21 @@ import { getValueViaPath } from '@island.is/application/core' import { Application } from '@island.is/application/types' -import { ApplicationEligibility, RequirementKey } from '../../types/schema' +import { + ApplicationEligibility, + ApplicationEligibilityRequirement, + RequirementKey, +} from '../../types/schema' import { useQuery, gql } from '@apollo/client' import { B_FULL, + B_FULL_RENEWAL_65, BE, + codesExtendedLicenseCategories, codesRequiringHealthCertificate, DrivingLicenseApplicationFor, DrivingLicenseFakeData, otherLicenseCategories, + remarksCannotRenew65, YES, } from '../../lib/constants' import { fakeEligibility } from './fakeEligibility' @@ -90,6 +97,23 @@ export const useEligibility = ( ) } + const hasExtendedDrivingLicense = ( + currentLicense: DrivingLicense | undefined, + drivingLicenseIssued: string | undefined, + ): boolean => { + if (!drivingLicenseIssued) return false + + const relevantCategories = currentLicense?.categories?.filter((x) => + codesExtendedLicenseCategories.includes(x.nr), + ) + + if (!relevantCategories?.length) return false + + // Check if any category was issued on a different date than the 'B' license + // (indicating an extended license) + return relevantCategories.some((x) => x.issued !== drivingLicenseIssued) + } + if (usingFakeData) { return { loading: false, @@ -111,13 +135,11 @@ export const useEligibility = ( } } + const eligibility: ApplicationEligibilityRequirement[] = + data.drivingLicenseApplicationEligibility?.requirements ?? [] + //TODO: Remove when RLS/SGS supports health certificate in BE license if (application.answers.applicationFor === BE) { - const eligibility = - data.drivingLicenseApplicationEligibility === undefined - ? [] - : (data.drivingLicenseApplicationEligibility as ApplicationEligibility) - .requirements return { loading: loading, eligibility: { @@ -143,6 +165,52 @@ export const useEligibility = ( } } + if (application.answers.applicationFor === B_FULL_RENEWAL_65) { + const licenseB = currentLicense?.categories?.find( + (license) => license.nr === 'B', + ) + const drivingLicenseIssued = licenseB?.issued + + const hasExtendedLicense = hasExtendedDrivingLicense( + currentLicense, + drivingLicenseIssued, + ) + + const hasAnyInvalidRemarks = + currentLicense?.remarks?.some((remark) => + remarksCannotRenew65.includes(remark.code), + ) ?? false + + const requirements = [ + ...eligibility, + { + key: RequirementKey.HasNoPhoto, + requirementMet: hasQualityPhoto, + }, + ...(hasExtendedLicense + ? [ + { + key: RequirementKey.NoExtendedDrivingLicense, + requirementMet: false, + }, + ] + : []), + ] + + return { + loading: loading, + eligibility: { + isEligible: loading + ? undefined + : (data.drivingLicenseApplicationEligibility?.isEligible ?? false) && + hasQualityPhoto && + !hasExtendedLicense && + !hasAnyInvalidRemarks, + requirements, + }, + } + } + return { loading, eligibility: data.drivingLicenseApplicationEligibility, diff --git a/libs/application/templates/driving-license/src/forms/prerequisites/sectionApplicationFor.ts b/libs/application/templates/driving-license/src/forms/prerequisites/sectionApplicationFor.ts index 5fd395596f55..a5ba5d014713 100644 --- a/libs/application/templates/driving-license/src/forms/prerequisites/sectionApplicationFor.ts +++ b/libs/application/templates/driving-license/src/forms/prerequisites/sectionApplicationFor.ts @@ -43,7 +43,7 @@ export const sectionApplicationFor = ( 'currentLicense.data', ) ?? { categories: null } - const age = + let age = getValueViaPath( app.externalData, 'nationalRegistry.data.age', @@ -53,6 +53,7 @@ export const sectionApplicationFor = ( app.answers, 'fakeData', ) + if (fakeData?.useFakeData === 'yes') { currentLicense = fakeData.currentLicense ?? null categories = @@ -66,6 +67,8 @@ export const sectionApplicationFor = ( { nr: 'BE', validToCode: 9 }, ] : [] + + age = fakeData?.age } let options = [ @@ -91,9 +94,7 @@ export const sectionApplicationFor = ( subLabel: m.applicationForRenewalLicenseDescription.defaultMessage, value: B_FULL_RENEWAL_65, - disabled: - !currentLicense || - (fakeData && fakeData.age ? fakeData.age < 65 : age < 65), + disabled: !currentLicense || age < 65, }) } diff --git a/libs/application/templates/driving-license/src/lib/constants.ts b/libs/application/templates/driving-license/src/lib/constants.ts index 27df9cbee563..672776f7bf29 100644 --- a/libs/application/templates/driving-license/src/lib/constants.ts +++ b/libs/application/templates/driving-license/src/lib/constants.ts @@ -26,6 +26,20 @@ export const CHARGE_ITEM_CODES: Record = { export const otherLicenseCategories = ['C', 'C1', 'CE', 'D', 'D1', 'DE'] export const codesRequiringHealthCertificate = ['400', '01.06'] +export const codesExtendedLicenseCategories = [ + 'C1', + 'C1E', + 'C', + 'CE', + 'D1', + 'D1E', + 'D', + 'DE', + 'Bfar', + 'Far', + 'FAR', +] +export const remarksCannotRenew65 = ['400', '450', '95'] export type DrivingLicenseApplicationFor = | typeof B_FULL diff --git a/libs/application/templates/driving-license/src/lib/messages.ts b/libs/application/templates/driving-license/src/lib/messages.ts index fdda81aaa35d..5bb5c988c112 100644 --- a/libs/application/templates/driving-license/src/lib/messages.ts +++ b/libs/application/templates/driving-license/src/lib/messages.ts @@ -980,4 +980,14 @@ export const requirementsMessages = defineMessages({ description: 'requirement unmet api returned false for an unspecified reason', }, + noExtendedDrivingLicenseTitle: { + id: 'dl.application:requirementunmet.noExtendedDrivingLicenseTitle', + defaultMessage: 'Ekki hægt að sækja um endurnýjun á 65+ ökuskírteini.', + description: 'requirement unmet 65 plus renewal', + }, + noExtendedDrivingLicenseDescription: { + id: 'dl.application:requirementunmet.noExtendedDrivingLicenseDescription#markdown', + defaultMessage: 'Ekki hægt að sækja um endurnýjun á 65+ ökuskírteini.', + description: 'requirement unmet 65 plus renewal', + }, }) diff --git a/libs/application/templates/driving-license/src/lib/types.ts b/libs/application/templates/driving-license/src/lib/types.ts index 92efcfc15176..9c5d92b9a323 100644 --- a/libs/application/templates/driving-license/src/lib/types.ts +++ b/libs/application/templates/driving-license/src/lib/types.ts @@ -11,6 +11,7 @@ export type ConditionFn = (answer: FormValue) => boolean export type DrivingLicenseCategory = { nr: string validToCode: number + issued?: string } export interface Remark { diff --git a/libs/application/templates/new-primary-school/src/lib/messages.ts b/libs/application/templates/new-primary-school/src/lib/messages.ts index d8cbfa7a7f80..ce9ac45d3f57 100644 --- a/libs/application/templates/new-primary-school/src/lib/messages.ts +++ b/libs/application/templates/new-primary-school/src/lib/messages.ts @@ -6,102 +6,102 @@ export const newPrimarySchoolMessages: MessageDir = { // Messages shared across the New Primary School application templates shared: defineMessages({ applicationName: { - id: 'dess.nps.application:application.name', + id: 'nps.application:application.name', defaultMessage: 'Umsókn í nýjan grunnskóla', description: 'Application for a new primary school', }, institution: { - id: 'dess.nps.application:institution.name', + id: 'nps.application:institution.name', defaultMessage: 'Sveitarfélög', description: 'Municipalities', }, formTitle: { - id: 'dess.nps.application:form.title', + id: 'nps.application:form.title', defaultMessage: 'Umsókn', description: 'Application', }, alertTitle: { - id: 'dess.nps.application:alert.title', + id: 'nps.application:alert.title', defaultMessage: 'Athugið', description: 'Attention', }, yes: { - id: 'dess.nps.application:yes', + id: 'nps.application:yes', defaultMessage: 'Já', description: 'Yes', }, no: { - id: 'dess.nps.application:no', + id: 'nps.application:no', defaultMessage: 'Nei', description: 'No', }, date: { - id: 'dess.nps.application:date', + id: 'nps.application:date', defaultMessage: 'Dagsetning', description: 'Date', }, datePlaceholder: { - id: 'dess.nps.application:date.placeholder', + id: 'nps.application:date.placeholder', defaultMessage: 'Veldu dagsetningu', description: 'Select date', }, fullName: { - id: 'dess.nps.application:full.name', + id: 'nps.application:full.name', defaultMessage: 'Fullt nafn', description: 'Full name', }, nationalId: { - id: 'dess.nps.application:nationalId', + id: 'nps.application:nationalId', defaultMessage: 'Kennitala', description: 'National id', }, email: { - id: 'dess.nps.application:email', + id: 'nps.application:email', defaultMessage: 'Netfang', description: 'Email address', }, municipality: { - id: 'dess.nps.application:municipality', + id: 'nps.application:municipality', defaultMessage: 'Sveitarfélag', description: 'Municipality', }, municipalityPlaceholder: { - id: 'dess.nps.application:municipality.placeholder', + id: 'nps.application:municipality.placeholder', defaultMessage: 'Veldu sveitarfélag', description: 'Select municipality', }, postalCode: { - id: 'dess.nps.application:postalCode', + id: 'nps.application:postalCode', defaultMessage: 'Póstnúmer', description: 'Postal code', }, address: { - id: 'dess.nps.application:address', + id: 'nps.application:address', defaultMessage: 'Heimilisfang', description: 'Address', }, phoneNumber: { - id: 'dess.nps.application:phoneNumber', + id: 'nps.application:phoneNumber', defaultMessage: 'Símanúmer', description: 'Phone number', }, relation: { - id: 'dess.nps.application:relation', + id: 'nps.application:relation', defaultMessage: 'Tengsl', description: 'Relation', }, relationPlaceholder: { - id: 'dess.nps.application:relation.placeholder', + id: 'nps.application:relation.placeholder', defaultMessage: 'Veldu tengsl', description: 'Select relation', }, school: { - id: 'dess.nps.application:school', + id: 'nps.application:school', defaultMessage: 'Skóli', description: 'School', }, schoolPlaceholder: { - id: 'dess.nps.application:school.placeholder', + id: 'nps.application:school.placeholder', defaultMessage: 'Veldu skóla', description: 'Select school', }, @@ -109,68 +109,68 @@ export const newPrimarySchoolMessages: MessageDir = { pre: defineMessages({ externalDataSection: { - id: 'dess.nps.application:external.data.section', + id: 'nps.application:external.data.section', defaultMessage: 'Forsendur', description: 'Prerequisites', }, externalDataSubSection: { - id: 'dess.nps.application:external.data.sub.section', + id: 'nps.application:external.data.sub.section', defaultMessage: 'Gagnaöflun', description: 'Data collection', }, externalDataDescription: { - id: 'dess.nps.application:external.data.description', + id: 'nps.application:external.data.description', defaultMessage: 'Eftirfarandi upplýsingar verða sóttar rafrænt', description: 'The following information will be retrieved electronically', }, nationalRegistryInformationTitle: { - id: 'dess.nps.application:prerequisites.national.registry.title', + id: 'nps.application:prerequisites.national.registry.title', defaultMessage: 'Upplýsingar frá Þjóðskrá', description: 'Information from Registers Iceland', }, nationalRegistryInformationSubTitle: { - id: 'dess.nps.application:prerequisites.national.registry.subtitle', + id: 'nps.application:prerequisites.national.registry.subtitle', defaultMessage: 'Upplýsingar um þig, maka og börn.', description: 'Information about you, spouse and children.', }, userProfileInformationTitle: { - id: 'dess.nps.application:prerequisites.userprofile.title', + id: 'nps.application:prerequisites.userprofile.title', defaultMessage: 'Upplýsingar af mínum síðum á Ísland.is', description: 'Information from My Pages at Ísland.is', }, userProfileInformationSubTitle: { - id: 'dess.nps.application:prerequisites.userprofile.subtitle', + id: 'nps.application:prerequisites.userprofile.subtitle', defaultMessage: 'Upplýsingar um netfang og símanúmer eru sóttar á mínar síður á Ísland.is.', description: 'Information about email address and phone number will be retrieved from My Pages at Ísland.is.', }, checkboxProvider: { - id: 'dess.nps.application:prerequisites.checkbox.provider', + id: 'nps.application:prerequisites.checkbox.provider', defaultMessage: 'Ég skil að ofangreindra upplýsinga verður aflað í umsóknarferlinu', description: 'I understand that the above information will be collected during the application process', }, startApplication: { - id: 'dess.nps.application:prerequisites.start.application', + id: 'nps.application:prerequisites.start.application', defaultMessage: 'Hefja umsókn', description: 'Start application', }, // Children childrenSubSectionTitle: { - id: 'dess.nps.application:prerequisites.children.sub.section.title', + id: 'nps.application:prerequisites.children.sub.section.title', defaultMessage: 'Börn', description: 'Children', }, childrenDescription: { - id: 'dess.nps.application:prerequisites.childrenDescription#markdown', + id: 'nps.application:prerequisites.childrenDescription#markdown', defaultMessage: `Samkvæmt uppflettingu í Þjóðskrá hefur þú forsjá með eftirfarandi barni/börnum. Ef þú sérð ekki barnið þitt hér, þá bendum við þér að hafa samband við Þjóðskrá. \n\nAthugaðu að einungis er hægt að sækja um fyrir eitt barn í einu. Ef skrá á tvö börn svo sem tvíbura er hægt að fara beint í að skrá annað barn þegar búið er að skrá það fyrra.`, description: `According to the Registers Iceland database you have the following children. If you do not see your child in this process, please contact the Registers Iceland. \n\nPlease note that you can only apply for one child at a time. If you have two children, such as twins, you can proceed to register the second child directly after completing the registration for the first one.`, }, childrenRadioTitle: { - id: 'dess.nps.application:prerequisites.childrenRadioTitle', + id: 'nps.application:prerequisites.childrenRadioTitle', defaultMessage: 'Veldu barn fyrir umsóknina', description: 'Select child for the application', }, @@ -178,68 +178,68 @@ export const newPrimarySchoolMessages: MessageDir = { childrenNParents: defineMessages({ sectionTitle: { - id: 'dess.nps.application:childrenNParents.section.title', + id: 'nps.application:childrenNParents.section.title', defaultMessage: 'Börn og foreldrar', description: 'Children and parents', }, // Child information childInfoSubSectionTitle: { - id: 'dess.nps.application:childrenNParents.child.info.sub.section.title', + id: 'nps.application:childrenNParents.child.info.sub.section.title', defaultMessage: 'Upplýsingar um barn', description: 'Information about child', }, childInfoDescription: { - id: 'dess.nps.application:childrenNParents.child.info.description', + id: 'nps.application:childrenNParents.child.info.description', defaultMessage: 'Athugaðu hvort upplýsingarnar séu réttar áður en þú heldur áfram.', description: 'Check that the information is correct before proceeding.', }, childInfoPreferredName: { - id: 'dess.nps.application:childrenNParents.child.info.preferred.name', + id: 'nps.application:childrenNParents.child.info.preferred.name', defaultMessage: 'Valið nafn', description: 'Preferred name', }, childInfoPronouns: { - id: 'dess.nps.application:childrenNParents.child.info.pronouns', + id: 'nps.application:childrenNParents.child.info.pronouns', defaultMessage: 'Fornafn', description: 'Pronoun', }, childInfoPronounsPlaceholder: { - id: 'dess.nps.application:childrenNParents.child.info.pronouns.placeholder', + id: 'nps.application:childrenNParents.child.info.pronouns.placeholder', defaultMessage: 'Veldu fornafn', description: 'Select pronoun', }, differentPlaceOfResidence: { - id: 'dess.nps.application:childrenNParents.child.info.different.place.of.residence', + id: 'nps.application:childrenNParents.child.info.different.place.of.residence', defaultMessage: 'Er dvalarstaður barns annað en skráð lögheimili?', description: "Is the child's place of residence different from the registered legal domicile?", }, childInfoPlaceOfResidence: { - id: 'dess.nps.application:childrenNParents.child.info.place.of.residence', + id: 'nps.application:childrenNParents.child.info.place.of.residence', defaultMessage: 'Dvalarstaður barns', description: "Child's place of residence", }, // Parents/guardians parentsSubSectionTitle: { - id: 'dess.nps.application:childrenNParents.parents.sub.section.title', + id: 'nps.application:childrenNParents.parents.sub.section.title', defaultMessage: 'Foreldrar/forsjáraðilar', description: 'Parents/guardians', }, otherParent: { - id: 'dess.nps.application:childrenNParents.otherParent', + id: 'nps.application:childrenNParents.otherParent', defaultMessage: 'Upplýsingar um foreldri/forsjáraðila 2', description: 'Information about parent/guardian 2', }, parent: { - id: 'dess.nps.application:childrenNParents.parent', + id: 'nps.application:childrenNParents.parent', defaultMessage: 'Upplýsingar um foreldri/forsjáraðila 1', description: 'Information about parent/guardian 1', }, parentsDescription: { - id: 'dess.nps.application:childrenNParents.parents.description', + id: 'nps.application:childrenNParents.parents.description', defaultMessage: 'Upplýsingar um foreldra og forsjáraðila eru sóttar í Þjóðskrá. Athugaðu hvort símanúmer og netföng séu rétt skráð áður en þú heldur áfram.', description: @@ -248,39 +248,39 @@ export const newPrimarySchoolMessages: MessageDir = { // Relatives relativesSubSectionTitle: { - id: 'dess.nps.application:childrenNParents.relatives.sub.section.title', + id: 'nps.application:childrenNParents.relatives.sub.section.title', defaultMessage: 'Aðstandendur', description: 'Relatives', }, relativesTitle: { - id: 'dess.nps.application:childrenNParents.relatives.title', + id: 'nps.application:childrenNParents.relatives.title', defaultMessage: 'Aðstandendur barnsins', description: "The child's relatives", }, relativesDescription: { - id: 'dess.nps.application:childrenNParents.relatives.description', + id: 'nps.application:childrenNParents.relatives.description', defaultMessage: 'Skráðu að minnsta kosti einn tengilið sem má hafa samband við ef ekki næst í foreldra/forsjáraðila barnsins. Þú getur bætt við allt að sex aðstandendum. Vinsamlegast látið aðstandendur vita af skráningunni.', description: "List at least one contact person who can be contacted if the child's parents/guardian cannot be reached. You can add up to six relatives. Please inform the relatives of the registration.", }, relativesRegistrationTitle: { - id: 'dess.nps.application:childrenNParents.relatives.registration.title', + id: 'nps.application:childrenNParents.relatives.registration.title', defaultMessage: 'Skráning aðstandanda', description: 'Registration of a relative', }, relativesAddRelative: { - id: 'dess.nps.application:childrenNParents.relatives.add.relative', + id: 'nps.application:childrenNParents.relatives.add.relative', defaultMessage: 'Bæta við aðstandanda', description: 'Add a relative', }, relativesRegisterRelative: { - id: 'dess.nps.application:childrenNParents.relatives.register.relative', + id: 'nps.application:childrenNParents.relatives.register.relative', defaultMessage: 'Skrá aðstandanda', description: 'Register relative', }, relativesDeleteRelative: { - id: 'dess.nps.application:childrenNParents.relatives.delete.relative', + id: 'nps.application:childrenNParents.relatives.delete.relative', defaultMessage: 'Eyða aðstandanda', description: 'Remove relative', }, @@ -288,113 +288,113 @@ export const newPrimarySchoolMessages: MessageDir = { primarySchool: defineMessages({ sectionTitle: { - id: 'dess.nps.application:primary.school.section.title', + id: 'nps.application:primary.school.section.title', defaultMessage: 'Grunnskóli', description: 'Primary school', }, // Reason for application reasonForApplicationSubSectionTitle: { - id: 'dess.nps.application:primary.school.reason.for.application.sub.section.title', + id: 'nps.application:primary.school.reason.for.application.sub.section.title', defaultMessage: 'Ástæða umsóknar', description: 'Reason for application', }, reasonForApplicationDescription: { - id: 'dess.nps.application:primary.school.reason.for.application.description', + id: 'nps.application:primary.school.reason.for.application.description', defaultMessage: 'Barn á alltaf rétt á skólavist í sínum hverfisskóla. Séu ástæður umsóknar aðrar en flutningur lögheimilis getur verið að skólinn sjái sér ekki fært að taka á móti barninu. Það fer eftir aðstæðum í skólanum hverju sinni, svo sem rými.', description: 'A child always has the right to attend school in his neighborhood school. If the reasons for the application are other than a change of legal residence, the school may not be able to accept the child. It depends on the situation in the school each time, such as space.', }, reasonForApplicationPlaceholder: { - id: 'dess.nps.application:primary.school.reason.for.application.placeholder', + id: 'nps.application:primary.school.reason.for.application.placeholder', defaultMessage: 'Veldu ástæðu', description: 'Select reason', }, registerNewDomicileAlertMessage: { - id: 'dess.nps.application:primary.school.register.new.domicile.alert.message', + id: 'nps.application:primary.school.register.new.domicile.alert.message', defaultMessage: 'Minnum þig á að skrá nýtt lögheimili í Þjóðskrá.', description: 'We remind you to register your new domicile in the National Registry.', }, country: { - id: 'dess.nps.application:primary.school.country', + id: 'nps.application:primary.school.country', defaultMessage: 'Til hvaða lands er verið að flytja?', description: 'What country are you moving to?', }, countryPlaceholder: { - id: 'dess.nps.application:primary.school.country.placeholder', + id: 'nps.application:primary.school.country.placeholder', defaultMessage: 'Veldu land', description: 'Select a country', }, // Siblings siblingsSubSectionTitle: { - id: 'dess.nps.application:primary.school.siblings.sub.section.title', + id: 'nps.application:primary.school.siblings.sub.section.title', defaultMessage: 'Systkini', description: 'Siblings', }, siblingsTitle: { - id: 'dess.nps.application:primary.school.siblings.title', + id: 'nps.application:primary.school.siblings.title', defaultMessage: 'Systkini í sama grunnskóla', description: 'Siblings in the same primary school', }, siblingsRegistrationTitle: { - id: 'dess.nps.application:primary.school.siblings.registration.title', + id: 'nps.application:primary.school.siblings.registration.title', defaultMessage: 'Skráning systkinis', description: 'Registration of a sibling', }, siblingsAddRelative: { - id: 'dess.nps.application:primary.school.siblings.add.relative', + id: 'nps.application:primary.school.siblings.add.relative', defaultMessage: 'Bæta við systkini', description: 'Add a sibling', }, siblingsRegisterRelative: { - id: 'dess.nps.application:primary.school.siblings.register.relative', + id: 'nps.application:primary.school.siblings.register.relative', defaultMessage: 'Skrá systkini', description: 'Register sibling', }, siblingsDeleteRelative: { - id: 'dess.nps.application:primary.school.siblings.delete.relative', + id: 'nps.application:primary.school.siblings.delete.relative', defaultMessage: 'Eyða systkini', description: 'Remove sibling', }, siblingsRelationSibling: { - id: 'dess.nps.application:primary.school.siblings.relation.sibling', + id: 'nps.application:primary.school.siblings.relation.sibling', defaultMessage: 'Alsystkini', description: 'Sibling', }, halfSiblingsRelationSibling: { - id: 'dess.nps.application:primary.school.siblings.relation.half.sibling', + id: 'nps.application:primary.school.siblings.relation.half.sibling', defaultMessage: 'Hálfsystkini', description: 'Half sibling', }, stepSiblingsRelationSibling: { - id: 'dess.nps.application:primary.school.siblings.relation.step.sibling', + id: 'nps.application:primary.school.siblings.relation.step.sibling', defaultMessage: 'Stjúpsystkini', description: 'Step sibling', }, // Apply to a new school newSchoolSubSectionTitle: { - id: 'dess.nps.application:primary.school.new.school.sub.section.title', + id: 'nps.application:primary.school.new.school.sub.section.title', defaultMessage: 'Nýr skóli', description: 'New school', }, // Starting school startingSchoolSubSectionTitle: { - id: 'dess.nps.application:primary.school.starting.school.sub.section.title', + id: 'nps.application:primary.school.starting.school.sub.section.title', defaultMessage: 'Byrjar í skóla', description: 'Starting school', }, startingSchoolTitle: { - id: 'dess.nps.application:primary.school.starting.school.title', + id: 'nps.application:primary.school.starting.school.title', defaultMessage: 'Hvenær óskar þú eftir að barnið byrji í nýjum skóla?', description: 'When do you wish the child to start in a new school?', }, startingSchoolDescription: { - id: 'dess.nps.application:primary.school.starting.school.description', + id: 'nps.application:primary.school.starting.school.description', defaultMessage: 'Við viljum taka vel á móti skólabarninu. Til þess að getað undirbúið komu þess þá biðjum við þig að velja dagsetningu fyrir fyrsta skóladaginn.', description: @@ -404,48 +404,48 @@ export const newPrimarySchoolMessages: MessageDir = { differentNeeds: defineMessages({ sectionTitle: { - id: 'dess.nps.application:different.needs.section.title', + id: 'nps.application:different.needs.section.title', defaultMessage: 'Ólíkar þarfir', description: 'Different needs', }, // Language languageSubSectionTitle: { - id: 'dess.nps.application:different.needs.language.sub.section.title', + id: 'nps.application:different.needs.language.sub.section.title', defaultMessage: 'Tungumál', description: 'Language', }, languageTitle: { - id: 'dess.nps.application:different.needs.language.title', + id: 'nps.application:different.needs.language.title', defaultMessage: 'Tungumál barnsins', description: "The child's languages", }, languageDescription: { - id: 'dess.nps.application:different.needs.language.description', + id: 'nps.application:different.needs.language.description', defaultMessage: 'Til að hægt sé að koma til móts við þarfir barnsins þarf skólinn að vita hvaða tungumál eru töluð í nærumhverfi þess. Veldu þau tungumál, eitt eða flerir sem töluð eru dagsdaglega á heimilinu í samskiptum við eða í kringum barn.', description: "In order to meet the child's needs, the school needs to know which languages ​​are spoken in their immediate environment. Choose the languages, one or more, that are spoken daily at home in communication with or around a child.", }, childNativeLanguage: { - id: 'dess.nps.application:different.needs.child.native.language', + id: 'nps.application:different.needs.child.native.language', defaultMessage: 'Hvert er móðurmál barnsins?', description: "What is the child's native language?", }, otherLanguagesSpokenDaily: { - id: 'dess.nps.application:different.needs.other.languages.spoken.daily', + id: 'nps.application:different.needs.other.languages.spoken.daily', defaultMessage: 'Eru önnur tungumál en móðurmál töluð daglega á heimili barnsins?', description: "Are languages ​​other than the native language spoken daily in the child's home?", }, languagePlaceholder: { - id: 'dess.nps.application:different.needs.language.placeholder', + id: 'nps.application:different.needs.language.placeholder', defaultMessage: 'Veldu tungumál', description: 'Choose a language', }, icelandicNotSpokenAroundChild: { - id: 'dess.nps.application:different.needs.icelandic.not.spoken.around.child', + id: 'nps.application:different.needs.icelandic.not.spoken.around.child', defaultMessage: 'Það er ekki töluð íslenska í nærumhverfi barnsins', description: "Icelandic is not spoken in the child's immediate environment", @@ -453,45 +453,45 @@ export const newPrimarySchoolMessages: MessageDir = { // Support supportSubSectionTitle: { - id: 'dess.nps.application:different.needs.support.sub.section.title', + id: 'nps.application:different.needs.support.sub.section.title', defaultMessage: 'Stuðningur', description: 'Support', }, support: { - id: 'dess.nps.application:different.needs.support', + id: 'nps.application:different.needs.support', defaultMessage: 'Stuðningur', description: 'Support', }, supportDescription: { - id: 'dess.nps.application:different.needs.support.description', + id: 'nps.application:different.needs.support.description', defaultMessage: 'Ef barnið þitt er með greiningu um fötlun, þroskafrávik, langvinn veikindi eða alvarlegan sjúkdóm sem gætu haft áhrif á skólagöngu þess, þá sér núverandi skóli barns um að miðla þeim upplýsingum áfram til þess skóla sem sótt er um í.', description: "If your child has a diagnosis of a disability, developmental disorder, chronic illness or serious illness that could affect their schooling, the child's current school will forward that information to the school they are applying to.", }, developmentalAssessment: { - id: 'dess.nps.application:different.needs.developmental.assessment', + id: 'nps.application:different.needs.developmental.assessment', defaultMessage: 'Hafa farið fram skimanir eða greiningar á þroska eða stöðu barnsins í núverandi skóla?', description: 'Have any assessments or diagnoses been conducted on the development or status of the child in the current school?', }, specialSupport: { - id: 'dess.nps.application:different.needs.special.support', + id: 'nps.application:different.needs.special.support', defaultMessage: 'Hefur nemandinn áður notið sérkennslu eða stuðnings í skóla?', description: 'Has the student previously received special education or support in school?', }, requestMeeting: { - id: 'dess.nps.application:different.needs.request.meeting', + id: 'nps.application:different.needs.request.meeting', defaultMessage: 'Ef þú telur að grunnskólinn þurfi nánari upplýsingar um þarfir barnsins getur þú óskað eftir samtali. Skólinn mun setja sig í samband við þig, þegar nær dregur fyrsta skóladegi', description: "If you believe that the elementary school needs more information about the child's needs, you can request a meeting. The school will contact you when the first day of school approaches.", }, requestMeetingDescription: { - id: 'dess.nps.application:different.needs.request.meeting.info', + id: 'nps.application:different.needs.request.meeting.info', defaultMessage: 'Óska eftir samtali við skóla', description: 'Request meeting with the school', }, @@ -499,68 +499,68 @@ export const newPrimarySchoolMessages: MessageDir = { overview: defineMessages({ sectionTitle: { - id: 'dess.nps.application:overview.section.title', + id: 'nps.application:overview.section.title', defaultMessage: 'Yfirlit', description: 'Overview', }, overviewDescription: { - id: 'dess.nps.application:overview.description', + id: 'nps.application:overview.description', defaultMessage: 'Vinsamlegast farðu yfir umsóknina áður en þú sendir hana inn.', description: 'Please review the application before submitting.', }, child: { - id: 'dess.nps.application:overview.child', + id: 'nps.application:overview.child', defaultMessage: 'Barn', description: 'Child', }, submitButton: { - id: 'dess.nps.application:overview.submit.button', + id: 'nps.application:overview.submit.button', defaultMessage: 'Senda inn umsókn', description: 'Submit application', }, editButton: { - id: 'dess.nps.application:overview.edit.button', + id: 'nps.application:overview.edit.button', defaultMessage: 'Breyta umsókn', description: 'Edit application', }, parents: { - id: 'dess.nps.application:overview.parents', + id: 'nps.application:overview.parents', defaultMessage: 'Foreldri/forsjáraðili', description: 'Parent / guardian', }, nativeLanguage: { - id: 'dess.nps.application:overview.native.language', + id: 'nps.application:overview.native.language', defaultMessage: 'Móðurmál barnsins', description: "The child's native language", }, icelandicSpokenAroundChild: { - id: 'dess.nps.application:overview.icelandic.spoken.around.child', + id: 'nps.application:overview.icelandic.spoken.around.child', defaultMessage: 'Íslenska er töluð í nærumhverfi barnsins', description: "Icelandic is spoken in the child's immediate environment", }, schoolTitle: { - id: 'dess.nps.application:overview.school.title', + id: 'nps.application:overview.school.title', defaultMessage: 'Upplýsingar um skóla', description: 'Information about school', }, currentSchool: { - id: 'dess.nps.application:overview.current.school', + id: 'nps.application:overview.current.school', defaultMessage: 'Núverandi skóli', description: 'Current school', }, selectedSchool: { - id: 'dess.nps.application:overview.selected.school', + id: 'nps.application:overview.selected.school', defaultMessage: 'Valinn skóli', description: 'Selected school', }, grade: { - id: 'dess.nps.application:overview.grade', + id: 'nps.application:overview.grade', defaultMessage: 'Bekkur', description: 'Grade', }, currentGrade: { - id: 'dess.nps.application:overview.current.grade', + id: 'nps.application:overview.current.grade', defaultMessage: '{grade}. bekkur', description: '{grade} grade', }, @@ -568,12 +568,12 @@ export const newPrimarySchoolMessages: MessageDir = { conclusion: defineMessages({ sectionTitle: { - id: 'dess.nps.application:conclusion.section.title', + id: 'nps.application:conclusion.section.title', defaultMessage: 'Staðfesting', description: 'Confirmation', }, expandableDescription: { - id: 'dess.nps.application:conclusion.expandable.description#markdown', + id: 'nps.application:conclusion.expandable.description#markdown', defaultMessage: 'Sveitarfélagið og skólinn munu taka afstöðu til umsóknarinnar og svara þér eins fljótt og auðið er.\n\nÁður en afstaða er tekin kann að vera þörf á því að afla frekari gagna í þeim tilgangi að upplýsa betur um aðstæður barns og/eða forsjáraðila. Mun þá sveitarfélagið eða skólinn setja sig í samband við þig.\n\nEf umsókn um nýjan skóla er samþykkt, verður forsjáraðili og barn boðað til móttökuviðtals.', description: @@ -584,7 +584,7 @@ export const newPrimarySchoolMessages: MessageDir = { export const inReviewFormMessages = defineMessages({ formTitle: { - id: 'dess.nps.application:inReview.form.title', + id: 'nps.application:inReview.form.title', defaultMessage: 'Umsókn í nýjan grunnskóla', description: 'Application for a new primary school', }, @@ -592,17 +592,17 @@ export const inReviewFormMessages = defineMessages({ export const statesMessages = defineMessages({ draftDescription: { - id: 'dess.nps.application:draft.description', + id: 'nps.application:draft.description', defaultMessage: 'Þú hefur útbúið drög að umsókn.', description: 'You have create a draft application.', }, applicationSent: { - id: 'dess.nps.application:application.sent.title', + id: 'nps.application:application.sent.title', defaultMessage: 'Umsókn hefur verið send.', description: 'The application has been sent', }, applicationSentDescription: { - id: 'dess.nps.application:application.sent.description', + id: 'nps.application:application.sent.description', defaultMessage: 'Hægt er að breyta umsókn', description: 'It is possible to edit the application', }, @@ -610,38 +610,38 @@ export const statesMessages = defineMessages({ export const errorMessages = defineMessages({ phoneNumber: { - id: 'dess.nps.application:error.phone.number', + id: 'nps.application:error.phone.number', defaultMessage: 'Símanúmerið þarf að vera gilt.', description: 'The phone number must be valid.', }, nationalId: { - id: 'dess.nps.application:error.national.id', + id: 'nps.application:error.national.id', defaultMessage: 'Kennitala þarf að vera gild.', description: 'Error message when the kennitala is invalid.', }, relativesRequired: { - id: 'dess.nps.application:error.relatives.required', + id: 'nps.application:error.relatives.required', defaultMessage: 'Nauðsynlegt er að bæta við a.m.k einum aðstandenda', description: 'You must add at least one relative', }, siblingsRequired: { - id: 'dess.nps.application:error.siblings.required', + id: 'nps.application:error.siblings.required', defaultMessage: 'Nauðsynlegt er að bæta við a.m.k einu systkini', description: 'You must add at least one sibling', }, languagesRequired: { - id: 'dess.nps.application:error.languages.required', + id: 'nps.application:error.languages.required', defaultMessage: 'Það þarf að velja a.m.k eitt tungumál', description: 'At least one language must be selected', }, noChildrenFoundTitle: { - id: 'dess.nps.application:error.no.children.found.title', + id: 'nps.application:error.no.children.found.title', defaultMessage: 'Því miður ert þú ekki með skráð barn á grunnskólaaldri', description: 'Unfortunately, you do not have a child registered at primary school age', }, noChildrenFoundMessage: { - id: 'dess.nps.application:error.no.children.found.message#markdown', + id: 'nps.application:error.no.children.found.message#markdown', defaultMessage: 'Eingöngu sá sem er með lögheimilisforsjá hefur heimild til að sækja um fyrir barn. \n\nÞjóðskrá skráir hver eða hverjir teljast foreldrar barns og hver fari með forsjárskyldur þess. Upplýsingar um skráningu forsjár og lögheimilisforeldris má nálgast hér: [Foreldrar og forsjá | Þjóðskrá (skra.is)](https://www.skra.is/folk/skraning-barns/foreldrar-og-forsja/)\n\nUpplýsingum um tengsl á milli barna og foreldra auk forsjáraðila eru einnig aðgengilegar á [Mínum síðum á Ísland.is](https://island.is/minarsidur)', description: diff --git a/libs/application/templates/parental-leave/src/fields/Duration/Duration.tsx b/libs/application/templates/parental-leave/src/fields/Duration/Duration.tsx index f0f558c2b258..43ad11be12ce 100644 --- a/libs/application/templates/parental-leave/src/fields/Duration/Duration.tsx +++ b/libs/application/templates/parental-leave/src/fields/Duration/Duration.tsx @@ -80,8 +80,9 @@ export const Duration: FC> = ({ useEffect(() => { if (chosenEndDate) { setValue(`periods[${currentIndex}].endDate`, chosenEndDate) + setValue(`periods[${currentIndex}].months`, chosenDuration) } - }, [chosenEndDate, setValue, currentIndex]) + }, [chosenEndDate, setValue, currentIndex, chosenDuration]) const handleChange = async (months: number) => { clearErrors([id, 'component']) diff --git a/libs/application/templates/parental-leave/src/lib/directorateOfLabour.utils.ts b/libs/application/templates/parental-leave/src/lib/directorateOfLabour.utils.ts index 90d35e6265b9..5d85ff7875c8 100644 --- a/libs/application/templates/parental-leave/src/lib/directorateOfLabour.utils.ts +++ b/libs/application/templates/parental-leave/src/lib/directorateOfLabour.utils.ts @@ -154,7 +154,11 @@ export const calculatePeriodLength = ( start: Date, end: Date, percentage = 1, + months?: number, ) => { + if (months) { + return months * 30 + } if (end < start) { throw errorMessages.periodsEndDateBeforeStartDate } diff --git a/libs/application/templates/parental-leave/src/lib/parentalLeaveUtils.ts b/libs/application/templates/parental-leave/src/lib/parentalLeaveUtils.ts index a1c9b24c60c5..416e12901e3d 100644 --- a/libs/application/templates/parental-leave/src/lib/parentalLeaveUtils.ts +++ b/libs/application/templates/parental-leave/src/lib/parentalLeaveUtils.ts @@ -254,6 +254,30 @@ export const getTransferredDays = ( return days } +export const getPersonalDays = (application: Application) => { + const selectedChild = getSelectedChild( + application.answers, + application.externalData, + ) + if (!selectedChild) { + throw new Error('Missing selected child') + } + const maximumDaysToSpend = getAvailableRightsInDays(application) + const maximumMultipleBirthsDaysToSpend = getMultipleBirthsDays(application) + const maximumAdditionalSingleParentDaysToSpend = + getAdditionalSingleParentRightsInDays(application) + const transferredDays = getTransferredDays(application, selectedChild) + const personalDays = + maximumDaysToSpend - + maximumAdditionalSingleParentDaysToSpend - + maximumMultipleBirthsDaysToSpend - + Math.max(transferredDays, 0) + return personalDays +} + +export const getPersonalDaysInMonths = (application: Application) => + daysToMonths(getPersonalDays(application)).toFixed(1) + export const getMultipleBirthsDays = (application: Application) => { const selectedChild = getSelectedChild( application.answers, @@ -271,6 +295,9 @@ export const getMultipleBirthsDays = (application: Application) => { return getMultipleBirthRequestDays(application.answers) } +export const getMultipleBirthsDaysInMonths = (application: Application) => + daysToMonths(getMultipleBirthsDays(application)).toFixed(1) + export const getMultipleBirthRequestDays = ( answers: Application['answers'], ) => { @@ -388,6 +415,10 @@ export const getAvailablePersonalRightsSingleParentInMonths = ( getAdditionalSingleParentRightsInDays(application), ) +export const getAdditionalSingleParentRightsInMonths = ( + application: Application, +) => daysToMonths(getAdditionalSingleParentRightsInDays(application)).toFixed(1) + export const getAvailablePersonalRightsInMonths = (application: Application) => daysToMonths(getAvailablePersonalRightsInDays(application)) @@ -397,6 +428,11 @@ export const getAvailablePersonalRightsInMonths = (application: Application) => export const getAvailableRightsInMonths = (application: Application) => daysToMonths(getAvailableRightsInDays(application)) +export const getTransferredDaysInMonths = ( + application: Application, + selectedChild: ChildInformation, +) => daysToMonths(getTransferredDays(application, selectedChild)).toFixed(1) + export const getSpouse = ( application: Application, ): PersonInformation['spouse'] | null => { @@ -1355,13 +1391,21 @@ export const getMinimumStartDate = (application: Application): Date => { return today } +export const clamp = (value: number, min: number, max: number): number => + Math.min(max, Math.max(min, value)) + export const calculateDaysUsedByPeriods = (periods: Period[]) => Math.round( periods.reduce((total, period) => { const start = parseISO(period.startDate) const end = parseISO(period.endDate) const percentage = Number(period.ratio) / 100 - const periodLength = calculatePeriodLength(start, end) + const periodLength = calculatePeriodLength( + start, + end, + undefined, + period.months, + ) const calculatedLength = period.daysToUse ? Number(period.daysToUse) @@ -1386,7 +1430,7 @@ export const calculateEndDateForPeriodWithStartAndLength = ( const daysInMonth = getDaysInMonth(lastMonthBeforeEndDate) // If startDay is first day of the month and daysToAdd = 0 - if (daysToAdd === 0 && start.getDate() === 1) { + if (daysToAdd === 0) { return endDate } diff --git a/libs/application/templates/parental-leave/src/types.ts b/libs/application/templates/parental-leave/src/types.ts index f649525949fa..51f0bd006c79 100644 --- a/libs/application/templates/parental-leave/src/types.ts +++ b/libs/application/templates/parental-leave/src/types.ts @@ -80,6 +80,7 @@ export interface Period { rightCodePeriod?: string paid?: boolean approved?: boolean + months?: number } export interface Payment { diff --git a/libs/application/templates/passport/README.md b/libs/application/templates/passport/README.md index 329270c0d408..52739d151797 100644 --- a/libs/application/templates/passport/README.md +++ b/libs/application/templates/passport/README.md @@ -5,3 +5,17 @@ This library was generated with [Nx](https://nx.dev). ## Running unit tests Run `ng test application-templates-passport` to execute the unit tests via [Jest](https://jestjs.io). + +## Environment Setup + +To properly run the application and go through the application process step by step, make sure to set the following environment variables: + +- `IDENTITY_SERVER_CLIENT_ID` +- `IDENTITY_SERVER_CLIENT_SECRET` + +## Test Users + +You can use the following test users during the application process: + +- **To apply for self**: 0101302989 +- **To apply for child**: 5555555559 diff --git a/libs/application/types/src/lib/ApplicationTypes.ts b/libs/application/types/src/lib/ApplicationTypes.ts index d2c45faf5978..2cae65fc3431 100644 --- a/libs/application/types/src/lib/ApplicationTypes.ts +++ b/libs/application/types/src/lib/ApplicationTypes.ts @@ -385,7 +385,7 @@ export const ApplicationConfigurations = { }, [ApplicationTypes.NEW_PRIMARY_SCHOOL]: { slug: 'nyr-grunnskoli', - translation: ['dess.nps.application', 'uiForms.application'], + translation: ['nps.application', 'uiForms.application'], }, [ApplicationTypes.MACHINE_REGISTRATION]: { slug: 'nyskraning-taekis', diff --git a/libs/auth-api-lib/src/index.ts b/libs/auth-api-lib/src/index.ts index b9305d456101..28b1b0007819 100644 --- a/libs/auth-api-lib/src/index.ts +++ b/libs/auth-api-lib/src/index.ts @@ -47,6 +47,7 @@ export * from './lib/delegations/dto/delegation-index.dto' export * from './lib/delegations/dto/paginated-delegation-provider.dto' export * from './lib/delegations/dto/delegation-provider.dto' export * from './lib/delegations/dto/merged-delegation.dto' +export * from './lib/delegations/dto/zendesk-webhook-input.dto' export * from './lib/delegations/models/delegation.model' export * from './lib/delegations/models/delegation.model' export * from './lib/delegations/models/delegation-scope.model' diff --git a/libs/auth-api-lib/src/lib/delegations/admin/delegation-admin-custom.service.ts b/libs/auth-api-lib/src/lib/delegations/admin/delegation-admin-custom.service.ts index 99f41074d6e3..b8ce95211bee 100644 --- a/libs/auth-api-lib/src/lib/delegations/admin/delegation-admin-custom.service.ts +++ b/libs/auth-api-lib/src/lib/delegations/admin/delegation-admin-custom.service.ts @@ -46,9 +46,11 @@ export class DelegationAdminCustomService { private sequelize: Sequelize, ) {} - private getNationalIdsFromZendeskTicket(ticket: Ticket): { + private getZendeskCustomFields(ticket: Ticket): { fromReferenceId: string toReferenceId: string + validTo: string | null + createdByNationalId: string | null } { const fromReferenceId = ticket.custom_fields.find( (field) => field.id === ZENDESK_CUSTOM_FIELDS.DelegationFromReferenceId, @@ -56,6 +58,12 @@ export class DelegationAdminCustomService { const toReferenceId = ticket.custom_fields.find( (field) => field.id === ZENDESK_CUSTOM_FIELDS.DelegationToReferenceId, ) + const validTo = ticket.custom_fields.find( + (field) => field.id === ZENDESK_CUSTOM_FIELDS.DelegationValidToId, + ) + const createdById = ticket.custom_fields.find( + (field) => field.id === ZENDESK_CUSTOM_FIELDS.DelegationCreatedById, + ) if (!fromReferenceId || !toReferenceId) { throw new BadRequestException({ @@ -67,6 +75,8 @@ export class DelegationAdminCustomService { return { fromReferenceId: fromReferenceId.value, toReferenceId: toReferenceId.value, + createdByNationalId: createdById?.value ?? null, + validTo: validTo?.value ?? null, } } @@ -143,72 +153,67 @@ export class DelegationAdminCustomService { } } - async createDelegation( - user: User, - delegation: CreatePaperDelegationDto, - ): Promise { - this.validatePersonsNationalIds( - delegation.toNationalId, - delegation.fromNationalId, - ) + async createDelegationByZendeskId(zendeskId: string): Promise { + const zendeskCase = await this.zendeskService.getTicket(zendeskId) - const zendeskCase = await this.zendeskService.getTicket( - delegation.referenceId, - ) + const { + fromReferenceId: fromNationalId, + toReferenceId: toNationalId, + validTo, + createdByNationalId, + } = this.getZendeskCustomFields(zendeskCase) - if (!zendeskCase.tags.includes(DELEGATION_TAG)) { + if (!createdByNationalId) { throw new BadRequestException({ - message: 'Zendesk case is missing required tag', - error: ErrorCodes.ZENDESK_TAG_MISSING, + message: 'Zendesk ticket is missing created by national id', + error: ErrorCodes.ZENDESK_CUSTOM_FIELDS_MISSING, }) } - if (zendeskCase.status !== TicketStatus.Solved) { + if (!kennitala.isPerson(createdByNationalId)) { throw new BadRequestException({ - message: 'Zendesk case is not solved', - error: ErrorCodes.ZENDESK_STATUS, + message: 'Created by National Id is not valid person national id', + error: ErrorCodes.INPUT_VALIDATION_INVALID_PERSON, }) } - const { fromReferenceId, toReferenceId } = - this.getNationalIdsFromZendeskTicket(zendeskCase) + this.validatePersonsNationalIds(toNationalId, fromNationalId) - if ( - fromReferenceId !== delegation.fromNationalId || - toReferenceId !== delegation.toNationalId - ) { - throw new BadRequestException({ - message: 'National Ids do not match the Zendesk ticket', - error: ErrorCodes.ZENDESK_NATIONAL_IDS_MISMATCH, - }) - } + this.verifyTicketCompletion(zendeskCase) - const [fromDisplayName, toName] = await Promise.all([ - this.namesService.getPersonName(delegation.fromNationalId), - this.namesService.getPersonName(delegation.toNationalId), - ]) + await this.insertDelegation({ + fromNationalId, + toNationalId, + referenceId: zendeskId, + validTo: this.formatZendeskDate(validTo), + createdBy: createdByNationalId, + }) + } - const newDelegation = await this.delegationModel.create( - { - id: uuid(), - toNationalId: delegation.toNationalId, - fromNationalId: delegation.fromNationalId, - createdByNationalId: user.actor?.nationalId ?? user.nationalId, - referenceId: delegation.referenceId, - toName, - fromDisplayName, - delegationDelegationTypes: [ - { - delegationTypeId: AuthDelegationType.GeneralMandate, - validTo: delegation.validTo, - }, - ] as DelegationDelegationType[], - }, - { - include: [this.delegationDelegationTypeModel], - }, + async createDelegation( + user: User, + delegation: CreatePaperDelegationDto, + ): Promise { + this.validatePersonsNationalIds( + delegation.toNationalId, + delegation.fromNationalId, + ) + + const zendeskCase = await this.zendeskService.getTicket( + delegation.referenceId, + ) + + this.verifyZendeskTicket( + zendeskCase, + delegation.fromNationalId, + delegation.toNationalId, ) + const newDelegation = await this.insertDelegation({ + ...delegation, + createdBy: user.actor?.nationalId ?? user.nationalId, + }) + // Index delegations for the toNationalId void this.indexDelegations(delegation.toNationalId, user) @@ -285,4 +290,90 @@ export class DelegationAdminCustomService { auth, ) } + + private async insertDelegation( + delegation: CreatePaperDelegationDto & { + createdBy: string + }, + ): Promise { + const [fromDisplayName, toName] = await Promise.all([ + this.namesService.getPersonName(delegation.fromNationalId), + this.namesService.getPersonName(delegation.toNationalId), + ]) + + return this.sequelize.transaction(async (transaction) => { + return this.delegationModel.create( + { + id: uuid(), + toNationalId: delegation.toNationalId, + fromNationalId: delegation.fromNationalId, + createdByNationalId: delegation.createdBy, + referenceId: delegation.referenceId, + toName, + fromDisplayName, + delegationDelegationTypes: [ + { + delegationTypeId: AuthDelegationType.GeneralMandate, + validTo: delegation.validTo ? new Date(delegation.validTo) : null, + }, + ] as DelegationDelegationType[], + }, + { + transaction, + include: [this.delegationDelegationTypeModel], + }, + ) + }) + } + + private verifyTicketCompletion(ticket: Ticket) { + if (!ticket.tags.includes(DELEGATION_TAG)) { + throw new BadRequestException({ + message: 'Zendesk case is missing required tag', + error: ErrorCodes.ZENDESK_TAG_MISSING, + }) + } + + if (ticket.status !== TicketStatus.Solved) { + throw new BadRequestException({ + message: 'Zendesk case is not solved', + error: ErrorCodes.ZENDESK_STATUS, + }) + } + } + + private verifyZendeskTicket( + ticket: Ticket, + fromNationalId: string, + toNationalId: string, + ) { + this.verifyTicketCompletion(ticket) + + const { fromReferenceId, toReferenceId } = + this.getZendeskCustomFields(ticket) + + if (fromReferenceId !== fromNationalId || toReferenceId !== toNationalId) { + throw new BadRequestException({ + message: 'National Ids do not match the Zendesk ticket', + error: ErrorCodes.ZENDESK_NATIONAL_IDS_MISMATCH, + }) + } + } + + private formatZendeskDate(date: string | null): Date | null { + if (!date) { + return null + } + + const [day, month, year] = date.split('.').map(Number) + + if (!day || !month || !year || isNaN(day) || isNaN(month) || isNaN(year)) { + throw new BadRequestException({ + message: 'Invalid date format in Zendesk ticket', + error: ErrorCodes.INVALID_DATE_FORMAT, + }) + } + + return new Date(year, month - 1, day) + } } diff --git a/libs/auth-api-lib/src/lib/delegations/constants/zendesk.ts b/libs/auth-api-lib/src/lib/delegations/constants/zendesk.ts index 75b1df95fc5c..b6d26abce195 100644 --- a/libs/auth-api-lib/src/lib/delegations/constants/zendesk.ts +++ b/libs/auth-api-lib/src/lib/delegations/constants/zendesk.ts @@ -2,4 +2,6 @@ export const DELEGATION_TAG = 'umsokn_um_umboð_a_mínum_síðum' export const ZENDESK_CUSTOM_FIELDS = { DelegationToReferenceId: 21401464004498, DelegationFromReferenceId: 21401435545234, + DelegationValidToId: 21683921674002, + DelegationCreatedById: 21718016990866, } diff --git a/libs/auth-api-lib/src/lib/delegations/dto/zendesk-webhook-input.dto.ts b/libs/auth-api-lib/src/lib/delegations/dto/zendesk-webhook-input.dto.ts new file mode 100644 index 000000000000..31d71c17ccad --- /dev/null +++ b/libs/auth-api-lib/src/lib/delegations/dto/zendesk-webhook-input.dto.ts @@ -0,0 +1,8 @@ +import { IsString } from 'class-validator' +import { ApiProperty } from '@nestjs/swagger' + +export class ZendeskWebhookInputDto { + @IsString() + @ApiProperty() + id!: string +} diff --git a/libs/auth-nest-tools/src/index.ts b/libs/auth-nest-tools/src/index.ts index 6980553c9229..613be5a55f12 100644 --- a/libs/auth-nest-tools/src/index.ts +++ b/libs/auth-nest-tools/src/index.ts @@ -13,5 +13,6 @@ export * from './lib/auth' export * from './lib/auth.middleware' export * from './lib/bypass-auth.decorator' export * from './lib/authHeader.middleware' +export * from './lib/zendeskAuth.guard' export type { GraphQLContext } from './lib/graphql.context' export { getRequest } from './lib/getRequest' diff --git a/libs/auth-nest-tools/src/lib/rawBodyRequest.type.ts b/libs/auth-nest-tools/src/lib/rawBodyRequest.type.ts new file mode 100644 index 000000000000..d664687c217d --- /dev/null +++ b/libs/auth-nest-tools/src/lib/rawBodyRequest.type.ts @@ -0,0 +1,5 @@ +import { Request } from 'express' + +export interface RawBodyRequest extends Request { + rawBody: Buffer +} diff --git a/libs/auth-nest-tools/src/lib/zendeskAuth.guard.ts b/libs/auth-nest-tools/src/lib/zendeskAuth.guard.ts new file mode 100644 index 000000000000..2291e24db0c9 --- /dev/null +++ b/libs/auth-nest-tools/src/lib/zendeskAuth.guard.ts @@ -0,0 +1,41 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common' +import * as crypto from 'crypto' + +import { RawBodyRequest } from './rawBodyRequest.type' + +const SIGNING_SECRET_ALGORITHM = 'sha256' + +@Injectable() +export class ZendeskAuthGuard implements CanActivate { + constructor(private secret: string | undefined) { + if (!secret) { + throw new Error('ZendeskAuthGuard: secret is required') + } + } + + canActivate(context: ExecutionContext): boolean { + const request = context.switchToHttp().getRequest() + + const signature = request.headers['x-zendesk-webhook-signature'] as string + const timestamp = request.headers[ + 'x-zendesk-webhook-signature-timestamp' + ] as string + const body = request.rawBody?.toString() ?? '' + + return this.isValidSignature(signature, body, timestamp) + } + + isValidSignature( + signature: string, + body: string, + timestamp: string, + ): boolean { + const hmac = crypto.createHmac( + SIGNING_SECRET_ALGORITHM, + this.secret as string, + ) + const sig = hmac.update(timestamp + body).digest('base64') + + return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(sig)) + } +} diff --git a/libs/clients/driving-license/src/lib/drivingLicenseApi.service.ts b/libs/clients/driving-license/src/lib/drivingLicenseApi.service.ts index b503dff8fef7..0f2d17f8df81 100644 --- a/libs/clients/driving-license/src/lib/drivingLicenseApi.service.ts +++ b/libs/clients/driving-license/src/lib/drivingLicenseApi.service.ts @@ -372,6 +372,23 @@ export class DrivingLicenseApi { } } + public async getCanApplyForRenewal65(params: { + token: string + }): Promise> { + const response = await this.v5.apiDrivinglicenseV5CanapplyforRenewal65Get({ + apiVersion: v5.DRIVING_LICENSE_API_VERSION_V5, + apiVersion2: v5.DRIVING_LICENSE_API_VERSION_V5, + jwttoken: params.token, + }) + + return { + result: !!response.result, + errorCode: response.errorCode + ? (response.errorCode as CanApplyErrorCodeBFull) + : undefined, + } + } + public async getCanApplyForCategoryTemporary(params: { token: string }): Promise> { diff --git a/libs/clients/driving-license/src/v5/clientConfig.json b/libs/clients/driving-license/src/v5/clientConfig.json index eb4a547bab5f..3bd7edd48d20 100644 --- a/libs/clients/driving-license/src/v5/clientConfig.json +++ b/libs/clients/driving-license/src/v5/clientConfig.json @@ -2,11 +2,8 @@ "openapi": "3.0.1", "info": { "title": "RLS driver licence Rest API", - "description": "RESTFul services for driver licences. Programmed in ASP.NET Core 3 API ", - "contact": { - "name": "TMD", - "email": "tmd@tmd.is" - }, + "description": "RESTFul services for driver licences. Programmed in .NET 8", + "contact": { "name": "TMD", "email": "tmd@tmd.is" }, "version": "5.0" }, "paths": { @@ -19,29 +16,21 @@ "name": "jwttoken", "in": "header", "description": "JWT token", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { @@ -56,7 +45,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -79,19 +68,13 @@ "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -108,34 +91,26 @@ "in": "path", "description": "RLS application id", "required": true, - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -158,19 +133,13 @@ "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -187,48 +156,36 @@ "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/TegSviptingaDto" - } + "items": { "$ref": "#/components/schemas/TegSviptingaDto" } } }, "application/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/TegSviptingaDto" - } + "items": { "$ref": "#/components/schemas/TegSviptingaDto" } } }, "text/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/TegSviptingaDto" - } + "items": { "$ref": "#/components/schemas/TegSviptingaDto" } } } } @@ -246,48 +203,36 @@ "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/TegundRettindaDto" - } + "items": { "$ref": "#/components/schemas/TegundRettindaDto" } } }, "application/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/TegundRettindaDto" - } + "items": { "$ref": "#/components/schemas/TegundRettindaDto" } } }, "text/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/TegundRettindaDto" - } + "items": { "$ref": "#/components/schemas/TegundRettindaDto" } } } } @@ -305,25 +250,19 @@ "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -364,48 +303,36 @@ "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/EmbaettiDto" - } + "items": { "$ref": "#/components/schemas/EmbaettiDto" } } }, "application/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/EmbaettiDto" - } + "items": { "$ref": "#/components/schemas/EmbaettiDto" } } }, "text/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/EmbaettiDto" - } + "items": { "$ref": "#/components/schemas/EmbaettiDto" } } } } @@ -422,52 +349,35 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "text/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } } } } @@ -483,52 +393,35 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "text/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } } } } @@ -544,58 +437,37 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "string", - "format": "byte" - } + "schema": { "type": "string", "format": "byte" } }, "application/json": { - "schema": { - "type": "string", - "format": "byte" - } + "schema": { "type": "string", "format": "byte" } }, - "text/json": { - "schema": { - "type": "string", - "format": "byte" - } - } + "text/json": { "schema": { "type": "string", "format": "byte" } } } }, - "204": { - "description": "No Content" - } + "204": { "description": "No Content" } } } }, @@ -608,58 +480,37 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "string", - "format": "byte" - } + "schema": { "type": "string", "format": "byte" } }, "application/json": { - "schema": { - "type": "string", - "format": "byte" - } + "schema": { "type": "string", "format": "byte" } }, - "text/json": { - "schema": { - "type": "string", - "format": "byte" - } - } + "text/json": { "schema": { "type": "string", "format": "byte" } } } }, - "204": { - "description": "No Content" - } + "204": { "description": "No Content" } } } }, @@ -673,34 +524,26 @@ "in": "path", "description": "Person social serial number", "required": true, - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -732,49 +575,35 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/DriverLicenseDto" - } + "schema": { "$ref": "#/components/schemas/DriverLicenseDto" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/DriverLicenseDto" - } + "schema": { "$ref": "#/components/schemas/DriverLicenseDto" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/DriverLicenseDto" - } + "schema": { "$ref": "#/components/schemas/DriverLicenseDto" } } } }, @@ -782,19 +611,13 @@ "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -810,52 +633,36 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { - "204": { - "description": "No Content" - }, + "204": { "description": "No Content" }, "400": { "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -872,42 +679,32 @@ "in": "path", "description": "Category to check for (B, C, and so on)", "required": true, - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -939,62 +736,43 @@ "in": "path", "description": "Category in license", "required": true, - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { "description": "Model of application", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/PostNewFinalLicense" - } + "schema": { "$ref": "#/components/schemas/PostNewFinalLicense" } } } }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "text/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } } } }, @@ -1002,19 +780,13 @@ "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -1030,38 +802,28 @@ "name": "jwttoken", "in": "header", "description": "JWT token", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { "description": "Model of application", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/PostNewCollaborative" - } + "schema": { "$ref": "#/components/schemas/PostNewCollaborative" } } } }, @@ -1070,22 +832,13 @@ "description": "Created", "content": { "text/plain": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "text/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } } } }, @@ -1093,19 +846,13 @@ "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -1121,49 +868,35 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/DeprivationDto" - } + "schema": { "$ref": "#/components/schemas/DeprivationDto" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/DeprivationDto" - } + "schema": { "$ref": "#/components/schemas/DeprivationDto" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/DeprivationDto" - } + "schema": { "$ref": "#/components/schemas/DeprivationDto" } } } } @@ -1180,29 +913,21 @@ "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { "description": "Model of application", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/PostTemporaryLicense" - } + "schema": { "$ref": "#/components/schemas/PostTemporaryLicense" } } } }, @@ -1259,29 +984,21 @@ "name": "jwttoken", "in": "header", "description": "JWT token", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { @@ -1349,20 +1066,14 @@ "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { @@ -1377,7 +1088,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -1428,49 +1139,35 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/PenaltyPointsDto" - } + "schema": { "$ref": "#/components/schemas/PenaltyPointsDto" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/PenaltyPointsDto" - } + "schema": { "$ref": "#/components/schemas/PenaltyPointsDto" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/PenaltyPointsDto" - } + "schema": { "$ref": "#/components/schemas/PenaltyPointsDto" } } } } @@ -1485,34 +1182,26 @@ { "name": "jwttoken", "in": "header", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -1544,19 +1233,13 @@ "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -1572,52 +1255,35 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "text/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } } } } @@ -1634,34 +1300,26 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -1692,52 +1350,35 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "text/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } } } } @@ -1753,52 +1394,35 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, "text/json": { - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } } } } @@ -1814,58 +1438,37 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "string", - "format": "byte" - } + "schema": { "type": "string", "format": "byte" } }, "application/json": { - "schema": { - "type": "string", - "format": "byte" - } + "schema": { "type": "string", "format": "byte" } }, - "text/json": { - "schema": { - "type": "string", - "format": "byte" - } - } + "text/json": { "schema": { "type": "string", "format": "byte" } } } }, - "204": { - "description": "No Content" - } + "204": { "description": "No Content" } } } }, @@ -1878,58 +1481,37 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "type": "string", - "format": "byte" - } + "schema": { "type": "string", "format": "byte" } }, "application/json": { - "schema": { - "type": "string", - "format": "byte" - } + "schema": { "type": "string", "format": "byte" } }, - "text/json": { - "schema": { - "type": "string", - "format": "byte" - } - } + "text/json": { "schema": { "type": "string", "format": "byte" } } } }, - "204": { - "description": "No Content" - } + "204": { "description": "No Content" } } } }, @@ -1941,34 +1523,26 @@ { "name": "jwttoken", "in": "header", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -2000,20 +1574,14 @@ "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { @@ -2027,26 +1595,18 @@ } }, "responses": { - "204": { - "description": "No Content" - }, + "204": { "description": "No Content" }, "400": { "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -2060,34 +1620,26 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -2110,19 +1662,13 @@ "description": "Not Found", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -2139,48 +1685,36 @@ "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/InstructorDto" - } + "items": { "$ref": "#/components/schemas/InstructorDto" } } }, "application/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/InstructorDto" - } + "items": { "$ref": "#/components/schemas/InstructorDto" } } }, "text/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/InstructorDto" - } + "items": { "$ref": "#/components/schemas/InstructorDto" } } } } @@ -2199,49 +1733,35 @@ "in": "path", "description": "Driver license number", "required": true, - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/LicenseStatusDto" - } + "schema": { "$ref": "#/components/schemas/LicenseStatusDto" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/LicenseStatusDto" - } + "schema": { "$ref": "#/components/schemas/LicenseStatusDto" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/LicenseStatusDto" - } + "schema": { "$ref": "#/components/schemas/LicenseStatusDto" } } } } @@ -2258,67 +1778,48 @@ "in": "path", "description": "Category to check, optional, default B", "required": true, - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "drivinglicenseid", "in": "path", "description": "License id, optional, default persons newest license", "required": true, - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, { "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/CategoryValidDto" - } + "schema": { "$ref": "#/components/schemas/CategoryValidDto" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/CategoryValidDto" - } + "schema": { "$ref": "#/components/schemas/CategoryValidDto" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/CategoryValidDto" - } + "schema": { "$ref": "#/components/schemas/CategoryValidDto" } } } } @@ -2335,42 +1836,32 @@ "in": "path", "description": "License category", "required": true, - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -2401,59 +1892,43 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { "description": "Model with information about the practice permit", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/PostPracticePermit" - } + "schema": { "$ref": "#/components/schemas/PostPracticePermit" } } } }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/PracticePermitDto" - } + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/PracticePermitDto" - } + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/PracticePermitDto" - } + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } } } }, @@ -2461,19 +1936,13 @@ "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/PracticePermitDto" - } + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/PracticePermitDto" - } + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/PracticePermitDto" - } + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } } } } @@ -2489,78 +1958,106 @@ "name": "jwttoken", "in": "header", "description": "Token with national id", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { "description": "Model with information about the practice permit", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/PostPracticePermit" - } + "schema": { "$ref": "#/components/schemas/PostPracticePermit" } } } }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/PracticePermitDto" - } + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/PracticePermitDto" - } + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/PracticePermitDto" - } + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } } } }, "400": { "description": "Bad Request", + "content": { + "text/plain": { + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } + }, + "application/json": { + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } + }, + "text/json": { + "schema": { "$ref": "#/components/schemas/PracticePermitDto" } + } + } + } + } + } + }, + "/api/drivinglicense/v5/canapplyfor/renewal65": { + "get": { + "tags": ["Okuskirteini"], + "summary": "Check if person over 65 years old can apply for renewal of drivers license", + "parameters": [ + { + "name": "jwttoken", + "in": "header", + "description": "Token with person social security number", + "schema": { "type": "string" } + }, + { + "name": "api-version", + "in": "header", + "description": "The requested API version", + "required": true, + "schema": { "type": "string", "default": "5.0" } + }, + { + "name": "api-version", + "in": "query", + "description": "The requested API version", + "required": true, + "schema": { "type": "string", "default": "5.0" } + } + ], + "responses": { + "200": { + "description": "OK", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/PracticePermitDto" + "$ref": "#/components/schemas/MayApplyForRenewal65" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/PracticePermitDto" + "$ref": "#/components/schemas/MayApplyForRenewal65" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/PracticePermitDto" + "$ref": "#/components/schemas/MayApplyForRenewal65" } } } @@ -2577,59 +2074,43 @@ "name": "jwttoken", "in": "header", "description": "Token with person social security number", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "requestBody": { "description": "Model with information about the person", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/PostRenewal65AndOver" - } + "schema": { "$ref": "#/components/schemas/PostRenewal65AndOver" } } } }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/Renewal65AndOver" - } + "schema": { "$ref": "#/components/schemas/Renewal65AndOver" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/Renewal65AndOver" - } + "schema": { "$ref": "#/components/schemas/Renewal65AndOver" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/Renewal65AndOver" - } + "schema": { "$ref": "#/components/schemas/Renewal65AndOver" } } } }, @@ -2637,19 +2118,13 @@ "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/Renewal65AndOver" - } + "schema": { "$ref": "#/components/schemas/Renewal65AndOver" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/Renewal65AndOver" - } + "schema": { "$ref": "#/components/schemas/Renewal65AndOver" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/Renewal65AndOver" - } + "schema": { "$ref": "#/components/schemas/Renewal65AndOver" } } } } @@ -2666,47 +2141,33 @@ "in": "path", "description": "Year from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 2022 - } + "schema": { "type": "integer", "format": "int32", "default": 2022 } }, { "name": "monthfrom", "in": "path", "description": "Month from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 1 - } + "schema": { "type": "integer", "format": "int32", "default": 1 } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -2747,47 +2208,33 @@ "in": "path", "description": "Year from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 2022 - } + "schema": { "type": "integer", "format": "int32", "default": 2022 } }, { "name": "monthfrom", "in": "path", "description": "Month from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 1 - } + "schema": { "type": "integer", "format": "int32", "default": 1 } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -2828,47 +2275,33 @@ "in": "path", "description": "Year from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 2022 - } + "schema": { "type": "integer", "format": "int32", "default": 2022 } }, { "name": "monthfrom", "in": "path", "description": "Month from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 1 - } + "schema": { "type": "integer", "format": "int32", "default": 1 } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -2909,47 +2342,33 @@ "in": "path", "description": "Year from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 2022 - } + "schema": { "type": "integer", "format": "int32", "default": 2022 } }, { "name": "monthfrom", "in": "path", "description": "Month from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 1 - } + "schema": { "type": "integer", "format": "int32", "default": 1 } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -2990,47 +2409,33 @@ "in": "path", "description": "Year from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 2022 - } + "schema": { "type": "integer", "format": "int32", "default": 2022 } }, { "name": "monthfrom", "in": "path", "description": "Month from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 1 - } + "schema": { "type": "integer", "format": "int32", "default": 1 } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -3071,47 +2476,33 @@ "in": "path", "description": "Year from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 2022 - } + "schema": { "type": "integer", "format": "int32", "default": 2022 } }, { "name": "monthfrom", "in": "path", "description": "Month from", "required": true, - "schema": { - "type": "integer", - "format": "int32", - "default": 1 - } + "schema": { "type": "integer", "format": "int32", "default": 1 } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -3151,67 +2542,50 @@ "name": "jwttoken", "in": "header", "description": "jwt token", - "schema": { - "type": "string" - } + "schema": { "type": "string" } }, { "name": "licenseid", "in": "path", "description": "License id", "required": true, - "schema": { - "type": "integer", - "format": "int32" - } + "schema": { "type": "integer", "format": "int32" } }, { "name": "api-version", "in": "header", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } }, { "name": "api-version", "in": "query", "description": "The requested API version", "required": true, - "schema": { - "type": "string", - "default": "5.0" - } + "schema": { "type": "string", "default": "5.0" } } ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/MedferdDto" - } + "items": { "$ref": "#/components/schemas/MedferdDto" } } }, "application/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/MedferdDto" - } + "items": { "$ref": "#/components/schemas/MedferdDto" } } }, "text/json": { "schema": { "type": "array", - "items": { - "$ref": "#/components/schemas/MedferdDto" - } + "items": { "$ref": "#/components/schemas/MedferdDto" } } } } @@ -3220,19 +2594,13 @@ "description": "Bad Request", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } }, "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } @@ -3410,20 +2778,12 @@ "DriverLicenseDto": { "type": "object", "properties": { - "id": { - "type": "integer", - "description": "Id", - "format": "int32" - }, + "id": { "type": "integer", "description": "Id", "format": "int32" }, "temporaryLicense": { "type": "boolean", "description": "Is this license marked as temporary" }, - "name": { - "type": "string", - "description": "Name", - "nullable": true - }, + "name": { "type": "string", "description": "Name", "nullable": true }, "socialSecurityNumber": { "type": "string", "description": "Persond id number", @@ -3462,17 +2822,13 @@ }, "categories": { "type": "array", - "items": { - "$ref": "#/components/schemas/CategoryDto" - }, + "items": { "$ref": "#/components/schemas/CategoryDto" }, "description": "Category list", "nullable": true }, "comments": { "type": "array", - "items": { - "$ref": "#/components/schemas/LicenseCommentsDto" - }, + "items": { "$ref": "#/components/schemas/LicenseCommentsDto" }, "description": "Remark list", "nullable": true }, @@ -3488,15 +2844,9 @@ "format": "int32", "nullable": true }, - "photo": { - "$ref": "#/components/schemas/ImageDto" - }, - "signature": { - "$ref": "#/components/schemas/ImageDto" - }, - "deprivation": { - "$ref": "#/components/schemas/DeprivationDto" - } + "photo": { "$ref": "#/components/schemas/ImageDto" }, + "signature": { "$ref": "#/components/schemas/ImageDto" }, + "deprivation": { "$ref": "#/components/schemas/DeprivationDto" } }, "additionalProperties": false, "description": "Licence entity" @@ -3504,16 +2854,8 @@ "DriverLicenseWithoutImagesDto": { "type": "object", "properties": { - "id": { - "type": "integer", - "description": "Id", - "format": "int32" - }, - "name": { - "type": "string", - "description": "Name", - "nullable": true - }, + "id": { "type": "integer", "description": "Id", "format": "int32" }, + "name": { "type": "string", "description": "Name", "nullable": true }, "socialSecurityNumber": { "type": "string", "description": "Persond id number", @@ -3552,9 +2894,7 @@ }, "categories": { "type": "array", - "items": { - "$ref": "#/components/schemas/CategoryDto" - }, + "items": { "$ref": "#/components/schemas/CategoryDto" }, "description": "Category list", "nullable": true }, @@ -3565,15 +2905,11 @@ }, "comments": { "type": "array", - "items": { - "$ref": "#/components/schemas/LicenseCommentsDto" - }, + "items": { "$ref": "#/components/schemas/LicenseCommentsDto" }, "description": "Remark list", "nullable": true }, - "deprivation": { - "$ref": "#/components/schemas/DeprivationDto" - } + "deprivation": { "$ref": "#/components/schemas/DeprivationDto" } }, "additionalProperties": false, "description": "Licence entity" @@ -3886,9 +3222,7 @@ "properties": { "images": { "type": "array", - "items": { - "$ref": "#/components/schemas/ImageFromThjodskraDto" - }, + "items": { "$ref": "#/components/schemas/ImageFromThjodskraDto" }, "description": "Array of all images from thjodskra", "nullable": true } @@ -3904,11 +3238,7 @@ "description": "Public identity number", "nullable": true }, - "name": { - "type": "string", - "description": "Name", - "nullable": true - }, + "name": { "type": "string", "description": "Name", "nullable": true }, "driverLicenseId": { "type": "integer", "description": "Númer ökuskírteinis ökukennara", @@ -4009,6 +3339,22 @@ "additionalProperties": false, "description": "Data transfer object for results for person applying for\r\nfull license" }, + "MayApplyForRenewal65": { + "type": "object", + "properties": { + "result": { + "type": "boolean", + "description": "Can person apply for full license for 65+ years old" + }, + "errorCode": { + "type": "string", + "description": "Error code if can not apply", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Can person apply for renewal of license for 65+ years old" + }, "MayApplyForTemporaryLicenseDto": { "type": "object", "properties": { @@ -4150,6 +3496,14 @@ "type": "integer", "description": "Ordering district", "format": "int32" + }, + "contentList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RLSApplicationContentModel" + }, + "description": "Content list", + "nullable": true } }, "additionalProperties": false, @@ -4467,10 +3821,7 @@ "description": "The instructor", "nullable": true }, - "isOk": { - "type": "boolean", - "description": "Is the record OK" - }, + "isOk": { "type": "boolean", "description": "Is the record OK" }, "errorCode": { "type": "string", "description": "Error code, if any", @@ -4483,29 +3834,45 @@ "ProblemDetails": { "type": "object", "properties": { - "type": { + "type": { "type": "string", "nullable": true }, + "title": { "type": "string", "nullable": true }, + "status": { "type": "integer", "format": "int32", "nullable": true }, + "detail": { "type": "string", "nullable": true }, + "instance": { "type": "string", "nullable": true } + }, + "additionalProperties": false + }, + "RLSApplicationContentModel": { + "type": "object", + "properties": { + "fileName": { "type": "string", + "description": "File name", "nullable": true }, - "title": { + "fileExtension": { "type": "string", + "description": "File extension", "nullable": true }, - "status": { - "type": "integer", - "format": "int32", + "contentType": { + "type": "string", + "description": "Content type", "nullable": true }, - "detail": { + "content": { "type": "string", + "description": "Content data", "nullable": true }, - "instance": { + "description": { "type": "string", + "description": "File description", "nullable": true } }, - "additionalProperties": false + "additionalProperties": false, + "description": "RLS Application content" }, "Renewal65AndOver": { "type": "object", @@ -4532,16 +3899,8 @@ "TegSviptingaDto": { "type": "object", "properties": { - "id": { - "type": "integer", - "description": "Id", - "format": "int32" - }, - "heiti": { - "type": "string", - "description": "Name", - "nullable": true - } + "id": { "type": "integer", "description": "Id", "format": "int32" }, + "heiti": { "type": "string", "description": "Name", "nullable": true } }, "additionalProperties": false, "description": "Dto for Tegund sviptinga" diff --git a/libs/clients/judicial-system-sp/src/clientConfig.json b/libs/clients/judicial-system-sp/src/clientConfig.json index 746792eb0b07..48c81122e582 100644 --- a/libs/clients/judicial-system-sp/src/clientConfig.json +++ b/libs/clients/judicial-system-sp/src/clientConfig.json @@ -459,9 +459,22 @@ "type": "array", "items": { "$ref": "#/components/schemas/AlertMessage" } }, - "hasBeenServed": { "type": "boolean" } + "hasBeenServed": { "type": "boolean" }, + "hasChosenDefender": { "type": "boolean" }, + "defaultDefenderChoice": { + "type": "string", + "enum": ["WAIVE", "CHOOSE", "DELAY", "DELEGATE"] + } }, - "required": ["title", "subtitle", "groups", "alerts", "hasBeenServed"] + "required": [ + "title", + "subtitle", + "groups", + "alerts", + "hasBeenServed", + "hasChosenDefender", + "defaultDefenderChoice" + ] }, "DefenderInfo": { "type": "object", diff --git a/libs/clients/signature-collection/src/lib/signature-collection-admin.service.ts b/libs/clients/signature-collection/src/lib/signature-collection-admin.service.ts index 6bfffcd74640..37522bfc51cf 100644 --- a/libs/clients/signature-collection/src/lib/signature-collection-admin.service.ts +++ b/libs/clients/signature-collection/src/lib/signature-collection-admin.service.ts @@ -78,7 +78,10 @@ export class SignatureCollectionAdminClientService { const list = await this.getApiWithAuth( this.adminApi, auth, - ).adminMedmaelalistiIDToggleListPatch({ iD: parseInt(listId) }) + ).adminMedmaelalistiIDToggleListPatch({ + iD: parseInt(listId), + shouldToggle: listStatus === ListStatus.InReview, + }) return { success: !!list } } return { success: false } diff --git a/libs/clients/vmst/src/clientConfig.yaml b/libs/clients/vmst/src/clientConfig.yaml index c41490cacc6c..3488c84eefaa 100644 --- a/libs/clients/vmst/src/clientConfig.yaml +++ b/libs/clients/vmst/src/clientConfig.yaml @@ -580,6 +580,10 @@ components: type: string otherParentBlocked: type: boolean + applicationRights: + type: array + items: + $ref: '#/components/schemas/ApplicationRights' PaymentInfo: type: object additionalProperties: false diff --git a/libs/clients/vmst/src/lib/utils.ts b/libs/clients/vmst/src/lib/utils.ts index 0f140250dbf8..bdf555ec9ec8 100644 --- a/libs/clients/vmst/src/lib/utils.ts +++ b/libs/clients/vmst/src/lib/utils.ts @@ -2,6 +2,7 @@ import fetch from 'isomorphic-fetch' import { logger } from '@island.is/logging' import omit from 'lodash/omit' import pick from 'lodash/pick' +import { ApplicationRights } from '../../gen/fetch' // A parental leave request body: type Init = { @@ -47,6 +48,7 @@ type Init = { }[] testData: string otherParentBlocked: boolean + applicationRights: ApplicationRights[] } export const createWrappedFetchWithLogging = ( @@ -79,6 +81,7 @@ export const createWrappedFetchWithLogging = ( // 'rightsCode', // 'status', 'testData', + 'applicationRights', ], ) // Then omit the sensitive sub-attributes diff --git a/libs/feature-flags/src/lib/features.ts b/libs/feature-flags/src/lib/features.ts index 0749442f1775..c3b1d9d48cb2 100644 --- a/libs/feature-flags/src/lib/features.ts +++ b/libs/feature-flags/src/lib/features.ts @@ -57,7 +57,7 @@ export enum Features { ServicePortalNotificationsEnabled = 'isServicePortalNotificationsPageEnabled', servicePortalLawAndOrderModuleEnabled = 'isServicePortalLawAndOrderModuleEnabled', servicePortalDocumentsActionsEnabled = 'isServicePortalDocumentsActionsEnabled', - + isServicePortalDocumentsV3PageEnabled = 'isServicePortalDocumentsV3PageEnabled', //Occupational License Health directorate fetch enabled occupationalLicensesHealthDirectorate = 'isHealthDirectorateOccupationalLicenseEnabled', diff --git a/libs/infra-nest-server/src/index.ts b/libs/infra-nest-server/src/index.ts index e6a5847c8151..6fe69a8ae2bd 100644 --- a/libs/infra-nest-server/src/index.ts +++ b/libs/infra-nest-server/src/index.ts @@ -3,5 +3,6 @@ export * from './lib/buildOpenApi' export * from './lib/infra/infra.controller' export { InfraModule } from './lib/infra/infra.module' export { HealthCheckOptions } from './lib/infra/health/types' +export * from './lib/includeRawBodyMiddleware' export * from './lib/processJob' export * from './lib/types' diff --git a/libs/infra-nest-server/src/lib/bootstrap.ts b/libs/infra-nest-server/src/lib/bootstrap.ts index edc410b7a285..01a442e76f77 100644 --- a/libs/infra-nest-server/src/lib/bootstrap.ts +++ b/libs/infra-nest-server/src/lib/bootstrap.ts @@ -127,6 +127,10 @@ export const bootstrap = async ( }) } + if (options.beforeServerStart) { + options.beforeServerStart(app) + } + const serverPort = process.env.PORT ? parseInt(process.env.PORT, 10) : options.port ?? 3333 diff --git a/libs/infra-nest-server/src/lib/includeRawBodyMiddleware.ts b/libs/infra-nest-server/src/lib/includeRawBodyMiddleware.ts new file mode 100644 index 000000000000..d059a4462ffb --- /dev/null +++ b/libs/infra-nest-server/src/lib/includeRawBodyMiddleware.ts @@ -0,0 +1,14 @@ +import bodyParser from 'body-parser' + +/** + * Middleware that includes the raw body in the request object. + */ +export const includeRawBodyMiddleware = () => { + return bodyParser.json({ + verify: (req: any, res, buf) => { + if (buf && buf.length) { + req.rawBody = buf + } + }, + }) +} diff --git a/libs/infra-nest-server/src/lib/types.ts b/libs/infra-nest-server/src/lib/types.ts index f0c6b054b20b..7e37851701ff 100644 --- a/libs/infra-nest-server/src/lib/types.ts +++ b/libs/infra-nest-server/src/lib/types.ts @@ -48,6 +48,13 @@ export type RunServerOptions = { */ enableVersioning?: boolean + /** + * Hook to run before server is started. + * @param app The nest application instance. + * @returns a promise that resolves when the hook is done. + */ + beforeServerStart?: (app: INestApplication) => void + /** * Configures metrics collection and starts metric server. Default: true. */ diff --git a/libs/judicial-system/types/src/index.ts b/libs/judicial-system/types/src/index.ts index 899aa61fe4b4..29817dad0bf6 100644 --- a/libs/judicial-system/types/src/index.ts +++ b/libs/judicial-system/types/src/index.ts @@ -7,6 +7,7 @@ export { DefendantPlea, ServiceRequirement, ServiceStatus, + isSuccessfulServiceStatus, } from './lib/defendant' export { InstitutionType } from './lib/institution' export { NotificationType } from './lib/notification' diff --git a/libs/judicial-system/types/src/lib/defendant.ts b/libs/judicial-system/types/src/lib/defendant.ts index 5994ab09f293..be51efe47cef 100644 --- a/libs/judicial-system/types/src/lib/defendant.ts +++ b/libs/judicial-system/types/src/lib/defendant.ts @@ -35,3 +35,13 @@ export enum ServiceStatus { FAILED = 'FAILED', EXPIRED = 'EXPIRED', // If a subpoena expires } + +export const successfulServiceStatus: string[] = [ + ServiceStatus.ELECTRONICALLY, + ServiceStatus.DEFENDER, + ServiceStatus.IN_PERSON, +] + +export const isSuccessfulServiceStatus = (status?: ServiceStatus): boolean => { + return Boolean(status && successfulServiceStatus.includes(status)) +} diff --git a/libs/portals/admin/delegation-admin/src/constants/errors.ts b/libs/portals/admin/delegation-admin/src/constants/errors.ts index 373be7f6a2d2..ca4a8a0d020b 100644 --- a/libs/portals/admin/delegation-admin/src/constants/errors.ts +++ b/libs/portals/admin/delegation-admin/src/constants/errors.ts @@ -11,4 +11,5 @@ export const FORM_ERRORS: Record = { [ErrorCodes.ZENDESK_STATUS]: m.zendeskCaseNotSolvedError, [ErrorCodes.INPUT_VALIDATION_SAME_NATIONAL_ID]: m.sameNationalIdError, [ErrorCodes.INPUT_VALIDATION_INVALID_PERSON]: m.validPersonError, + [ErrorCodes.INVALID_DATE_FORMAT]: m.invalidDateFormatError, } diff --git a/libs/portals/admin/delegation-admin/src/lib/messages.ts b/libs/portals/admin/delegation-admin/src/lib/messages.ts index 1735e3f2b94d..32baab4f9048 100644 --- a/libs/portals/admin/delegation-admin/src/lib/messages.ts +++ b/libs/portals/admin/delegation-admin/src/lib/messages.ts @@ -147,4 +147,8 @@ export const m = defineMessages({ id: 'admin.delegationAdmin:validPersonError', defaultMessage: 'Kennitölur þurfa að vera gildar kennitölur', }, + invalidDateFormatError: { + id: 'admin.delegationAdmin:invalidDateFormatError', + defaultMessage: 'Dagsetning er ekki á réttu sniði', + }, }) diff --git a/libs/portals/admin/regulations-admin/src/components/EditSignature.tsx b/libs/portals/admin/regulations-admin/src/components/EditSignature.tsx index 7d6a5089c3b8..8422c36d7e31 100644 --- a/libs/portals/admin/regulations-admin/src/components/EditSignature.tsx +++ b/libs/portals/admin/regulations-admin/src/components/EditSignature.tsx @@ -20,6 +20,7 @@ import { downloadUrl } from '../utils/files' import { DownloadDraftButton } from './DownloadDraftButton' import { useLocale } from '@island.is/localization' import { useS3Upload } from '../utils/dataHooks' +import { formatDate } from '../utils/formatAmendingUtils' // --------------------------------------------------------------------------- @@ -31,7 +32,6 @@ const defaultSignatureText = ` ` as HTMLText const getDefaultSignatureText = ( - dateFormatter: (date: Date, str?: string) => string, /** The ministry of the author-type user that created the RegulationDraft */ authorMinistry?: PlainText, ) => { @@ -41,7 +41,7 @@ const getDefaultSignatureText = ( const defaultMinister = '⸻ráðherra' return defaultSignatureText - .replace('{dags}', dateFormatter(new Date(), 'dd. MMMM yyyy')) + .replace('{dags}', formatDate(new Date())) .replace('{ministry}', authorMinistry || defaultMinistry) .replace('{minister}', authorMinister || defaultMinister) as HTMLText } @@ -159,7 +159,7 @@ export const EditSignature = () => { draftId={draft.id} value={ draft.signatureText.value || - getDefaultSignatureText(formatDateFns, draft.ministry.value) + getDefaultSignatureText(draft.ministry.value) } onChange={(text) => updateState('signatureText', text)} required={!!draft.signatureText.required} diff --git a/libs/portals/admin/regulations-admin/src/components/impacts/EditChange.tsx b/libs/portals/admin/regulations-admin/src/components/impacts/EditChange.tsx index 5610b4fbccc2..604120d52675 100644 --- a/libs/portals/admin/regulations-admin/src/components/impacts/EditChange.tsx +++ b/libs/portals/admin/regulations-admin/src/components/impacts/EditChange.tsx @@ -240,11 +240,17 @@ export const EditChange = (props: EditChangeProp) => { return `
${diff}
` as HTMLText } + const noChange = dirtyClean(previous) === dirtyClean(current) + if (noChange) { + // If the appendix has no changes + return undefined + } + if (diff) { // If the appendix has changes return diff } else { - // If the appendix has no changes + // If the appendix has no diff return undefined } } diff --git a/libs/portals/admin/regulations-admin/src/components/impacts/ImpactBaseSelection.tsx b/libs/portals/admin/regulations-admin/src/components/impacts/ImpactBaseSelection.tsx index 13c5a81887aa..338d8042e0fa 100644 --- a/libs/portals/admin/regulations-admin/src/components/impacts/ImpactBaseSelection.tsx +++ b/libs/portals/admin/regulations-admin/src/components/impacts/ImpactBaseSelection.tsx @@ -44,6 +44,9 @@ export const ImpactBaseSelection = ({ if (loading) { return null } + if (!mentionedOptions.length) { + return null + } return (