import { Injectable } from '@angular/core';
import { CurrencyService } from '@core/services/currency.service';
import { FormMaskingService } from '@core/services/form-masking.service';
import { SpecialHandlingService } from '@core/services/special-handling.service';
import { TimeZoneService } from '@core/services/time-zone.service';
import { ReferenceFieldAPI } from '@core/typings/api/reference-fields.typing';
import { ApplicationInfoForPDF } from '@core/typings/pdf.typing';
import { ReferenceFieldsUI } from '@core/typings/ui/reference-fields.typing';
import { ClientSettingsService } from '@features/client-settings/client-settings.service';
import { FormDecisionTypes, FormDefinitionComponent, FormioAnswerValues, ReportFieldDataOptions } from '@features/configure-forms/form.typing';
import { CustomDataTablesService } from '@features/custom-data-tables/custom-data-table.service';
import { EmployeeSSOFieldsData } from '@features/employee-sso-fields/employee-sso-fields.typing';
import { ComponentHelperService, EMPLOYEE_SSO_TYPE_PREFIX } from '@features/formio/services/component-helper/component-helper.service';
import { ReportFieldService } from '@features/formio/services/report-field/report-field.service';
import { InKindService } from '@features/in-kind/in-kind.service';
import { ReferenceFieldsService } from '@features/reference-fields/services/reference-fields.service';
import { YcFile } from '@yourcause/common';
import { I18nService } from '@yourcause/common/i18n';
import { CurrencyValue } from '@yourcause/common/masking';
@Injectable({ providedIn: 'root' })
export class ApplicationDownloadAnswerService {
constructor (
private referenceFieldService: ReferenceFieldsService,
private formMaskingService: FormMaskingService,
private specialHandlingService: SpecialHandlingService,
private i18n: I18nService,
private currencyService: CurrencyService,
private timezoneService: TimeZoneService,
private customDataTableService: CustomDataTablesService,
private clientSettingsService: ClientSettingsService,
private inKindService: InKindService,
private reportFieldService: ReportFieldService,
private componentHelper: ComponentHelperService
) { }
/**
* Gets the answer of a particular component
*
* @param component: the component
* @param application: the application
* @param referenceFields: the reference fields
* @param decision: the decision
* @param responsesMasked: are responses masked?
* @param reviewerRecommendedFundingAmount: reviewer recommended funding amount
* @returns the answer
*/
getAnswerFromForm (
component: FormDefinitionComponent,
application: ApplicationInfoForPDF,
referenceFields: ReferenceFieldsUI.RefResponseMap = {},
decision: FormDecisionTypes,
responsesMasked: boolean,
reviewerRecommendedFundingAmount = 0
) {
let answer: FormioAnswerValues;
const field = this.referenceFieldService.getReferenceFieldFromCompType(component.type);
const type = this.adaptTypeForGetAnswer(component.type, field);
const key = field ? field.key : component.key;
if (field?.isMasked && responsesMasked) {
return this.formMaskingService.formFieldMask;
}
switch (type) {
case 'refFieldDefault': {
answer = this.getTextFieldAnswer(referenceFields, key);
break;
}
case 'refFieldTextMulti': {
answer = this.getMultiTextFieldAnswer(referenceFields, field.key);
break;
}
case 'refFieldCheckbox': {
answer = this.getCheckboxAnswer(referenceFields, field.key);
break;
}
case 'refFieldCurrency': {
answer = this.getCurrencyFieldAnswer(referenceFields, field.key);
break;
}
case 'refFieldDate': {
answer = this.getDateFieldAnswer(referenceFields, field.key);
break;
}
case 'refFieldNumber': {
answer = this.getNumberFieldAnswer(
referenceFields,
field.key,
component.requireDecimal,
component.decimalLimit
);
break;
}
case 'refCustomDataTable': {
answer = this.getCustomDataTableAnswer(
referenceFields,
field.key,
field.customDataTableGuid,
field.supportsMultiple
);
break;
}
case 'refFieldFileUpload':
answer = this.getFileUploadAnswer(referenceFields, field.key);
break;
case 'subset':
answer = this.getSubsetFieldAnswer(
referenceFields,
field
);
break;
case 'refFieldTable':
answer = this.getTableFieldAnswer(
referenceFields,
field
);
break;
case 'decision': {
answer = this.getDecisionAnswer(decision, component);
break;
}
case 'careOf': {
answer = this.getCareOfAnswer(application);
break;
}
case 'specialHandling': {
answer = this.specialHandlingService.getSpecialHandlingAnswer(application.specialHandling);
break;
}
case 'amountRequested': {
answer = this.getAmountRequestedAnswer(application);
break;
}
case 'designation': {
answer = this.getDesignationAnswer(application);
break;
}
case 'reviewerRecommendedFundingAmount': {
answer = this.getRecommendedFundingAnswer(reviewerRecommendedFundingAmount);
break;
}
case 'inKindItems': {
answer = this.getInKindAnswer(application);
break;
}
case 'reportField': {
answer = this.getReportFieldAnswer(application, component);
}
break;
case 'ssoField': {
answer = this.getSsoFieldAnswer(application, component);
break;
}
}
if (!!answer || answer === 0) {
if (component.prefix) {
answer = component.prefix + answer;
}
if (component.suffix) {
answer = answer + component.suffix;
}
}
if (!answer && (answer !== 0)) {
answer = this.i18n.translate(
'common:textNoAnswer',
{},
'No answer'
);
}
return answer;
}
/**
* Gets the answer for a text field
*
* @param referenceFields: reference field responses
* @param key: ref key
* @returns the adapted answer
*/
getTextFieldAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
key: string
) {
let answer = referenceFields[key];
if (typeof answer === 'string') {
answer = answer.replace(/\r?\n/g, '
');
}
return answer;
}
/**
* Gets the answer for a checkbox field
*
* @param referenceFields: reference field responses
* @param key: ref key
* @returns the adapted answer
*/
getCheckboxAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
key: string
) {
const answer = referenceFields[key];
if (answer === true) {
return this.i18n.translate('common:textTrue', {}, 'True');
}
return '';
}
/**
* Gets the answer for a multi text field
*
* @param referenceFields: reference field responses
* @param key: ref key
* @returns the adapted answer
*/
getMultiTextFieldAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
key: string
) {
let refAnswer = referenceFields[key] || [];
if (refAnswer instanceof Array) {
refAnswer = refAnswer.join('
');
} else {
refAnswer = refAnswer;
}
return refAnswer;
}
/**
* Gets the date field answer
*
* @param referenceFields: reference field responses
* @param key: ref key
* @returns the adapted answer
*/
getDateFieldAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
key: string
) {
let answer = referenceFields[key];
if (answer) {
answer = this.timezoneService.returnMidnightUTCDateShort(answer as string);
}
return answer;
}
/**
* Gets the currency field answer
*
* @param referenceFields: reference field responses
* @param key: ref key
* @returns the adapted answer
*/
getCurrencyFieldAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
key: string
) {
let answer;
const currencyValue = (referenceFields[key] as CurrencyValue);
if (currencyValue) {
answer = this.currencyService.formatMoney(
currencyValue.amountForControl,
currencyValue.currency,
true
);
}
return answer;
}
/**
* Gets the number field answer
*
* @param referenceFields: reference field responses
* @param key: ref key
* @param requireDecimal: require decimal setting
* @param decimalLimit: decimal limit setting
* @returns the adapted answer
*/
getNumberFieldAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
key: string,
requireDecimal: boolean,
decimalLimit: number
) {
let answer = referenceFields[key];
if (answer || answer === 0) {
answer = this.i18n.formatNumber(
answer as number,
{
allowDecimal: true,
requireDecimal,
decimalLimit,
thousands: this.i18n.numberFormattingData.group,
decimal: this.i18n.numberFormattingData.decimal
}
);
}
return answer;
}
/**
* Gets the custom data table answer
*
* @param referenceFields: reference field responses
* @param key: ref key
* @param customDataTableGuid: custom data table guid
* @param supportsMultiple: does it support multiple?
* @returns the adapted answer
*/
getCustomDataTableAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
key: string,
customDataTableGuid: string,
supportsMultiple: boolean
) {
let answer;
const options = customDataTableGuid ?
this.customDataTableService.customDataTableOptionsMap[
customDataTableGuid
] :
[];
if (supportsMultiple) {
const refAnswer = (referenceFields[key] || []) as string[];
answer = (options || []).filter((opt) => {
return (refAnswer || []).includes(opt.key);
}).map((opt) => {
return opt.value;
}).join('
');
} else {
const refAnswer = referenceFields[key];
const found = (options || []).find((option) => {
return option.key === refAnswer;
});
answer = found ? found.value : refAnswer;
}
return answer;
}
/**
* Gets the file upload answer
*
* @param referenceFields: reference fields responses
* @param key: ref key
* @returns the adapted answer
*/
getFileUploadAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
key: string
) {
let answer;
const files = (referenceFields[key] || []) as YcFile[];
const fileNames = files.map((file) => {
return file.fileName;
}).join(', ');
if (files.length === 0) {
answer = this.i18n.translate(
'common:textNoFileUploaded',
{},
'No file uploaded'
);
} else {
answer = this.i18n.translate(
'common:textSeeFileNamesDynamic',
{
fileNames
},
'See __fileNames__'
);
}
return answer;
}
/**
* Gets teh subset field answer
*
* @param referenceFields: reference field responses
* @param field: the reference field
* @returns the adapted answer
*/
getSubsetFieldAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
field: ReferenceFieldAPI.ReferenceFieldDisplayModel
) {
let answer;
const subsetRows = (referenceFields[field.key] || []) as ReferenceFieldsUI.TableResponseRowForUiMapped[];
const subsetResponse = this.componentHelper.getShowTableOnPdfAndRecordCount(
referenceFields,
field,
this.referenceFieldService.getNumberOfColumns(field)
);
if (subsetResponse.showTableOnPdf) {
answer = this.referenceFieldService.mapRowsForTable(
subsetRows,
field.referenceFieldId,
true
);
} else {
answer = this.i18n.translate(
'common:textSeeFileNameDynamic',
{
fileName: `${field.name}.csv`
},
'See __fileName__'
);
}
return answer;
}
/**
* Gets the table field answer
*
* @param referenceFields: reference field responses
* @param field: the reference field
* @returns the adapted answer
*/
getTableFieldAnswer (
referenceFields: ReferenceFieldsUI.RefResponseMap,
field: ReferenceFieldAPI.ReferenceFieldDisplayModel
) {
let answer;
const rows = (referenceFields[field.key] || []) as ReferenceFieldsUI.TableResponseRowForUiMapped[];
const response = this.componentHelper.getShowTableOnPdfAndRecordCount(
referenceFields,
field,
this.referenceFieldService.getNumberOfColumns(field)
);
if (response.showTableOnPdf) {
answer = this.referenceFieldService.mapRowsForTable(
rows,
field.referenceFieldId,
true,
true
);
} else {
answer = this.i18n.translate(
'common:textSeeFileNameDynamic',
{
fileName: `${field.name}.csv`
},
'See __fileName__'
);
}
return answer;
}
/**
* Gets the designation answer
*
* @param application: the application
* @returns the designation answer
*/
getDesignationAnswer (application: ApplicationInfoForPDF) {
return application.designation;
}
/**
* Gets the amount requested answer
*
* @param application: the application
* @returns the amount requested answer
*/
getAmountRequestedAnswer (application: ApplicationInfoForPDF) {
return this.currencyService.formatMoney(
application.currencyRequestedAmountEquivalent,
application.currencyRequested,
true
);
}
/**
* Gets the recommended funding answer
*
* @param application: the application
* @returns the amount requested answer
*/
getRecommendedFundingAnswer (reviewerRecommendedFundingAmount: number) {
return this.currencyService.formatMoney(
reviewerRecommendedFundingAmount,
this.clientSettingsService.defaultCurrency,
true
);
}
/**
* Gets the care of answer
*
* @param application: the application
* @returns the care of answer
*/
getCareOfAnswer (application: ApplicationInfoForPDF) {
return application.careOf;
}
/**
* Gets the decision answer
*
* @param decision: decision response
* @param component: the form component
* @returns the decision answer
*/
getDecisionAnswer (
decision: FormDecisionTypes,
component: FormDefinitionComponent
) {
let answer;
if (decision === FormDecisionTypes.Approve) {
answer = component.truthyValue;
} else if (decision === FormDecisionTypes.Decline) {
answer = component.falsyValue;
} else if (decision === FormDecisionTypes.Recused) {
answer = component.recuseValue;
}
return answer;
}
/**
* Gets the in-kind answer
*
* @param application: the application
* @returns the in kind answer
*/
getInKindAnswer (application: ApplicationInfoForPDF) {
const allItemsMap = this.inKindService.allItemsMap;
return application.inKindItems.filter((item) => {
return item.count > 0;
}).map((item) => {
let name = allItemsMap[item.itemIdentification] ?
allItemsMap[item.itemIdentification].name :
'';
if (!name) {
const found = application.availableItemsForApplicant.find((i) => {
return i.identification === item.itemIdentification;
});
name = found ? found.name : item.itemIdentification;
}
return `${item.count} ${name}`;
}).join(', ');
}
/**
* Gets the report field answer
*
* @param application: the application
* @param component: the form component
* @param answer: the answer to adapt
* @returns the adapted answer
*/
getReportFieldAnswer (
application: ApplicationInfoForPDF,
component: FormDefinitionComponent
) {
let answer;
const reportFieldOptions = component.reportFieldDataOptions;
let reportFieldOptionsParsed: ReportFieldDataOptions;
if (reportFieldOptions) {
try {
if (typeof reportFieldOptions === 'string') {
reportFieldOptionsParsed = JSON.parse(reportFieldOptions as any);
} else {
reportFieldOptionsParsed = component.reportFieldDataOptions;
}
} catch (e) { }
}
if (reportFieldOptionsParsed && application.reportFieldResponse) {
answer = this.reportFieldService.getReportFieldValue(
reportFieldOptionsParsed,
application.reportFieldResponse
);
}
return answer;
}
/**
* Gets the SSO answer
*
* @param application: the application
* @param component: the form component
* @returns the adapted answer
*/
getSsoFieldAnswer (
application: ApplicationInfoForPDF,
component: FormDefinitionComponent
) {
let answer;
const ssoKey = component.type.split(`${EMPLOYEE_SSO_TYPE_PREFIX}-`)[1] as keyof EmployeeSSOFieldsData;
if (ssoKey) {
answer = application.employeeInfo[ssoKey];
}
return answer;
}
/**
* Adapts the type for get answer
*
* @param compType: component type
* @param field: reference field is applicable
* @returns the adapted type
*/
adaptTypeForGetAnswer (
compType: string,
field: ReferenceFieldAPI.ReferenceFieldDisplayModel
) {
let adaptedType = compType;
if (!!field) {
switch (field.type) {
case ReferenceFieldsUI.ReferenceFieldTypes.TextArea:
case ReferenceFieldsUI.ReferenceFieldTypes.TextField:
if (field.supportsMultiple) {
adaptedType = 'refFieldTextMulti';
} else {
adaptedType = 'refFieldDefault';
}
break;
case ReferenceFieldsUI.ReferenceFieldTypes.Checkbox:
adaptedType = 'refFieldCheckbox';
break;
case ReferenceFieldsUI.ReferenceFieldTypes.CustomDataTable:
case ReferenceFieldsUI.ReferenceFieldTypes.SelectBoxes:
case ReferenceFieldsUI.ReferenceFieldTypes.Radio:
adaptedType = 'refCustomDataTable';
break;
case ReferenceFieldsUI.ReferenceFieldTypes.Currency:
adaptedType = 'refFieldCurrency';
break;
case ReferenceFieldsUI.ReferenceFieldTypes.Date:
adaptedType = 'refFieldDate';
break;
case ReferenceFieldsUI.ReferenceFieldTypes.FileUpload:
adaptedType = 'refFieldFileUpload';
break;
case ReferenceFieldsUI.ReferenceFieldTypes.Table:
adaptedType = 'refFieldTable';
break;
case ReferenceFieldsUI.ReferenceFieldTypes.Number:
case ReferenceFieldsUI.ReferenceFieldTypes.Aggregate:
adaptedType = 'refFieldNumber';
break;
case ReferenceFieldsUI.ReferenceFieldTypes.Subset:
adaptedType = 'subset';
break;
}
} else if (compType.startsWith(`${EMPLOYEE_SSO_TYPE_PREFIX}-`)) {
adaptedType = 'ssoField';
}
return adaptedType;
}
}