cala_ledger/account_set/
repo.rs

1use chrono::{DateTime, Utc};
2use es_entity::*;
3use sqlx::{Executor, PgPool, Postgres, Transaction};
4
5use std::collections::HashMap;
6
7use crate::primitives::{AccountId, DataSourceId, JournalId};
8
9use super::{entity::*, error::*};
10
11const ADDVISORY_LOCK_ID: i64 = 123456;
12
13pub mod members_cursor {
14    use cala_types::account_set::AccountSetMember;
15    use serde::{Deserialize, Serialize};
16
17    #[derive(Debug, Serialize, Deserialize)]
18    pub struct AccountSetMembersCursor {
19        pub member_created_at: chrono::DateTime<chrono::Utc>,
20    }
21
22    impl From<&AccountSetMember> for AccountSetMembersCursor {
23        fn from(member: &AccountSetMember) -> Self {
24            Self {
25                member_created_at: member.created_at,
26            }
27        }
28    }
29
30    #[cfg(feature = "graphql")]
31    impl async_graphql::connection::CursorType for AccountSetMembersCursor {
32        type Error = String;
33
34        fn encode_cursor(&self) -> String {
35            use base64::{engine::general_purpose, Engine as _};
36            let json = serde_json::to_string(&self).expect("could not serialize token");
37            general_purpose::STANDARD_NO_PAD.encode(json.as_bytes())
38        }
39
40        fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
41            use base64::{engine::general_purpose, Engine as _};
42            let bytes = general_purpose::STANDARD_NO_PAD
43                .decode(s.as_bytes())
44                .map_err(|e| e.to_string())?;
45            let json = String::from_utf8(bytes).map_err(|e| e.to_string())?;
46            serde_json::from_str(&json).map_err(|e| e.to_string())
47        }
48    }
49}
50
51use account_set_cursor::*;
52use members_cursor::*;
53
54#[derive(EsRepo, Debug, Clone)]
55#[es_repo(
56    entity = "AccountSet",
57    err = "AccountSetError",
58    columns(
59        name(ty = "String", update(accessor = "values().name"), list_by, list_for),
60        journal_id(ty = "JournalId", update(persist = false)),
61        external_id(
62            ty = "Option<String>",
63            update(accessor = "values().external_id"),
64            list_by
65        ),
66        data_source_id(
67            ty = "DataSourceId",
68            create(accessor = "data_source().into()"),
69            update(persist = false)
70        ),
71    ),
72    tbl_prefix = "cala"
73)]
74pub(super) struct AccountSetRepo {
75    pool: PgPool,
76}
77
78impl AccountSetRepo {
79    pub fn new(pool: &PgPool) -> Self {
80        Self { pool: pool.clone() }
81    }
82
83    pub async fn list_children(
84        &self,
85        id: AccountSetId,
86        args: es_entity::PaginatedQueryArgs<AccountSetMembersCursor>,
87    ) -> Result<
88        es_entity::PaginatedQueryRet<AccountSetMember, AccountSetMembersCursor>,
89        AccountSetError,
90    > {
91        self.list_children_in_executor(&self.pool, id, args).await
92    }
93
94    pub async fn list_children_in_tx(
95        &self,
96        db: &mut Transaction<'_, Postgres>,
97        id: AccountSetId,
98        args: es_entity::PaginatedQueryArgs<AccountSetMembersCursor>,
99    ) -> Result<
100        es_entity::PaginatedQueryRet<AccountSetMember, AccountSetMembersCursor>,
101        AccountSetError,
102    > {
103        self.list_children_in_executor(&mut **db, id, args).await
104    }
105
106    async fn list_children_in_executor(
107        &self,
108        executor: impl Executor<'_, Database = Postgres>,
109        id: AccountSetId,
110        args: es_entity::PaginatedQueryArgs<AccountSetMembersCursor>,
111    ) -> Result<
112        es_entity::PaginatedQueryRet<AccountSetMember, AccountSetMembersCursor>,
113        AccountSetError,
114    > {
115        let after = args.after.map(|c| c.member_created_at) as Option<DateTime<Utc>>;
116        let rows = sqlx::query!(
117            r#"
118            WITH member_accounts AS (
119              SELECT
120                member_account_id AS member_id,
121                member_account_id,
122                NULL::uuid AS member_account_set_id,
123                created_at
124              FROM cala_account_set_member_accounts
125              WHERE
126                transitive IS FALSE
127                AND account_set_id = $1
128                AND (created_at < $2 OR $2 IS NULL)
129              ORDER BY created_at DESC
130              LIMIT $3
131            ), member_sets AS (
132              SELECT
133                member_account_set_id AS member_id,
134                NULL::uuid AS member_account_id,
135                member_account_set_id,
136                created_at
137              FROM cala_account_set_member_account_sets
138              WHERE
139                account_set_id = $1
140                AND (created_at < $2 OR $2 IS NULL)
141              ORDER BY created_at DESC
142              LIMIT $3
143            ), all_members AS (
144              SELECT * FROM member_accounts
145              UNION ALL
146              SELECT * FROM member_sets
147            )
148            SELECT * FROM all_members
149            ORDER BY created_at DESC
150            LIMIT $3
151          "#,
152            id as AccountSetId,
153            after,
154            args.first as i64 + 1,
155        )
156        .fetch_all(executor)
157        .await?;
158        let has_next_page = rows.len() > args.first;
159        let mut end_cursor = None;
160        if let Some(last) = rows.last() {
161            end_cursor = Some(AccountSetMembersCursor {
162                member_created_at: last.created_at.expect("created_at not set"),
163            });
164        }
165
166        let account_set_members = rows
167            .into_iter()
168            .take(args.first)
169            .map(
170                |row| match (row.member_account_id, row.member_account_set_id) {
171                    (Some(member_account_id), _) => AccountSetMember::from((
172                        AccountSetMemberId::Account(AccountId::from(member_account_id)),
173                        row.created_at.expect("created at should always be present"),
174                    )),
175                    (_, Some(member_account_set_id)) => AccountSetMember::from((
176                        AccountSetMemberId::AccountSet(AccountSetId::from(member_account_set_id)),
177                        row.created_at.expect("created at should always be present"),
178                    )),
179                    _ => unreachable!(),
180                },
181            )
182            .collect::<Vec<AccountSetMember>>();
183
184        Ok(es_entity::PaginatedQueryRet {
185            entities: account_set_members,
186            has_next_page,
187            end_cursor,
188        })
189    }
190
191    pub async fn add_member_account_and_return_parents(
192        &self,
193        db: &mut Transaction<'_, Postgres>,
194        account_set_id: AccountSetId,
195        account_id: AccountId,
196    ) -> Result<(DateTime<Utc>, Vec<AccountSetId>), AccountSetError> {
197        sqlx::query!("SELECT pg_advisory_xact_lock($1)", ADDVISORY_LOCK_ID)
198            .execute(&mut **db)
199            .await?;
200        let rows = sqlx::query!(r#"
201          WITH RECURSIVE parents AS (
202            SELECT m.member_account_set_id, m.account_set_id
203            FROM cala_account_set_member_account_sets m
204            JOIN cala_account_sets s
205            ON s.id = m.account_set_id
206            WHERE m.member_account_set_id = $1
207
208            UNION ALL
209            SELECT p.member_account_set_id, m.account_set_id
210            FROM parents p
211            JOIN cala_account_set_member_account_sets m
212                ON p.account_set_id = m.member_account_set_id
213          ),
214          non_transitive_insert AS (
215            INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id)
216            VALUES ($1, $2)
217          ),
218          transitive_insert AS (
219            INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id, transitive)
220            SELECT p.account_set_id, $2, TRUE
221            FROM parents p
222          )
223          SELECT account_set_id, NULL AS now
224          FROM parents
225          UNION ALL
226          SELECT NULL AS account_set_id, NOW() AS now
227          "#,
228            account_set_id as AccountSetId,
229            account_id as AccountId,
230        )
231        .fetch_all(&mut **db)
232        .await?;
233        let mut time = None;
234        let ret = rows
235            .into_iter()
236            .filter_map(|row| {
237                if let Some(t) = row.now {
238                    time = Some(t);
239                    None
240                } else {
241                    Some(AccountSetId::from(
242                        row.account_set_id.expect("account_set_id not set"),
243                    ))
244                }
245            })
246            .collect();
247        Ok((time.expect("time not set"), ret))
248    }
249
250    pub async fn remove_member_account_and_return_parents(
251        &self,
252        db: &mut Transaction<'_, Postgres>,
253        account_set_id: AccountSetId,
254        account_id: AccountId,
255    ) -> Result<(DateTime<Utc>, Vec<AccountSetId>), AccountSetError> {
256        sqlx::query!("SELECT pg_advisory_xact_lock($1)", ADDVISORY_LOCK_ID)
257            .execute(&mut **db)
258            .await?;
259        let rows = sqlx::query!(
260            r#"
261          WITH RECURSIVE parents AS (
262            SELECT m.member_account_set_id, m.account_set_id
263            FROM cala_account_set_member_account_sets m
264            JOIN cala_account_sets s
265            ON s.id = m.account_set_id
266            WHERE m.member_account_set_id = $1
267
268            UNION ALL
269            SELECT p.member_account_set_id, m.account_set_id
270            FROM parents p
271            JOIN cala_account_set_member_account_sets m
272                ON p.account_set_id = m.member_account_set_id
273          ),
274          deletions as (
275            DELETE FROM cala_account_set_member_accounts
276            WHERE account_set_id IN (SELECT account_set_id FROM parents UNION SELECT $1)
277            AND member_account_id = $2
278          )
279          SELECT account_set_id, NULL AS now
280          FROM parents
281          UNION ALL
282          SELECT NULL AS account_set_id, NOW() AS now
283          "#,
284            account_set_id as AccountSetId,
285            account_id as AccountId,
286        )
287        .fetch_all(&mut **db)
288        .await?;
289        let mut time = None;
290        let ret = rows
291            .into_iter()
292            .filter_map(|row| {
293                if let Some(t) = row.now {
294                    time = Some(t);
295                    None
296                } else {
297                    Some(AccountSetId::from(
298                        row.account_set_id.expect("account_set_id not set"),
299                    ))
300                }
301            })
302            .collect();
303        Ok((time.expect("time not set"), ret))
304    }
305
306    pub async fn add_member_set_and_return_parents(
307        &self,
308        db: &mut Transaction<'_, Postgres>,
309        account_set_id: AccountSetId,
310        member_account_set_id: AccountSetId,
311    ) -> Result<(DateTime<Utc>, Vec<AccountSetId>), AccountSetError> {
312        sqlx::query!("SELECT pg_advisory_xact_lock($1)", ADDVISORY_LOCK_ID)
313            .execute(&mut **db)
314            .await?;
315        let rows = sqlx::query!(r#"
316          WITH RECURSIVE parents AS (
317            SELECT m.member_account_set_id, m.account_set_id
318            FROM cala_account_set_member_account_sets m
319            JOIN cala_account_sets s
320            ON s.id = m.account_set_id
321            WHERE m.member_account_set_id = $1
322
323            UNION ALL
324            SELECT p.member_account_set_id, m.account_set_id
325            FROM parents p
326            JOIN cala_account_set_member_account_sets m
327                ON p.account_set_id = m.member_account_set_id
328          ),
329          set_insert AS (
330            INSERT INTO cala_account_set_member_account_sets (account_set_id, member_account_set_id)
331            VALUES ($1, $2)
332          ),
333          new_members AS (
334            INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id, transitive)
335            SELECT $1, m.member_account_id, TRUE
336            FROM cala_account_set_member_accounts m
337            WHERE m.account_set_id = $2
338            RETURNING member_account_id
339          ),
340          transitive_inserts AS (
341            INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id, transitive)
342            SELECT p.account_set_id, n.member_account_id, TRUE
343            FROM parents p
344            CROSS JOIN new_members n
345          )
346          SELECT account_set_id, NULL AS now
347          FROM parents
348          UNION ALL
349          SELECT NULL AS account_set_id, NOW() AS now
350          "#,
351            account_set_id as AccountSetId,
352            member_account_set_id as AccountSetId,
353        )
354        .fetch_all(&mut **db)
355        .await?;
356        let mut time = None;
357        let ret = rows
358            .into_iter()
359            .filter_map(|row| {
360                if let Some(t) = row.now {
361                    time = Some(t);
362                    None
363                } else {
364                    Some(AccountSetId::from(
365                        row.account_set_id.expect("account_set_id not set"),
366                    ))
367                }
368            })
369            .collect();
370        Ok((time.expect("time not set"), ret))
371    }
372
373    pub async fn remove_member_set_and_return_parents(
374        &self,
375        db: &mut Transaction<'_, Postgres>,
376        account_set_id: AccountSetId,
377        member_account_set_id: AccountSetId,
378    ) -> Result<(DateTime<Utc>, Vec<AccountSetId>), AccountSetError> {
379        sqlx::query!("SELECT pg_advisory_xact_lock($1)", ADDVISORY_LOCK_ID)
380            .execute(&mut **db)
381            .await?;
382        let rows = sqlx::query!(
383            r#"
384          WITH RECURSIVE parents AS (
385            SELECT m.member_account_set_id, m.account_set_id
386            FROM cala_account_set_member_account_sets m
387            JOIN cala_account_sets s
388            ON s.id = m.account_set_id
389            WHERE m.member_account_set_id = $1
390
391            UNION ALL
392            SELECT p.member_account_set_id, m.account_set_id
393            FROM parents p
394            JOIN cala_account_set_member_account_sets m
395                ON p.account_set_id = m.member_account_set_id
396          ),
397          member_accounts_deletion AS (
398            DELETE FROM cala_account_set_member_accounts
399            WHERE account_set_id IN (SELECT account_set_id FROM parents UNION SELECT $1)
400            AND member_account_id IN (SELECT member_account_id FROM cala_account_set_member_accounts
401                                      WHERE account_set_id = $2)
402          ),
403          member_account_set_deletion AS (
404            DELETE FROM cala_account_set_member_account_sets
405            WHERE account_set_id IN (SELECT account_set_id FROM parents UNION SELECT $1)
406            AND member_account_set_id = $2
407          )
408          SELECT account_set_id, NULL AS now
409          FROM parents
410          UNION ALL
411          SELECT NULL AS account_set_id, NOW() AS now
412          "#,
413            account_set_id as AccountSetId,
414            member_account_set_id as AccountSetId,
415        )
416        .fetch_all(&mut **db)
417        .await?;
418        let mut time = None;
419        let ret = rows
420            .into_iter()
421            .filter_map(|row| {
422                if let Some(t) = row.now {
423                    time = Some(t);
424                    None
425                } else {
426                    Some(AccountSetId::from(
427                        row.account_set_id.expect("account_set_id not set"),
428                    ))
429                }
430            })
431            .collect();
432        Ok((time.expect("time not set"), ret))
433    }
434
435    pub async fn find_where_account_is_member(
436        &self,
437        account_id: AccountId,
438        query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
439    ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
440    {
441        self.find_where_account_is_member_in_executor(&self.pool, account_id, query)
442            .await
443    }
444
445    pub async fn find_where_account_is_member_in_tx(
446        &self,
447        tx: &mut Transaction<'_, Postgres>,
448        account_id: AccountId,
449        query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
450    ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
451    {
452        self.find_where_account_is_member_in_executor(&mut **tx, account_id, query)
453            .await
454    }
455
456    async fn find_where_account_is_member_in_executor(
457        &self,
458        executor: impl Executor<'_, Database = Postgres>,
459        account_id: AccountId,
460        query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
461    ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
462    {
463        let rows = sqlx::query_as!(
464            account_set_repo_types::Repo__DbEvent,
465            r#"
466            WITH member_account_sets AS (
467              SELECT a.id, a.name, a.created_at
468              FROM cala_account_set_member_accounts asm
469              JOIN cala_account_sets a ON asm.account_set_id = a.id
470              WHERE asm.member_account_id = $1 AND transitive IS FALSE
471              AND ((a.name, a.id) > ($3, $2) OR ($3 IS NULL AND $2 IS NULL))
472              ORDER BY a.name, a.id
473              LIMIT $4
474            )
475            SELECT mas.id AS "entity_id!: AccountSetId", e.sequence, e.event, e.recorded_at
476              FROM member_account_sets mas
477              JOIN cala_account_set_events e ON mas.id = e.id
478              ORDER BY mas.name, mas.id, e.sequence
479            "#,
480            account_id as AccountId,
481            query.after.as_ref().map(|c| c.id) as Option<AccountSetId>,
482            query.after.map(|c| c.name),
483            query.first as i64 + 1
484        )
485        .fetch_all(executor)
486        .await?;
487
488        let (entities, has_next_page) = EntityEvents::load_n::<AccountSet>(rows, query.first)?;
489        let mut end_cursor = None;
490        if let Some(last) = entities.last() {
491            end_cursor = Some(AccountSetsByNameCursor {
492                id: last.values().id,
493                name: last.values().name.clone(),
494            });
495        }
496        Ok(es_entity::PaginatedQueryRet {
497            entities,
498            has_next_page,
499            end_cursor,
500        })
501    }
502
503    pub async fn find_where_account_set_is_member(
504        &self,
505        account_set_id: AccountSetId,
506        query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
507    ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
508    {
509        self.find_where_account_set_is_member_in_executor(&self.pool, account_set_id, query)
510            .await
511    }
512
513    pub async fn find_where_account_set_is_member_in_tx(
514        &self,
515        tx: &mut Transaction<'_, Postgres>,
516        account_set_id: AccountSetId,
517        query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
518    ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
519    {
520        self.find_where_account_set_is_member_in_executor(&mut **tx, account_set_id, query)
521            .await
522    }
523
524    async fn find_where_account_set_is_member_in_executor(
525        &self,
526        executor: impl Executor<'_, Database = Postgres>,
527        account_set_id: AccountSetId,
528        query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
529    ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
530    {
531        let rows = sqlx::query_as!(
532            account_set_repo_types::Repo__DbEvent,
533            r#"
534            WITH member_account_sets AS (
535              SELECT a.id, a.name, a.created_at
536              FROM cala_account_set_member_account_sets asm
537              JOIN cala_account_sets a ON asm.account_set_id = a.id
538              WHERE asm.member_account_set_id = $1
539              AND ((a.name, a.id) > ($3, $2) OR ($3 IS NULL AND $2 IS NULL))
540              ORDER BY a.name, a.id
541              LIMIT $4
542            )
543            SELECT mas.id AS "entity_id!: AccountSetId", e.sequence, e.event, e.recorded_at
544              FROM member_account_sets mas
545              JOIN cala_account_set_events e ON mas.id = e.id
546              ORDER BY mas.name, mas.id, e.sequence
547            "#,
548            account_set_id as AccountSetId,
549            query.after.as_ref().map(|c| c.id) as Option<AccountSetId>,
550            query.after.map(|c| c.name),
551            query.first as i64 + 1
552        )
553        .fetch_all(executor)
554        .await?;
555
556        let (entities, has_next_page) = EntityEvents::load_n::<AccountSet>(rows, query.first)?;
557        let mut end_cursor = None;
558        if let Some(last) = entities.last() {
559            end_cursor = Some(AccountSetsByNameCursor {
560                id: last.values().id,
561                name: last.values().name.clone(),
562            });
563        }
564        Ok(es_entity::PaginatedQueryRet {
565            entities,
566            has_next_page,
567            end_cursor,
568        })
569    }
570
571    #[cfg(feature = "import")]
572    pub async fn import_in_op(
573        &self,
574        op: &mut DbOp<'_>,
575        origin: DataSourceId,
576        account_set: &mut AccountSet,
577    ) -> Result<(), AccountSetError> {
578        let recorded_at = op.now();
579        sqlx::query!(
580            r#"INSERT INTO cala_account_sets (data_source_id, id, journal_id, name, external_id, created_at)
581            VALUES ($1, $2, $3, $4, $5, $6)"#,
582            origin as DataSourceId,
583            account_set.values().id as AccountSetId,
584            account_set.values().journal_id as JournalId,
585            account_set.values().name,
586            account_set.values().external_id,
587            recorded_at
588        )
589        .execute(&mut **op.tx())
590        .await?;
591        self.persist_events(op, &mut account_set.events).await?;
592        Ok(())
593    }
594
595    pub async fn fetch_mappings(
596        &self,
597        journal_id: JournalId,
598        account_ids: &[AccountId],
599    ) -> Result<HashMap<AccountId, Vec<AccountSetId>>, AccountSetError> {
600        let rows = sqlx::query!(
601            r#"
602          SELECT m.account_set_id AS "set_id!: AccountSetId", m.member_account_id AS "account_id!: AccountId"
603          FROM cala_account_set_member_accounts m
604          JOIN cala_account_sets s
605          ON m.account_set_id = s.id AND s.journal_id = $1
606          WHERE m.member_account_id = ANY($2)
607          "#,
608            journal_id as JournalId,
609            account_ids as &[AccountId]
610        )
611        .fetch_all(&self.pool)
612        .await?;
613        let mut mappings = HashMap::new();
614        for row in rows {
615            mappings
616                .entry(row.account_id)
617                .or_insert_with(Vec::new)
618                .push(row.set_id);
619        }
620        Ok(mappings)
621    }
622
623    #[cfg(feature = "import")]
624    pub async fn import_member_account_in_op(
625        &self,
626        op: &mut DbOp<'_>,
627        account_set_id: AccountSetId,
628        account_id: AccountId,
629    ) -> Result<(), AccountSetError> {
630        let recorded_at = op.now();
631        sqlx::query!(
632            r#"INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id, created_at)
633            VALUES ($1, $2, $3)"#,
634            account_set_id as AccountSetId,
635            account_id as AccountId,
636            recorded_at
637        )
638        .execute(&mut **op.tx())
639        .await?;
640        Ok(())
641    }
642
643    #[cfg(feature = "import")]
644    pub async fn import_remove_member_account(
645        &self,
646        db: &mut Transaction<'_, Postgres>,
647        account_set_id: AccountSetId,
648        account_id: AccountId,
649    ) -> Result<(), AccountSetError> {
650        sqlx::query!(
651            r#"DELETE FROM cala_account_set_member_accounts
652            WHERE account_set_id = $1 AND member_account_id = $2"#,
653            account_set_id as AccountSetId,
654            account_id as AccountId,
655        )
656        .execute(&mut **db)
657        .await?;
658        Ok(())
659    }
660
661    #[cfg(feature = "import")]
662    pub async fn import_member_set_in_op(
663        &self,
664        op: &mut DbOp<'_>,
665        account_set_id: AccountSetId,
666        member_account_set_id: AccountSetId,
667    ) -> Result<(), AccountSetError> {
668        let recorded_at = op.now();
669        sqlx::query!(
670            r#"INSERT INTO cala_account_set_member_account_sets (account_set_id, member_account_set_id, created_at)
671            VALUES ($1, $2, $3)"#,
672            account_set_id as AccountSetId,
673            member_account_set_id as AccountSetId,
674            recorded_at
675        )
676        .execute(&mut **op.tx())
677        .await?;
678        Ok(())
679    }
680
681    #[cfg(feature = "import")]
682    pub async fn import_remove_member_set(
683        &self,
684        db: &mut Transaction<'_, Postgres>,
685        account_set_id: AccountSetId,
686        member_account_set_id: AccountSetId,
687    ) -> Result<(), AccountSetError> {
688        sqlx::query!(
689            r#"DELETE FROM cala_account_set_member_account_sets
690            WHERE account_set_id = $1 AND member_account_set_id = $2"#,
691            account_set_id as AccountSetId,
692            member_account_set_id as AccountSetId,
693        )
694        .execute(&mut **db)
695        .await?;
696        Ok(())
697    }
698}