|
@@ -1,13 +1,16 @@
|
|
|
import { apiTypes } from "#api/current-api.js";
|
|
|
-import { selPool } from "#db/db.js";
|
|
|
+import { 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";
|
|
|
import { sql } from "slonik";
|
|
|
import { z } from "zod";
|
|
|
|
|
|
class CActService {
|
|
|
- addDataToActValidator = async (
|
|
|
+ async addDataToActValidator(
|
|
|
validator: z.infer<typeof apiTypes.activities.ActValidator>,
|
|
|
activityId: string,
|
|
|
- ): Promise<z.infer<typeof apiTypes.activities.ActValidatorWithData>> => {
|
|
|
+ ): Promise<z.infer<typeof apiTypes.activities.ActValidatorWithData>> {
|
|
|
switch (validator.code) {
|
|
|
case "max-regs": {
|
|
|
const currentRegs = await selPool.oneFirst(sql.type(
|
|
@@ -35,7 +38,339 @@ class CActService {
|
|
|
return validator;
|
|
|
}
|
|
|
}
|
|
|
- };
|
|
|
+ }
|
|
|
+
|
|
|
+ async getActRegs(userId: string) {
|
|
|
+ const actRegs = await selPool.any(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,
|
|
|
+ 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,
|
|
|
+ }),
|
|
|
+ ),
|
|
|
+ }),
|
|
|
+ )`
|
|
|
+ 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"
|
|
|
+ from
|
|
|
+ act.act_regs_with_status ar
|
|
|
+ where
|
|
|
+ ar.user_id = ${userId}
|
|
|
+ or
|
|
|
+ ar.pe_owner_id = ${userId}
|
|
|
+ or
|
|
|
+ EXISTS (
|
|
|
+ select 1
|
|
|
+ from act.pe_members pm_check
|
|
|
+ where pm_check.pe_id = ar.pe_id
|
|
|
+ and pm_check.user_id = ${userId}
|
|
|
+ );
|
|
|
+ `);
|
|
|
+ return actRegs;
|
|
|
+ }
|
|
|
+
|
|
|
+ async getActReg(activityRegId: string) {
|
|
|
+ const actReg = await 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,
|
|
|
+ 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(
|
|
|
+ CustomFieldWithValue.extend({
|
|
|
+ arffId: DbSchema.act.activityRegFormFields.arffId,
|
|
|
+ isChangeResetStatus:
|
|
|
+ DbSchema.act.activityRegFormFields.isChangeResetStatus,
|
|
|
+ }),
|
|
|
+ ),
|
|
|
+ }),
|
|
|
+ )`
|
|
|
+ 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",
|
|
|
+ ar.fields "fields"
|
|
|
+ from
|
|
|
+ act.act_regs_with_values ar
|
|
|
+ where
|
|
|
+ ar.activity_reg_id = ${activityRegId}
|
|
|
+ `);
|
|
|
+ return actReg;
|
|
|
+ }
|
|
|
+
|
|
|
+ async getActRegForPeMember(activityRegId: string) {
|
|
|
+ const actReg = await 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,
|
|
|
+ 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,
|
|
|
+ }),
|
|
|
+ ),
|
|
|
+ }),
|
|
|
+ )`
|
|
|
+ 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"
|
|
|
+ from
|
|
|
+ act.act_regs_with_values ar
|
|
|
+ where
|
|
|
+ ar.activity_reg_id = ${activityRegId}
|
|
|
+ `);
|
|
|
+ return actReg;
|
|
|
+ }
|
|
|
+
|
|
|
+ async getInitialRegStatusId(activityId: string) {
|
|
|
+ const initialRegStatusId = await selPool.oneFirst(sql.type(
|
|
|
+ z.object({
|
|
|
+ initialRegStatusId: DbSchema.act.activities.initialRegStatusId,
|
|
|
+ }),
|
|
|
+ )`
|
|
|
+ select
|
|
|
+ initial_reg_status_id "initialRegStatusId"
|
|
|
+ from
|
|
|
+ act.activities
|
|
|
+ where
|
|
|
+ activity_id = ${activityId}
|
|
|
+ `);
|
|
|
+ return initialRegStatusId;
|
|
|
+ }
|
|
|
+
|
|
|
+ async getActivity(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,
|
|
|
+ isUserReg: DbSchema.act.activities.isUserReg,
|
|
|
+ paymentConfig: DbSchema.act.activities.paymentConfig,
|
|
|
+ registrationProductId: DbSchema.act.activities.registrationProductId,
|
|
|
+ participantProductId: DbSchema.act.activities.participantProductId,
|
|
|
+ nextRegStatusIdAfterPayment:
|
|
|
+ DbSchema.act.activities.nextRegStatusIdAfterPayment,
|
|
|
+ }),
|
|
|
+ )`
|
|
|
+ select
|
|
|
+ a.activity_id "activityId",
|
|
|
+ a.code "code",
|
|
|
+ a.public_name "publicName",
|
|
|
+ a.event_inst_id "eventInstId",
|
|
|
+ a.category_id "categoryId",
|
|
|
+ a.is_user_reg "isUserReg",
|
|
|
+ a.payment_config "paymentConfig",
|
|
|
+ a.registration_product_id "registrationProductId",
|
|
|
+ a.participant_product_id "participantProductId",
|
|
|
+ a.next_reg_status_id_after_payment "nextRegStatusIdAfterPayment"
|
|
|
+ from
|
|
|
+ act.activities a
|
|
|
+ where
|
|
|
+ a.code = ${activityCode}
|
|
|
+ `);
|
|
|
+ }
|
|
|
+
|
|
|
+ async checkActivityRegNumber(activityRegNumber: string) {
|
|
|
+ const isExist = await selPool.exists(sql.unsafe`
|
|
|
+ select
|
|
|
+ number
|
|
|
+ from
|
|
|
+ act.activity_regs
|
|
|
+ where
|
|
|
+ number = ${activityRegNumber}
|
|
|
+ `);
|
|
|
+ return !!isExist;
|
|
|
+ }
|
|
|
+
|
|
|
+ async updateActRegPaymentStatus(activityRegId: string) {
|
|
|
+ const actReg = await this.getActReg(activityRegId);
|
|
|
+ if (!actReg) {
|
|
|
+ throw ApiError.BadRequest("actRegNotFound", "Не найдена регистрация");
|
|
|
+ }
|
|
|
+
|
|
|
+ const activity = await this.getActivity(actReg.activityCode);
|
|
|
+ if (!activity) {
|
|
|
+ throw ApiError.BadRequest("activityNotFound", "Не найдена активность");
|
|
|
+ }
|
|
|
+
|
|
|
+ // оплата за всю регистрацию
|
|
|
+ if (activity.paymentConfig === "PER_REGISTRATION") {
|
|
|
+ await updPool.query(sql.unsafe`
|
|
|
+ update act.activity_regs
|
|
|
+ set is_paid = true
|
|
|
+ where activity_reg_id = ${activityRegId}
|
|
|
+ `);
|
|
|
+
|
|
|
+ await updPool.query(sql.unsafe`
|
|
|
+ insert into act.act_reg_status_history (
|
|
|
+ activity_reg_id,
|
|
|
+ act_reg_status_id,
|
|
|
+ note
|
|
|
+ )
|
|
|
+ values (
|
|
|
+ ${activityRegId},
|
|
|
+ ${activity.nextRegStatusIdAfterPayment},
|
|
|
+ 'Оплачено'
|
|
|
+ )
|
|
|
+ `);
|
|
|
+
|
|
|
+ // TODO: QR
|
|
|
+ }
|
|
|
+
|
|
|
+ // оплата за каждого участника
|
|
|
+ if (activity.paymentConfig === "PER_PARTICIPANT") {
|
|
|
+ if (!actReg.peId) {
|
|
|
+ throw Error("peId not found");
|
|
|
+ }
|
|
|
+
|
|
|
+ const members = await selPool.any(sql.type(
|
|
|
+ z.object({
|
|
|
+ peMemberId: DbSchema.act.peMembers.peMemberId,
|
|
|
+ userId: DbSchema.act.peMembers.userId,
|
|
|
+ }),
|
|
|
+ )`
|
|
|
+ select
|
|
|
+ pm.pe_member_id "peMemberId",
|
|
|
+ pm.user_id "userId"
|
|
|
+ from
|
|
|
+ act.pe_members pm
|
|
|
+ where
|
|
|
+ pm.pe_id = ${actReg.peId}
|
|
|
+ `);
|
|
|
+
|
|
|
+ const memberIds = members.map((member) => member.peMemberId);
|
|
|
+
|
|
|
+ const paidMemberRows = await selPool.any(sql.unsafe`
|
|
|
+ select distinct
|
|
|
+ oi.pe_member_id -- Выбираем ID тех, кто заплатил
|
|
|
+ from
|
|
|
+ shop.order_items oi
|
|
|
+ where
|
|
|
+ oi.pe_member_id = ANY(${sql.array(memberIds, "uuid")})
|
|
|
+ and oi.status = 'PAID'
|
|
|
+ `);
|
|
|
+
|
|
|
+ if (memberIds.length !== paidMemberRows.length) {
|
|
|
+ await updPool.query(sql.unsafe`
|
|
|
+ update act.activity_regs
|
|
|
+ set is_paid = false
|
|
|
+ where activity_reg_id = ${activityRegId}
|
|
|
+ `);
|
|
|
+ } else {
|
|
|
+ await updPool.query(sql.unsafe`
|
|
|
+ update act.activity_regs
|
|
|
+ set is_paid = true
|
|
|
+ where activity_reg_id = ${activityRegId}
|
|
|
+ `);
|
|
|
+ await updPool.query(sql.unsafe`
|
|
|
+ insert into act.act_reg_status_history (
|
|
|
+ activity_reg_id,
|
|
|
+ act_reg_status_id,
|
|
|
+ note
|
|
|
+ )
|
|
|
+ values (
|
|
|
+ ${activityRegId},
|
|
|
+ ${activity.nextRegStatusIdAfterPayment},
|
|
|
+ 'Оплачены все участники'
|
|
|
+ )
|
|
|
+ `);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async checkActRegOwner(userId: string, activityRegId: string) {
|
|
|
+ const actReg = await this.getActReg(activityRegId);
|
|
|
+ if (!actReg) {
|
|
|
+ throw ApiError.BadRequest("actRegNotFound", "Не найдена регистрация");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (actReg.userId === userId) {
|
|
|
+ return "owner";
|
|
|
+ }
|
|
|
+
|
|
|
+ const isMemeber = await selPool.exists(sql.unsafe`
|
|
|
+ select 1
|
|
|
+ from act.pe_members
|
|
|
+ where pe_id = ${actReg.peId}
|
|
|
+ and user_id = ${userId}
|
|
|
+ `);
|
|
|
+
|
|
|
+ if (isMemeber) return "member";
|
|
|
+
|
|
|
+ return undefined;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
export const cActService = new CActService();
|