Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(financial-statement-political-party): political party as a new application #16076

Merged
merged 12 commits into from
Oct 14, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,45 @@ import {
CemeteryFinancialStatementValues,
FinancialStatementsInaoClientService,
ClientRoles,
Contact,
ContactType,
DigitalSignee,
} from '@island.is/clients/financial-statements-inao'
import {
ApplicationTypes,
ApplicationWithAttachments as Application,
PerformActionResult,
} from '@island.is/application/types'
import { getValueViaPath } from '@island.is/application/core'
import AmazonS3URI from 'amazon-s3-uri'
import { TemplateApiModuleActionProps } from '../../../types'
import * as kennitala from 'kennitala'
import {
DataResponse,
getCurrentUserType,
} from '../financial-statements-inao/financial-statements-inao.service'
import {
BoardMember,
FSIUSERTYPE,
} from '@island.is/application/templates/financial-statements-inao/types'
import {
mapValuesToCemeterytype,
getNeededCemeteryValues,
mapContactsAnswersToContacts,
mapDigitalSignee,
} from '../financial-statement-cemetery/mappers/mapValuesToUserType'
import { TemplateApiError } from '@island.is/nest/problem'
import { ApplicationApiAction } from '../../template-api.service'

export type AttachmentData = {
key: string
name: string
}

export interface DataResponse {
success: boolean
message?: string
}

export const getCurrentUserType = (
answers: Application['answers'],
externalData: Application['externalData'],
) => {
const fakeUserType: any = getValueViaPath(answers, 'fakeData.options')

const currentUserType: any = getValueViaPath(
externalData,
'getUserType.data.value',
)
return fakeUserType ?? currentUserType
}

export class FinancialStatementCemeteryTemplateService extends BaseTemplateApiService {
s3: S3
constructor(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { getValueViaPath } from '@island.is/application/core'
import { BoardMember } from '@island.is/application/templates/financial-statements-inao/types'
import { FormValue } from '@island.is/application/types'
import {
Contact,
Expand All @@ -8,6 +7,12 @@ import {
DigitalSignee,
} from '@island.is/clients/financial-statements-inao'

type BoardMember = {
nationalId: string
name: string
role: string
}

export const mapValuesToCemeterytype = (answers: FormValue) => {
return {
careIncome: Number(getValueViaPath(answers, 'cemetryIncome.careIncome')),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { DynamicModule } from '@nestjs/common'
import { BaseTemplateAPIModuleConfig } from '../../../types'
import { SharedTemplateAPIModule } from '../../shared'
import { ConfigModule } from '@nestjs/config'
import {
FinancialStatementsInaoClientConfig,
FinancialStatementsInaoClientModule,
} from '@island.is/clients/financial-statements-inao'
import { FinancialStatementPoliticalPartyTemplateService } from './financial-statement-political-party.service'

export class FinancialStatementPoliticalPartyTemplateModule {
static register(config: BaseTemplateAPIModuleConfig): DynamicModule {
return {
module: FinancialStatementPoliticalPartyTemplateModule,
imports: [
SharedTemplateAPIModule.register(config),

Check failure on line 16 in libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.modules.ts

View workflow job for this annotation

GitHub Actions / prepare

Property 'register' does not exist on type 'typeof SharedTemplateAPIModule'.

Check failure on line 16 in libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.modules.ts

View workflow job for this annotation

GitHub Actions / prepare

Property 'register' does not exist on type 'typeof SharedTemplateAPIModule'.

Check failure on line 16 in libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.modules.ts

View workflow job for this annotation

GitHub Actions / prepare

Property 'register' does not exist on type 'typeof SharedTemplateAPIModule'.

Check failure on line 16 in libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.modules.ts

View workflow job for this annotation

GitHub Actions / prepare

Property 'register' does not exist on type 'typeof SharedTemplateAPIModule'.

Check failure on line 16 in libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.modules.ts

View workflow job for this annotation

GitHub Actions / prepare

Property 'register' does not exist on type 'typeof SharedTemplateAPIModule'.

Check failure on line 16 in libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.modules.ts

View workflow job for this annotation

GitHub Actions / prepare

Property 'register' does not exist on type 'typeof SharedTemplateAPIModule'.
ConfigModule.forRoot({
load: [FinancialStatementsInaoClientConfig],
}),
FinancialStatementsInaoClientModule,
],
providers: [FinancialStatementPoliticalPartyTemplateService],
exports: [FinancialStatementPoliticalPartyTemplateService],
}
}
}
jonnigs marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import { Inject, Injectable } from '@nestjs/common'
import { BaseTemplateApiService } from '../../base-template-api.service'
import { S3 } from 'aws-sdk'
import { LOGGER_PROVIDER } from '@island.is/logging'
import type { Logger } from '@island.is/logging'
import {
ApplicationTypes,
ApplicationWithAttachments as Application,
} from '@island.is/application/types'
import {
Contact,
ContactType,
DigitalSignee,
FinancialStatementsInaoClientService,
PoliticalPartyFinancialStatementValues,
} from '@island.is/clients/financial-statements-inao'
import { getValueViaPath } from '@island.is/application/core'
import AmazonS3Uri from 'amazon-s3-uri'
import { TemplateApiModuleActionProps } from '../../../types'
import * as kennitala from 'kennitala'
import { mapValuesToPartyTypes } from './mappers/mapValuesToPartyTypes'

export interface AttachmentData {
key: string
name: string
}

export interface DataResponse {
success: boolean
message?: string
}

export const getCurrentUserType = (
answers: Application['answers'],
externalData: Application['externalData'],
) => {
const fakeUserType = getValueViaPath(answers, 'fakeData.options') as
| number
| undefined

const currentUserType = getValueViaPath(
externalData,
'getUserType.data.value',
) as number | undefined

return fakeUserType ?? currentUserType
}
jonnigs marked this conversation as resolved.
Show resolved Hide resolved

const PARTY_USER_TYPE = 150000001

@Injectable()
export class FinancialStatementPoliticalPartyTemplateService extends BaseTemplateApiService {
s3: S3
constructor(
@Inject(LOGGER_PROVIDER) private logger: Logger,
private financialStatementClientService: FinancialStatementsInaoClientService,
) {
super(ApplicationTypes.FINANCIAL_STATEMENT_POLITICAL_PARTY)
this.s3 = new S3()
}

private async getAttachment(application: Application): Promise<string> {
const attachments = getValueViaPath(
application.answers,
'attachments.files',
) as Array<AttachmentData>

if (!attachments || attachments.length === 0) {
throw new Error('No attachments found in application')
}

const attachmentKey = attachments[0].key

const fileName = (
application.attachments as {
[key: string]: string
}
)[attachmentKey]

if (!fileName) {
throw new Error('Attachment file name not found')
}

const { bucket, key } = AmazonS3Uri(fileName)

const uploadBucket = bucket
try {
const file = await this.s3
.getObject({ Bucket: uploadBucket, Key: key })
.promise()
const fileContent = file.Body as Buffer
return fileContent?.toString('base64') || ''
} catch (error) {
this.logger.error('Error retrieving attachment from S3', error)
throw new Error('Failed to retrieve attachment from S3')
}
}

async getUserType({ auth }: TemplateApiModuleActionProps) {
const { nationalId } = auth
if (kennitala.isPerson(nationalId)) {
return this.financialStatementClientService.getClientType('Einstaklingur')
} else {
return this.financialStatementClientService.getUserClientType(nationalId)
}
}

async submitApplication({ application, auth }: TemplateApiModuleActionProps) {
const { nationalId, actor } = auth

if (!actor) {
throw new Error('Enginn umboðsmaður fannst')
}

this.validateUserType(application)

const values = this.prepareValues(application)
const year = this.getOperatingYear(application)
const fileName = await this.getAttachment(application)
const client = { nationalId }
const contacts = this.prepareContacts(application, actor)
const digitalSignee = this.prepareDigitalSignee(application)

try {
const result =
await this.financialStatementClientService.postFinancialStatementForPoliticalParty(
client,
contacts,
digitalSignee,
year,
'',
values,
fileName,
)

if (!result) {
throw new Error('Application submission failed')
}

return { success: true }
} catch (error) {
this.logger.error('Error submitting application', error)
return {
success: false,
message: error.message,
}
// throw new Error('Application submission failed')
}
}
jonnigs marked this conversation as resolved.
Show resolved Hide resolved

private prepareValues(
application: Application,
): PoliticalPartyFinancialStatementValues {
return mapValuesToPartyTypes(application.answers)
}

private getOperatingYear(application: Application) {
const year = getValueViaPath(
application.answers,
'conditionalAbout.operatingYear',
)
if (typeof year !== 'string') {
throw new Error('Operating year not found or invalid')
}
return year
}

private validateUserType(application: Application) {
const currentUserType = getCurrentUserType(
application.answers,
application.externalData,
)
if (currentUserType !== PARTY_USER_TYPE) {
throw new Error('Invalid user type for application submission')
}
}

private prepareContacts(
application: Application,
actor: { nationalId: string },
): Array<Contact> {
const actorsName = getValueViaPath(
application.answers,
'about.powerOfAttorneyName',
) as string
return [
{
nationalId: actor.nationalId,
name: actorsName,
contactType: ContactType.Actor,
},
]
}
jonnigs marked this conversation as resolved.
Show resolved Hide resolved

private prepareDigitalSignee(application: Application): DigitalSignee {
const clientPhone = getValueViaPath(
application.answers,
'about.phoneNumber',
) as string
const clientEmail = getValueViaPath(
application.answers,
'about.email',
) as string
return {
email: clientEmail,
phone: clientPhone,
}
}
jonnigs marked this conversation as resolved.
Show resolved Hide resolved
jonnigs marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { getValueViaPath } from '@island.is/application/core'
import { FormValue } from '@island.is/application/types'

export const mapValuesToPartyTypes = (answers: FormValue) => {
return {
contributionsFromTheTreasury: Number(
getValueViaPath(answers, 'partyIncome.contributionsFromTheTreasury'),
),
parliamentaryPartySupport: Number(
getValueViaPath(answers, 'partyIncome.parliamentaryPartySupport'),
),
municipalContributions: Number(
getValueViaPath(answers, 'partyIncome.municipalContributions'),
),
contributionsFromLegalEntities: Number(
getValueViaPath(answers, 'partyIncome.contributionsFromLegalEntities'),
),
contributionsFromIndividuals: Number(
getValueViaPath(answers, 'partyIncome.contributionsFromIndividuals'),
),
generalMembershipFees: Number(
getValueViaPath(answers, 'partyIncome.generalMembershipFees'),
),
otherIncome: Number(getValueViaPath(answers, 'partyIncome.otherIncome')),
capitalIncome: Number(
getValueViaPath(answers, 'capitalNumbers.capitalIncome'),
),
officeOperations: Number(
getValueViaPath(answers, 'partyExpense.electionOffice'),
),
otherOperatingExpenses: Number(
getValueViaPath(answers, 'partyExpense.otherCost'),
),
financialExpenses: Number(
getValueViaPath(answers, 'capitalNumbers.capitalCost'),
),
fixedAssetsTotal: Number(
getValueViaPath(answers, 'asset.fixedAssetsTotal'),
),
currentAssets: Number(getValueViaPath(answers, 'asset.currentAssets')),
longTermLiabilitiesTotal: Number(
getValueViaPath(answers, 'liability.longTerm'),
),
shortTermLiabilitiesTotal: Number(
getValueViaPath(answers, 'liability.shortTerm'),
),
equityTotal: Number(getValueViaPath(answers, 'equity.totalEquity')),
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type KeyValue = {
key: number
value: number
}
Loading
Loading