c-act-service.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. import { apiTypes } from "#api/current-api.js";
  2. import {
  3. CustomFieldWithUserCopyValue,
  4. CustomFieldWithValidators,
  5. CustomFieldWithValidatorsAndValue,
  6. CustomFieldWithValue,
  7. } from "#api/v_0.1.0/types/custom-fields-types.js";
  8. import { DbSchema } from "#db/db-schema.js";
  9. import { selPool, updPool } from "#db/db.js";
  10. import { ApiError } from "#exceptions/api-error.js";
  11. import { logger } from "#plugins/logger.js";
  12. import { sql } from "slonik";
  13. import { z } from "zod";
  14. import { ordersService } from "../shop/orders-service.js";
  15. class CActService {
  16. async addDataToActValidator(
  17. validator: z.infer<typeof apiTypes.activities.ActValidator>,
  18. activityId: string,
  19. ): Promise<z.infer<typeof apiTypes.activities.ActValidatorWithData>> {
  20. switch (validator.code) {
  21. case "max-regs": {
  22. const currentRegs = await selPool.oneFirst(sql.type(
  23. z.object({
  24. count: z.number(),
  25. }),
  26. )`
  27. select
  28. count(*)
  29. from
  30. act.activity_regs
  31. where
  32. activity_id = ${activityId}
  33. `);
  34. return {
  35. ...validator,
  36. serverData: {
  37. currentRegs,
  38. },
  39. };
  40. }
  41. default: {
  42. return validator;
  43. }
  44. }
  45. }
  46. async getActRegs(userId: string) {
  47. const actRegs = await selPool.any(sql.type(
  48. z.object({
  49. activityRegId: DbSchema.act.activityRegs.activityRegId,
  50. activityId: DbSchema.act.activityRegs.activityId,
  51. activityCode: DbSchema.act.activities.code,
  52. activityPublicName: DbSchema.act.activities.publicName,
  53. peId: DbSchema.act.activityRegs.peId,
  54. peName: DbSchema.act.partEntities.name.nullable(),
  55. peOwnerId: DbSchema.act.partEntities.ownerId.nullable(),
  56. userId: DbSchema.act.activityRegs.userId.nullable(),
  57. isPaid: DbSchema.act.activityRegs.isPaid,
  58. isCanceled: DbSchema.act.activityRegs.isCanceled,
  59. statusHistory: z.array(
  60. z.object({
  61. statusHistoryId: DbSchema.act.actRegStatusHistory.statusHistoryId,
  62. name: DbSchema.act.actRegStatuses.name,
  63. code: DbSchema.act.actRegStatuses.code,
  64. note: DbSchema.act.actRegStatusHistory.note,
  65. setDate: DbSchema.act.actRegStatusHistory.setDate,
  66. actRegStatusId: DbSchema.act.actRegStatuses.actRegStatusId,
  67. color: DbSchema.act.actRegStatuses.color,
  68. isPaymentOpen: DbSchema.act.actRegStatuses.isPaymentOpen,
  69. }),
  70. ),
  71. isUserReg: DbSchema.act.activities.isUserReg,
  72. }),
  73. )`
  74. select
  75. ar.activity_reg_id "activityRegId",
  76. ar.activity_id "activityId",
  77. ar.activity_code "activityCode",
  78. ar.activity_public_name "activityPublicName",
  79. ar.pe_id "peId",
  80. ar.pe_name "peName",
  81. ar.pe_owner_id "peOwnerId",
  82. ar.user_id "userId",
  83. ar.is_paid "isPaid",
  84. ar.status_history "statusHistory",
  85. ar.is_user_reg "isUserReg",
  86. ar.is_canceled "isCanceled"
  87. from
  88. act.act_regs_with_status ar
  89. where
  90. ar.user_id = ${userId}
  91. or
  92. ar.pe_owner_id = ${userId}
  93. or
  94. EXISTS (
  95. select 1
  96. from act.pe_members pm_check
  97. where pm_check.pe_id = ar.pe_id
  98. and pm_check.user_id = ${userId}
  99. and pm_check.is_active = true
  100. );
  101. `);
  102. return actRegs;
  103. }
  104. async getActRegWithValues(activityRegId: string) {
  105. const actReg = await selPool.maybeOne(sql.type(
  106. z.object({
  107. activityRegId: DbSchema.act.activityRegs.activityRegId,
  108. activityId: DbSchema.act.activityRegs.activityId,
  109. activityCode: DbSchema.act.activities.code,
  110. activityPublicName: DbSchema.act.activities.publicName,
  111. peId: DbSchema.act.activityRegs.peId,
  112. peName: DbSchema.act.partEntities.name.nullable(),
  113. peOwnerId: DbSchema.act.partEntities.ownerId.nullable(),
  114. userId: DbSchema.act.activityRegs.userId.nullable(),
  115. isPaid: DbSchema.act.activityRegs.isPaid,
  116. isCanceled: DbSchema.act.activityRegs.isCanceled,
  117. statusHistory: z.array(
  118. z.object({
  119. statusHistoryId: DbSchema.act.actRegStatusHistory.statusHistoryId,
  120. name: DbSchema.act.actRegStatuses.name,
  121. code: DbSchema.act.actRegStatuses.code,
  122. note: DbSchema.act.actRegStatusHistory.note,
  123. setDate: DbSchema.act.actRegStatusHistory.setDate,
  124. actRegStatusId: DbSchema.act.actRegStatuses.actRegStatusId,
  125. color: DbSchema.act.actRegStatuses.color,
  126. isPaymentOpen: DbSchema.act.actRegStatuses.isPaymentOpen,
  127. }),
  128. ),
  129. fields: z.array(
  130. CustomFieldWithValue.extend({
  131. arffId: DbSchema.act.activityRegFormFields.arffId,
  132. isChangeResetStatus:
  133. DbSchema.act.activityRegFormFields.isChangeResetStatus,
  134. }),
  135. ),
  136. isUserReg: DbSchema.act.activities.isUserReg,
  137. }),
  138. )`
  139. select
  140. ar.activity_reg_id "activityRegId",
  141. ar.activity_id "activityId",
  142. ar.activity_code "activityCode",
  143. ar.activity_public_name "activityPublicName",
  144. ar.pe_id "peId",
  145. ar.pe_name "peName",
  146. ar.pe_owner_id "peOwnerId",
  147. ar.user_id "userId",
  148. ar.is_paid "isPaid",
  149. ar.status_history "statusHistory",
  150. ar.fields "fields",
  151. ar.is_user_reg "isUserReg",
  152. ar.is_canceled "isCanceled"
  153. from
  154. act.act_regs_with_values ar
  155. where
  156. ar.activity_reg_id = ${activityRegId}
  157. `);
  158. return actReg;
  159. }
  160. async getActRegForPeMember(activityRegId: string) {
  161. const actReg = await selPool.maybeOne(sql.type(
  162. z.object({
  163. activityRegId: DbSchema.act.activityRegs.activityRegId,
  164. activityId: DbSchema.act.activityRegs.activityId,
  165. activityCode: DbSchema.act.activities.code,
  166. activityPublicName: DbSchema.act.activities.publicName,
  167. peId: DbSchema.act.activityRegs.peId,
  168. peName: DbSchema.act.partEntities.name.nullable(),
  169. peOwnerId: DbSchema.act.partEntities.ownerId.nullable(),
  170. userId: DbSchema.act.activityRegs.userId.nullable(),
  171. isPaid: DbSchema.act.activityRegs.isPaid,
  172. isCanceled: DbSchema.act.activityRegs.isCanceled,
  173. statusHistory: z.array(
  174. z.object({
  175. statusHistoryId: DbSchema.act.actRegStatusHistory.statusHistoryId,
  176. name: DbSchema.act.actRegStatuses.name,
  177. code: DbSchema.act.actRegStatuses.code,
  178. note: DbSchema.act.actRegStatusHistory.note,
  179. setDate: DbSchema.act.actRegStatusHistory.setDate,
  180. actRegStatusId: DbSchema.act.actRegStatuses.actRegStatusId,
  181. color: DbSchema.act.actRegStatuses.color,
  182. isPaymentOpen: DbSchema.act.actRegStatuses.isPaymentOpen,
  183. }),
  184. ),
  185. isUserReg: DbSchema.act.activities.isUserReg,
  186. }),
  187. )`
  188. select
  189. ar.activity_reg_id "activityRegId",
  190. ar.activity_id "activityId",
  191. ar.activity_code "activityCode",
  192. ar.activity_public_name "activityPublicName",
  193. ar.pe_id "peId",
  194. ar.pe_name "peName",
  195. ar.pe_owner_id "peOwnerId",
  196. ar.user_id "userId",
  197. ar.is_paid "isPaid",
  198. ar.status_history "statusHistory",
  199. ar.is_user_reg "isUserReg",
  200. ar.is_canceled "isCanceled"
  201. from
  202. act.act_regs_with_values ar
  203. where
  204. ar.activity_reg_id = ${activityRegId}
  205. `);
  206. return actReg;
  207. }
  208. async getInitialRegStatusId(activityId: string) {
  209. const initialRegStatusId = await selPool.oneFirst(sql.type(
  210. z.object({
  211. initialRegStatusId: DbSchema.act.activities.initialRegStatusId,
  212. }),
  213. )`
  214. select
  215. initial_reg_status_id "initialRegStatusId"
  216. from
  217. act.activities
  218. where
  219. activity_id = ${activityId}
  220. `);
  221. return initialRegStatusId;
  222. }
  223. async getActivity(activityCode: string) {
  224. return await selPool.maybeOne(sql.type(
  225. z.object({
  226. activityId: DbSchema.act.activities.activityId,
  227. code: DbSchema.act.activities.code,
  228. publicName: DbSchema.act.activities.publicName,
  229. eventInstId: DbSchema.act.activities.eventInstId,
  230. categoryId: DbSchema.act.activities.categoryId,
  231. isUserReg: DbSchema.act.activities.isUserReg,
  232. paymentConfig: DbSchema.act.activities.paymentConfig,
  233. registrationProductId: DbSchema.act.activities.registrationProductId,
  234. participantProductId: DbSchema.act.activities.participantProductId,
  235. nextRegStatusIdAfterPayment:
  236. DbSchema.act.activities.nextRegStatusIdAfterPayment,
  237. }),
  238. )`
  239. select
  240. a.activity_id "activityId",
  241. a.code "code",
  242. a.public_name "publicName",
  243. a.event_inst_id "eventInstId",
  244. a.category_id "categoryId",
  245. a.is_user_reg "isUserReg",
  246. a.payment_config "paymentConfig",
  247. a.registration_product_id "registrationProductId",
  248. a.participant_product_id "participantProductId",
  249. a.next_reg_status_id_after_payment "nextRegStatusIdAfterPayment"
  250. from
  251. act.activities a
  252. where
  253. a.code = ${activityCode}
  254. `);
  255. }
  256. async checkActivityRegNumber(activityRegNumber: string) {
  257. const isExist = await selPool.exists(sql.unsafe`
  258. select
  259. number
  260. from
  261. act.activity_regs
  262. where
  263. number = ${activityRegNumber}
  264. `);
  265. return !!isExist;
  266. }
  267. async updateActRegPaymentStatus(activityRegId: string) {
  268. const actReg = await this.getActRegForPeMember(activityRegId);
  269. if (!actReg) {
  270. throw ApiError.BadRequest("actRegNotFound", "Не найдена регистрация");
  271. }
  272. const activity = await this.getActivity(actReg.activityCode);
  273. if (!activity) {
  274. throw ApiError.BadRequest("activityNotFound", "Не найдена активность");
  275. }
  276. // оплата за всю регистрацию
  277. if (activity.paymentConfig === "PER_REGISTRATION") {
  278. const isPaid = await this.checkActivityRegPayment({
  279. activityRegId,
  280. });
  281. // если надо поменять
  282. if (actReg.isPaid !== isPaid) {
  283. if (isPaid) {
  284. await updPool.query(sql.unsafe`
  285. update act.activity_regs
  286. set is_paid = true
  287. where activity_reg_id = ${activityRegId}
  288. `);
  289. await updPool.query(sql.unsafe`
  290. insert into act.act_reg_status_history (
  291. activity_reg_id,
  292. act_reg_status_id,
  293. note
  294. )
  295. values (
  296. ${activityRegId},
  297. ${activity.nextRegStatusIdAfterPayment},
  298. 'Оплачено'
  299. )
  300. `);
  301. } else {
  302. // такого по идее не должно быть
  303. logger.error("isPaid !== actReg.isPaid", {
  304. isPaid,
  305. actReg,
  306. activity,
  307. });
  308. }
  309. }
  310. // TODO: QR
  311. }
  312. // оплата за каждого участника
  313. if (activity.paymentConfig === "PER_PARTICIPANT") {
  314. if (!actReg.peId) {
  315. throw Error("peId not found");
  316. }
  317. const isAllPaid = await this.checkMembersPayment({
  318. activityRegId,
  319. peId: actReg.peId,
  320. });
  321. // если надо поменять
  322. if (isAllPaid !== actReg.isPaid) {
  323. if (!isAllPaid) {
  324. await updPool.query(sql.unsafe`
  325. update act.activity_regs
  326. set is_paid = false
  327. where activity_reg_id = ${activityRegId}
  328. `);
  329. // TODO: Возможно стоит добавить в act.activities поле payment_status_id
  330. await updPool.query(sql.unsafe`
  331. insert into act.act_reg_status_history (
  332. activity_reg_id,
  333. act_reg_status_id,
  334. note
  335. )
  336. values (
  337. ${activityRegId},
  338. 'd6d27702-cded-4625-be07-e339c4003c2f',
  339. 'Оплачены не все участники'
  340. )
  341. `);
  342. } else {
  343. await updPool.query(sql.unsafe`
  344. update act.activity_regs
  345. set is_paid = true
  346. where activity_reg_id = ${activityRegId}
  347. `);
  348. await updPool.query(sql.unsafe`
  349. insert into act.act_reg_status_history (
  350. activity_reg_id,
  351. act_reg_status_id,
  352. note
  353. )
  354. values (
  355. ${activityRegId},
  356. ${activity.nextRegStatusIdAfterPayment},
  357. 'Оплачены все участники'
  358. )
  359. `);
  360. }
  361. }
  362. }
  363. }
  364. async checkMembersPayment({
  365. peId,
  366. activityRegId,
  367. }: {
  368. peId: string;
  369. activityRegId: string;
  370. }) {
  371. const members = await selPool.any(sql.type(
  372. z.object({
  373. peMemberId: DbSchema.act.peMembers.peMemberId,
  374. userId: DbSchema.act.peMembers.userId,
  375. }),
  376. )`
  377. select
  378. pm.pe_member_id "peMemberId",
  379. pm.user_id "userId"
  380. from
  381. act.pe_members pm
  382. where
  383. pm.pe_id = ${peId}
  384. and pm.is_active = true
  385. `);
  386. const memberIds = members.map((member) => member.peMemberId);
  387. const paidMemberRows = await selPool.any(sql.unsafe`
  388. select distinct
  389. oi.pe_member_id -- Выбираем ID тех, кто заплатил
  390. from
  391. shop.order_items oi
  392. where
  393. oi.pe_member_id = ANY(${sql.array(memberIds, "uuid")})
  394. and oi.status = 'PAID'
  395. and oi.activity_reg_id = ${activityRegId}
  396. `);
  397. return memberIds.length === paidMemberRows.length;
  398. }
  399. async checkActivityRegPayment({ activityRegId }: { activityRegId: string }) {
  400. const isPaid = await selPool.exists(sql.unsafe`
  401. select 1
  402. from shop.order_items oi
  403. where oi.activity_reg_id = ${activityRegId}
  404. and oi.status = 'PAID'
  405. `);
  406. return !!isPaid;
  407. }
  408. async checkActRegOwner(userId: string, activityRegId: string) {
  409. const actReg = await this.getActRegWithValues(activityRegId);
  410. if (!actReg) {
  411. throw ApiError.BadRequest("actRegNotFound", "Не найдена регистрация");
  412. }
  413. if (actReg.peOwnerId === userId || actReg.userId === userId) {
  414. return "owner";
  415. }
  416. const isMemeber = await selPool.exists(sql.unsafe`
  417. select 1
  418. from act.pe_members
  419. where pe_id = ${actReg.peId}
  420. and user_id = ${userId}
  421. and is_active = true
  422. `);
  423. if (isMemeber) return "member";
  424. return undefined;
  425. }
  426. async checkPeMemberActivityRegPayment({
  427. peMemberId,
  428. activityRegId,
  429. }: {
  430. peMemberId: string;
  431. activityRegId: string;
  432. }) {
  433. const isPaid = await selPool.exists(sql.unsafe`
  434. select 1
  435. from shop.order_items oi
  436. where oi.pe_member_id = ${peMemberId}
  437. and oi.activity_reg_id = ${activityRegId}
  438. and oi.status = 'PAID'
  439. `);
  440. return !!isPaid;
  441. }
  442. async getActRegDataWithUserCopyValuesAndActValidators(
  443. userId: string,
  444. eventId: string,
  445. activityCode: string,
  446. ) {
  447. return await selPool.maybeOne(sql.type(
  448. z.object({
  449. activityId: DbSchema.act.activities.activityId,
  450. code: DbSchema.act.activities.code,
  451. publicName: DbSchema.act.activities.publicName,
  452. eventInstId: DbSchema.act.activities.eventInstId,
  453. categoryId: DbSchema.act.activities.categoryId,
  454. categoryCode: DbSchema.act.activityCategories.code,
  455. validators: z.array(apiTypes.activities.ActValidator),
  456. peTypes: z.array(
  457. z.object({
  458. peTypeId: DbSchema.act.peTypes.peTypeId,
  459. code: DbSchema.act.peTypes.code,
  460. name: DbSchema.act.peTypes.name,
  461. }),
  462. ),
  463. fields: z.array(
  464. CustomFieldWithUserCopyValue.extend({ arffId: z.string() }),
  465. ),
  466. isUserReg: DbSchema.act.activities.isUserReg,
  467. }),
  468. )`
  469. select
  470. a.activity_id "activityId",
  471. a.code,
  472. a.public_name "publicName",
  473. a.event_inst_id "eventInstId",
  474. a.category_id "categoryId",
  475. a.category_code "categoryCode",
  476. a.validators,
  477. a.pe_types "peTypes",
  478. coalesce(f.fields, '[]'::jsonb) "fields",
  479. a.is_user_reg "isUserReg"
  480. from
  481. act.act_with_validators a
  482. left join lateral (
  483. select
  484. jsonb_agg(jsonb_build_object(
  485. 'fieldDefinitionId', f.field_definition_id,
  486. 'arffId', af.arff_id,
  487. 'isCopyUserValue', af.is_copy_user_value,
  488. 'code', f.code,
  489. 'userCopyValue', ufwv.value,
  490. 'fieldTypeCode', f.field_type_code,
  491. 'title', COALESCE(af.field_title_override, f.title),
  492. 'mask', f.mask,
  493. 'options', f."options",
  494. 'validators', f.validators
  495. )) as fields
  496. from
  497. act.activity_reg_form_fields af
  498. left join cf.custom_fields_with_validators f on
  499. f.field_definition_id = af.field_definition_id
  500. -- значение из профиля юзера
  501. left join ev.user_fields_with_values ufwv on
  502. af.field_definition_id = ufwv.field_definition_id
  503. and ufwv.user_id = ${userId}
  504. and ufwv.event_id = ${eventId}
  505. and ufwv.value is not null
  506. -- только если нужно копировать
  507. and af.is_copy_user_value = true
  508. where
  509. af.activity_id = a.activity_id
  510. ) f on true
  511. where a.code = ${activityCode}
  512. `);
  513. }
  514. async getActRegDataWithFieldsAndValidatorsAndActValidators(
  515. activityCode: string,
  516. ) {
  517. return await selPool.maybeOne(sql.type(
  518. z.object({
  519. activityId: DbSchema.act.activities.activityId,
  520. code: DbSchema.act.activities.code,
  521. publicName: DbSchema.act.activities.publicName,
  522. eventInstId: DbSchema.act.activities.eventInstId,
  523. categoryId: DbSchema.act.activities.categoryId,
  524. categoryCode: DbSchema.act.activityCategories.code,
  525. validators: z.array(apiTypes.activities.ActValidator),
  526. peTypes: z.array(
  527. z.object({
  528. peTypeId: DbSchema.act.peTypes.peTypeId,
  529. code: DbSchema.act.peTypes.code,
  530. name: DbSchema.act.peTypes.name,
  531. }),
  532. ),
  533. fields: z.array(
  534. CustomFieldWithValidators.extend({ arffId: z.string() }),
  535. ),
  536. isUserReg: DbSchema.act.activities.isUserReg,
  537. }),
  538. )`
  539. select
  540. a.activity_id "activityId",
  541. a.code,
  542. a.public_name "publicName",
  543. a.event_inst_id "eventInstId",
  544. a.category_id "categoryId",
  545. a.category_code "categoryCode",
  546. a.validators,
  547. a.pe_types "peTypes",
  548. coalesce(f.fields, '[]'::jsonb) "fields",
  549. a.is_user_reg "isUserReg"
  550. from
  551. act.act_with_validators a
  552. left join lateral (
  553. select
  554. jsonb_agg(jsonb_build_object(
  555. 'fieldDefinitionId', f.field_definition_id,
  556. 'arffId', af.arff_id,
  557. 'isCopyUserValue', af.is_copy_user_value,
  558. 'code', f.code,
  559. 'fieldTypeCode', f.field_type_code,
  560. 'title', COALESCE(af.field_title_override, f.title),
  561. 'mask', f.mask,
  562. 'options', f."options",
  563. 'validators', f.validators
  564. )) as fields
  565. from
  566. act.activity_reg_form_fields af
  567. left join cf.custom_fields_with_validators f on
  568. f.field_definition_id = af.field_definition_id
  569. where
  570. af.activity_id = a.activity_id
  571. ) f on true
  572. where a.code = ${activityCode}
  573. `);
  574. }
  575. async getActRegWithFieldsAndValidatorsAndValuesAndActValidators(
  576. activityRegId: string,
  577. ) {
  578. return selPool.maybeOne(sql.type(
  579. z.object({
  580. activityRegId: DbSchema.act.activityRegs.activityRegId,
  581. activityId: DbSchema.act.activityRegs.activityId,
  582. activityCode: DbSchema.act.activities.code,
  583. activityPublicName: DbSchema.act.activities.publicName,
  584. peId: DbSchema.act.activityRegs.peId,
  585. peName: DbSchema.act.partEntities.name.nullable(),
  586. peOwnerId: DbSchema.act.partEntities.ownerId.nullable(),
  587. userId: DbSchema.act.activityRegs.userId.nullable(),
  588. isPaid: DbSchema.act.activityRegs.isPaid,
  589. isCanceled: DbSchema.act.activityRegs.isCanceled,
  590. validators: z.array(apiTypes.activities.ActValidator),
  591. statusHistory: z.array(
  592. z.object({
  593. statusHistoryId: DbSchema.act.actRegStatusHistory.statusHistoryId,
  594. name: DbSchema.act.actRegStatuses.name,
  595. code: DbSchema.act.actRegStatuses.code,
  596. note: DbSchema.act.actRegStatusHistory.note,
  597. setDate: DbSchema.act.actRegStatusHistory.setDate,
  598. actRegStatusId: DbSchema.act.actRegStatuses.actRegStatusId,
  599. color: DbSchema.act.actRegStatuses.color,
  600. isPaymentOpen: DbSchema.act.actRegStatuses.isPaymentOpen,
  601. }),
  602. ),
  603. fields: z.array(
  604. CustomFieldWithValidatorsAndValue.extend({
  605. arffId: DbSchema.act.activityRegFormFields.arffId,
  606. isChangeResetStatus:
  607. DbSchema.act.activityRegFormFields.isChangeResetStatus,
  608. }),
  609. ),
  610. isUserReg: DbSchema.act.activities.isUserReg,
  611. peTypes: z.array(
  612. z.object({
  613. peTypeId: DbSchema.act.peTypes.peTypeId,
  614. code: DbSchema.act.peTypes.code,
  615. name: DbSchema.act.peTypes.name,
  616. }),
  617. ),
  618. }),
  619. )`
  620. select
  621. ar.activity_reg_id "activityRegId",
  622. ar.activity_id "activityId",
  623. ar.activity_code "activityCode",
  624. ar.activity_public_name "activityPublicName",
  625. ar.pe_id "peId",
  626. ar.pe_name "peName",
  627. ar.pe_owner_id "peOwnerId",
  628. ar.user_id "userId",
  629. ar.is_paid "isPaid",
  630. ar.is_canceled "isCanceled",
  631. ar.status_history "statusHistory",
  632. f.fields "fields",
  633. awv.validators,
  634. ar.is_user_reg "isUserReg",
  635. awv.pe_types "peTypes"
  636. from
  637. act.act_regs_with_status ar
  638. left join lateral (
  639. select
  640. jsonb_agg(jsonb_build_object(
  641. 'fieldDefinitionId', cfd.field_definition_id,
  642. 'arffId', f_1.arff_id,
  643. 'isCopyUserValue', f_1.is_copy_user_value,
  644. 'code', cfd.code,
  645. 'value', afv.value,
  646. 'fieldTypeCode', ft.code,
  647. 'title', coalesce(f_1.field_title_override, cfd.title),
  648. 'mask', cfd.mask,
  649. 'options', cfd.options,
  650. 'isChangeResetStatus', f_1.is_change_reset_status,
  651. 'validators', cfwv.validators
  652. )) as fields
  653. from
  654. act.activity_reg_form_fields f_1
  655. left join cf.custom_field_definitions cfd on
  656. f_1.field_definition_id = cfd.field_definition_id
  657. left join cf.field_types ft on
  658. cfd.field_type_id = ft.field_type_id
  659. left join act.ar_field_values afv on
  660. f_1.arff_id = afv.arff_id
  661. and afv.activity_reg_id = ar.activity_reg_id
  662. left join cf.custom_fields_with_validators cfwv on
  663. cfwv.field_definition_id = cfd.field_definition_id
  664. where
  665. f_1.activity_id = ar.activity_id) f on
  666. true
  667. left join act.act_with_validators awv on
  668. awv.activity_id = ar.activity_id
  669. where ar.activity_reg_id = ${activityRegId}
  670. `);
  671. }
  672. async refundByPeMemberId(peMember: {
  673. peMemberId: string;
  674. peId: string;
  675. userId: string;
  676. }) {
  677. const actRegs = await selPool.any(sql.type(
  678. z.object({
  679. activityRegId: DbSchema.act.activityRegs.activityRegId,
  680. activityId: DbSchema.act.activityRegs.activityId,
  681. }),
  682. )`
  683. select
  684. r.activity_reg_id "activityRegId",
  685. r.activity_id "activityId"
  686. from
  687. act.activity_regs r
  688. left join act.activities a on
  689. a.activity_id = r.activity_id and
  690. a.payment_config = 'PER_PARTICIPANT'
  691. where
  692. pe_id = ${peMember.peId}
  693. `);
  694. for (const actReg of actRegs) {
  695. const orderItem = await selPool.maybeOne(sql.type(
  696. z.object({
  697. orderItemId: DbSchema.shop.orderItems.orderItemId,
  698. orderId: DbSchema.shop.orderItems.orderId,
  699. productId: DbSchema.shop.orderItems.productId,
  700. unitPrice: DbSchema.shop.orderItems.unitPrice,
  701. }),
  702. )`
  703. select
  704. oi.order_item_id "orderItemId",
  705. oi.order_id "orderId",
  706. oi.product_id "productId",
  707. oi.unit_price::float "unitPrice"
  708. from
  709. shop.order_items oi
  710. where
  711. oi.activity_reg_id = ${actReg.activityRegId} and
  712. oi.pe_member_id = ${peMember.peMemberId}
  713. `);
  714. if (!orderItem) {
  715. throw new Error("Order item not found");
  716. }
  717. await ordersService.refundOrderItem(orderItem.orderItemId);
  718. }
  719. }
  720. async getActivityRegsByPeId(peId: string) {
  721. return await selPool.any(sql.type(
  722. z.object({
  723. activityRegId: DbSchema.act.activityRegs.activityRegId,
  724. activityId: DbSchema.act.activityRegs.activityId,
  725. activityRegNumber: DbSchema.act.activityRegs.number,
  726. }),
  727. )`
  728. select
  729. r.activity_reg_id "activityRegId",
  730. r.activity_id "activityId",
  731. r.number "activityRegNumber"
  732. from
  733. act.activity_regs r
  734. where
  735. pe_id = ${peId}
  736. `);
  737. }
  738. async refundByActivityRegId(activityRegId: string) {
  739. const activityId = await selPool.maybeOneFirst(sql.type(
  740. z.object({
  741. activityId: DbSchema.act.activityRegs.activityId,
  742. }),
  743. )`
  744. select
  745. r.activity_id "activityId"
  746. from
  747. act.activity_regs r
  748. left join act.activities a on
  749. a.activity_id = r.activity_id
  750. where
  751. r.activity_reg_id = ${activityRegId}
  752. `);
  753. if (!activityId) {
  754. throw new Error("Activity reg not found");
  755. }
  756. const orderItems = await selPool.any(sql.type(
  757. z.object({
  758. orderItemId: DbSchema.shop.orderItems.orderItemId,
  759. orderId: DbSchema.shop.orderItems.orderId,
  760. paymentId: DbSchema.shop.payments.paymentId,
  761. paymentExternalTransactionId:
  762. DbSchema.shop.payments.externalTransactionId,
  763. }),
  764. )`
  765. select
  766. oi.order_item_id "orderItemId",
  767. oi.order_id "orderId"
  768. from
  769. shop.order_items oi
  770. where
  771. oi.activity_reg_id = ${activityRegId}
  772. `);
  773. for (const orderItem of orderItems) {
  774. await ordersService.refundOrderItem(orderItem.orderItemId);
  775. }
  776. }
  777. }
  778. export const cActService = new CActService();