瀏覽代碼

Добавлена возобжность изменять регистрацию

Vadim 3 月之前
父節點
當前提交
24cfdf3788

+ 50 - 0
src/api/v_0.1.0/client/client-activities-api.ts

@@ -2,6 +2,7 @@ import { z } from "zod";
 import { actTypes } from "../types/act-types.js";
 import {
   CustomFieldWithUserCopyValue,
+  CustomFieldWithValidatorsAndValue,
   InputFieldValue,
 } from "../types/custom-fields-types.js";
 
@@ -130,6 +131,55 @@ class ClientActivitiesApi {
       }),
     }),
   };
+
+  GET_ActRegForPatch = {
+    req: {
+      params: z.object({
+        activityRegId: z.string().uuid(),
+      }),
+    },
+    res: z.object({
+      code: z.enum(["success"]),
+      actReg: actTypes.ActivityReg.extend({
+        validators: z.array(actTypes.ActValidatorWithData),
+        peTypes: z.array(
+          z.object({
+            peTypeId: z.string().uuid(),
+            code: z.string(),
+            name: z.string(),
+          }),
+        ),
+        fields: z.array(
+          CustomFieldWithValidatorsAndValue.extend({
+            arffId: z.string().uuid(),
+            isChangeResetStatus: z.boolean(),
+          }),
+        ),
+      }),
+    }),
+  };
+
+  PATCH_ActReg = {
+    req: {
+      params: z.object({
+        activityRegId: z.string().uuid(),
+      }),
+      formData: {
+        body: z.object({
+          peId: z.string().uuid().optional(),
+          fields: z.array(
+            z.object({
+              value: InputFieldValue,
+              arffId: z.string().uuid(),
+            }),
+          ),
+        }),
+      },
+    },
+    res: z.object({
+      code: z.enum(["success"]),
+    }),
+  };
 }
 
 export const clientActivitiesApi = new ClientActivitiesApi();

+ 1 - 0
src/api/v_0.1.0/types/act-types.ts

@@ -91,6 +91,7 @@ class ActTypes {
         isPaymentOpen: z.boolean(),
       }),
     ),
+    isUserReg: z.boolean(),
   });
 
   ActivityRegWithFields = this.ActivityReg.extend({

+ 201 - 128
src/modules/client/activities/c-act-controller.ts

@@ -15,10 +15,7 @@ import { Request, Response } from "express";
 import sessionService from "#modules/users/auth/services/session-service.js";
 import { apiTypes } from "#api/current-api.js";
 import { validateActValidators } from "./validators/act-validators.js";
-import {
-  CustomFieldWithUserCopyValue,
-  CustomFieldWithValidators,
-} from "#api/v_0.1.0/types/custom-fields-types.js";
+
 import { ApiError } from "#exceptions/api-error.js";
 
 import { cCustomFieldsValidateService } from "../custom-fields/c-cf-validate-service.js";
@@ -201,73 +198,12 @@ class ClientActivitiesController {
     const { activityCode } =
       api.client.activities.GET_ActRegData.req.params.parse(req.params);
 
-    const actRegData = await selPool.maybeOne(sql.type(
-      z.object({
-        activityId: DbSchema.act.activities.activityId,
-        code: DbSchema.act.activities.code,
-        publicName: DbSchema.act.activities.publicName,
-        eventInstId: DbSchema.act.activities.eventInstId,
-        categoryId: DbSchema.act.activities.categoryId,
-        categoryCode: DbSchema.act.activityCategories.code,
-        validators: z.array(apiTypes.activities.ActValidator),
-        peTypes: z.array(
-          z.object({
-            peTypeId: DbSchema.act.peTypes.peTypeId,
-            code: DbSchema.act.peTypes.code,
-            name: DbSchema.act.peTypes.name,
-          }),
-        ),
-        fields: z.array(
-          CustomFieldWithUserCopyValue.extend({ arffId: z.string() }),
-        ),
-        isUserReg: DbSchema.act.activities.isUserReg,
-      }),
-    )`
-        select
-          a.activity_id "activityId",
-          a.code,
-          a.public_name "publicName",
-          a.event_inst_id "eventInstId",
-          a.category_id "categoryId",
-          a.category_code "categoryCode",
-          a.validators,
-          a.pe_types "peTypes",
-          coalesce(f.fields, '[]'::jsonb) "fields",
-          a.is_user_reg "isUserReg"
-        from
-          act.act_with_validators a
-        left join lateral (
-          select
-            jsonb_agg(jsonb_build_object(
-              'fieldDefinitionId', f.field_definition_id,
-              'arffId', af.arff_id,
-              'isCopyUserValue', af.is_copy_user_value,
-              'code', f.code,
-              'userCopyValue', ufwv.value,
-              'fieldTypeCode', f.field_type_code,
-              'title', COALESCE(af.field_title_override, f.title),
-              'mask', f.mask,
-              'options', f."options",
-              'validators', f.validators
-            )) as fields
-          from
-            act.activity_reg_form_fields af
-          left join cf.custom_fields_with_validators f on
-            f.field_definition_id = af.field_definition_id
-              -- значение из профиля юзера
-          left join ev.user_fields_with_values ufwv on
-              af.field_definition_id = ufwv.field_definition_id
-              and ufwv.user_id = ${user.userId}
-              and ufwv.event_id = ${event.eventId}
-              and ufwv.value is not null
-              -- только если нужно копировать
-              and af.is_copy_user_value = true
-          where
-            af.activity_id = a.activity_id
-        ) f on true
-          where a.code = ${activityCode}
-      `);
-
+    const actRegData =
+      await cActService.getActRegDataWithUserCopyValuesAndActValidators(
+        user.userId,
+        event.eventId,
+        activityCode,
+      );
     // TODO: заменить все BadRequest
     if (!actRegData)
       throw ApiError.BadRequest(
@@ -297,63 +233,11 @@ class ClientActivitiesController {
 
     const user = sessionService.getUserFromReq(req);
 
-    const actRegData = await selPool.maybeOne(sql.type(
-      z.object({
-        activityId: DbSchema.act.activities.activityId,
-        code: DbSchema.act.activities.code,
-        publicName: DbSchema.act.activities.publicName,
-        eventInstId: DbSchema.act.activities.eventInstId,
-        categoryId: DbSchema.act.activities.categoryId,
-        categoryCode: DbSchema.act.activityCategories.code,
-        validators: z.array(apiTypes.activities.ActValidator),
-        peTypes: z.array(
-          z.object({
-            peTypeId: DbSchema.act.peTypes.peTypeId,
-            code: DbSchema.act.peTypes.code,
-            name: DbSchema.act.peTypes.name,
-          }),
-        ),
-        fields: z.array(
-          CustomFieldWithValidators.extend({ arffId: z.string() }),
-        ),
-        isUserReg: DbSchema.act.activities.isUserReg,
-      }),
-    )`
-        select
-          a.activity_id "activityId",
-          a.code,
-          a.public_name "publicName",
-          a.event_inst_id "eventInstId",
-          a.category_id "categoryId",
-          a.category_code "categoryCode",
-          a.validators,
-          a.pe_types "peTypes",
-          coalesce(f.fields, '[]'::jsonb) "fields",
-          a.is_user_reg "isUserReg"
-        from
-          act.act_with_validators a
-        left join lateral (
-          select
-            jsonb_agg(jsonb_build_object(
-              'fieldDefinitionId', f.field_definition_id,
-              'arffId', af.arff_id,
-              'isCopyUserValue', af.is_copy_user_value,
-              'code', f.code,
-              'fieldTypeCode', f.field_type_code,
-              'title', COALESCE(af.field_title_override, f.title),
-              'mask', f.mask,
-              'options', f."options",
-              'validators', f.validators
-            )) as fields
-          from
-            act.activity_reg_form_fields af
-          left join cf.custom_fields_with_validators f on
-            f.field_definition_id = af.field_definition_id
-          where
-            af.activity_id = a.activity_id
-        ) f on true
-          where a.code = ${activityCode}
-      `);
+    const actRegData =
+      await cActService.getActRegDataWithFieldsAndValidatorsAndActValidators(
+        activityCode,
+      );
+
     if (!actRegData)
       throw ApiError.BadRequest(
         "actRegDataNotFound",
@@ -585,6 +469,195 @@ class ClientActivitiesController {
       },
     );
   }
+
+  async getActRegForPatch(req: Request, res: Response) {
+    const { activityRegId } =
+      api.client.activities.GET_ActRegForPatch.req.params.parse(req.params);
+    const actReg =
+      await cActService.getActRegWithFieldsAndValidatorsAndValuesAndActValidators(
+        activityRegId,
+      );
+
+    if (!actReg)
+      throw ApiError.BadRequest(
+        "actRegNotFound",
+        "Регистрация на мероприятии не найдена",
+      );
+
+    const user = sessionService.getUserFromReq(req);
+    if (actReg.userId !== user.userId && actReg.peOwnerId !== user.userId)
+      throw ApiError.ForbiddenError();
+
+    RouterUtils.validAndSendResponse(
+      api.client.activities.GET_ActRegForPatch.res,
+      res,
+      {
+        code: "success",
+        actReg,
+      },
+    );
+  }
+
+  async patchActReg(req: Request, res: Response) {
+    const { activityRegId } =
+      api.client.activities.PATCH_ActReg.req.params.parse(req.params);
+    const { peId, fields } =
+      api.client.activities.PATCH_ActReg.req.formData.body.parse(
+        JSON.parse(req.body.body),
+      );
+
+    const files = req.files;
+    const user = sessionService.getUserFromReq(req);
+
+    const actReg = await cActService.getActRegForPeMember(activityRegId);
+    if (!actReg)
+      throw ApiError.BadRequest(
+        "actRegNotFound",
+        "Регистрация на мероприятии не найдена",
+      );
+
+    if (actReg.userId !== user.userId && actReg.peOwnerId !== user.userId)
+      throw ApiError.ForbiddenError();
+
+    const actRegData =
+      await cActService.getActRegDataWithFieldsAndValidatorsAndActValidators(
+        actReg.activityCode,
+      );
+
+    const oldActRegWithValues = await cActService.getActRegWithValues(
+      actReg.activityRegId,
+    );
+
+    if (!actRegData || !oldActRegWithValues)
+      throw ApiError.BadRequest(
+        "actRegDataNotFound",
+        "Данные для регистрации на мероприятии не найдены",
+      );
+    //
+
+    //
+    // -- ВАЛИДАЦИЯ --
+    const validatorsWithData = await Promise.all(
+      actRegData.validators.map(
+        async (validator) =>
+          await cActService.addDataToActValidator(
+            validator,
+            actRegData.activityId,
+          ),
+      ),
+    );
+    // валидация активности
+
+    const validatedActivity = validateActValidators(validatorsWithData);
+    if (!validatedActivity.isOpen)
+      throw ApiError.BadRequest(
+        "actValidatorFailed",
+        validatedActivity.messages.join(", "),
+      );
+    //
+
+    // валидация pe
+    if (
+      !actRegData.isUserReg &&
+      peId // может не быть потому что patch
+    ) {
+      const pe = await cPeService.getPeWithValues(peId);
+      const members = await cPeService.getMembers(peId);
+      if (!pe)
+        throw ApiError.BadRequest("peNotFound", "Сущность участия не найдена");
+
+      const fullPe = {
+        ...pe,
+        members: [...members],
+      };
+      const validationResult = validatePeForAct(actRegData, fullPe);
+      if (!validationResult.isValid)
+        throw ApiError.BadRequest(
+          "peValidatorFailed",
+          validationResult.messages.join(", "),
+        );
+    }
+
+    //
+
+    // валидация формы
+    const refFields = actRegData.fields
+      .map((f) => ({
+        ...f,
+        idKey: "arffId",
+        value:
+          oldActRegWithValues.fields.find((v) => v.arffId === f.arffId)
+            ?.value || null,
+        isChangeResetStatus:
+          oldActRegWithValues.fields.find((v) => v.arffId === f.arffId)
+            ?.isChangeResetStatus || false,
+      }))
+      // только изменяемые
+      .filter((f) => fields.some((ff) => ff.arffId === f.arffId));
+
+    const validationResult =
+      await cCustomFieldsValidateService.processAndValidateFields({
+        inputFields: fields,
+        referenceFields: refFields,
+        files,
+        idKey: "arffId",
+        addOldValue: true,
+      });
+
+    if (!validationResult.isValid)
+      throw ApiError.BadRequest(
+        "fieldsValidationFailed",
+        JSON.stringify(validationResult.messages),
+      );
+    const validatedFields = validationResult.checkedfields;
+    //
+
+    const isNeedReset = refFields.some((f) => f.isChangeResetStatus);
+    //
+    // вставляем в базу и сохраняем файлы
+    await updPool.transaction(async (tr) => {
+      if (!actRegData.isUserReg && peId) {
+        await tr.query(sql.unsafe`
+          update act.activity_regs
+          set
+            pe_id = ${peId}
+          where
+            activity_reg_id = ${activityRegId}
+        `);
+      }
+
+      const initialRegStatusId = await cActService.getInitialRegStatusId(
+        actRegData.activityId,
+      );
+
+      // устанавливаем начальный статус если были измены поля сбрасывающие регистрацию
+      if (isNeedReset) {
+        await tr.query(sql.unsafe`
+        insert into act.act_reg_status_history
+          (status_history_id, activity_reg_id, act_reg_status_id, note)
+        values
+          (${v7()}, ${activityRegId}, ${initialRegStatusId}, 'Изменена заявка на регистрацию')
+      `);
+      }
+
+      await cCustomFieldsValidateService.saveCustomFieldValuesInTransaction({
+        tr,
+        parentId: activityRegId,
+        action: "activityPeReg",
+        inputFields: validatedFields,
+        files,
+        isDeleteBefore: true,
+      });
+    });
+
+    RouterUtils.validAndSendResponse(
+      api.client.activities.PATCH_ActReg.res,
+      res,
+      {
+        code: "success",
+      },
+    );
+  }
 }
 
 export const clientActController = new ClientActivitiesController();

+ 11 - 0
src/modules/client/activities/c-act-router.ts

@@ -19,6 +19,17 @@ router.get(
   RouterUtils.asyncHandler(clientActController.getActReg),
 );
 
+router.get(
+  "/reg/:activityRegId/forPatch",
+  RouterUtils.asyncHandler(clientActController.getActRegForPatch),
+);
+
+router.patch(
+  "/reg/:activityRegId",
+  upload.any(),
+  RouterUtils.asyncHandler(clientActController.patchActReg),
+);
+
 router.get(
   "/cat/:categoryCode",
   RouterUtils.asyncHandler(clientActController.getCategory),

+ 250 - 8
src/modules/client/activities/c-act-service.ts

@@ -1,5 +1,10 @@
 import { apiTypes } from "#api/current-api.js";
-import { CustomFieldWithValue } from "#api/v_0.1.0/types/custom-fields-types.js";
+import {
+  CustomFieldWithUserCopyValue,
+  CustomFieldWithValidators,
+  CustomFieldWithValidatorsAndValue,
+  CustomFieldWithValue,
+} from "#api/v_0.1.0/types/custom-fields-types.js";
 import { DbSchema } from "#db/db-schema.js";
 import { selPool, updPool } from "#db/db.js";
 import { ApiError } from "#exceptions/api-error.js";
@@ -64,6 +69,7 @@ class CActService {
             isPaymentOpen: DbSchema.act.actRegStatuses.isPaymentOpen,
           }),
         ),
+        isUserReg: DbSchema.act.activities.isUserReg,
       }),
     )`
       select
@@ -76,7 +82,8 @@ class CActService {
         ar.pe_owner_id "peOwnerId",
         ar.user_id "userId",
         ar.is_paid "isPaid",
-        ar.status_history "statusHistory"
+        ar.status_history "statusHistory",
+        ar.is_user_reg "isUserReg"
       from
         act.act_regs_with_status ar
       where
@@ -125,6 +132,7 @@ class CActService {
               DbSchema.act.activityRegFormFields.isChangeResetStatus,
           }),
         ),
+        isUserReg: DbSchema.act.activities.isUserReg,
       }),
     )`
       select
@@ -138,7 +146,8 @@ class CActService {
         ar.user_id "userId",
         ar.is_paid "isPaid",
         ar.status_history "statusHistory",
-        ar.fields "fields"
+        ar.fields "fields",
+        ar.is_user_reg "isUserReg"
       from
         act.act_regs_with_values ar
       where
@@ -171,6 +180,7 @@ class CActService {
             isPaymentOpen: DbSchema.act.actRegStatuses.isPaymentOpen,
           }),
         ),
+        isUserReg: DbSchema.act.activities.isUserReg,
       }),
     )`
       select
@@ -183,7 +193,8 @@ class CActService {
         ar.pe_owner_id "peOwnerId",
         ar.user_id "userId",
         ar.is_paid "isPaid",
-        ar.status_history "statusHistory"
+        ar.status_history "statusHistory",
+        ar.is_user_reg "isUserReg"
       from
         act.act_regs_with_values ar
       where
@@ -267,17 +278,17 @@ class CActService {
 
     // оплата за всю регистрацию
     if (activity.paymentConfig === "PER_REGISTRATION") {
-const isPaid = await this.checkActivityRegPayment({
+      const isPaid = await this.checkActivityRegPayment({
         activityRegId,
       });
       if (isPaid) {
-      await updPool.query(sql.unsafe`
+        await updPool.query(sql.unsafe`
           update act.activity_regs
           set is_paid = true
           where activity_reg_id = ${activityRegId}
         `);
 
-      await updPool.query(sql.unsafe`
+        await updPool.query(sql.unsafe`
           insert into act.act_reg_status_history (
             activity_reg_id,
             act_reg_status_id,
@@ -289,7 +300,7 @@ const isPaid = await this.checkActivityRegPayment({
             'Оплачено'
           )
         `);
-}
+      }
 
       // TODO: QR
     }
@@ -403,6 +414,237 @@ const isPaid = await this.checkActivityRegPayment({
 
     return undefined;
   }
+
+  async getActRegDataWithUserCopyValuesAndActValidators(
+    userId: string,
+    eventId: string,
+    activityCode: string,
+  ) {
+    return await selPool.maybeOne(sql.type(
+      z.object({
+        activityId: DbSchema.act.activities.activityId,
+        code: DbSchema.act.activities.code,
+        publicName: DbSchema.act.activities.publicName,
+        eventInstId: DbSchema.act.activities.eventInstId,
+        categoryId: DbSchema.act.activities.categoryId,
+        categoryCode: DbSchema.act.activityCategories.code,
+        validators: z.array(apiTypes.activities.ActValidator),
+        peTypes: z.array(
+          z.object({
+            peTypeId: DbSchema.act.peTypes.peTypeId,
+            code: DbSchema.act.peTypes.code,
+            name: DbSchema.act.peTypes.name,
+          }),
+        ),
+        fields: z.array(
+          CustomFieldWithUserCopyValue.extend({ arffId: z.string() }),
+        ),
+        isUserReg: DbSchema.act.activities.isUserReg,
+      }),
+    )`
+        select
+          a.activity_id "activityId",
+          a.code,
+          a.public_name "publicName",
+          a.event_inst_id "eventInstId",
+          a.category_id "categoryId",
+          a.category_code "categoryCode",
+          a.validators,
+          a.pe_types "peTypes",
+          coalesce(f.fields, '[]'::jsonb) "fields",
+          a.is_user_reg "isUserReg"
+        from
+          act.act_with_validators a
+        left join lateral (
+          select
+            jsonb_agg(jsonb_build_object(
+              'fieldDefinitionId', f.field_definition_id,
+              'arffId', af.arff_id,
+              'isCopyUserValue', af.is_copy_user_value,
+              'code', f.code,
+              'userCopyValue', ufwv.value,
+              'fieldTypeCode', f.field_type_code,
+              'title', COALESCE(af.field_title_override, f.title),
+              'mask', f.mask,
+              'options', f."options",
+              'validators', f.validators
+            )) as fields
+          from
+            act.activity_reg_form_fields af
+          left join cf.custom_fields_with_validators f on
+            f.field_definition_id = af.field_definition_id
+              -- значение из профиля юзера
+          left join ev.user_fields_with_values ufwv on
+              af.field_definition_id = ufwv.field_definition_id
+              and ufwv.user_id = ${userId}
+              and ufwv.event_id = ${eventId}
+              and ufwv.value is not null
+              -- только если нужно копировать
+              and af.is_copy_user_value = true
+          where
+            af.activity_id = a.activity_id
+        ) f on true
+          where a.code = ${activityCode}
+      `);
+  }
+
+  async getActRegDataWithFieldsAndValidatorsAndActValidators(
+    activityCode: string,
+  ) {
+    return await selPool.maybeOne(sql.type(
+      z.object({
+        activityId: DbSchema.act.activities.activityId,
+        code: DbSchema.act.activities.code,
+        publicName: DbSchema.act.activities.publicName,
+        eventInstId: DbSchema.act.activities.eventInstId,
+        categoryId: DbSchema.act.activities.categoryId,
+        categoryCode: DbSchema.act.activityCategories.code,
+        validators: z.array(apiTypes.activities.ActValidator),
+        peTypes: z.array(
+          z.object({
+            peTypeId: DbSchema.act.peTypes.peTypeId,
+            code: DbSchema.act.peTypes.code,
+            name: DbSchema.act.peTypes.name,
+          }),
+        ),
+        fields: z.array(
+          CustomFieldWithValidators.extend({ arffId: z.string() }),
+        ),
+        isUserReg: DbSchema.act.activities.isUserReg,
+      }),
+    )`
+            select
+              a.activity_id "activityId",
+              a.code,
+              a.public_name "publicName",
+              a.event_inst_id "eventInstId",
+              a.category_id "categoryId",
+              a.category_code "categoryCode",
+              a.validators,
+              a.pe_types "peTypes",
+              coalesce(f.fields, '[]'::jsonb) "fields",
+              a.is_user_reg "isUserReg"
+            from
+              act.act_with_validators a
+            left join lateral (
+              select
+                jsonb_agg(jsonb_build_object(
+                  'fieldDefinitionId', f.field_definition_id,
+                  'arffId', af.arff_id,
+                  'isCopyUserValue', af.is_copy_user_value,
+                  'code', f.code,
+                  'fieldTypeCode', f.field_type_code,
+                  'title', COALESCE(af.field_title_override, f.title),
+                  'mask', f.mask,
+                  'options', f."options",
+                  'validators', f.validators
+                )) as fields
+              from
+                act.activity_reg_form_fields af
+              left join cf.custom_fields_with_validators f on
+                f.field_definition_id = af.field_definition_id
+              where
+                af.activity_id = a.activity_id
+            ) f on true
+              where a.code = ${activityCode}
+          `);
+  }
+
+  async getActRegWithFieldsAndValidatorsAndValuesAndActValidators(
+    activityRegId: string,
+  ) {
+    return selPool.maybeOne(sql.type(
+      z.object({
+        activityRegId: DbSchema.act.activityRegs.activityRegId,
+        activityId: DbSchema.act.activityRegs.activityId,
+        activityCode: DbSchema.act.activities.code,
+        activityPublicName: DbSchema.act.activities.publicName,
+        peId: DbSchema.act.activityRegs.peId,
+        peName: DbSchema.act.partEntities.name.nullable(),
+        peOwnerId: DbSchema.act.partEntities.ownerId.nullable(),
+        userId: DbSchema.act.activityRegs.userId.nullable(),
+        isPaid: DbSchema.act.activityRegs.isPaid,
+        validators: z.array(apiTypes.activities.ActValidator),
+        statusHistory: z.array(
+          z.object({
+            statusHistoryId: DbSchema.act.actRegStatusHistory.statusHistoryId,
+            name: DbSchema.act.actRegStatuses.name,
+            code: DbSchema.act.actRegStatuses.code,
+            note: DbSchema.act.actRegStatusHistory.note,
+            setDate: DbSchema.act.actRegStatusHistory.setDate,
+            actRegStatusId: DbSchema.act.actRegStatuses.actRegStatusId,
+            color: DbSchema.act.actRegStatuses.color,
+            isPaymentOpen: DbSchema.act.actRegStatuses.isPaymentOpen,
+          }),
+        ),
+        fields: z.array(
+          CustomFieldWithValidatorsAndValue.extend({
+            arffId: DbSchema.act.activityRegFormFields.arffId,
+            isChangeResetStatus:
+              DbSchema.act.activityRegFormFields.isChangeResetStatus,
+          }),
+        ),
+        isUserReg: DbSchema.act.activities.isUserReg,
+        peTypes: z.array(
+          z.object({
+            peTypeId: DbSchema.act.peTypes.peTypeId,
+            code: DbSchema.act.peTypes.code,
+            name: DbSchema.act.peTypes.name,
+          }),
+        ),
+      }),
+    )`
+      select
+        ar.activity_reg_id "activityRegId",
+        ar.activity_id "activityId",
+        ar.activity_code "activityCode",
+        ar.activity_public_name "activityPublicName",
+        ar.pe_id "peId",
+        ar.pe_name "peName",
+        ar.pe_owner_id "peOwnerId",
+        ar.user_id "userId",
+        ar.is_paid "isPaid",
+        ar.status_history "statusHistory",
+        f.fields "fields",
+        awv.validators,
+	      ar.is_user_reg "isUserReg",
+        awv.pe_types "peTypes"
+      from
+        act.act_regs_with_status ar
+      left join lateral (
+        select
+          jsonb_agg(jsonb_build_object(
+                  'fieldDefinitionId', cfd.field_definition_id, 
+                  'arffId', f_1.arff_id, 
+                  'isCopyUserValue', f_1.is_copy_user_value, 
+                  'code', cfd.code, 
+                  'value', afv.value, 
+                  'fieldTypeCode', ft.code, 
+                  'title', coalesce(f_1.field_title_override, cfd.title), 
+                  'mask', cfd.mask, 
+                  'options', cfd.options, 
+                  'isChangeResetStatus', f_1.is_change_reset_status,
+                      'validators', cfwv.validators
+                    )) as fields
+        from
+          act.activity_reg_form_fields f_1
+        left join cf.custom_field_definitions cfd on
+          f_1.field_definition_id = cfd.field_definition_id
+        left join cf.field_types ft on
+          cfd.field_type_id = ft.field_type_id
+        left join act.ar_field_values afv on
+          f_1.arff_id = afv.arff_id
+          and afv.activity_reg_id = ar.activity_reg_id
+        left join cf.custom_fields_with_validators cfwv on
+          cfwv.field_definition_id = cfd.field_definition_id
+        where
+          f_1.activity_id = ar.activity_id) f on
+        true
+      left join act.act_with_validators awv on
+        awv.activity_id = ar.activity_id
+      where ar.activity_reg_id = ${activityRegId}
+    `);
+  }
 }
 
 export const cActService = new CActService();