diff --git a/backend/src/migrations/1721632351165-BalanceSheetAndNonPoliticalAffiliation.ts b/backend/src/migrations/1721632351165-BalanceSheetAndNonPoliticalAffiliation.ts new file mode 100644 index 000000000..4e3f43de2 --- /dev/null +++ b/backend/src/migrations/1721632351165-BalanceSheetAndNonPoliticalAffiliation.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class BalanceSheetAndNonPoliticalAffiliation1721632351165 + implements MigrationInterface +{ + name = 'BalanceSheetAndNonPoliticalAffiliation1721632351165'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "organization_legal" ADD "balance_sheet_file" character varying`, + ); + await queryRunner.query( + `ALTER TABLE "organization_legal" ADD "non_political_affiliation_file" character varying`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "organization_legal" DROP COLUMN "non_political_affiliation_file"`, + ); + await queryRunner.query( + `ALTER TABLE "organization_legal" DROP COLUMN "balance_sheet_file"`, + ); + } +} diff --git a/backend/src/migrations/1721741119685-AddOrganizationAliasToUserApplicationsView.ts b/backend/src/migrations/1721741119685-AddOrganizationAliasToUserApplicationsView.ts new file mode 100644 index 000000000..986bd3868 --- /dev/null +++ b/backend/src/migrations/1721741119685-AddOrganizationAliasToUserApplicationsView.ts @@ -0,0 +1,84 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddOrganizationAliasToUserApplicationsView1721741119685 + implements MigrationInterface +{ + name = 'AddOrganizationAliasToUserApplicationsView1721741119685'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, + ['VIEW', 'UserApplicationsView', 'public'], + ); + await queryRunner.query(`DROP VIEW "UserApplicationsView"`); + await queryRunner.query(`CREATE VIEW "UserApplicationsView" AS + SELECT u.id, + u.name, + u.email, + u.phone, + u.status, + u.role, + u.organization_id AS "organizationId", + u.created_on AS "createdOn", + u.updated_on AS "updatedOn", + og.alias as "organizationAlias", + array_agg(DISTINCT a.id) AS "availableAppsIDs", + json_agg(DISTINCT jsonb_build_object('id', a.id, 'name', a.name, 'type', a.type)) AS "availableApps" + FROM "user" u + LEFT JOIN user_ong_application uoa ON u.id = uoa.user_id AND uoa.status = 'active'::user_ong_application_status_enum + LEFT JOIN ong_application oa ON uoa.ong_application_id = oa.id AND oa.status = 'active'::ong_application_status_enum + LEFT JOIN application a ON (oa.application_id = a.id OR a.type = 'independent'::application_type_enum) AND a.status = 'active'::application_status_enum + LEFT JOIN organization o ON u.organization_id = o.id + LEFT JOIN organization_general og ON o.organization_general_id = og.id + WHERE u.role = 'employee'::user_role_enum AND (u.status = ANY (ARRAY['active'::user_status_enum, 'restricted'::user_status_enum])) AND u.deleted_on IS NULL + GROUP BY u.id, og.alias; + + `); + await queryRunner.query( + `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, + [ + 'public', + 'VIEW', + 'UserApplicationsView', + "SELECT u.id,\n u.name,\n u.email,\n u.phone,\n u.status,\n u.role,\n u.organization_id AS \"organizationId\",\n u.created_on AS \"createdOn\",\n u.updated_on AS \"updatedOn\",\n og.alias as \"organizationAlias\",\n array_agg(DISTINCT a.id) AS \"availableAppsIDs\",\n json_agg(DISTINCT jsonb_build_object('id', a.id, 'name', a.name, 'type', a.type)) AS \"availableApps\"\n FROM \"user\" u\n LEFT JOIN user_ong_application uoa ON u.id = uoa.user_id AND uoa.status = 'active'::user_ong_application_status_enum\n LEFT JOIN ong_application oa ON uoa.ong_application_id = oa.id AND oa.status = 'active'::ong_application_status_enum\n LEFT JOIN application a ON (oa.application_id = a.id OR a.type = 'independent'::application_type_enum) AND a.status = 'active'::application_status_enum\n LEFT JOIN organization o ON u.organization_id = o.id\n LEFT JOIN organization_general og ON o.organization_general_id = og.id\n WHERE u.role = 'employee'::user_role_enum AND (u.status = ANY (ARRAY['active'::user_status_enum, 'restricted'::user_status_enum])) AND u.deleted_on IS NULL\n GROUP BY u.id, og.alias;", + ], + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, + ['VIEW', 'UserApplicationsView', 'public'], + ); + await queryRunner.query(`DROP VIEW "UserApplicationsView"`); + await queryRunner.query(`CREATE VIEW "UserApplicationsView" AS SELECT u.id, + u.name, + u.email, + u.phone, + u.status, + u.role, + u.organization_id AS "organizationId", + u.created_on AS "createdOn", + u.updated_on AS "updatedOn", + og.alias, + array_agg(DISTINCT a.id) AS "availableAppsIDs", + json_agg(DISTINCT jsonb_build_object('id', a.id, 'name', a.name, 'type', a.type)) AS "availableApps" + FROM "user" u + LEFT JOIN user_ong_application uoa ON u.id = uoa.user_id AND uoa.status = 'active'::user_ong_application_status_enum + LEFT JOIN ong_application oa ON uoa.ong_application_id = oa.id AND oa.status = 'active'::ong_application_status_enum + LEFT JOIN application a ON (oa.application_id = a.id OR a.type = 'independent'::application_type_enum) AND a.status = 'active'::application_status_enum + LEFT JOIN organization o ON u.organization_id = o.id + LEFT JOIN organization_general og ON o.organization_general_id = og.id + WHERE u.role = 'employee'::user_role_enum AND (u.status = ANY (ARRAY['active'::user_status_enum, 'restricted'::user_status_enum])) AND u.deleted_on IS NULL + GROUP BY u.id, og.alias;`); + await queryRunner.query( + `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, + [ + 'public', + 'VIEW', + 'UserApplicationsView', + "SELECT u.id,\n u.name,\n u.email,\n u.phone,\n u.status,\n u.role,\n u.organization_id AS \"organizationId\",\n u.created_on AS \"createdOn\",\n u.updated_on AS \"updatedOn\",\n og.alias,\n array_agg(DISTINCT a.id) AS \"availableAppsIDs\",\n json_agg(DISTINCT jsonb_build_object('id', a.id, 'name', a.name, 'type', a.type)) AS \"availableApps\"\n FROM \"user\" u\n LEFT JOIN user_ong_application uoa ON u.id = uoa.user_id AND uoa.status = 'active'::user_ong_application_status_enum\n LEFT JOIN ong_application oa ON uoa.ong_application_id = oa.id AND oa.status = 'active'::ong_application_status_enum\n LEFT JOIN application a ON (oa.application_id = a.id OR a.type = 'independent'::application_type_enum) AND a.status = 'active'::application_status_enum\n LEFT JOIN organization o ON u.organization_id = o.id\n LEFT JOIN organization_general og ON o.organization_general_id = og.id\n WHERE u.role = 'employee'::user_role_enum AND (u.status = ANY (ARRAY['active'::user_status_enum, 'restricted'::user_status_enum])) AND u.deleted_on IS NULL\n GROUP BY u.id, og.alias;", + ], + ); + } +} diff --git a/backend/src/migrations/1721829705666-AddIssuer.ts b/backend/src/migrations/1721829705666-AddIssuer.ts new file mode 100644 index 000000000..b9eaae49b --- /dev/null +++ b/backend/src/migrations/1721829705666-AddIssuer.ts @@ -0,0 +1,34 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { ISSUERS } from './seed/issuers.seed'; +import { Issuer } from 'src/shared/entities'; +export class AddIssuer1721829705666 implements MigrationInterface { + name = 'AddIssuer1721829705666'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "_issuer" ("id" SERIAL NOT NULL, "deleted_on" TIMESTAMP WITH TIME ZONE, "created_on" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_on" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" text NOT NULL, CONSTRAINT "PK_cfe878b7aa3dc098f2ac736adc6" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_20b3d0cad6a13638d5d485eada" ON "_issuer" ("created_on") `, + ); + await queryRunner.manager + .createQueryBuilder() + .insert() + .into(Issuer) + .values(ISSUERS) + .execute(); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.manager + .createQueryBuilder() + .delete() + .from(Issuer) + .execute(); + + await queryRunner.query( + `DROP INDEX "public"."IDX_20b3d0cad6a13638d5d485eada"`, + ); + await queryRunner.query(`DROP TABLE "_issuer"`); + } +} diff --git a/backend/src/migrations/1721899705666-NewGeneralFields.ts b/backend/src/migrations/1721899705666-NewGeneralFields.ts new file mode 100644 index 000000000..fc7d14b2c --- /dev/null +++ b/backend/src/migrations/1721899705666-NewGeneralFields.ts @@ -0,0 +1,53 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class NewGeneralFields1721899705666 implements MigrationInterface { + name = 'NewGeneralFields1721899705666'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "organization_general" ALTER COLUMN "raf_number" DROP NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" ADD "association_registry_number" text`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" ADD "association_registry_part" text`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" ADD "association_registry_section" text`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" ADD "association_registry_issuer_id" integer`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" ADD CONSTRAINT "FK_302f085592ed8a6c5d06f1182ef" FOREIGN KEY ("association_registry_issuer_id") REFERENCES "_issuer"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" ADD "national_registry_number" text`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "organization_general" ALTER COLUMN "raf_number" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" DROP COLUMN "national_registry_number"`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" DROP CONSTRAINT "FK_302f085592ed8a6c5d06f1182ef"`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" DROP COLUMN "association_registry_issuer_id"`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" DROP COLUMN "association_registry_section"`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" DROP COLUMN "association_registry_part"`, + ); + await queryRunner.query( + `ALTER TABLE "organization_general" DROP COLUMN "association_registry_number"`, + ); + } +} diff --git a/backend/src/migrations/1722099705666-SectorsSeed.ts b/backend/src/migrations/1722099705666-SectorsSeed.ts new file mode 100644 index 000000000..da55d6569 --- /dev/null +++ b/backend/src/migrations/1722099705666-SectorsSeed.ts @@ -0,0 +1,27 @@ +import { City } from 'src/shared/entities'; +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { SECTORS } from './seed/sectors.seed'; + +export class SectorsSeed1722099705666 implements MigrationInterface { + name = 'SectorsSeed1722099705666'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.manager + .createQueryBuilder() + .insert() + .into(City) + .values(SECTORS) + .execute(); + } + + public async down(queryRunner: QueryRunner): Promise { + const sectorIds = SECTORS.map((sector) => sector.id); + + await queryRunner.manager + .createQueryBuilder() + .delete() + .from(City) + .where('id IN (:...ids)', { ids: sectorIds }) + .execute(); + } +} diff --git a/backend/src/migrations/1722423180665-ContactRole.ts b/backend/src/migrations/1722423180665-ContactRole.ts new file mode 100644 index 000000000..13e6b78da --- /dev/null +++ b/backend/src/migrations/1722423180665-ContactRole.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ContactRole1722423180665 implements MigrationInterface { + name = 'ContactRole1722423180665'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "_contact" ADD "role" text`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "_contact" DROP COLUMN "role"`); + } +} diff --git a/backend/src/migrations/seed/city.seed.ts b/backend/src/migrations/seed/city.seed.ts index f96d7d520..4de45a9dc 100644 --- a/backend/src/migrations/seed/city.seed.ts +++ b/backend/src/migrations/seed/city.seed.ts @@ -8899,11 +8899,11 @@ export const CITIES = [ countyId: 33, name: 'Bucuresci', }, - { - id: 1781, - countyId: 11, - name: 'Bucuresti', - }, + // { + // id: 1781, + // countyId: 11, + // name: 'Bucuresti', + // }, { id: 1782, countyId: 6, diff --git a/backend/src/migrations/seed/issuers.seed.ts b/backend/src/migrations/seed/issuers.seed.ts new file mode 100644 index 000000000..299b11a9a --- /dev/null +++ b/backend/src/migrations/seed/issuers.seed.ts @@ -0,0 +1,970 @@ +export const ISSUERS = [ + { + id: 1, + name: 'Curtea de Apel ALBA IULIA', + }, + { + id: 2, + name: 'Tribunalul ALBA', + }, + { + id: 3, + name: 'Judecătoria AIUD', + }, + { + id: 4, + name: 'Judecătoria ALBA IULIA', + }, + { + id: 5, + name: 'Judecătoria BLAJ', + }, + { + id: 6, + name: 'Judecătoria CÂMPENI', + }, + { + id: 7, + name: 'Judecătoria SEBEŞ', + }, + { + id: 8, + name: 'Tribunalul ARAD', + }, + { + id: 9, + name: 'Judecătoria CHIŞINEU CRIŞ', + }, + { + id: 10, + name: 'Judecătoria GURA HONŢ', + }, + { + id: 11, + name: 'Judecătoria INEU', + }, + { + id: 12, + name: 'Judecătoria LIPOVA', + }, + { + id: 13, + name: 'Curtea de Apel PITEŞTI', + }, + { + id: 14, + name: 'Tribunalul ARGEŞ', + }, + { + id: 15, + name: 'Tribunalul Specializat ARGEŞ', + }, + { + id: 16, + name: 'Judecătoria CĂMPULUNG', + }, + { + id: 17, + name: 'Judecătoria COSTEŞTI', + }, + { + id: 18, + name: 'Judecătoria CURTEA DE ARGEŞ', + }, + { + id: 19, + name: 'Judecătoria PITEŞTI', + }, + { + id: 20, + name: 'Judecătoria TOPOLOVENI', + }, + { + id: 21, + name: 'Curtea de Apel BACĂU', + }, + { + id: 22, + name: 'Tribunalul BACĂU', + }, + { + id: 23, + name: 'Judecătoria BACĂU', + }, + { + id: 24, + name: 'Judecătoria BUHUŞI', + }, + { + id: 25, + name: 'Judecătoria MOINEŞTI', + }, + { + id: 26, + name: 'Judecătoria ONEŞTI', + }, + { + id: 27, + name: 'Judecătoria PODU TURCULUI', + }, + { + id: 28, + name: 'Curtea de Apel ORADEA', + }, + { + id: 29, + name: 'Tribunalul BIHOR', + }, + { + id: 30, + name: 'Judecătoria ALESD', + }, + { + id: 31, + name: 'Judecătoria BEIUŞ', + }, + { + id: 32, + name: 'Judecătoria MARGHITA', + }, + { + id: 33, + name: 'Judecătoria ORADEA', + }, + { + id: 34, + name: 'Judecătoria SALONTA', + }, + { + id: 35, + name: 'Tribunalul BISTRIŢA NĂSĂUD', + }, + { + id: 36, + name: 'Judecătoria BECLEAN', + }, + { + id: 37, + name: 'Judecătoria BISTRIŢA', + }, + { + id: 38, + name: 'Judecătoria NĂSĂUD', + }, + { + id: 39, + name: 'Tribunalul BOTOŞANI', + }, + { + id: 40, + name: 'Judecătoria BOTOŞANI', + }, + { + id: 41, + name: 'Judecătoria Darabani', + }, + { + id: 42, + name: 'Judecătoria DOROHOI', + }, + { + id: 43, + name: 'Judecătoria SĂVENI', + }, + { + id: 44, + name: 'Tribunalul BRĂILA', + }, + { + id: 45, + name: 'Judecătoria BRĂILA', + }, + { + id: 46, + name: 'Judecătoria FĂUREI', + }, + { + id: 47, + name: 'Judecătoria ÎNSURĂŢEI', + }, + { + id: 48, + name: 'Curtea de Apel BRAŞOV', + }, + { + id: 49, + name: 'Tribunalul BRAŞOV', + }, + { + id: 50, + name: 'Tribunalul pentru minori şi familie BRAŞOV', + }, + { + id: 51, + name: 'Judecătoria BRAŞOV', + }, + { + id: 52, + name: 'Judecătoria FĂGĂRAŞ', + }, + { + id: 53, + name: 'Judecătoria RUPEA', + }, + { + id: 54, + name: 'Judecătoria ZĂRNEŞTI', + }, + { + id: 55, + name: 'Curtea de Apel BUCUREŞTI', + }, + { + id: 56, + name: 'Curtea Militara de Apel BUCUREŞTI', + }, + { + id: 57, + name: 'Tribunalul BUCUREŞTI', + }, + { + id: 58, + name: 'Tribunalul Militar BUCUREŞTI', + }, + { + id: 59, + name: 'Tribunalul Militar Teritorial BUCUREŞTI', + }, + { + id: 60, + name: 'Judecătoria SECTORUL 1 BUCUREŞTI', + }, + { + id: 61, + name: 'Judecătoria SECTORUL 2 BUCUREŞTI', + }, + { + id: 62, + name: 'Judecătoria SECTORUL 3 BUCUREŞTI', + }, + { + id: 63, + name: 'Judecătoria SECTORUL 4 BUCUREŞTI', + }, + { + id: 64, + name: 'Judecătoria SECTORUL 5 BUCUREŞTI', + }, + { + id: 65, + name: 'Judecătoria SECTORUL 6 BUCUREŞTI', + }, + { + id: 66, + name: 'Tribunalul BUZĂU', + }, + { + id: 67, + name: 'Judecătoria BUZĂU', + }, + { + id: 68, + name: 'Judecătoria PĂTÂRLAGELE', + }, + { + id: 69, + name: 'Judecătoria POGOANELE', + }, + { + id: 70, + name: 'Judecătoria RÂMNICU SARAT', + }, + { + id: 71, + name: 'Tribunalul CĂLĂRAŞI', + }, + { + id: 72, + name: 'Judecătoria CĂLĂRAŞI', + }, + { + id: 73, + name: 'Judecătoria LEHLIU-GARA', + }, + { + id: 74, + name: 'Judecătoria OLTENIŢA', + }, + { + id: 75, + name: 'Tribunalul CARAŞ SEVERIN', + }, + { + id: 76, + name: 'Judecătoria CARANSEBEŞ', + }, + { + id: 77, + name: 'Judecătoria MOLDOVA-NOUĂ', + }, + { + id: 78, + name: 'Judecătoria ORAVIŢA', + }, + { + id: 79, + name: 'Judecătoria REŞITA', + }, + { + id: 80, + name: 'Curtea de Apel CLUJ', + }, + { + id: 81, + name: 'Tribunalul CLUJ', + }, + { + id: 82, + name: 'Tribunalul Militar CLUJ-NAPOCA', + }, + { + id: 83, + name: 'Tribunalul Specializat CLUJ', + }, + { + id: 84, + name: 'Judecătoria CLUJ-NAPOCA', + }, + { + id: 85, + name: 'Judecătoria DEJ', + }, + { + id: 86, + name: 'Judecătoria GHERLA', + }, + { + id: 87, + name: 'Judecătoria HUEDIN', + }, + { + id: 88, + name: 'Judecătoria TURDA', + }, + { + id: 89, + name: 'Curtea de Apel CONSTANŢA', + }, + { + id: 90, + name: 'Tribunalul CONSTANŢA', + }, + { + id: 91, + name: 'Judecătoria CONSTANŢA', + }, + { + id: 92, + name: 'Judecătoria MANGALIA', + }, + { + id: 93, + name: 'Judecătoria MEDGIDIA', + }, + { + id: 94, + name: 'Judecătoria HÂRŞOVA', + }, + { + id: 95, + name: 'Tribunalul COVASNA', + }, + { + id: 96, + name: 'Judecătoria ÎNTORSURA BUZĂULUI', + }, + { + id: 97, + name: 'Judecătoria SFÂNTU GHEORGHE', + }, + { + id: 98, + name: 'Judecătoria TÂRGU SECUIESC', + }, + { + id: 99, + name: 'Tribunalul DÂMBOVIŢA', + }, + { + id: 100, + name: 'Judecătoria GĂEŞTI', + }, + { + id: 101, + name: 'Judecătoria MORENI', + }, + { + id: 102, + name: 'Judecătoria PUCIOASA', + }, + { + id: 103, + name: 'Judecătoria RĂCARI', + }, + { + id: 104, + name: 'Judecătoria TÂRGOVIŞTE', + }, + { + id: 105, + name: 'Curtea de Apel CRAIOVA', + }, + { + id: 106, + name: 'Tribunalul DOLJ', + }, + { + id: 107, + name: 'Judecătoria BĂILEŞTI', + }, + { + id: 108, + name: 'Judecătoria CALAFAT', + }, + { + id: 109, + name: 'Judecătoria CRAIOVA', + }, + { + id: 110, + name: 'Judecătoria FILIAŞI', + }, + { + id: 111, + name: 'Judecătoria SEGARCEA', + }, + { + id: 112, + name: 'Curtea de Apel GALAŢI', + }, + { + id: 113, + name: 'Tribunalul GALAŢI', + }, + { + id: 114, + name: 'Judecătoria GALAŢI', + }, + { + id: 115, + name: 'Judecătoria TÂRGU BUJOR', + }, + { + id: 116, + name: 'Judecătoria TECUCI', + }, + { + id: 117, + name: 'Judecătoria LIEŞTI', + }, + { + id: 118, + name: 'Tribunalul GIURGIU', + }, + { + id: 119, + name: 'Judecătoria BOLINTIN VALE', + }, + { + id: 120, + name: 'Judecătoria GIURGIU', + }, + { + id: 121, + name: 'Tribunalul GORJ', + }, + { + id: 122, + name: 'Judecătoria MOTRU', + }, + { + id: 123, + name: 'Judecătoria NOVACI', + }, + { + id: 124, + name: 'Judecătoria TÂRGU-CĂRBUNEŞTI', + }, + { + id: 125, + name: 'Judecătoria TÂRGU JIU', + }, + { + id: 126, + name: 'Tribunalul HARGHITA', + }, + { + id: 127, + name: 'Judecătoria GHEORGHENI', + }, + { + id: 128, + name: 'Judecătoria MIERCUREA CIUC', + }, + { + id: 129, + name: 'Judecătoria ODORHEIUL SECUIESC', + }, + { + id: 130, + name: 'Judecătoria TOPLIŢA', + }, + { + id: 131, + name: 'Tribunalul HUNEDOARA', + }, + { + id: 132, + name: 'Judecătoria BRAD', + }, + { + id: 133, + name: 'Judecătoria DEVA', + }, + { + id: 134, + name: 'Judecătoria HAŢEG', + }, + { + id: 135, + name: 'Judecătoria HUNEDOARA', + }, + { + id: 136, + name: 'Judecătoria ORAŞTIE', + }, + { + id: 137, + name: 'Judecătoria PETROŞANI', + }, + { + id: 138, + name: 'Tribunalul IALOMIŢA', + }, + { + id: 139, + name: 'Judecătoria FETEŞTI', + }, + { + id: 140, + name: 'Judecătoria SLOBOZIA', + }, + { + id: 141, + name: 'Judecătoria URZICENI', + }, + { + id: 142, + name: 'Curtea de Apel IAŞI', + }, + { + id: 143, + name: 'Tribunalul IAŞI', + }, + { + id: 144, + name: 'Tribunalul Militar IAŞI', + }, + { + id: 145, + name: 'Judecătoria PAŞCANI', + }, + { + id: 146, + name: 'Judecătoria HÂRLĂU', + }, + { + id: 147, + name: 'Judecătoria IAŞI', + }, + { + id: 148, + name: 'Judecătoria RĂDUCĂNENI', + }, + { + id: 149, + name: 'Tribunalul ILFOV', + }, + { + id: 150, + name: 'Judecătoria BUFTEA', + }, + { + id: 151, + name: 'Judecătoria CORNETU', + }, + { + id: 152, + name: 'Tribunalul MARAMUREŞ', + }, + { + id: 153, + name: 'Judecătoria BAIA MARE', + }, + { + id: 154, + name: 'Judecătoria DRAGOMIREŞTI', + }, + { + id: 155, + name: 'Judecătoria SIGHETU MARMAŢIEI', + }, + { + id: 156, + name: 'Judecătoria TÂRGU LAPUŞ', + }, + { + id: 157, + name: 'Judecătoria VIŞEU DE SUS', + }, + { + id: 158, + name: 'Tribunalul MEHEDINŢI', + }, + { + id: 159, + name: 'Judecătoria BAIA DE ARAMĂ', + }, + { + id: 160, + name: 'Judecătoria DROBETA-TURNU SEVERIN', + }, + { + id: 161, + name: 'Judecătoria ORŞOVA', + }, + { + id: 162, + name: 'Judecătoria STREHAIA', + }, + { + id: 163, + name: 'Judecătoria VÂNJU MARE', + }, + { + id: 164, + name: 'Curtea de Apel TÂRGU MUREŞ', + }, + { + id: 165, + name: 'Tribunalul MUREŞ', + }, + { + id: 166, + name: 'Tribunalul Specializat MUREŞ', + }, + { + id: 167, + name: 'Judecătoria LUDUŞ', + }, + { + id: 168, + name: 'Judecătoria REGHIN', + }, + { + id: 169, + name: 'Judecătoria SIGHIŞOARA', + }, + { + id: 170, + name: 'Judecătoria TÂRGU MUREŞ', + }, + { + id: 171, + name: 'Judecătoria TÂRNAVENI', + }, + { + id: 172, + name: 'Tribunalul NEAMŢ', + }, + { + id: 173, + name: 'Judecătoria BICAZ', + }, + { + id: 174, + name: 'Judecătoria PIATRA-NEAMT', + }, + { + id: 175, + name: 'Judecătoria ROMAN', + }, + { + id: 176, + name: 'Judecătoria TÂRGU NEAMŢ', + }, + { + id: 177, + name: 'Tribunalul OLT', + }, + { + id: 178, + name: 'Judecătoria BALŞ', + }, + { + id: 179, + name: 'Judecătoria CARACAL', + }, + { + id: 180, + name: 'Judecătoria CORABIA', + }, + { + id: 181, + name: 'Judecătoria SLATINA', + }, + { + id: 182, + name: 'Curtea de Apel PLOIEŞTI', + }, + { + id: 183, + name: 'Tribunalul PRAHOVA', + }, + { + id: 184, + name: 'Judecătoria CÂMPINA', + }, + { + id: 185, + name: 'Judecătoria MIZIL', + }, + { + id: 186, + name: 'Judecătoria PLOIEŞTI', + }, + { + id: 187, + name: 'Judecătoria SINAIA', + }, + { + id: 188, + name: 'Judecătoria VĂLENII DE MUNTE', + }, + { + id: 189, + name: 'Tribunalul SĂLAJ', + }, + { + id: 190, + name: 'Judecătoria ŞIMLEUL SILVANIEI', + }, + { + id: 191, + name: 'Judecătoria ZALĂU', + }, + { + id: 192, + name: 'Judecătoria JIBOU', + }, + { + id: 193, + name: 'Tribunalul SATU MARE', + }, + { + id: 194, + name: 'Judecătoria CAREI', + }, + { + id: 195, + name: 'Judecătoria NEGREŞTI-OAŞ', + }, + { + id: 196, + name: 'Judecătoria SATU MARE', + }, + { + id: 197, + name: 'Tribunalul SIBIU', + }, + { + id: 198, + name: 'Judecătoria AGNITA', + }, + { + id: 199, + name: 'Judecătoria MEDIAŞ', + }, + { + id: 200, + name: 'Judecătoria SALIŞTE', + }, + { + id: 201, + name: 'Judecătoria SIBIU', + }, + { + id: 202, + name: 'Judecătoria AVRIG', + }, + { + id: 203, + name: 'Curtea de Apel SUCEAVA', + }, + { + id: 204, + name: 'Tribunalul SUCEAVA', + }, + { + id: 205, + name: 'Judecătoria CÂMPULUNG MOLDOVENESC', + }, + { + id: 206, + name: 'Judecătoria FĂLTICENI', + }, + { + id: 207, + name: 'Judecătoria GURA HUMORULUI', + }, + { + id: 208, + name: 'Judecătoria RĂDĂUŢI', + }, + { + id: 209, + name: 'Judecătoria SUCEAVA', + }, + { + id: 210, + name: 'Judecătoria VATRA DORNEI', + }, + { + id: 211, + name: 'Tribunalul TELEORMAN', + }, + { + id: 212, + name: 'Judecătoria ROŞIORI DE VEDE', + }, + { + id: 213, + name: 'Judecătoria TURNU MĂGURELE', + }, + { + id: 214, + name: 'Judecătoria VIDELE', + }, + { + id: 215, + name: 'Judecătoria ZIMNICEA', + }, + { + id: 216, + name: 'Judecătoria ALEXANDRIA', + }, + { + id: 217, + name: 'Curtea de Apel TIMIŞOARA', + }, + { + id: 218, + name: 'Tribunalul Militar TIMIŞOARA', + }, + { + id: 219, + name: 'Tribunalul TIMIŞ', + }, + { + id: 220, + name: 'Judecătoria FĂGET', + }, + { + id: 221, + name: 'Judecătoria DETA', + }, + { + id: 222, + name: 'Judecătoria LUGOJ', + }, + { + id: 223, + name: 'Judecătoria SÂNNICOLAUL MARE', + }, + { + id: 224, + name: 'Judecătoria TIMIŞOARA', + }, + { + id: 225, + name: 'Tribunalul TULCEA', + }, + { + id: 226, + name: 'Judecătoria BABADAG', + }, + { + id: 227, + name: 'Judecătoria MACIN', + }, + { + id: 228, + name: 'Judecătoria TULCEA', + }, + { + id: 229, + name: 'Tribunalul VÂLCEA', + }, + { + id: 230, + name: 'Judecătoria BĂLCEŞTI', + }, + { + id: 231, + name: 'Judecătoria BREZOI', + }, + { + id: 232, + name: 'Judecătoria DRĂGĂŞANI', + }, + { + id: 233, + name: 'Judecătoria HOREZU', + }, + { + id: 234, + name: 'Judecătoria RÂMNICU VALCEA', + }, + { + id: 235, + name: 'Tribunalul VASLUI', + }, + { + id: 236, + name: 'Judecătoria BÂRLAD', + }, + { + id: 237, + name: 'Judecătoria HUŞI', + }, + { + id: 238, + name: 'Judecătoria VASLUI', + }, + { + id: 239, + name: 'Tribunalul VRANCEA', + }, + { + id: 240, + name: 'Judecătoria ADJUD', + }, + { + id: 241, + name: 'Judecătoria FOCŞANI', + }, + { + id: 242, + name: 'Judecătoria PANCIU', + }, +]; diff --git a/backend/src/migrations/seed/sectors.seed.ts b/backend/src/migrations/seed/sectors.seed.ts new file mode 100644 index 000000000..242488802 --- /dev/null +++ b/backend/src/migrations/seed/sectors.seed.ts @@ -0,0 +1,32 @@ +export const SECTORS = [ + { + id: 13930, + countyId: 11, + name: 'Sector 1', + }, + { + id: 13931, + countyId: 11, + name: 'Sector 2', + }, + { + id: 13932, + countyId: 11, + name: 'Sector 3', + }, + { + id: 13933, + countyId: 11, + name: 'Sector 4', + }, + { + id: 13934, + countyId: 11, + name: 'Sector 5', + }, + { + id: 13935, + countyId: 11, + name: 'Sector 6', + }, +]; diff --git a/backend/src/modules/application/services/application.service.ts b/backend/src/modules/application/services/application.service.ts index b91ca1a02..901e8637f 100644 --- a/backend/src/modules/application/services/application.service.ts +++ b/backend/src/modules/application/services/application.service.ts @@ -62,7 +62,6 @@ export class ApplicationService { `${APPLICATIONS_FILES_DIR}`, logo, FILE_TYPE.IMAGE, - createApplicationDto.name, ); createApplicationDto = { @@ -339,7 +338,6 @@ export class ApplicationService { `${APPLICATIONS_FILES_DIR}`, logo, FILE_TYPE.IMAGE, - application.name, ); applicationPayload = { diff --git a/backend/src/modules/organization/constants/errors.constants.ts b/backend/src/modules/organization/constants/errors.constants.ts index 47868e21f..1702cb3c1 100644 --- a/backend/src/modules/organization/constants/errors.constants.ts +++ b/backend/src/modules/organization/constants/errors.constants.ts @@ -43,6 +43,15 @@ export const ORGANIZATION_ERRORS = { message: 'Error while uploading the files', errorCode: 'ORG_010', }, + UPLOAD_NON_POLITICAL_AFFILIATION: { + message: 'Error while uploading the non political affiliation file', + errorCode: 'ORG_030', + }, + + UPLOAD_BALANCE_SHEET: { + message: 'Error while uploading the balance sheet file', + errorCode: 'ORG_031', + }, GET_REPORT: { message: 'Report not found', errorCode: 'ORG_011', @@ -68,6 +77,15 @@ export const ORGANIZATION_ERRORS = { message: 'Error while deleting the organization statute', errorCode: 'ORG_027', }, + NON_POLITICAL_AFFILIATION: { + message: + 'Error while deleting the organization non political affiliation file', + errorCode: 'ORG_027', + }, + BALANCE_SHEET: { + message: 'Error while deleting the organization balance sheet file', + errorCode: 'ORG_027', + }, }, ALREADY_RESTRICTED: { message: 'Organization is already RESTRICTED', @@ -157,9 +175,17 @@ export const ORGANIZATION_REQUEST_ERRORS = { message: 'An organization with this cui already exists', errorCode: 'REQ_007', }, - RAF_NUMBER_EXISTS: { - message: 'An organization with this rafNumber already exists', - errorCode: 'REQ_008', + + ASSOCIATION_REGISTRY_NUMBER_EXISTS: { + message: + 'An organization with this asscotiation registry number already exists', + errorCode: 'REQ_013', + }, + + NATIONAL_REGISTRY_NUMBER_EXISTS: { + message: + 'An organization with this national registry number already exists', + errorCode: 'REQ_014', }, ORGANIZATION_NAME_EXISTS: { message: 'An organization with this name already exists', diff --git a/backend/src/modules/organization/constants/files.constants.ts b/backend/src/modules/organization/constants/files.constants.ts index f6bd56f46..92d6e1982 100644 --- a/backend/src/modules/organization/constants/files.constants.ts +++ b/backend/src/modules/organization/constants/files.constants.ts @@ -1,6 +1,8 @@ export const ORGANIZATION_FILES_DIR = { LOGO: 'logo', STATUTE: 'statute', + NON_POLITICAL_AFFILITION: 'non_political_affiliation', + BALANCE_SHEET: 'balance_sheet', REPORTS: 'reports', PARTNERS: 'partners', INVESTORS: 'investors', diff --git a/backend/src/modules/organization/controllers/organization-profile.controller.ts b/backend/src/modules/organization/controllers/organization-profile.controller.ts index 6b66635b7..b279368c5 100644 --- a/backend/src/modules/organization/controllers/organization-profile.controller.ts +++ b/backend/src/modules/organization/controllers/organization-profile.controller.ts @@ -55,6 +55,8 @@ export class OrganizationProfileController { FileFieldsInterceptor([ { name: 'logo', maxCount: 1 }, { name: 'organizationStatute', maxCount: 1 }, + { name: 'nonPoliticalAffiliationFile', maxCount: 1 }, + { name: 'balanceSheetFile', maxCount: 1 }, ]), ) @Patch() @@ -65,9 +67,13 @@ export class OrganizationProfileController { { logo, organizationStatute, + nonPoliticalAffiliationFile, + balanceSheetFile, }: { logo: Express.Multer.File[]; organizationStatute: Express.Multer.File[]; + nonPoliticalAffiliationFile: Express.Multer.File[]; + balanceSheetFile: Express.Multer.File[]; }, ) { return this.organizationService.update( @@ -75,6 +81,8 @@ export class OrganizationProfileController { updateOrganizationDto, logo, organizationStatute, + nonPoliticalAffiliationFile, + balanceSheetFile, ); } diff --git a/backend/src/modules/organization/controllers/organization.controller.ts b/backend/src/modules/organization/controllers/organization.controller.ts index 7e5224686..eea984489 100644 --- a/backend/src/modules/organization/controllers/organization.controller.ts +++ b/backend/src/modules/organization/controllers/organization.controller.ts @@ -212,6 +212,8 @@ export class OrganizationController { FileFieldsInterceptor([ { name: 'logo', maxCount: 1 }, { name: 'organizationStatute', maxCount: 1 }, + { name: 'nonPoliticalAffiliationFile', maxCount: 1 }, + { name: 'balanceSheetFile', maxCount: 1 }, ]), ) @Patch(':id') @@ -222,9 +224,13 @@ export class OrganizationController { { logo, organizationStatute, + nonPoliticalAffiliationFile, + balanceSheetFile, }: { logo: Express.Multer.File[]; organizationStatute: Express.Multer.File[]; + nonPoliticalAffiliationFile: Express.Multer.File[]; + balanceSheetFile: Express.Multer.File[]; }, ) { return this.organizationService.update( @@ -232,6 +238,8 @@ export class OrganizationController { updateOrganizationDto, logo, organizationStatute, + nonPoliticalAffiliationFile, + balanceSheetFile, ); } @@ -321,6 +329,29 @@ export class OrganizationController { ); } + @Roles(Role.SUPER_ADMIN, Role.ADMIN) + @ApiParam({ name: 'id', type: String }) + @Delete(':id/non-political-affiliation') + deleteNonPoliticalAffiliation( + @ExtractUser() user: User, + @Param('id') id: number, + ) { + // for admin user.organizationId has precedence + return this.organizationService.deleteNonPoliticalAffiliation( + user?.organizationId || id, + ); + } + + @Roles(Role.SUPER_ADMIN, Role.ADMIN) + @ApiParam({ name: 'id', type: String }) + @Delete(':id/balance-sheet') + deleteBalanceSheet(@ExtractUser() user: User, @Param('id') id: number) { + // for admin user.organizationId has precedence + return this.organizationService.deleteBalanceSheet( + user?.organizationId || id, + ); + } + @Roles(Role.SUPER_ADMIN) @ApiParam({ name: 'id', type: String }) @ApiBody({ diff --git a/backend/src/modules/organization/dto/create-contact.dto.ts b/backend/src/modules/organization/dto/create-contact.dto.ts index 1655ce7db..56672924f 100644 --- a/backend/src/modules/organization/dto/create-contact.dto.ts +++ b/backend/src/modules/organization/dto/create-contact.dto.ts @@ -28,4 +28,9 @@ export class CreateContactDto { @IsNotEmpty() @MaxLength(50) email: string; + + @IsOptional() + @IsString() + @MaxLength(50) + role: string; } diff --git a/backend/src/modules/organization/dto/create-organization-general.dto.ts b/backend/src/modules/organization/dto/create-organization-general.dto.ts index 0089ac6b8..ae33713e0 100644 --- a/backend/src/modules/organization/dto/create-organization-general.dto.ts +++ b/backend/src/modules/organization/dto/create-organization-general.dto.ts @@ -75,6 +75,30 @@ export class CreateOrganizationGeneralDto { @Trim() cui: string; + @IsString() + @MaxLength(30) + @Trim() + associationRegistryNumber: string; + + @IsString() + @MaxLength(10) + @Trim() + associationRegistryPart: string; + + @IsString() + @MaxLength(10) + @Trim() + associationRegistrySection: string; + + @IsNumber() + @ToNumber() + associationRegistryIssuerId: number; + + @IsString() + @MaxLength(30) + @Trim() + nationalRegistryNumber: string; + /* Organization Register of Associations and Foundations Number @example 1249/A/2020 diff --git a/backend/src/modules/organization/dto/update-organization-legal.dto.ts b/backend/src/modules/organization/dto/update-organization-legal.dto.ts index a4165b469..c150d2da3 100644 --- a/backend/src/modules/organization/dto/update-organization-legal.dto.ts +++ b/backend/src/modules/organization/dto/update-organization-legal.dto.ts @@ -28,4 +28,12 @@ export class UpdateOrganizationLegalDto { @IsOptional() @IsString() organizationStatute?: string; + + @IsOptional() + @IsString() + nonPoliticalAffiliationFile?: string; + + @IsOptional() + @IsString() + balanceSheetFile?: string; } diff --git a/backend/src/modules/organization/entities/contact.entity.ts b/backend/src/modules/organization/entities/contact.entity.ts index 895b547ed..f1863fa7e 100644 --- a/backend/src/modules/organization/entities/contact.entity.ts +++ b/backend/src/modules/organization/entities/contact.entity.ts @@ -13,6 +13,9 @@ export class Contact extends BaseEntity { @Column({ type: 'text', name: 'phone', nullable: true }) phone: string; + @Column({ type: 'text', name: 'role', nullable: true }) + role: string; + @ManyToMany( (type) => OrganizationLegal, (organizationLegal) => organizationLegal.directors, diff --git a/backend/src/modules/organization/entities/organization-general.entity.ts b/backend/src/modules/organization/entities/organization-general.entity.ts index 38add69db..3133b1614 100644 --- a/backend/src/modules/organization/entities/organization-general.entity.ts +++ b/backend/src/modules/organization/entities/organization-general.entity.ts @@ -6,6 +6,7 @@ import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from 'typeorm'; import { ContactPerson } from '../interfaces/contact-person.interface'; import { OrganizationType } from '../enums/organization-type.enum'; import { Organization } from './organization.entity'; +import { Issuer } from 'src/shared/entities'; @Entity() export class OrganizationGeneral extends BaseEntity { @@ -30,7 +31,34 @@ export class OrganizationGeneral extends BaseEntity { @Column({ type: 'text', name: 'cui', unique: true }) cui: string; - @Column({ type: 'text', name: 'raf_number', unique: true }) + @Column({ type: 'text', name: 'association_registry_number', nullable: true }) + associationRegistryNumber: string; + + @Column({ type: 'text', name: 'association_registry_part', nullable: true }) + associationRegistryPart: string; + + @Column({ + type: 'text', + name: 'association_registry_section', + nullable: true, + }) + associationRegistrySection: string; + + @Column({ + type: 'integer', + name: 'association_registry_issuer_id', + nullable: true, + }) + associationRegistryIssuerId: number; + + @ManyToOne((type) => Issuer) + @JoinColumn({ name: 'association_registry_issuer_id' }) + associationRegistryIssuer: Issuer; + + @Column({ type: 'text', name: 'national_registry_number', nullable: true }) + nationalRegistryNumber: string; + + @Column({ type: 'text', name: 'raf_number', unique: true, nullable: true }) rafNumber: string; @Column({ type: 'text', name: 'short_description', nullable: true }) diff --git a/backend/src/modules/organization/entities/organization-legal.entity.ts b/backend/src/modules/organization/entities/organization-legal.entity.ts index 821bfef82..1c286569f 100644 --- a/backend/src/modules/organization/entities/organization-legal.entity.ts +++ b/backend/src/modules/organization/entities/organization-legal.entity.ts @@ -45,4 +45,18 @@ export class OrganizationLegal extends BaseEntity { @Column({ type: 'varchar', name: 'organization_statute', nullable: true }) organizationStatute: string; + + @Column({ + type: 'varchar', + name: 'non_political_affiliation_file', + nullable: true, + }) + nonPoliticalAffiliationFile: string; + + @Column({ + type: 'varchar', + name: 'balance_sheet_file', + nullable: true, + }) + balanceSheetFile: string; } diff --git a/backend/src/modules/organization/services/organization-general.service.ts b/backend/src/modules/organization/services/organization-general.service.ts index bab548e74..a66ed0705 100644 --- a/backend/src/modules/organization/services/organization-general.service.ts +++ b/backend/src/modules/organization/services/organization-general.service.ts @@ -90,7 +90,13 @@ export class OrganizationGeneralService { let organizationGeneral = await this.organizationGeneralRepository.get({ where: { id: organization.organizationGeneralId }, - relations: ['city', 'county', 'organizationCounty', 'organizationCity'], + relations: [ + 'city', + 'county', + 'organizationCounty', + 'organizationCity', + 'associationRegistryIssuer', + ], }); // Effect 1: Update financial data if CUI has changed @@ -150,6 +156,14 @@ export class OrganizationGeneralService { { id: Not(orgGeneralId), alias: newDTO?.alias }, { id: Not(orgGeneralId), email: newDTO?.email }, { id: Not(orgGeneralId), phone: newDTO?.phone }, + { + id: Not(orgGeneralId), + nationalRegistryNumber: newDTO?.nationalRegistryNumber, + }, + { + id: Not(orgGeneralId), + associationRegistryNumber: newDTO?.associationRegistryNumber, + }, ], }); for (let i = 0; i < existing.length; i++) { @@ -173,6 +187,21 @@ export class OrganizationGeneralService { ORGANIZATION_REQUEST_ERRORS.CREATE.ORGANIZATION_PHONE_EXISTS, ); } + if ( + existing[i].nationalRegistryNumber === newDTO?.nationalRegistryNumber + ) { + errors.push( + ORGANIZATION_REQUEST_ERRORS.CREATE.NATIONAL_REGISTRY_NUMBER_EXISTS, + ); + } + if ( + existing[i].associationRegistryNumber === + newDTO?.associationRegistryNumber + ) { + errors.push( + ORGANIZATION_REQUEST_ERRORS.CREATE.ASSOCIATION_REGISTRY_NUMBER_EXISTS, + ); + } } if (errors.length) { diff --git a/backend/src/modules/organization/services/organization-legal.service.ts b/backend/src/modules/organization/services/organization-legal.service.ts index 525175d46..9d0817d0d 100644 --- a/backend/src/modules/organization/services/organization-legal.service.ts +++ b/backend/src/modules/organization/services/organization-legal.service.ts @@ -13,6 +13,8 @@ import { ORGANIZATION_ERRORS } from '../constants/errors.constants'; import { UpdateOrganizationLegalDto } from '../dto/update-organization-legal.dto'; import { OrganizationLegalRepository } from '../repositories'; import { ContactService } from './contact.service'; +import { ORGANIZATION_FILES_DIR } from '../constants/files.constants'; +import * as Sentry from '@sentry/node'; @Injectable() export class OrganizationLegalService { @@ -26,8 +28,9 @@ export class OrganizationLegalService { public async update( id: number, updateOrganizationLegalDto: UpdateOrganizationLegalDto, - organizationStatutePath?: string, organizationStatute?: Express.Multer.File[], + nonPoliticalAffiliationFile?: Express.Multer.File[], + balanceSheetFile?: Express.Multer.File[], ) { const orgLegal = await this.organizationLegalRepostory.get({ where: { id }, @@ -62,7 +65,7 @@ export class OrganizationLegalService { try { const uploadedFile = await this.fileManagerService.uploadFiles( - organizationStatutePath, + `${id}/${ORGANIZATION_FILES_DIR.STATUTE}`, organizationStatute, FILE_TYPE.FILE, ); @@ -76,6 +79,7 @@ export class OrganizationLegalService { error: { error }, ...ORGANIZATION_ERRORS.UPLOAD, }); + Sentry.captureException(error); if (error instanceof HttpException) { throw error; } else { @@ -87,10 +91,79 @@ export class OrganizationLegalService { } } + // Non Political Affiliation File + if (nonPoliticalAffiliationFile) { + if (orgLegal.nonPoliticalAffiliationFile) { + await this.fileManagerService.deleteFiles([ + orgLegal.nonPoliticalAffiliationFile, + ]); + } + + try { + const uploadedFile = await this.fileManagerService.uploadFiles( + `${id}/${ORGANIZATION_FILES_DIR.NON_POLITICAL_AFFILITION}`, + nonPoliticalAffiliationFile, + FILE_TYPE.FILE, + ); + + organizationLegalData = { + ...organizationLegalData, + nonPoliticalAffiliationFile: uploadedFile[0], + }; + } catch (error) { + this.logger.error({ + error: { error }, + ...ORGANIZATION_ERRORS.UPLOAD_NON_POLITICAL_AFFILIATION, + }); + Sentry.captureException(error); + if (error instanceof HttpException) { + throw error; + } else { + throw new InternalServerErrorException({ + ...ORGANIZATION_ERRORS.UPLOAD_NON_POLITICAL_AFFILIATION, + error, + }); + } + } + } + + // Balance Sheet File + if (balanceSheetFile) { + if (orgLegal.balanceSheetFile) { + await this.fileManagerService.deleteFiles([orgLegal.balanceSheetFile]); + } + + try { + const uploadedFile = await this.fileManagerService.uploadFiles( + `${id}/${ORGANIZATION_FILES_DIR.BALANCE_SHEET}`, + balanceSheetFile, + FILE_TYPE.FILE, + ); + + organizationLegalData = { + ...organizationLegalData, + balanceSheetFile: uploadedFile[0], + }; + } catch (error) { + this.logger.error({ + error: { error }, + ...ORGANIZATION_ERRORS.UPLOAD_BALANCE_SHEET, + }); + Sentry.captureException(error); + if (error instanceof HttpException) { + throw error; + } else { + throw new InternalServerErrorException({ + ...ORGANIZATION_ERRORS.UPLOAD_BALANCE_SHEET, + error, + }); + } + } + } + await this.organizationLegalRepostory.save({ id, ...organizationLegalData, - others: organizationLegalData?.others || null, }); let organizationLegal = await this.organizationLegalRepostory.get({ @@ -109,6 +182,28 @@ export class OrganizationLegalService { }; } + if (organizationLegal.nonPoliticalAffiliationFile) { + const nonPoliticalAffiliationFilePublicUrl = + await this.fileManagerService.generatePresignedURL( + organizationLegal.nonPoliticalAffiliationFile, + ); + organizationLegal = { + ...organizationLegal, + nonPoliticalAffiliationFile: nonPoliticalAffiliationFilePublicUrl, + }; + } + + if (organizationLegal.balanceSheetFile) { + const balanceSheetFile = + await this.fileManagerService.generatePresignedURL( + organizationLegal.balanceSheetFile, + ); + organizationLegal = { + ...organizationLegal, + balanceSheetFile: balanceSheetFile, + }; + } + return organizationLegal; } @@ -139,6 +234,7 @@ export class OrganizationLegalService { ...ORGANIZATION_ERRORS.DELETE.STATUTE, }); + Sentry.captureException(error); const err = error?.response; throw new InternalServerErrorException({ ...ORGANIZATION_ERRORS.DELETE.STATUTE, @@ -146,4 +242,75 @@ export class OrganizationLegalService { }); } } + + public async deleteNonPoliticalAffiliation( + organizationLegalId: number, + ): Promise { + try { + // 1. Query organization legal data + const organizationLegal = await this.organizationLegalRepostory.get({ + where: { id: organizationLegalId }, + }); + + if (organizationLegal?.nonPoliticalAffiliationFile) { + // 2. remove file from s3 + await this.fileManagerService.deleteFiles([ + organizationLegal.nonPoliticalAffiliationFile, + ]); + + // 3. remove path from database + await this.organizationLegalRepostory.save({ + ...organizationLegal, + nonPoliticalAffiliationFile: null, + }); + } + } catch (error) { + this.logger.error({ + error, + ...ORGANIZATION_ERRORS.DELETE.NON_POLITICAL_AFFILIATION, + }); + Sentry.captureException(error); + + const err = error?.response; + throw new InternalServerErrorException({ + ...ORGANIZATION_ERRORS.DELETE.NON_POLITICAL_AFFILIATION, + error: err, + }); + } + } + + public async deleteBalanceSheetFile( + organizationLegalId: number, + ): Promise { + try { + const organizationLegal = await this.organizationLegalRepostory.get({ + where: { id: organizationLegalId }, + }); + + if (organizationLegal?.balanceSheetFile) { + // 2. remove file from s3 + await this.fileManagerService.deleteFiles([ + organizationLegal.balanceSheetFile, + ]); + + // 3. remove path from database + await this.organizationLegalRepostory.save({ + ...organizationLegal, + balanceSheetFile: null, + }); + } + } catch (error) { + this.logger.error({ + error, + ...ORGANIZATION_ERRORS.DELETE.BALANCE_SHEET, + }); + + Sentry.captureException(error); + const err = error?.response; + throw new InternalServerErrorException({ + ...ORGANIZATION_ERRORS.DELETE.BALANCE_SHEET, + error: err, + }); + } + } } diff --git a/backend/src/modules/organization/services/organization-report.service.ts b/backend/src/modules/organization/services/organization-report.service.ts index 5e36a14fd..cd3d7bf93 100644 --- a/backend/src/modules/organization/services/organization-report.service.ts +++ b/backend/src/modules/organization/services/organization-report.service.ts @@ -107,7 +107,6 @@ export class OrganizationReportService { `${organizationId}/${ORGANIZATION_FILES_DIR.PARTNERS}`, files, FILE_TYPE.FILE, - `${partner.year}_${PARTNER_LIST}`, ); await this.partnerRepository.save({ @@ -158,7 +157,6 @@ export class OrganizationReportService { `${organizationId}/${ORGANIZATION_FILES_DIR.INVESTORS}`, files, FILE_TYPE.FILE, - `${investor.year}_${INVESTOR_LIST}`, ); await this.investorRepository.save({ diff --git a/backend/src/modules/organization/services/organization-request.service.ts b/backend/src/modules/organization/services/organization-request.service.ts index dcf7b219d..f882233e8 100644 --- a/backend/src/modules/organization/services/organization-request.service.ts +++ b/backend/src/modules/organization/services/organization-request.service.ts @@ -73,6 +73,7 @@ export class OrganizationRequestService { 'organization.organizationGeneral.county', 'organization.organizationGeneral.organizationCity', 'organization.organizationGeneral.organizationCounty', + 'organization.organizationGeneral.associationRegistryIssuer', 'organization.organizationActivity', 'organization.organizationActivity.federations', 'organization.organizationActivity.coalitions', @@ -172,13 +173,21 @@ export class OrganizationRequestService { if (organization) { // 2.1 validate organization general if (organization.general) { - const { cui, rafNumber, name, email, phone, alias } = - organization.general; + const { + cui, + name, + email, + phone, + alias, + associationRegistryNumber, + nationalRegistryNumber, + } = organization.general; errors.push( ...(await this.organizationService.validateOrganizationGeneral( cui, - rafNumber, + associationRegistryNumber, + nationalRegistryNumber, name, email, phone, @@ -340,9 +349,8 @@ export class OrganizationRequestService { public async sendRestrictRequest(organizationId: number): Promise { try { - const organization = await this.organizationService.findWithUsers( - organizationId, - ); + const organization = + await this.organizationService.findWithUsers(organizationId); this.eventEmitter.emit( EVENTS.DISABLE_ORGANIZATION_REQUEST, diff --git a/backend/src/modules/organization/services/organization.service.ts b/backend/src/modules/organization/services/organization.service.ts index 9b3b4e1ee..90d114729 100644 --- a/backend/src/modules/organization/services/organization.service.ts +++ b/backend/src/modules/organization/services/organization.service.ts @@ -325,14 +325,6 @@ export class OrganizationService { paginationOptions, ); - for (let index in ongList.items) { - const data = await this.findWithRelations(ongList.items[index].id); - ongList.items[index] = { - ...ongList.items[index], - ...data, - }; - } - // Map the logo url const items = await this.fileManagerService.mapLogoToEntity( @@ -415,7 +407,15 @@ export class OrganizationService { 'organizationGeneral.phone': 'Organization Phone', 'organizationGeneral.yearCreated': 'Year Created', 'organizationGeneral.cui': 'CUI', - 'organizationGeneral.rafNumber': 'RAF Number', + 'organizationGeneral.associationRegistryNumber': + 'Association Registry Number', + 'organizationGeneral.associationRegistryPart': + 'Association Registry Part', + 'organizationGeneral.associationRegistrySection': + 'Association Registry Section', + 'organizationGeneral.associationRegistryIssuer': + 'Association Registry Issuer', + 'organizationGeneral.nationalRegistryNumber': 'National Registry Number', 'organizationGeneral.shortDescription': 'Short Description', 'organizationGeneral.description': 'Description', 'organizationGeneral.address': 'Address', @@ -560,6 +560,7 @@ export class OrganizationService { 'organizationGeneral.county', 'organizationGeneral.organizationCity', 'organizationGeneral.organizationCounty', + 'organizationGeneral.associationRegistryIssuer', 'organizationActivity', 'organizationActivity.federations', 'organizationActivity.coalitions', @@ -597,6 +598,8 @@ export class OrganizationService { } // Public: The URL can be replaced as above OR move all public in the "public" folder of each organization for better structure + + // Statute if (organization.organizationLegal.organizationStatute) { const organizationStatute = await this.fileManagerService.generatePresignedURL( @@ -605,6 +608,25 @@ export class OrganizationService { organization.organizationLegal.organizationStatute = organizationStatute; } + // Non Political Affiliation File + if (organization.organizationLegal.nonPoliticalAffiliationFile) { + const nonPoliticalAffiliationFile = + await this.fileManagerService.generatePresignedURL( + organization.organizationLegal.nonPoliticalAffiliationFile, + ); + organization.organizationLegal.nonPoliticalAffiliationFile = + nonPoliticalAffiliationFile; + } + + // Balance Sheet File + if (organization.organizationLegal.balanceSheetFile) { + const balanceSheetFile = + await this.fileManagerService.generatePresignedURL( + organization.organizationLegal.balanceSheetFile, + ); + organization.organizationLegal.balanceSheetFile = balanceSheetFile; + } + return organization; } @@ -969,6 +991,8 @@ export class OrganizationService { updateOrganizationDto: UpdateOrganizationDto, logo?: Express.Multer.File[], organizationStatute?: Express.Multer.File[], + nonPoliticalAffiliationFile?: Express.Multer.File[], + balanceSheetFile?: Express.Multer.File[], ): Promise { const organization = await this.find(id); @@ -994,11 +1018,19 @@ export class OrganizationService { FILE_TYPE.FILE, ); + this.fileManagerService.validateFiles( + nonPoliticalAffiliationFile, + FILE_TYPE.FILE, + ); + + this.fileManagerService.validateFiles(balanceSheetFile, FILE_TYPE.FILE); + return this.organizationLegalService.update( organization.organizationLegalId, updateOrganizationDto.legal, - `${id}/${ORGANIZATION_FILES_DIR.STATUTE}`, organizationStatute, + nonPoliticalAffiliationFile, + balanceSheetFile, ); } @@ -1163,6 +1195,64 @@ export class OrganizationService { } } + public async deleteNonPoliticalAffiliation( + organizationId: number, + ): Promise { + try { + const organization = await this.organizationRepository.get({ + where: { id: organizationId }, + relations: ['organizationLegal'], + }); + + await this.organizationLegalService.deleteNonPoliticalAffiliation( + organization.organizationLegalId, + ); + } catch (error) { + this.logger.error({ + error, + ...ORGANIZATION_ERRORS.DELETE.NON_POLITICAL_AFFILIATION, + }); + + if (error instanceof HttpException) { + throw error; + } else { + const err = error?.response; + throw new InternalServerErrorException({ + ...ORGANIZATION_ERRORS.DELETE.NON_POLITICAL_AFFILIATION, + error: err, + }); + } + } + } + + public async deleteBalanceSheet(organizationId: number): Promise { + try { + const organization = await this.organizationRepository.get({ + where: { id: organizationId }, + relations: ['organizationLegal'], + }); + + await this.organizationLegalService.deleteBalanceSheetFile( + organization.organizationLegalId, + ); + } catch (error) { + this.logger.error({ + error, + ...ORGANIZATION_ERRORS.DELETE.BALANCE_SHEET, + }); + + if (error instanceof HttpException) { + throw error; + } else { + const err = error?.response; + throw new InternalServerErrorException({ + ...ORGANIZATION_ERRORS.DELETE.BALANCE_SHEET, + error: err, + }); + } + } + } + /** * Will update the status from PENDING to ACTIVE * @@ -1300,7 +1390,8 @@ export class OrganizationService { public async validateOrganizationGeneral( cui: string, - rafNumber: string, + associationRegistryNumber: string, + nationalRegistryNumber: string, name: string, email: string, phone: string, @@ -1329,15 +1420,28 @@ export class OrganizationService { ); } - const organizationWithRafNumber = + const organizationWithAssociationRegistryNumber = + await this.organizationGeneralService.findOne({ + where: { associationRegistryNumber }, + }); + + if (organizationWithAssociationRegistryNumber) { + errors.push( + new BadRequestException( + ORGANIZATION_REQUEST_ERRORS.CREATE.ASSOCIATION_REGISTRY_NUMBER_EXISTS, + ), + ); + } + + const organizationWithNationalRegistryNumber = await this.organizationGeneralService.findOne({ - where: { rafNumber }, + where: { nationalRegistryNumber }, }); - if (organizationWithRafNumber) { + if (organizationWithNationalRegistryNumber) { errors.push( new BadRequestException( - ORGANIZATION_REQUEST_ERRORS.CREATE.RAF_NUMBER_EXISTS, + ORGANIZATION_REQUEST_ERRORS.CREATE.NATIONAL_REGISTRY_NUMBER_EXISTS, ), ); } diff --git a/backend/src/modules/user/constants/invites-filters.config.ts b/backend/src/modules/user/constants/invites-filters.config.ts index cdbb46197..950f7199e 100644 --- a/backend/src/modules/user/constants/invites-filters.config.ts +++ b/backend/src/modules/user/constants/invites-filters.config.ts @@ -7,10 +7,21 @@ export const INVITE_FILTERS_CONFIG = { email: true, phone: true, createdOn: true, + role: true, + organization: { + id: true, + organizationGeneral: { + alias: true, + }, + }, }, searchableColumns: ['name'], defaultSortBy: 'name', defaultOrderDirection: OrderDirection.ASC, - relations: {}, + relations: { + organization: { + organizationGeneral: true, + }, + }, rangeColumn: 'createdOn', }; diff --git a/backend/src/modules/user/constants/user-filters.config.ts b/backend/src/modules/user/constants/user-filters.config.ts index 356259cd8..f0d6400ae 100644 --- a/backend/src/modules/user/constants/user-filters.config.ts +++ b/backend/src/modules/user/constants/user-filters.config.ts @@ -1,6 +1,6 @@ import { OrderDirection } from 'src/common/enums/order-direction.enum'; -export const USER_FILTERS_CONFIG = { +export const USER_APPLICATIONS_FILTERS_CONFIG = { selectColumns: { id: true, name: true, @@ -10,6 +10,8 @@ export const USER_FILTERS_CONFIG = { status: true, availableApps: true, availableAppsIDs: true, + organizationId: true, + organizationAlias: true, }, searchableColumns: ['name', 'email'], defaultSortBy: 'name', diff --git a/backend/src/modules/user/entities/user-applications-view.entity.ts b/backend/src/modules/user/entities/user-applications-view.entity.ts index 05acfde5e..b7c0b1752 100644 --- a/backend/src/modules/user/entities/user-applications-view.entity.ts +++ b/backend/src/modules/user/entities/user-applications-view.entity.ts @@ -17,29 +17,27 @@ import { Role } from '../enums/role.enum'; @ViewEntity('UserApplicationsView', { expression: ` - SELECT - u.id, - u.name, - u.email, - u.phone, - u.status, - u.role, - u.organization_id as "organizationId", - u.created_on as "createdOn", - u.updated_on as "updatedOn", - ARRAY_AGG(DISTINCT a.id) as "availableAppsIDs", - json_agg(json_build_object('id', a.id, 'name', a.name, 'type', a.type)) as "availableApps" - FROM - "user" u - LEFT JOIN user_ong_application uoa ON u.id = uoa.user_id AND uoa.status = 'active' - LEFT JOIN ong_application oa ON uoa.ong_application_id = oa.id AND oa.status = 'active' - LEFT JOIN application a ON (oa.application_id = a.id OR a.type = 'independent') AND a.status = 'active' - WHERE - "u"."role" = 'employee' AND - "u"."status" IN('active', 'restricted') AND - "u"."deleted_on" IS NULL - GROUP BY - u.id + SELECT u.id, + u.name, + u.email, + u.phone, + u.status, + u.role, + u.organization_id AS "organizationId", + u.created_on AS "createdOn", + u.updated_on AS "updatedOn", + og.alias as "organizationAlias", + array_agg(DISTINCT a.id) AS "availableAppsIDs", + json_agg(DISTINCT jsonb_build_object('id', a.id, 'name', a.name, 'type', a.type)) AS "availableApps" + FROM "user" u + LEFT JOIN user_ong_application uoa ON u.id = uoa.user_id AND uoa.status = 'active'::user_ong_application_status_enum + LEFT JOIN ong_application oa ON uoa.ong_application_id = oa.id AND oa.status = 'active'::ong_application_status_enum + LEFT JOIN application a ON (oa.application_id = a.id OR a.type = 'independent'::application_type_enum) AND a.status = 'active'::application_status_enum + LEFT JOIN organization o ON u.organization_id = o.id + LEFT JOIN organization_general og ON o.organization_general_id = og.id + WHERE u.role = 'employee'::user_role_enum AND (u.status = ANY (ARRAY['active'::user_status_enum, 'restricted'::user_status_enum])) AND u.deleted_on IS NULL + GROUP BY u.id, og.alias; + `, }) export class UserApplicationsView { @@ -75,4 +73,7 @@ export class UserApplicationsView { @ViewColumn() organizationId: number; + + @ViewColumn() + organizationAlias: string; } diff --git a/backend/src/modules/user/services/user.service.ts b/backend/src/modules/user/services/user.service.ts index a3acd1a02..1e7a6b941 100644 --- a/backend/src/modules/user/services/user.service.ts +++ b/backend/src/modules/user/services/user.service.ts @@ -16,7 +16,7 @@ import { Not, UpdateResult, } from 'typeorm'; -import { USER_FILTERS_CONFIG } from '../constants/user-filters.config'; +import { USER_APPLICATIONS_FILTERS_CONFIG } from '../constants/user-filters.config'; import { CreateUserDto } from '../dto/create-user.dto'; import { UpdateUserDto } from '../dto/update-user.dto'; import { UserFilterDto } from '../dto/user-filter.dto'; @@ -194,26 +194,7 @@ export class UserService { // For Admin user we will sort by organizationId return this.userApplicationsView.getManyPaginated( - USER_FILTERS_CONFIG, - organizationId - ? { ...paginationOptions, organizationId } - : paginationOptions, - ); - } - - public async findAll( - options: UserFilterDto, - organizationId?: number, - ): Promise> { - const paginationOptions: any = { - role: Role.EMPLOYEE, - status: `$in:${UserStatus.ACTIVE},${UserStatus.RESTRICTED}`, - ...options, - }; - - // For Admin user we will sort by organizationId - return this.userRepository.getManyPaginated( - USER_FILTERS_CONFIG, + USER_APPLICATIONS_FILTERS_CONFIG, organizationId ? { ...paginationOptions, organizationId } : paginationOptions, @@ -353,7 +334,7 @@ export class UserService { // For Admin user we will sort by organizationId const users = await this.userApplicationsView.getManyPaginated( - USER_FILTERS_CONFIG, + USER_APPLICATIONS_FILTERS_CONFIG, organizationId ? { ...paginationOptions, organizationId } : paginationOptions, diff --git a/backend/src/shared/controllers/nomenclatures.controller.ts b/backend/src/shared/controllers/nomenclatures.controller.ts index 4a3baadbc..26060590d 100644 --- a/backend/src/shared/controllers/nomenclatures.controller.ts +++ b/backend/src/shared/controllers/nomenclatures.controller.ts @@ -73,4 +73,9 @@ export class NomenclaturesController { getBeneficiaries() { return this.nomenclaturesService.getBeneficiaries({}); } + + @Get('issuers') + getIssuers() { + return this.nomenclaturesService.getIssuers({}); + } } diff --git a/backend/src/shared/entities/index.ts b/backend/src/shared/entities/index.ts index 835d9bf8e..62e0463f6 100644 --- a/backend/src/shared/entities/index.ts +++ b/backend/src/shared/entities/index.ts @@ -6,3 +6,4 @@ export * from './federation.entity'; export * from './coalition.entity'; export * from './faculty.entity'; export * from './skill.entity'; +export * from './issuer.entity'; diff --git a/backend/src/shared/entities/issuer.entity.ts b/backend/src/shared/entities/issuer.entity.ts new file mode 100644 index 000000000..2b895d243 --- /dev/null +++ b/backend/src/shared/entities/issuer.entity.ts @@ -0,0 +1,8 @@ +import { BaseEntity } from 'src/common/base/base-entity.class'; +import { Column, Entity } from 'typeorm'; + +@Entity({ name: '_issuer' }) +export class Issuer extends BaseEntity { + @Column({ type: 'text', name: 'name' }) + name: string; +} diff --git a/backend/src/shared/services/nomenclatures.service.ts b/backend/src/shared/services/nomenclatures.service.ts index 336ff626c..af2d37465 100644 --- a/backend/src/shared/services/nomenclatures.service.ts +++ b/backend/src/shared/services/nomenclatures.service.ts @@ -5,6 +5,7 @@ import { County, Domain, Faculty, + Issuer, Region, Skill, } from 'src/shared/entities'; @@ -42,6 +43,8 @@ export class NomenclaturesService { private readonly serviceDomainRepository: Repository, @InjectRepository(Beneficiary) private readonly beneficiaryRepository: Repository, + @InjectRepository(Issuer) + private readonly issuersRepository: Repository, ) {} public getCity(conditions: FindOneOptions) { @@ -162,4 +165,8 @@ export class NomenclaturesService { public getBeneficiaries(conditions: FindManyOptions) { return this.beneficiaryRepository.find(conditions); } + + public getIssuers(conditions: FindManyOptions) { + return this.issuersRepository.find(conditions); + } } diff --git a/backend/src/shared/services/s3-file-manager.service.ts b/backend/src/shared/services/s3-file-manager.service.ts index 0dee77d43..2c7139986 100644 --- a/backend/src/shared/services/s3-file-manager.service.ts +++ b/backend/src/shared/services/s3-file-manager.service.ts @@ -95,17 +95,18 @@ export class S3FileManagerService { path: string, files: Express.Multer.File[], fileType: FILE_TYPE, - fileName?: string, ): Promise { this.logger.log(`Preparing to upload ${files.length} files...`); this.validateFiles(files, fileType); + const extension = files[0].originalname.split('.').pop(); + // Create upload params const params: FileUploadParams[] = files.map((file) => ({ Body: file.buffer, Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), - Key: `${path}/${uuid()}`, + Key: `${path}/${uuid()}.${extension}`, })); // Prepare upload diff --git a/backend/src/shared/shared.module.ts b/backend/src/shared/shared.module.ts index b7a4769c0..dc13eab07 100644 --- a/backend/src/shared/shared.module.ts +++ b/backend/src/shared/shared.module.ts @@ -9,6 +9,7 @@ import { Domain, Faculty, Federation, + Issuer, Region, Skill, } from './entities'; @@ -35,6 +36,7 @@ import { Beneficiary } from 'src/modules/civic-center-service/entities/beneficia PracticeDomain, ServiceDomain, Beneficiary, + Issuer, ]), HttpModule, ], diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json index 27b6549ca..921f3e26a 100644 --- a/frontend/src/assets/locales/en/translation.json +++ b/frontend/src/assets/locales/en/translation.json @@ -35,7 +35,7 @@ "show_more": "See all", "show_less": "Show less", "upload": { - "file": "Only PDF files are allowed for the organisation's status.", + "file": "Only PDF files are allowed for upload.", "image": "Only PNG, JPG, JPEG files are allowed for the logo", "image_size": "The maximum size for the logo is 6 MB", "document_size": "The maximum size for the organisation's charter is 25 MB", @@ -494,9 +494,22 @@ "other_information": "This information will be public, be careful what you share.", "statute": "The status of the organisation", "statute_information": "This information will be public, be careful what you share.", - "document": "Document", + "no_document": "No document uploaded", "file_name": "Statute_Organisation", "statute_upload": "Upload file", + + "non_political_affiliation": "Declaration of non-political affiliation", + "non_political_affiliation_information": "Upload the self-declaration by the legal representative of the organization stating that the president and members of the Board of Directors are not part of the leadership of a political party and/or do not hold any public office. This declaration will not be displayed publicly. The document is only used to validate the organization's eligibility to access certain solutions in NGO Hub (such as Vot ONG and RO Help). Accepted file formats: pdf, maximum 25 MB", + "non_political_affiliation_no_document": "No document uploaded", + "non_political_affiliation_file_name": "Declaration of non-political affiliation", + "non_political_affiliation_upload": "Upload file", + + "balance_sheet": "Balance Sheet", + "balance_sheet_information": "Upload the latest balance sheet submitted to the Ministry of Finance. This will not be displayed publicly. The document is only used to validate the organization's eligibility to access certain solutions in NGO Hub (such as Vot ONG and RO Help). Accepted file formats: pdf, maximum 50 MB", + "balance_sheet_no_document": "No document uploaded", + "balance_sheet_file_name": "Balance Sheet", + "balance_sheet_upload": "Upload file", + "modal": { "add": "Add", "edit_director": "Editing of the Board member", @@ -518,6 +531,16 @@ "title": "Are you sure you want to delete the Organisation's Constitution?", "description": "TODO#1: Lorem ipsum." }, + + "delete_non_politicial_affiliation_modal": { + "title": "Are you sure you want to delete the Declaration of non-political affiliation?", + "description": "" + }, + + "delete_balance_sheet_modal": { + "title": "Are you sure you want to delete the Balance Sheet?", + "description": "" + }, "header": { "name": "Full name", "role": "Role", @@ -843,13 +866,16 @@ "email": "E-mail", "phone": "Phone", "status": "General access", - "created": "Date added" + "created": "Date added", + "organizationAlias": "Organization" }, "invites_header": { "name": "Name", "email": "E-mail", "phone": "Phone", - "added_on": "Date added" + "added_on": "Date added", + "role": "Role", + "organizationAlias": "Organization" }, "status": { "active": "Activ", @@ -1580,4 +1606,4 @@ "date_added": "Date of feedback" } } -} \ No newline at end of file +} diff --git a/frontend/src/assets/locales/ro/translation.json b/frontend/src/assets/locales/ro/translation.json index 64a9979d4..0e215641a 100644 --- a/frontend/src/assets/locales/ro/translation.json +++ b/frontend/src/assets/locales/ro/translation.json @@ -35,12 +35,12 @@ "show_more": "Vezi tot", "show_less": "Arată mai puțin", "upload": { - "file": "Numai fișiere de tip PDF sunt permise pentru statutul organizației.", + "file": "Numai fișiere de tip PDF sunt permise pentru incarcare.", "image": "Numai fișiere de tip PNG, JPG, JPEG sunt permise pentru logo", "image_size": "Dimensiunea maximă pentru logo este de 6 MB", - "document_size": "Dimensiunea maximă pentru statutul organizației este de 25 MB", + "document_size": "Dimensiunea maximă pentru fișiere este de 25 MB", "logo": "Eroare la încărcarea logo-ului", - "statute": "Eroare la încărcarea statutului organizației" + "statute": "Eroare la încărcarea fișierului" }, "filters": { "hide": "Ascunde filtre", @@ -183,6 +183,8 @@ "REQ_010": "Există deja o organizație cu acest alias", "REQ_011": "Există deja o organizație cu acest e-mail", "REQ_012": "Există deja o organizație cu acest număr de telefon", + "REQ_013": "Există deja o organizație cu acest număr si dată de înscriere în Registrul Special", + "REQ_014": "Există deja o organizație cu acest număr de înregistrare în Registrul Național", "REQ_002": "Există deja o cerere cu acest e-mail sau cu acest număr de telefon.", "ORG_026": "Eroare la actualizarea datelor generale" } @@ -312,6 +314,40 @@ "label": "Număr de înregistrare în RAF*", "placeholder": "56/14.09.2020" }, + "association_registry_number": { + "required": "Numărul de înregistrare în Registrul Special este obligatoriu", + "max": "Numărul de înregistrare în Registrul Special poate avea maxim 30 de caractere", + "helper": "Numărul care apare pe Certificatul de înscriere a persoanei juridice fără scop patrimonial.", + "label": "Număr si data de înscriere în Registrul Special", + "placeholder": "123/14.09.2020" + }, + "association_registry_part": { + "required": "Partea din Registrul Special este obligatorie", + "max": "Partea din Registrul Special poate avea maxim 10 de caractere", + "helper": "Partea care apare pe Certificatul de înscriere a persoanei juridice fără scop patrimonial, după număr, și dată. A corespunde asociațiilor, B fundațiilor, C federațiilor.", + "label": "Partea", + "placeholder": "A" + }, + "association_registry_section": { + "required": "Secțiunea din Registrul Special este obligatorie", + "max": "Secțiunea din Registrul Special poate avea maxim 10 de caractere", + "helper": "Secțiunea care apare pe Certificatul de înscriere a persoanei juridice fără scop patrimonial, după număr, dată și parte.", + "label": "Secțiunea", + "placeholder": "I" + }, + "association_registry_issuer": { + "required": "Emitentul Registrului Special este obligatoriu", + "max": "Emitentul Registrului Special poate avea maxim 100 de caractere", + "helper": "Selectează din lista instanțelor de judecată, instanța care a conferit organizației statutul juridic. Instanța emitentă se află în antetul Certificatului de înscriere a persoanei juridice fără scop patrimonial.", + "label": "Instanța emitentă" + }, + "national_registry_number": { + "required": "Numărul de înregistrare în Registrul Național este obligatoriu", + "max": "Numărul de înregistrare în Registrul Național poate avea maxim 30 de caractere", + "helper": "Acest număr se regăsește pe extrasele din Registrul Național pe care le solicitați (de exemplu pentru actualizarea anuală a datelor la bancă). Numărul poate fi regăsit și pe site-ul Ministerului Justiției - https://www.just.ro/registrul-national-ong/", + "label": "Numărul de înregistrare în Registrul Național, oferit de Ministerul Justiției", + "placeholder": "123/A/2020" + }, "city": { "required": "Localitatea este obligatorie", "label": "Localitate*" @@ -497,9 +533,23 @@ "other_information": "Numele persoanelor adăugate sunt pentru a ușura comunicarea cu diferite persoane din organizația ta. De exemplu, aici poți adăuga managerul de comunicare sau persoana de fundraising dedicată din ONG-ul tău, sau coordonatori de sucursale din alte orașe. Datele de contact ale persoanelor relevante nu vor fi afișate public.", "statute": "Statutul organizației", "statute_information": "Încarcă statutul anonimizat. Acesta nu va fi afișat public. Documentul este folosit doar pentru a valida statutul de organizație neguvernamentală, și a confirma că scopul și misiunea organizației nu contravin cu criteliile de eligibilitate (ca de exemplu organizația pe care o reprezinți nu desfășoară activități care să încalce drepturile omului, nu este afiliată unui partid politic, nu este o asociație de locatari, etc.) Fișiere acceptate: pdf, maxim 25 MB.", - "document": "Document", + + "no_document": "Nu există niciun document adăugat", "file_name": "Statut_Organizație", "statute_upload": "Încarcă fișier", + + "non_political_affiliation": "Declarație de neapartenență politică", + "non_political_affiliation_information": "Încarcă declarația pe proprie răspundere prin care reprezentantul legal al organizației declară că președintele și membrii Consiliul Director nu fac parte din conducerea unui partid politic și/sau nu dețin nicio funcție publică. Această declarație nu va fi afișată public. Documentul este folosit doar pentru a valida eligibilitatea organizației de a accesa anumite soluții din NGO Hub (ca de exemplu Vot ONG și RO Help). Fișiere acceptate: pdf, maxim 25 MB", + "non_political_affiliation_no_document": "Nu există niciun document adăugat", + "non_political_affiliation_file_name": "Declarație de neapartenență politică", + "non_political_affiliation_upload": "Încarcă fișier", + + "balance_sheet": "Bilanț contabil", + "balance_sheet_information": "Încarcă ultimul bilanțul contabil depus la Ministerul de Finanțe. Acesta nu va fi afișat public. Documentul este folosit doar pentru a valida eligibilitatea organizației de a accesa anumite soluții din NGO Hub (ca de exemplu Vot ONG și RO Help). Fișiere acceptate: pdf, maxim 25 MB", + "balance_sheet_no_document": "Nu există niciun document adăugat", + "balance_sheet_file_name": "Bilanț contabil", + "balance_sheet_upload": "Încarcă fișier", + "modal": { "add": "Adaugă", "edit_director": "Editare membru Consiliu Director", @@ -521,11 +571,22 @@ "title": "Ești sigur ca dorești ștergerea Statutului Organizației?", "description": "În cazul ștergerii statutului, un nou statut trebuie încărcat în maxim 30 de zile pentru a menține activ contul organizației în NGO Hub." }, + + "delete_non_politicial_affiliation_modal": { + "title": "Ești sigur ca dorești ștergerea Declarației de neapartenență politică?", + "description": "" + }, + + "delete_balance_sheet_modal": { + "title": "Ești sigur ca dorești ștergerea Bilanțului contabil?", + "description": "" + }, "header": { "name": "Nume și prenume", "role": "Rol", "email": "E-mail", - "phone": "Telefon" + "phone": "Telefon", + "role": "Rol" }, "legal_config": { "name": { @@ -547,6 +608,10 @@ "minim": "Numărul de telefon al reprezentantului legal nu poate avea mai puțin de 10 caractere.", "invalid": "Numărul de telefon are un format invalid.", "label": "Telefon" + }, + "role": { + "maxim": "Rolul reprezentantului legal nu poate avea mai mult de 50 de caractere.", + "label": "Rol" } }, "director_config": { @@ -569,6 +634,10 @@ "minim": "Numărul de telefon al membrului Consiliului Director nu poate avea mai puțin de 10 caractere.", "invalid": "Numărul de telefon are un format invalid.", "label": "Telefon" + }, + "role": { + "maxim": "Rolul membrului Consiliului Director nu poate avea mai mult de 50 de caractere.", + "label": "Rol" } }, "other_config": { @@ -846,13 +915,16 @@ "email": "E-mail", "phone": "Telefon", "status": "Acces general", - "created": "Data adăugării" + "created": "Data adăugării", + "organizationAlias": "Organizație" }, "invites_header": { "name": "Nume", "email": "E-mail", "phone": "Telefon", - "added_on": "Data adăugării" + "added_on": "Data adăugării", + "role": "Rol", + "organizationAlias": "Organizație" }, "status": { "active": "Activ", @@ -1584,4 +1656,4 @@ "date_added": "Data acordării feedback-ului" } } -} \ No newline at end of file +} diff --git a/frontend/src/common/constants/error.constants.ts b/frontend/src/common/constants/error.constants.ts index 5c660a371..0398b5c36 100644 --- a/frontend/src/common/constants/error.constants.ts +++ b/frontend/src/common/constants/error.constants.ts @@ -10,6 +10,8 @@ export const ORGANIZATION_ERRORS: Record = { REQ_010: i18n.t('organization:create.errors.REQ_010'), REQ_011: i18n.t('organization:create.errors.REQ_011'), REQ_012: i18n.t('organization:create.errors.REQ_012'), + REQ_013: i18n.t('organization:create.errors.REQ_013'), + REQ_014: i18n.t('organization:create.errors.REQ_014'), UPDATE_GENERAL: i18n.t('organization:create.errors.ORG_026'), }; diff --git a/frontend/src/common/constants/file.constants.ts b/frontend/src/common/constants/file.constants.ts index 3d0bfc3cb..8403b28a2 100644 --- a/frontend/src/common/constants/file.constants.ts +++ b/frontend/src/common/constants/file.constants.ts @@ -1,5 +1,7 @@ export const FILE_TYPES_ACCEPT = { LOGO: 'image/png, image/jpeg, image/svg', STATUTE: '.pdf', + NON_POLITICAL_AFFILIATION: '.pdf', + BALANCE_SHEET: '.pdf', EXCEL: '.xls,.xlsx', }; diff --git a/frontend/src/common/errors/entities/create-organization-errors.class.ts b/frontend/src/common/errors/entities/create-organization-errors.class.ts index 28edc3897..4cf723a01 100644 --- a/frontend/src/common/errors/entities/create-organization-errors.class.ts +++ b/frontend/src/common/errors/entities/create-organization-errors.class.ts @@ -10,6 +10,8 @@ export const enum CREATE_ORGANIZARION_ERRORS { REQ_010 = 'REQ_010', REQ_011 = 'REQ_011', REQ_012 = 'REQ_012', + REQ_013 = 'REQ_013', + REQ_014 = 'REQ_014', FILE_002 = 'FILE_002', FILE_003 = 'FILE_003', FILE_004 = 'FILE_004', @@ -39,6 +41,8 @@ export class CreateOrganizationError extends ErrorClass - {props.config.label} - - )} + + {props.config.label} + + )}