Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
2d3482f
[TM-2367] Disrtubance entity dto
Marcolr-dev Sep 3, 2025
a1e3b3b
[TM-2367] Entity dto
Marcolr-dev Sep 3, 2025
12bc6fd
[TM-2367] add distrubance report components
LimberHope Sep 3, 2025
a900012
[TM-2367] Merge branch 'feat/TM-2367-build-index-view-disturbance-rep…
LimberHope Sep 3, 2025
fb42d05
[TM-2367] fix lint
LimberHope Sep 3, 2025
62459fb
[TM-2367] update components for disturbance report
LimberHope Sep 3, 2025
52f8fe7
[TM-2367] change siteId -> projectId
LimberHope Sep 3, 2025
fc129b4
[TM-2367] add createdBy
LimberHope Sep 3, 2025
0024881
[TM-2367] add updateChangeRequest for dto
LimberHope Sep 4, 2025
13b9c5e
[TM-2367] Merge branch 'staging' into feat/TM-2367-build-index-view-d…
LimberHope Sep 4, 2025
d8a72d5
[TM-2367] update unit test
LimberHope Sep 5, 2025
eebe958
[TM-2367] update unit test
LimberHope Sep 5, 2025
f445e4d
[TM-2367] add disturbance report configuration for linked fields
LimberHope Sep 5, 2025
5f37c16
[TM-2367] update intensity field
LimberHope Sep 8, 2025
b0a8074
[TM-2367] [TM-2369] update disturbance report entity
LimberHope Sep 8, 2025
a7f75e0
[TM-2367] [TM-2369] add propertyAffected field
LimberHope Sep 8, 2025
bf5203c
[TM-2367] [TM-2369] update entity entity and dto
LimberHope Sep 8, 2025
1e82fa3
[TM-2367] [TM-2366] add siteAffected and polygonAffected fields
LimberHope Sep 10, 2025
33be906
[TM-2367] Merge branch 'staging' into feat/TM-2367-build-index-view-d…
LimberHope Sep 11, 2025
d40b41c
[TM-2367] add forUuid for project entity
LimberHope Sep 12, 2025
02b444f
[TM-2367] [TM-2366] add disturbanceAffectedSite and disturbanceAffect…
LimberHope Sep 15, 2025
ef56e5f
[TM-2433] feat/TM-2433-Remove-migrated-leaderships-fields-from-organi…
Scriptmatico Sep 16, 2025
4fe7a74
[TM-2458] add selfvalidation with error ( research service is not run…
egrojMonroy Sep 16, 2025
5f7c36c
[TM-2433] add changes for organisation.airtable-entity.ts
Scriptmatico Sep 16, 2025
8e845b1
Merge pull request #306 from wri/main
roguenet Sep 17, 2025
4e60dd9
[TM-2441] update funding type entity
LimberHope Sep 18, 2025
e5dfc1f
[TM-2441] remove unneccesary import
LimberHope Sep 18, 2025
422249d
[TM-2441] update unit test
LimberHope Sep 18, 2025
22794eb
[TM-2441] update unit test
LimberHope Sep 18, 2025
9a651cb
Merge pull request #307 from wri/TM-2441-financial-report-change-requ…
LimberHope Sep 18, 2025
4e4f8bc
[TM-2367] Merge branch 'staging' into feat/TM-2367-build-index-view-d…
LimberHope Sep 18, 2025
b8e8acf
[TM-2338] add site validation get
egrojMonroy Sep 18, 2025
e084d0e
[TM-2338] add specs for site validation
egrojMonroy Sep 18, 2025
148ec3a
[TM-2338] remove unnnecesary spec and fix lint
egrojMonroy Sep 18, 2025
d926f73
[TM-2338] filter invalid criteria in site query, and changes
egrojMonroy Sep 18, 2025
50a942c
[TM-2367] [TM-2357] remove unneccesary fieds
LimberHope Sep 19, 2025
06828b0
[TM-2338] fix unit tests
egrojMonroy Sep 19, 2025
9975d0b
[TM-2338] remove unused variables
egrojMonroy Sep 19, 2025
7d86f3f
[TM-2458] update validation response to use json decorator and fix se…
cesarLima1 Sep 19, 2025
2b941b7
[TM-2338] add complete criteria list
egrojMonroy Sep 19, 2025
4e24f89
[TM-2367] [TM-2366] add disturbance report entry entity
LimberHope Sep 19, 2025
bdf3ab2
[TM-2338] remove unused variable
egrojMonroy Sep 19, 2025
795398c
[TM-2367] [TM-2366] remove comment
LimberHope Sep 19, 2025
9b579ad
[TM-2367] [TM-2366] remove unneccesary input types
LimberHope Sep 19, 2025
5b6b854
[TM-2367] [TM-2366] fix lint
LimberHope Sep 19, 2025
34d3d3c
[TM-2367] [TM-2366] update full dto response
LimberHope Sep 19, 2025
af14000
[TM-2367] [TM-2366] update unit test
LimberHope Sep 19, 2025
351578a
[TM-2367] [TM-2366] update unit test
LimberHope Sep 19, 2025
5d1b5fb
[TM-2367] [TM-2366] resolved comments
LimberHope Sep 19, 2025
cd6c8b9
Merge pull request #308 from wri/feat/TM-2338-add-site-validation-get
egrojMonroy Sep 19, 2025
9e6564b
[TM-2367] [TM-2366] update dto
LimberHope Sep 19, 2025
fef46fb
Merge pull request #290 from wri/feat/TM-2367-build-index-view-distur…
LimberHope Sep 19, 2025
fdb91a0
[TM-2338] add index page standar for resesarch service
egrojMonroy Sep 22, 2025
712812f
[TM-2338] remove page test
egrojMonroy Sep 22, 2025
bc6a31b
[TM-2338] return validation without index
egrojMonroy Sep 22, 2025
e364101
[TM-2458] start up spikes validator
cesarLima1 Sep 22, 2025
5356118
[TM-2338] fix again pages
egrojMonroy Sep 22, 2025
400780a
[TM-2458] add unit tests for spike and self intersection validators
cesarLima1 Sep 22, 2025
fe93132
[TM-2458] Merge branch 'staging' into feat/TM-2458-self-spike-validation
cesarLima1 Sep 22, 2025
da72764
[TM-2458] use correct dto reference
cesarLima1 Sep 22, 2025
3be3baf
[TM-2338] clean code
egrojMonroy Sep 22, 2025
b1acc23
Merge pull request #310 from wri/feat/TM-2338-fix-index-standar-research
egrojMonroy Sep 22, 2025
757b447
[TM-2458] REST principles instead of RPC patterns
cesarLima1 Sep 23, 2025
858aa8e
[TM-2458] update unit test
cesarLima1 Sep 23, 2025
430d696
[TM-2458] Merge branch 'staging' into feat/TM-2458-self-spike-validation
cesarLima1 Sep 23, 2025
17023a0
[TM-2458] update paths to be REST
cesarLima1 Sep 24, 2025
0a234aa
[TM-2458] [TM2458] update validation dto
cesarLima1 Sep 24, 2025
0868131
[TM-2458] improving type safety, error handling, and code consistency
cesarLima1 Sep 25, 2025
060a323
[TM-2458] update unit test
cesarLima1 Sep 25, 2025
33d0c69
Merge pull request #304 from wri/feat/TM-2433-Remove-migrated-leaders…
Scriptmatico Sep 25, 2025
84c7163
[TM-2458] improving type safety and following the established pattern…
cesarLima1 Sep 25, 2025
c5d82c8
[TM-2458] update unit tests
cesarLima1 Sep 25, 2025
887e316
[TM-2458] update unit tests
cesarLima1 Sep 25, 2025
a503109
[TM-2458] update unit tests
cesarLima1 Sep 25, 2025
f17258c
[TM-2489] start up complete data validator
cesarLima1 Sep 26, 2025
41d0eb9
[TM-2489] add plant start date validator
cesarLima1 Sep 26, 2025
4525acc
Merge pull request #311 from wri/feat/TM-2458-self-spike-validation
cesarLima1 Sep 26, 2025
cd76bac
[TM-2489] use luxon library for dates in plant start date validator
cesarLima1 Sep 26, 2025
4aebfd7
[TM-2478] include polygon size validator
cesarLima1 Sep 26, 2025
78d7d7a
[TM-2478] add estimated area validator
cesarLima1 Sep 26, 2025
3b104f5
[TM-2478] update unit test
cesarLima1 Sep 26, 2025
84c1967
[TM-2480] add update indicators for financial report
LimberHope Sep 26, 2025
b86965a
[TM-2480] fix lint
LimberHope Sep 26, 2025
addba69
[TM-2480] update unit test
LimberHope Sep 26, 2025
645441f
[TM-2480] update logic
LimberHope Sep 26, 2025
796ed34
[TM-2480] update unit test
LimberHope Sep 26, 2025
d13f7ac
[TM-2480] update unit test
LimberHope Sep 26, 2025
3135857
[TM-2480] update unit test
LimberHope Sep 26, 2025
c95afbf
Merge pull request #315 from wri/TM-2480-duplication-of-financial-dat…
LimberHope Sep 26, 2025
80e74eb
[TM-2478] Merge branch 'staging' into feat/TM-2478-polygon-size-estim…
cesarLima1 Sep 26, 2025
d600c1a
[TM-2489] Merge branch 'staging' into feat/TM-2489-complete-data-and-…
cesarLima1 Sep 26, 2025
adc7ea5
[TM-2478] Merge branch 'feat/TM-2489-complete-data-and-plant-start-da…
cesarLima1 Sep 26, 2025
4003ff2
[TM-2469] Added Funding Types to Airtable
roguenet Sep 27, 2025
357a53e
[TM-2469] Added Financial Reports to Airtable
roguenet Sep 27, 2025
d58c463
[TM-2489] include validations ids for possible cases
cesarLima1 Sep 29, 2025
cebfc53
Merge pull request #320 from wri/feat/TM-2489-include-validation-ids
cesarLima1 Sep 29, 2025
54dddc9
[TM-2489] Merge branch 'release/opulent-opal' into feat/TM-2489-compl…
cesarLima1 Sep 29, 2025
97438e0
[TM-2478] Merge branch 'feat/TM-2489-complete-data-and-plant-start-da…
cesarLima1 Sep 29, 2025
19deb48
Merge pull request #319 from wri/feat/TM-2469-funding-financial-repor…
roguenet Sep 29, 2025
9c26e2d
[TM-2489] improve code readability and use native date method
cesarLima1 Sep 29, 2025
482c241
[TM-2478] Merge branch 'feat/TM-2489-complete-data-and-plant-start-da…
cesarLima1 Sep 29, 2025
958d3da
[TM-2489] rollback to use luxon dependency for dates
cesarLima1 Sep 29, 2025
4faeb02
[TM-2478] Merge branch 'feat/TM-2489-complete-data-and-plant-start-da…
cesarLima1 Sep 29, 2025
8b0e5eb
Merge pull request #313 from wri/feat/TM-2489-complete-data-and-plant…
cesarLima1 Sep 30, 2025
dbd7059
[TM-2337] fix data type for criteriaID
egrojMonroy Sep 30, 2025
6df42a2
[TM-2367] revert changes
LimberHope Sep 30, 2025
1e6059a
[TM-2337] explicit check
egrojMonroy Sep 30, 2025
80389d4
[TM-2337] manually controll number conversion
egrojMonroy Sep 30, 2025
5ed3576
[TM-2367] revert changes
LimberHope Sep 30, 2025
57ab24e
Merge pull request #321 from wri/fix/TM-2337-fix-type
egrojMonroy Sep 30, 2025
54384da
Merge pull request #322 from wri/TM-2367-building-out-disturbance-rep…
LimberHope Sep 30, 2025
cb2d407
[TM-2478] following existing patterns
cesarLima1 Oct 1, 2025
325026f
[TM-2478] update unit test
cesarLima1 Oct 1, 2025
9589f96
Merge pull request #314 from wri/feat/TM-2478-polygon-size-estimated-…
cesarLima1 Oct 1, 2025
e3dec09
[TM-2433] These two were removed from PHP's linked fields, but missed…
roguenet Oct 1, 2025
3ef27ca
Merge pull request #324 from wri/feat/TM-2433-remove-org-relation-config
roguenet Oct 1, 2025
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
3 changes: 3 additions & 0 deletions apps/entity-service/src/entities/dto/funding-type.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ export class FundingTypeDto extends AssociationDto {

@ApiProperty({ nullable: true, type: String })
organisationUuid: string | null;

@ApiProperty({ nullable: true, type: Number })
financialReportId: string | null;
}
15 changes: 14 additions & 1 deletion apps/entity-service/src/entities/processors/entity-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
NEEDS_MORE_INFORMATION,
RESTORATION_IN_PROGRESS
} from "@terramatch-microservices/database/constants/status";
import { ProjectReport } from "@terramatch-microservices/database/entities";
import { FinancialReport, ProjectReport } from "@terramatch-microservices/database/entities";

export type Aggregate<M extends Model<M>> = {
func: string;
Expand Down Expand Up @@ -204,6 +204,10 @@ export abstract class ReportProcessor<
}
}

if (model instanceof FinancialReport && update.status === "approved") {
await this.processFinancialReportSpecificLogic(model);
}

await super.update(model, update);
}

Expand All @@ -214,4 +218,13 @@ export abstract class ReportProcessor<
return { [Op.or]: [null, false] };
}
};

/**
* Override this method in specific report processors to add custom logic
* @param model The report model being processed
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async processFinancialReportSpecificLogic(model: FinancialReport): Promise<void> {
// This method will be overridden in FinancialReportProcessor
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { FinancialReport, Media } from "@terramatch-microservices/database/entities";
import {
FinancialReport,
FinancialIndicator,
Media,
Organisation,
FundingType
} from "@terramatch-microservices/database/entities";
import { Test } from "@nestjs/testing";
import { MediaService } from "@terramatch-microservices/common/media/media.service";
import { createMock, DeepMocked } from "@golevelup/ts-jest";
Expand Down Expand Up @@ -30,6 +36,9 @@ describe("FinancialReportProcessor", () => {

beforeEach(async () => {
await FinancialReport.truncate();
await FinancialIndicator.truncate();
await FundingType.truncate();
await Organisation.truncate();

const module = await Test.createTestingModule({
providers: [
Expand Down Expand Up @@ -198,19 +207,22 @@ describe("FinancialReportProcessor", () => {
});

describe("getFundingTypes", () => {
it("should return funding types for organisation", async () => {
it("should return funding types for financial report", async () => {
const organisation = await OrganisationFactory.create();
const financialReport = await FinancialReportFactory.create({ organisationId: organisation.id });
await FundingTypeFactory.createMany(2, { organisationId: organisation.uuid });
await FundingTypeFactory.createMany(2, { financialReportId: financialReport.id });

await financialReport.reload({ include: [{ association: "organisation" }] });

const result = await (
processor as unknown as { getFundingTypes: (report: FinancialReport) => Promise<unknown[]> }
processor as unknown as { getFundingTypes: (report: FinancialReport) => Promise<FundingTypeDto[]> }
).getFundingTypes(financialReport);

expect(result).toHaveLength(2);
expect(result[0]).toBeInstanceOf(FundingTypeDto);
// Ensure DTOs are populated with association props
expect(result[0].entityType).toBe("financialReports");
expect(typeof result[0].entityUuid).toBe("string");
});
});

Expand All @@ -233,4 +245,278 @@ describe("FinancialReportProcessor", () => {
expect(result[0]).toBeInstanceOf(Promise);
});
});

describe("processFinancialReportSpecificLogic", () => {
it("should update organisation fields when financial report is approved", async () => {
const organisation = await OrganisationFactory.create({
finStartMonth: null,
currency: undefined
});
const financialReport = await FinancialReportFactory.create({
organisationId: organisation.id,
finStartMonth: 3,
currency: "USD"
});

await (
processor as unknown as {
processFinancialReportSpecificLogic: (report: FinancialReport) => Promise<void>;
}
).processFinancialReportSpecificLogic(financialReport);

await organisation.reload();
expect(organisation.finStartMonth).toBe(3);
expect(organisation.currency).toBe("USD");
});

it("should log warning and return early when organisation is not found", async () => {
const organisation = await OrganisationFactory.create();
const financialReport = await FinancialReportFactory.create({
organisationId: organisation.id
});

financialReport.organisationId = 99999;

const mockWarn = jest.fn();
(processor as unknown as { logger: { warn: jest.Mock } }).logger = {
warn: mockWarn
};

await (
processor as unknown as {
processFinancialReportSpecificLogic: (report: FinancialReport) => Promise<void>;
}
).processFinancialReportSpecificLogic(financialReport);

expect(mockWarn).toHaveBeenCalledWith(`Organisation not found for FinancialReport ${financialReport.uuid}`);
});

it("should create new financial indicators when none exist in organisation", async () => {
const organisation = await OrganisationFactory.create();
const financialReport = await FinancialReportFactory.create({
organisationId: organisation.id
});

await FinancialIndicatorFactory.create({
financialReportId: financialReport.id,
organisationId: organisation.id,
year: 2023,
collection: "revenue",
amount: 1000,
description: "Test revenue",
exchangeRate: 1.0
});

await FinancialIndicatorFactory.create({
financialReportId: financialReport.id,
organisationId: organisation.id,
year: 2023,
collection: "expenses",
amount: 500,
description: "Test expenses",
exchangeRate: 1.0
});

const mockBulkCreate = jest.spyOn(FinancialIndicator, "bulkCreate").mockResolvedValue([]);
const mockUpdate = jest.spyOn(FinancialIndicator, "update").mockResolvedValue([1]);

await (
processor as unknown as {
processFinancialReportSpecificLogic: (report: FinancialReport) => Promise<void>;
}
).processFinancialReportSpecificLogic(financialReport);

expect(mockBulkCreate).toHaveBeenCalledWith([
{
organisationId: organisation.id,
year: 2023,
collection: "revenue",
amount: 1000,
description: "Test revenue",
exchangeRate: 1.0
},
{
organisationId: organisation.id,
year: 2023,
collection: "expenses",
amount: 500,
description: "Test expenses",
exchangeRate: 1.0
}
]);

expect(mockUpdate).not.toHaveBeenCalled();

mockBulkCreate.mockRestore();
mockUpdate.mockRestore();
});

it("should update existing financial indicators when they exist in organisation", async () => {
const organisation = await OrganisationFactory.create();
const financialReport = await FinancialReportFactory.create({
organisationId: organisation.id
});

const existingIndicator1 = await FinancialIndicatorFactory.create({
organisationId: organisation.id,
year: 2023,
collection: "revenue",
amount: 500,
description: "Old revenue",
exchangeRate: 1.2
});

const existingIndicator2 = await FinancialIndicatorFactory.create({
organisationId: organisation.id,
year: 2023,
collection: "expenses",
amount: 200,
description: "Old expenses",
exchangeRate: 1.1
});

await FinancialIndicatorFactory.create({
financialReportId: financialReport.id,
organisationId: organisation.id,
year: 2023,
collection: "revenue",
amount: 1000,
description: "New revenue",
exchangeRate: 1.0
});

await FinancialIndicatorFactory.create({
financialReportId: financialReport.id,
organisationId: organisation.id,
year: 2023,
collection: "expenses",
amount: 500,
description: "New expenses",
exchangeRate: 1.0
});

const mockBulkCreate = jest.spyOn(FinancialIndicator, "bulkCreate").mockResolvedValue([]);
const mockUpdate = jest.spyOn(FinancialIndicator, "update").mockResolvedValue([1]);

await (
processor as unknown as {
processFinancialReportSpecificLogic: (report: FinancialReport) => Promise<void>;
}
).processFinancialReportSpecificLogic(financialReport);

expect(mockUpdate).toHaveBeenCalledTimes(2);
expect(mockUpdate).toHaveBeenCalledWith(
{
amount: 1000,
description: "New revenue",
exchangeRate: 1.0
},
{ where: { id: existingIndicator1.id } }
);
expect(mockUpdate).toHaveBeenCalledWith(
{
amount: 500,
description: "New expenses",
exchangeRate: 1.0
},
{ where: { id: existingIndicator2.id } }
);

expect(mockBulkCreate).not.toHaveBeenCalled();

mockBulkCreate.mockRestore();
mockUpdate.mockRestore();
});

it("should handle mixed scenario with both creating and updating indicators", async () => {
const organisation = await OrganisationFactory.create();
const financialReport = await FinancialReportFactory.create({
organisationId: organisation.id
});

const existingIndicator = await FinancialIndicatorFactory.create({
organisationId: organisation.id,
year: 2023,
collection: "revenue",
amount: 500,
description: "Old revenue",
exchangeRate: 1.2
});

await FinancialIndicatorFactory.create({
financialReportId: financialReport.id,
organisationId: organisation.id,
year: 2023,
collection: "revenue",
amount: 1000,
description: "New revenue",
exchangeRate: 1.0
});

await FinancialIndicatorFactory.create({
financialReportId: financialReport.id,
organisationId: organisation.id,
year: 2023,
collection: "expenses",
amount: 500,
description: "New expenses",
exchangeRate: 1.0
});

const mockBulkCreate = jest.spyOn(FinancialIndicator, "bulkCreate").mockResolvedValue([]);
const mockUpdate = jest.spyOn(FinancialIndicator, "update").mockResolvedValue([1]);

await (
processor as unknown as {
processFinancialReportSpecificLogic: (report: FinancialReport) => Promise<void>;
}
).processFinancialReportSpecificLogic(financialReport);

expect(mockBulkCreate).toHaveBeenCalledWith([
{
organisationId: organisation.id,
year: 2023,
collection: "expenses",
amount: 500,
description: "New expenses",
exchangeRate: 1.0
}
]);

expect(mockUpdate).toHaveBeenCalledTimes(1);
expect(mockUpdate).toHaveBeenCalledWith(
{
amount: 1000,
description: "New revenue",
exchangeRate: 1.0
},
{ where: { id: existingIndicator.id } }
);

mockBulkCreate.mockRestore();
mockUpdate.mockRestore();
});

it("should handle case when no financial indicators exist in report", async () => {
const organisation = await OrganisationFactory.create();
const financialReport = await FinancialReportFactory.create({
organisationId: organisation.id
});

const mockBulkCreate = jest.spyOn(FinancialIndicator, "bulkCreate").mockResolvedValue([]);
const mockUpdate = jest.spyOn(FinancialIndicator, "update").mockResolvedValue([1]);

await (
processor as unknown as {
processFinancialReportSpecificLogic: (report: FinancialReport) => Promise<void>;
}
).processFinancialReportSpecificLogic(financialReport);

expect(mockBulkCreate).not.toHaveBeenCalled();
expect(mockUpdate).not.toHaveBeenCalled();

mockBulkCreate.mockRestore();
mockUpdate.mockRestore();
});
});
});
Loading
Loading