Add multi-approver support
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
committed by
Sacha Al Himdani
parent
3d8c7c4dd6
commit
93c7b0c2dc
@@ -32,7 +32,6 @@ type (
|
||||
Document struct {
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
ApproverID gid.GID `db:"approver_profile_id"`
|
||||
Title string `db:"title"`
|
||||
DocumentType DocumentType `db:"document_type"`
|
||||
Classification DocumentClassification `db:"classification"`
|
||||
@@ -83,7 +82,6 @@ func (p *Document) LoadByID(
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
approver_profile_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
@@ -135,7 +133,6 @@ func (p *Document) LoadByIDWithFilter(
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
approver_profile_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
@@ -188,7 +185,6 @@ func (p *Documents) LoadByIDs(
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
approver_profile_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
@@ -270,7 +266,6 @@ func (p *Documents) LoadByOrganizationID(
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
approver_profile_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
@@ -321,7 +316,6 @@ func (p *Documents) LoadAllByOrganizationID(
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
approver_profile_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
@@ -371,7 +365,6 @@ INSERT INTO
|
||||
tenant_id,
|
||||
id,
|
||||
organization_id,
|
||||
approver_profile_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
@@ -384,7 +377,6 @@ VALUES (
|
||||
@tenant_id,
|
||||
@document_id,
|
||||
@organization_id,
|
||||
@approver_profile_id,
|
||||
@title,
|
||||
@document_type,
|
||||
@classification,
|
||||
@@ -399,7 +391,6 @@ VALUES (
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"document_id": p.ID,
|
||||
"organization_id": p.OrganizationID,
|
||||
"approver_profile_id": p.ApproverID,
|
||||
"title": p.Title,
|
||||
"document_type": p.DocumentType,
|
||||
"classification": p.Classification,
|
||||
@@ -460,7 +451,6 @@ UPDATE
|
||||
SET
|
||||
title = @title,
|
||||
current_published_version = @current_published_version,
|
||||
approver_profile_id = @approver_profile_id,
|
||||
document_type = @document_type,
|
||||
classification = @classification,
|
||||
trust_center_visibility = @trust_center_visibility,
|
||||
@@ -477,7 +467,6 @@ WHERE
|
||||
"updated_at": time.Now(),
|
||||
"title": p.Title,
|
||||
"current_published_version": p.CurrentPublishedVersion,
|
||||
"approver_profile_id": p.ApproverID,
|
||||
"document_type": p.DocumentType,
|
||||
"classification": p.Classification,
|
||||
"trust_center_visibility": p.TrustCenterVisibility,
|
||||
@@ -548,7 +537,6 @@ WITH scoped_documents AS (
|
||||
SELECT
|
||||
scoped_documents.id,
|
||||
scoped_documents.organization_id,
|
||||
scoped_documents.approver_profile_id,
|
||||
scoped_documents.title,
|
||||
scoped_documents.document_type,
|
||||
scoped_documents.classification,
|
||||
@@ -638,7 +626,6 @@ WITH scoped_documents AS (
|
||||
SELECT
|
||||
scoped_documents.id,
|
||||
scoped_documents.organization_id,
|
||||
scoped_documents.approver_profile_id,
|
||||
scoped_documents.title,
|
||||
scoped_documents.document_type,
|
||||
scoped_documents.classification,
|
||||
|
||||
154
pkg/coredata/document_approver.go
Normal file
154
pkg/coredata/document_approver.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
)
|
||||
|
||||
type (
|
||||
DocumentApprover struct {
|
||||
DocumentID gid.GID `db:"document_id"`
|
||||
ApproverProfileID gid.GID `db:"approver_profile_id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
TenantID gid.TenantID `db:"tenant_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
DocumentApprovers []*DocumentApprover
|
||||
)
|
||||
|
||||
func (da DocumentApprover) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
INSERT INTO
|
||||
document_approvers (
|
||||
document_id,
|
||||
approver_profile_id,
|
||||
organization_id,
|
||||
tenant_id,
|
||||
created_at
|
||||
)
|
||||
VALUES (
|
||||
@document_id,
|
||||
@approver_profile_id,
|
||||
@organization_id,
|
||||
@tenant_id,
|
||||
@created_at
|
||||
)
|
||||
ON CONFLICT (document_id, approver_profile_id) DO NOTHING;
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"document_id": da.DocumentID,
|
||||
"approver_profile_id": da.ApproverProfileID,
|
||||
"organization_id": da.OrganizationID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"created_at": da.CreatedAt,
|
||||
}
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert document approver: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (da *DocumentApprovers) LoadByDocumentID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
document_id,
|
||||
approver_profile_id,
|
||||
organization_id,
|
||||
tenant_id,
|
||||
created_at
|
||||
FROM
|
||||
document_approvers
|
||||
WHERE
|
||||
%s
|
||||
AND document_id = @document_id
|
||||
ORDER BY created_at ASC
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"document_id": documentID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query document approvers: %w", err)
|
||||
}
|
||||
|
||||
approvers, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[DocumentApprover])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect document approvers: %w", err)
|
||||
}
|
||||
|
||||
*da = approvers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (da *DocumentApprovers) DeleteByDocumentID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
DELETE FROM
|
||||
document_approvers
|
||||
WHERE
|
||||
%s
|
||||
AND document_id = @document_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"document_id": documentID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete document approvers: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (da *DocumentApprovers) ApproverProfileIDs() []gid.GID {
|
||||
ids := make([]gid.GID, len(*da))
|
||||
for i, a := range *da {
|
||||
ids[i] = a.ApproverProfileID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
@@ -34,7 +34,6 @@ type (
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
DocumentID gid.GID `db:"document_id"`
|
||||
Title string `db:"title"`
|
||||
ApproverID gid.GID `db:"approver_profile_id"`
|
||||
VersionNumber int `db:"version_number"`
|
||||
Classification DocumentClassification `db:"classification"`
|
||||
Content string `db:"content"`
|
||||
@@ -77,7 +76,6 @@ SELECT
|
||||
organization_id,
|
||||
document_id,
|
||||
title,
|
||||
approver_profile_id,
|
||||
version_number,
|
||||
classification,
|
||||
content,
|
||||
@@ -139,7 +137,6 @@ SELECT
|
||||
organization_id,
|
||||
document_id,
|
||||
title,
|
||||
approver_profile_id,
|
||||
version_number,
|
||||
classification,
|
||||
content,
|
||||
@@ -190,7 +187,6 @@ INSERT INTO document_versions (
|
||||
organization_id,
|
||||
document_id,
|
||||
title,
|
||||
approver_profile_id,
|
||||
version_number,
|
||||
classification,
|
||||
content,
|
||||
@@ -205,7 +201,6 @@ VALUES (
|
||||
@organization_id,
|
||||
@document_id,
|
||||
@title,
|
||||
@approver_profile_id,
|
||||
@version_number,
|
||||
@classification,
|
||||
@content,
|
||||
@@ -216,19 +211,18 @@ VALUES (
|
||||
)
|
||||
`
|
||||
args := pgx.StrictNamedArgs{
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"id": dv.ID,
|
||||
"organization_id": dv.OrganizationID,
|
||||
"document_id": dv.DocumentID,
|
||||
"title": dv.Title,
|
||||
"approver_profile_id": dv.ApproverID,
|
||||
"version_number": dv.VersionNumber,
|
||||
"classification": dv.Classification,
|
||||
"content": dv.Content,
|
||||
"changelog": dv.Changelog,
|
||||
"status": dv.Status,
|
||||
"created_at": dv.CreatedAt,
|
||||
"updated_at": dv.UpdatedAt,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"id": dv.ID,
|
||||
"organization_id": dv.OrganizationID,
|
||||
"document_id": dv.DocumentID,
|
||||
"title": dv.Title,
|
||||
"version_number": dv.VersionNumber,
|
||||
"classification": dv.Classification,
|
||||
"content": dv.Content,
|
||||
"changelog": dv.Changelog,
|
||||
"status": dv.Status,
|
||||
"created_at": dv.CreatedAt,
|
||||
"updated_at": dv.UpdatedAt,
|
||||
}
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
@@ -260,7 +254,6 @@ SELECT
|
||||
organization_id,
|
||||
document_id,
|
||||
title,
|
||||
approver_profile_id,
|
||||
version_number,
|
||||
classification,
|
||||
content,
|
||||
@@ -313,7 +306,6 @@ SELECT
|
||||
organization_id,
|
||||
document_id,
|
||||
title,
|
||||
approver_profile_id,
|
||||
version_number,
|
||||
classification,
|
||||
content,
|
||||
@@ -364,7 +356,6 @@ SELECT
|
||||
organization_id,
|
||||
document_id,
|
||||
title,
|
||||
approver_profile_id,
|
||||
version_number,
|
||||
classification,
|
||||
content,
|
||||
@@ -413,7 +404,6 @@ func (dv DocumentVersion) Update(
|
||||
q := `
|
||||
UPDATE document_versions SET
|
||||
title = @title,
|
||||
approver_profile_id = @approver_profile_id,
|
||||
changelog = @changelog,
|
||||
status = @status,
|
||||
content = @content,
|
||||
@@ -429,7 +419,6 @@ WHERE %s
|
||||
args := pgx.StrictNamedArgs{
|
||||
"document_version_id": dv.ID,
|
||||
"title": dv.Title,
|
||||
"approver_profile_id": dv.ApproverID,
|
||||
"changelog": dv.Changelog,
|
||||
"status": dv.Status,
|
||||
"content": dv.Content,
|
||||
|
||||
154
pkg/coredata/document_version_approver.go
Normal file
154
pkg/coredata/document_version_approver.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
)
|
||||
|
||||
type (
|
||||
DocumentVersionApprover struct {
|
||||
DocumentVersionID gid.GID `db:"document_version_id"`
|
||||
ApproverProfileID gid.GID `db:"approver_profile_id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
TenantID gid.TenantID `db:"tenant_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
DocumentVersionApprovers []*DocumentVersionApprover
|
||||
)
|
||||
|
||||
func (dva DocumentVersionApprover) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
INSERT INTO
|
||||
document_version_approvers (
|
||||
document_version_id,
|
||||
approver_profile_id,
|
||||
organization_id,
|
||||
tenant_id,
|
||||
created_at
|
||||
)
|
||||
VALUES (
|
||||
@document_version_id,
|
||||
@approver_profile_id,
|
||||
@organization_id,
|
||||
@tenant_id,
|
||||
@created_at
|
||||
)
|
||||
ON CONFLICT (document_version_id, approver_profile_id) DO NOTHING;
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"document_version_id": dva.DocumentVersionID,
|
||||
"approver_profile_id": dva.ApproverProfileID,
|
||||
"organization_id": dva.OrganizationID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"created_at": dva.CreatedAt,
|
||||
}
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert document version approver: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dva *DocumentVersionApprovers) LoadByDocumentVersionID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentVersionID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
document_version_id,
|
||||
approver_profile_id,
|
||||
organization_id,
|
||||
tenant_id,
|
||||
created_at
|
||||
FROM
|
||||
document_version_approvers
|
||||
WHERE
|
||||
%s
|
||||
AND document_version_id = @document_version_id
|
||||
ORDER BY created_at ASC
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"document_version_id": documentVersionID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query document version approvers: %w", err)
|
||||
}
|
||||
|
||||
approvers, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[DocumentVersionApprover])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect document version approvers: %w", err)
|
||||
}
|
||||
|
||||
*dva = approvers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dva *DocumentVersionApprovers) DeleteByDocumentVersionID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentVersionID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
DELETE FROM
|
||||
document_version_approvers
|
||||
WHERE
|
||||
%s
|
||||
AND document_version_id = @document_version_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"document_version_id": documentVersionID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete document version approvers: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dva *DocumentVersionApprovers) ApproverProfileIDs() []gid.GID {
|
||||
ids := make([]gid.GID, len(*dva))
|
||||
for i, a := range *dva {
|
||||
ids[i] = a.ApproverProfileID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
@@ -364,7 +364,7 @@ WITH sigs AS (
|
||||
FROM
|
||||
document_version_signatures dvs
|
||||
INNER JOIN
|
||||
iam_membership_profile_id p ON dvs.signed_by_profile_id = p.id
|
||||
iam_membership_profiles p ON dvs.signed_by_profile_id = p.id
|
||||
WHERE
|
||||
dvs.document_version_id = @document_version_id
|
||||
ORDER BY
|
||||
|
||||
@@ -368,6 +368,212 @@ INNER JOIN identities i ON i.id = p.identity_id
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MembershipProfiles) LoadByDocumentID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
cursor *page.Cursor[MembershipProfileOrderField],
|
||||
) error {
|
||||
q := `
|
||||
WITH profiles AS (
|
||||
SELECT
|
||||
mp.id,
|
||||
mp.identity_id,
|
||||
mp.organization_id,
|
||||
mp.membership_id,
|
||||
mp.full_name,
|
||||
mp.kind,
|
||||
mp.additional_email_addresses,
|
||||
mp.position,
|
||||
mp.contract_start_date,
|
||||
mp.contract_end_date,
|
||||
mp.created_at,
|
||||
mp.updated_at
|
||||
FROM
|
||||
iam_membership_profiles mp
|
||||
WHERE
|
||||
mp.%s
|
||||
AND mp.id IN (
|
||||
SELECT approver_profile_id
|
||||
FROM document_approvers
|
||||
WHERE document_id = @document_id
|
||||
)
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
p.id,
|
||||
p.identity_id,
|
||||
p.organization_id,
|
||||
p.membership_id,
|
||||
i.email_address,
|
||||
p.full_name,
|
||||
p.kind,
|
||||
p.additional_email_addresses,
|
||||
p.position,
|
||||
p.contract_start_date,
|
||||
p.contract_end_date,
|
||||
p.created_at,
|
||||
p.updated_at
|
||||
FROM profiles p
|
||||
INNER JOIN identities i ON i.id = p.identity_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.NamedArgs{"document_id": documentID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, cursor.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query document approver profiles: %w", err)
|
||||
}
|
||||
|
||||
profiles, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[MembershipProfile])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect document approver profiles: %w", err)
|
||||
}
|
||||
|
||||
*p = profiles
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MembershipProfiles) CountByDocumentID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
) (int, error) {
|
||||
q := `
|
||||
SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
iam_membership_profiles mp
|
||||
INNER JOIN document_approvers da ON mp.id = da.approver_profile_id
|
||||
WHERE
|
||||
mp.%s
|
||||
AND da.document_id = @document_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"document_id": documentID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
var count int
|
||||
err := conn.QueryRow(ctx, q, args).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot query document approver profiles count: %w", err)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (p *MembershipProfiles) LoadByDocumentVersionID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentVersionID gid.GID,
|
||||
cursor *page.Cursor[MembershipProfileOrderField],
|
||||
) error {
|
||||
q := `
|
||||
WITH profiles AS (
|
||||
SELECT
|
||||
mp.id,
|
||||
mp.identity_id,
|
||||
mp.organization_id,
|
||||
mp.membership_id,
|
||||
mp.full_name,
|
||||
mp.kind,
|
||||
mp.additional_email_addresses,
|
||||
mp.position,
|
||||
mp.contract_start_date,
|
||||
mp.contract_end_date,
|
||||
mp.created_at,
|
||||
mp.updated_at
|
||||
FROM
|
||||
iam_membership_profiles mp
|
||||
WHERE
|
||||
mp.%s
|
||||
AND mp.id IN (
|
||||
SELECT approver_profile_id
|
||||
FROM document_version_approvers
|
||||
WHERE document_version_id = @document_version_id
|
||||
)
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
p.id,
|
||||
p.identity_id,
|
||||
p.organization_id,
|
||||
p.membership_id,
|
||||
i.email_address,
|
||||
p.full_name,
|
||||
p.kind,
|
||||
p.additional_email_addresses,
|
||||
p.position,
|
||||
p.contract_start_date,
|
||||
p.contract_end_date,
|
||||
p.created_at,
|
||||
p.updated_at
|
||||
FROM profiles p
|
||||
INNER JOIN identities i ON i.id = p.identity_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.NamedArgs{"document_version_id": documentVersionID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, cursor.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query document version approver profiles: %w", err)
|
||||
}
|
||||
|
||||
profiles, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[MembershipProfile])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect document version approver profiles: %w", err)
|
||||
}
|
||||
|
||||
*p = profiles
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MembershipProfiles) CountByDocumentVersionID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentVersionID gid.GID,
|
||||
) (int, error) {
|
||||
q := `
|
||||
SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
iam_membership_profiles mp
|
||||
INNER JOIN document_version_approvers dva ON mp.id = dva.approver_profile_id
|
||||
WHERE
|
||||
mp.%s
|
||||
AND dva.document_version_id = @document_version_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"document_version_id": documentVersionID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
var count int
|
||||
err := conn.QueryRow(ctx, q, args).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot query document version approver profiles count: %w", err)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (p *MembershipProfiles) LoadByMeetingID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
36
pkg/coredata/migrations/20260213T120000Z.sql
Normal file
36
pkg/coredata/migrations/20260213T120000Z.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
-- Create document_approvers join table
|
||||
CREATE TABLE document_approvers (
|
||||
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
||||
approver_profile_id TEXT NOT NULL REFERENCES iam_membership_profiles(id) ON UPDATE CASCADE ON DELETE RESTRICT,
|
||||
tenant_id TEXT NOT NULL,
|
||||
organization_id TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
PRIMARY KEY (document_id, approver_profile_id)
|
||||
);
|
||||
|
||||
-- Create document_version_approvers join table
|
||||
CREATE TABLE document_version_approvers (
|
||||
document_version_id TEXT NOT NULL REFERENCES document_versions(id) ON DELETE CASCADE,
|
||||
approver_profile_id TEXT NOT NULL REFERENCES iam_membership_profiles(id) ON UPDATE CASCADE ON DELETE RESTRICT,
|
||||
tenant_id TEXT NOT NULL,
|
||||
organization_id TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
PRIMARY KEY (document_version_id, approver_profile_id)
|
||||
);
|
||||
|
||||
-- Migrate existing approver data from documents to document_approvers
|
||||
INSERT INTO document_approvers (document_id, approver_profile_id, tenant_id, organization_id, created_at)
|
||||
SELECT id, approver_profile_id, tenant_id, organization_id, created_at
|
||||
FROM documents
|
||||
WHERE approver_profile_id IS NOT NULL
|
||||
AND deleted_at IS NULL;
|
||||
|
||||
-- Migrate existing approver data from document_versions to document_version_approvers
|
||||
INSERT INTO document_version_approvers (document_version_id, approver_profile_id, tenant_id, organization_id, created_at)
|
||||
SELECT id, approver_profile_id, tenant_id, organization_id, created_at
|
||||
FROM document_versions
|
||||
WHERE approver_profile_id IS NOT NULL;
|
||||
|
||||
-- Drop the old columns
|
||||
ALTER TABLE documents DROP COLUMN approver_profile_id;
|
||||
ALTER TABLE document_versions DROP COLUMN approver_profile_id;
|
||||
Reference in New Issue
Block a user