Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-- AlterTable: change income_vouchers from boolean to text array

ALTER TABLE "applications"
ADD COLUMN "income_vouchers_new" TEXT[] NOT NULL DEFAULT '{}';

-- Migrate existing true values to ['incomeVoucher'], false/null to ['none']
UPDATE "applications"
SET "income_vouchers_new" = ARRAY['incomeVoucher']
WHERE "income_vouchers" = true;

UPDATE "applications"
SET "income_vouchers_new" = ARRAY['none']
WHERE "income_vouchers" = false;

ALTER TABLE "applications"
DROP COLUMN "income_vouchers";

ALTER TABLE "applications"
RENAME COLUMN "income_vouchers_new" TO "income_vouchers";

-- AlterTable: change income_vouchers from boolean to text array on snapshot table
ALTER TABLE "application_snapshot"
ADD COLUMN "income_vouchers_new" TEXT[] NOT NULL DEFAULT '{}';

-- Migrate existing true values to ['incomeVoucher'], false/null to empty array
UPDATE "application_snapshot"
SET "income_vouchers_new" = ARRAY['incomeVoucher']
WHERE "income_vouchers" = true;

ALTER TABLE "application_snapshot"
DROP COLUMN "income_vouchers";

ALTER TABLE "application_snapshot"
RENAME COLUMN "income_vouchers_new" TO "income_vouchers";
4 changes: 2 additions & 2 deletions api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ model Applications {
housingStatus String? @map("housing_status")
income String?
incomePeriod IncomePeriodEnum? @map("income_period")
incomeVouchers Boolean? @map("income_vouchers")
incomeVouchers String[] @map("income_vouchers")
// This application is the most recent for the user
isNewest Boolean? @default(false) @map("is_newest")
language LanguagesEnum?
Expand Down Expand Up @@ -481,7 +481,7 @@ model ApplicationSnapshot {
housingStatus String? @map("housing_status")
income String?
incomePeriod IncomePeriodEnum? @map("income_period")
incomeVouchers Boolean? @map("income_vouchers")
incomeVouchers String[] @map("income_vouchers")
// This application is the most recent for the user
isNewest Boolean? @default(false) @map("is_newest")
language LanguagesEnum?
Expand Down
4 changes: 2 additions & 2 deletions api/prisma/seed-helpers/application-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const applicationFactory = async (optionalParams?: {
? createdAtDate
: dayjs(createdAtDate).add(2, 'days').toDate(),
householdSize: householdSize,
income: '40000',
income: randomInt(500, 100000).toString(),
incomePeriod: randomBoolean()
? IncomePeriodEnum.perYear
: IncomePeriodEnum.perMonth,
Expand Down Expand Up @@ -132,7 +132,7 @@ export const applicationFactory = async (optionalParams?: {
},
}
: undefined,
incomeVouchers: randomBoolean(),
incomeVouchers: randomBoolean() ? ['incomeVoucher'] : [],
additionalPhoneNumber: includeAdditionalPhone
? optionalParams?.additionalPhone || '(456) 456-4564'
: undefined,
Expand Down
1 change: 1 addition & 0 deletions api/prisma/seed-staging/seed-angelopolis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export const createAngelopolisJurisdiction = async (
FeatureFlagEnum.disableReservedCommunityTypeEdit,
FeatureFlagEnum.enableAccessibilityFeatures,
FeatureFlagEnum.enableApplicationStatus,
FeatureFlagEnum.enableAutoOpenDate,
FeatureFlagEnum.enableAutopublish,
FeatureFlagEnum.enableConfigurableRegions,
FeatureFlagEnum.enableCreditScreeningFee,
Expand Down
1 change: 1 addition & 0 deletions api/prisma/seed-staging/seed-bridge-bay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2411,6 +2411,7 @@ const featureFlags = [
FeatureFlagEnum.enableListingMap,
FeatureFlagEnum.enableListingOpportunity,
FeatureFlagEnum.enableListingPagination,
FeatureFlagEnum.enableMultiselectVoucherQuestion,
FeatureFlagEnum.enablePartnerDemographics,
FeatureFlagEnum.enablePartnerSettings,
FeatureFlagEnum.enableReceivedAtAndByFields,
Expand Down
21 changes: 21 additions & 0 deletions api/src/controllers/application.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
import { ApiKeyGuard } from '../guards/api-key.guard';
import { PublicAppsViewQueryParams } from '../dtos/applications/public-apps-view-params.dto';
import { PublicAppsViewResponse } from '../dtos/applications/public-apps-view-response.dto';
import { ApplicationBulkUploadService } from '../services/application-bulk-upload.service';

@Controller('applications')
@ApiTags('applications')
Expand All @@ -70,6 +71,7 @@ export class ApplicationController {
constructor(
private readonly applicationService: ApplicationService,
private readonly applicationExportService: ApplicationExporterService,
private readonly applicationBulkUploadService: ApplicationBulkUploadService,
) {}

@Get()
Expand Down Expand Up @@ -204,6 +206,25 @@ export class ApplicationController {
return this.applicationService.findOne(applicationId, req);
}

@Get('bulk-update/template/:listingId')
@ApiOperation({
summary:
'Download a template CSV for bulk updating applications for a listing',
operationId: 'downloadBulkUpdateTemplate',
})
@Header('Content-Type', 'application/zip')
@UseInterceptors(ExportLogInterceptor)
@ApiOkResponse({ type: StreamableFile })
async downloadBulkUpdateTemplate(
@Request() req: ExpressRequest,
@Param('listingId') listingId: string,
): Promise<StreamableFile> {
return await this.applicationBulkUploadService.downloadBulkUpdateTemplate(
listingId,
mapTo(User, req['user']),
);
}

@Post()
@ApiOperation({
summary:
Expand Down
3 changes: 2 additions & 1 deletion api/src/decorators/validate-listing-publish.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ class ListingPublishRequiredConstraint implements ValidatorConstraintInterface {

/*
* Check if the listing is being published.
* A listing is considered "publishing" if its status is 'active' or 'pending review'.
* A listing is considered "publishing" if its status is 'active', 'pending review', or 'scheduled'.
* If status is undefined, null, or an empty string we have to assume that it is publishing in order to apply validation
* so that strict validation will be applied
*/
static isPublishing(status?: ListingsStatusEnum | ''): boolean {
return (
status === ListingsStatusEnum.active ||
status === ListingsStatusEnum.pendingReview ||
status === ListingsStatusEnum.scheduled ||
status === undefined ||
status === null ||
status === ''
Expand Down
5 changes: 3 additions & 2 deletions api/src/dtos/applications/application.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ export class Application extends AbstractDTO {
reasonableAccommodations?: string;

@Expose()
@IsBoolean({ groups: [ValidationsGroupsEnum.default] })
@IsArray({ groups: [ValidationsGroupsEnum.default] })
@IsString({ each: true, groups: [ValidationsGroupsEnum.default] })
@IsDefined({ groups: [ValidationsGroupsEnum.applicants] })
@ApiPropertyOptional()
incomeVouchers?: boolean;
incomeVouchers?: string[];

@Expose()
@IsString({ groups: [ValidationsGroupsEnum.default] })
Expand Down
12 changes: 12 additions & 0 deletions api/src/enums/feature-flags/feature-flags-enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export enum FeatureFlagEnum {
enableAccessibilityFeatures = 'enableAccessibilityFeatures',
enableAdditionalResources = 'enableAdditionalResources',
enableApplicationStatus = 'enableApplicationStatus',
enableAutoOpenDate = 'enableAutoOpenDate',
enableAutopublish = 'enableAutopublish',
enableCompanyWebsite = 'enableCompanyWebsite',
enableCustomListingNotifications = 'enableCustomListingNotifications',
Expand Down Expand Up @@ -43,6 +44,7 @@ export enum FeatureFlagEnum {
enableMarketingFlyer = 'enableMarketingFlyer',
enableMarketingStatus = 'enableMarketingStatus',
enableMarketingStatusMonths = 'enableMarketingStatusMonths',
enableMultiselectVoucherQuestion = 'enableMultiselectVoucherQuestion',
enableNeighborhoodAmenities = 'enableNeighborhoodAmenities',
enableNeighborhoodAmenitiesDropdown = 'enableNeighborhoodAmenitiesDropdown',
enableNonRegulatedListings = 'enableNonRegulatedListings',
Expand Down Expand Up @@ -143,6 +145,11 @@ export const featureFlagMap: {
description:
'When true, the application status and notifications feature is enabled on public and partners',
},
{
name: FeatureFlagEnum.enableAutoOpenDate,
description:
'When true, partners can set an optional scheduled listing applications open date',
},
{
name: FeatureFlagEnum.enableAutopublish,
description:
Expand Down Expand Up @@ -280,6 +287,11 @@ export const featureFlagMap: {
description:
"When true, the 'marketing status' sub-section uses months instead of seasons (functions only if enableMarketingStatus is also true)",
},
{
name: FeatureFlagEnum.enableMultiselectVoucherQuestion,
description:
'When true, the vouchers question on the application form becomes a multi-select checkbox experience with Section 8, rental assistance, and none of the above as options',
},
{
name: FeatureFlagEnum.enableNeighborhoodAmenities,
description:
Expand Down
13 changes: 13 additions & 0 deletions api/src/modules/application-bulk-upload.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Global, Module } from '@nestjs/common';
import { ApplicationBulkUploadService } from '../services/application-bulk-upload.service';
import { PrismaModule } from './prisma.module';
import { ListingModule } from './listing.module';
import { PermissionModule } from './permission.module';

@Global()
@Module({
imports: [PrismaModule, ListingModule, PermissionModule],
providers: [ApplicationBulkUploadService],
exports: [ApplicationBulkUploadService],
})
export class ApplicationBulkUploadModule {}
2 changes: 2 additions & 0 deletions api/src/modules/application.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import { PermissionModule } from './permission.module';
import { PrismaModule } from './prisma.module';
import { SnapshotCreateModule } from './snapshot-create.module';
import { UnitTypeModule } from './unit-type.module';
import { ApplicationBulkUploadModule } from './application-bulk-upload.module';

@Module({
imports: [
ApplicationExporterModule,
ApplicationBulkUploadModule,
CronJobModule,
EmailModule,
ListingModule,
Expand Down
Loading
Loading