forked from ytgov/elcc-data-management
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
…tion-of-employee-benefits-section ELCC 18: Support Customization of Employee Benefits Section
- Loading branch information
Showing
25 changed files
with
1,126 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { isNil } from "lodash" | ||
import { WhereOptions } from "sequelize" | ||
|
||
import BaseController from "./base-controller" | ||
|
||
import { EmployeeBenefit } from "@/models" | ||
import { EmployeeBenefitSerializer } from "@/serializers" | ||
|
||
export class EmployeeBenefitsController extends BaseController { | ||
index() { | ||
const where = this.query.where as WhereOptions<EmployeeBenefit> | ||
return EmployeeBenefit.findAll({ | ||
where, | ||
}) | ||
.then((employeeBenefits) => { | ||
const serializedEmployeeBenefits = EmployeeBenefitSerializer.asTable(employeeBenefits) | ||
return this.response.json({ | ||
employeeBenefits: serializedEmployeeBenefits, | ||
}) | ||
}) | ||
.catch((error) => { | ||
return this.response | ||
.status(400) | ||
.json({ message: `Invalid query for employee benefits: ${error}` }) | ||
}) | ||
} | ||
|
||
async show() { | ||
const employeeBenefit = await this.loadEmployeeBenefit() | ||
if (isNil(employeeBenefit)) | ||
return this.response.status(404).json({ message: "employee benefit not found." }) | ||
|
||
const serializedemployeeBenefit = EmployeeBenefitSerializer.asDetailed(employeeBenefit) | ||
return this.response.json({ | ||
employeeBenefit: serializedemployeeBenefit, | ||
}) | ||
} | ||
|
||
async create() { | ||
return EmployeeBenefit.create(this.request.body) | ||
.then((employeeBenefit) => { | ||
return this.response.status(201).json({ employeeBenefit }) | ||
}) | ||
.catch((error) => { | ||
return this.response | ||
.status(422) | ||
.json({ message: `employee benefit creation failed: ${error}` }) | ||
}) | ||
} | ||
|
||
async update() { | ||
const employeeBenefit = await this.loadEmployeeBenefit() | ||
if (isNil(employeeBenefit)) | ||
return this.response.status(404).json({ message: "employee benefit not found." }) | ||
|
||
return employeeBenefit | ||
.update(this.request.body) | ||
.then((employeeBenefit) => { | ||
const serializedemployeeBenefit = EmployeeBenefitSerializer.asDetailed(employeeBenefit) | ||
return this.response.json({ | ||
employeeBenefit: serializedemployeeBenefit, | ||
}) | ||
}) | ||
.catch((error) => { | ||
return this.response | ||
.status(422) | ||
.json({ message: `employee benefit update failed: ${error}` }) | ||
}) | ||
} | ||
|
||
async destroy() { | ||
const employeeBenefit = await this.loadEmployeeBenefit() | ||
if (isNil(employeeBenefit)) | ||
return this.response.status(404).json({ message: "employee benefit not found." }) | ||
|
||
return employeeBenefit | ||
.destroy() | ||
.then(() => { | ||
return this.response.status(204).end() | ||
}) | ||
.catch((error) => { | ||
return this.response | ||
.status(422) | ||
.json({ message: `employee benefit deletion failed: ${error}` }) | ||
}) | ||
} | ||
|
||
private loadEmployeeBenefit(): Promise<EmployeeBenefit | null> { | ||
return EmployeeBenefit.findByPk(this.params.employeeBenefitId) | ||
} | ||
} | ||
|
||
export default EmployeeBenefitsController |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export { EmployeeBenefitsController } from "./employee-benefits-controller" | ||
export { FiscalPeriodsController } from "./fiscal-periods-controller" | ||
export { FundingSubmissionLineJsonsController } from "./funding-submission-line-jsons-controller" | ||
export { PaymentsController } from "./payments-controller" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
...ions/2023.12.13T16.09.55.add-unique-index-to-fiscal-year-month-on-fiscal-periods-table.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { Migration } from "@/db/umzug" | ||
|
||
export const up: Migration = async ({ context: queryInterface }) => { | ||
await queryInterface.addIndex("fiscal_periods", { | ||
fields: ["fiscal_year", "month"], | ||
unique: true, | ||
}) | ||
} | ||
|
||
export const down: Migration = async ({ context: queryInterface }) => { | ||
await queryInterface.removeIndex("fiscal_periods", ["fiscal_year", "month"]) | ||
} |
64 changes: 64 additions & 0 deletions
64
...c/db/migrations/2023.12.13T16.09.56.fix-typo-in-foriegn-key-in-employee-benefits-table.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import type { Migration } from "@/db/umzug" | ||
|
||
type ForeignKeyReference = { | ||
constraint_name: string // "FK__employee___fislc__22401542" | ||
constraintName: string // "FK__employee___fislc__22401542" | ||
constraintCatalog: string // "elcc_development" | ||
constraintSchema: string // "dbo" | ||
tableName: string // "employee_benefits" | ||
tableSchema: string // "dbo" | ||
tableCatalog: string // "elcc_development" | ||
columnName: string // "fislcal_period_id" | ||
referencedTableSchema: string // "dbo" | ||
referencedCatalog: string // "elcc_development" | ||
referencedTableName: string // "fiscal_periods" | ||
referencedColumnName: string // "id" | ||
} | ||
|
||
export const up: Migration = async ({ context: queryInterface }) => { | ||
const references = (await queryInterface.getForeignKeyReferencesForTable( | ||
"employee_benefits" | ||
)) as ForeignKeyReference[] | ||
|
||
const foreignKey = references.find((reference) => reference.columnName === "fislcal_period_id") | ||
if (foreignKey !== undefined) { | ||
await queryInterface.removeConstraint("employee_benefits", foreignKey.constraintName) | ||
} | ||
|
||
await queryInterface.renameColumn("employee_benefits", "fislcal_period_id", "fiscal_period_id") | ||
|
||
await queryInterface.addConstraint("employee_benefits", { | ||
fields: ["fiscal_period_id"], | ||
type: "foreign key", | ||
references: { | ||
table: "fiscal_periods", | ||
field: "id", | ||
}, | ||
onDelete: "", // RESTRICT is default for MSSQL, so you can't set it; and must use and empty string | ||
onUpdate: "", | ||
}) | ||
} | ||
|
||
export const down: Migration = async ({ context: queryInterface }) => { | ||
const references = (await queryInterface.getForeignKeyReferencesForTable( | ||
"employee_benefits" | ||
)) as ForeignKeyReference[] | ||
|
||
const foreignKey = references.find((reference) => reference.columnName === "fiscal_period_id") | ||
if (foreignKey !== undefined) { | ||
await queryInterface.removeConstraint("employee_benefits", foreignKey.constraintName) | ||
} | ||
|
||
await queryInterface.renameColumn("employee_benefits", "fiscal_period_id", "fislcal_period_id") | ||
|
||
await queryInterface.addConstraint("employee_benefits", { | ||
fields: ["fislcal_period_id"], | ||
type: "foreign key", | ||
references: { | ||
table: "fiscal_periods", | ||
field: "id", | ||
}, | ||
onDelete: "", // RESTRICT is default for MSSQL, so you can't set it; and must use and empty string | ||
onUpdate: "", | ||
}) | ||
} |
12 changes: 12 additions & 0 deletions
12
....13T16.09.57.add-unique-index-to-centre-id-fiscal-period-id-on-employee-benefits-table.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { Migration } from "@/db/umzug" | ||
|
||
export const up: Migration = async ({ context: queryInterface }) => { | ||
await queryInterface.addIndex("employee_benefits", { | ||
fields: ["centre_id", "fiscal_period_id"], | ||
unique: true, | ||
}) | ||
} | ||
|
||
export const down: Migration = async ({ context: queryInterface }) => { | ||
await queryInterface.removeIndex("employee_benefits", ["centre_id", "fiscal_period_id"]) | ||
} |
36 changes: 36 additions & 0 deletions
36
api/src/db/migrations/2023.12.14T16.04.37.remove-employee-benefits-from-old-tables.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import type { Migration } from "@/db/umzug" | ||
|
||
import { FundingSubmissionLine, FundingSubmissionLineJson } from "@/models" | ||
|
||
export const up: Migration = async () => { | ||
const EMPLOYEE_BENEFITS_SECTION_NAME = "Employee Benefits" | ||
|
||
const fundingSubmissionLines = await FundingSubmissionLine.findAll({ | ||
where: { | ||
sectionName: EMPLOYEE_BENEFITS_SECTION_NAME, | ||
}, | ||
}) | ||
const fundingSubmissionLinesIds = fundingSubmissionLines.map( | ||
(fundingSubmissionLine) => fundingSubmissionLine.id | ||
) | ||
|
||
await FundingSubmissionLineJson.findEach(async (fundingSubmissionLineJson) => { | ||
const lines = fundingSubmissionLineJson.lines | ||
const cleanLines = lines.filter( | ||
(line) => !fundingSubmissionLinesIds.includes(line.submissionLineId) | ||
) | ||
|
||
await fundingSubmissionLineJson.update({ lines: cleanLines }) | ||
return | ||
}) | ||
|
||
await FundingSubmissionLine.destroy({ | ||
where: { | ||
sectionName: EMPLOYEE_BENEFITS_SECTION_NAME, | ||
}, | ||
}) | ||
} | ||
|
||
export const down: Migration = async () => { | ||
// no-op - this migration is not reversible, it is however idempotent | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { Attributes, CreationOptional, FindOptions, Model, ModelStatic, Op } from "sequelize" | ||
|
||
// See /home/marlen/code/icefoganalytics/elcc-data-management/api/node_modules/sequelize/types/model.d.ts -> Model | ||
export abstract class BaseModel< | ||
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any | ||
TModelAttributes extends {} = any, | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
TCreationAttributes extends {} = TModelAttributes, | ||
> extends Model<TModelAttributes, TCreationAttributes> { | ||
declare id: CreationOptional<number> | ||
|
||
// See /home/marlen/code/icefoganalytics/elcc-data-management/api/node_modules/sequelize/types/model.d.ts -> findAll | ||
// Taken from https://api.rubyonrails.org/v7.1.0/classes/ActiveRecord/Batches.html#method-i-find_each | ||
// Enforces sort by id, overwriting any supplied order | ||
public static async findEach<M extends BaseModel>( | ||
this: ModelStatic<M>, | ||
processFunction: (record: M) => Promise<void> | ||
): Promise<void> | ||
public static async findEach<M extends BaseModel>( | ||
this: ModelStatic<M>, | ||
options: FindOptions<Attributes<M>> & { batchSize?: number }, | ||
processFunction: (record: M) => Promise<void> | ||
): Promise<void> | ||
public static async findEach<M extends BaseModel>( | ||
this: ModelStatic<M>, | ||
optionsOrFunction: | ||
| ((record: M) => Promise<void>) | ||
| (FindOptions<Attributes<M>> & { batchSize?: number }), | ||
maybeFunction?: (record: M) => Promise<void> | ||
): Promise<void> { | ||
let options: FindOptions<Attributes<M>> & { batchSize?: number } | ||
let processFunction: (record: M) => Promise<void> | ||
|
||
if (typeof optionsOrFunction === "function") { | ||
options = {} | ||
processFunction = optionsOrFunction | ||
} else { | ||
options = optionsOrFunction | ||
processFunction = maybeFunction! | ||
} | ||
|
||
const batchSize = options.batchSize ?? 1000 | ||
let lastId = 0 | ||
let continueProcessing = true | ||
|
||
while (continueProcessing) { | ||
const whereClause = { | ||
...options.where, | ||
id: { [Op.gt]: lastId }, | ||
} | ||
const records = await this.findAll({ | ||
...options, | ||
where: whereClause, | ||
limit: batchSize, | ||
order: [["id", "ASC"]], | ||
}) | ||
|
||
for (const record of records) { | ||
await processFunction(record) | ||
lastId = record.id | ||
} | ||
|
||
if (records.length < batchSize) { | ||
continueProcessing = false | ||
} | ||
} | ||
} | ||
} | ||
|
||
export default BaseModel |
Oops, something went wrong.