Files
paliad/internal/services/party_service.go
m 460736ad1e refactor(t-paliad-092): rename Go module path patholo → paliad
F-6 from t-paliad-074 architecture audit. The Gitea repo was renamed
m/patholo → mAi/paliad → m/paliad, but go.mod still declared
`mgit.msbls.de/m/patholo` and every internal import echoed the
pre-rebrand name.

Sweep:
- go.mod: module path → mgit.msbls.de/m/paliad
- All *.go files: imports rewritten via sed
- README.md, docs/design-kanzlai-integration.md: mAi/paliad → m/paliad
- Frontend issue-reference comments (mAi/paliad#N → m/paliad#N) in
  i18n.ts, theme.ts, sidebar.ts, app.ts, Sidebar.tsx, PWAHead.tsx,
  global.css

Verified: go build/vet/test ./... clean, bun run build clean,
no remaining mgit.msbls.de/m/patholo or mAi/paliad references
outside docs that intentionally describe the rename history.
2026-04-30 16:46:31 +02:00

122 lines
3.5 KiB
Go

package services
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
"mgit.msbls.de/m/paliad/internal/models"
)
// PartyService reads and writes paliad.parties. Visibility inherits from
// the parent Project.
type PartyService struct {
db *sqlx.DB
projects *ProjectService
}
// NewPartyService wires the service.
func NewPartyService(db *sqlx.DB, projects *ProjectService) *PartyService {
return &PartyService{db: db, projects: projects}
}
const partyColumns = `id, project_id, name, role, representative, contact_info,
created_at, updated_at`
// CreatePartyInput is the payload for Create.
type CreatePartyInput struct {
Name string `json:"name"`
Role *string `json:"role,omitempty"`
Representative *string `json:"representative,omitempty"`
ContactInfo json.RawMessage `json:"contact_info,omitempty"`
}
// ListForProject returns all Parties for the Project, visibility-checked.
func (s *PartyService) ListForProject(ctx context.Context, userID, projectID uuid.UUID) ([]models.Party, error) {
if _, err := s.projects.GetByID(ctx, userID, projectID); err != nil {
return nil, err
}
rows := []models.Party{}
if err := s.db.SelectContext(ctx, &rows,
`SELECT `+partyColumns+`
FROM paliad.parties
WHERE project_id = $1
ORDER BY name`, projectID); err != nil {
return nil, fmt.Errorf("list parties: %w", err)
}
return rows, nil
}
// Create inserts a Party under a Project; visibility is checked on the parent.
func (s *PartyService) Create(ctx context.Context, userID, projectID uuid.UUID, input CreatePartyInput) (*models.Party, error) {
if strings.TrimSpace(input.Name) == "" {
return nil, fmt.Errorf("%w: name is required", ErrInvalidInput)
}
if _, err := s.projects.GetByID(ctx, userID, projectID); err != nil {
return nil, err
}
contact := input.ContactInfo
if len(contact) == 0 {
contact = json.RawMessage(`{}`)
}
id := uuid.New()
now := time.Now().UTC()
if _, err := s.db.ExecContext(ctx,
`INSERT INTO paliad.parties
(id, project_id, name, role, representative, contact_info,
created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $7)`,
id, projectID, input.Name, input.Role, input.Representative, contact, now,
); err != nil {
return nil, fmt.Errorf("insert party: %w", err)
}
var p models.Party
if err := s.db.GetContext(ctx, &p,
`SELECT `+partyColumns+` FROM paliad.parties WHERE id = $1`, id); err != nil {
return nil, fmt.Errorf("fetch created party: %w", err)
}
return &p, nil
}
// Delete removes a Party. Partner/admin only.
func (s *PartyService) Delete(ctx context.Context, userID, partyID uuid.UUID) error {
user, err := s.projects.Users().GetByID(ctx, userID)
if err != nil {
return err
}
if user == nil {
return ErrNotVisible
}
if user.GlobalRole != "global_admin" {
return fmt.Errorf("%w: only partners/admins can delete Parties", ErrForbidden)
}
var projectID uuid.UUID
err = s.db.GetContext(ctx, &projectID,
`SELECT project_id FROM paliad.parties WHERE id = $1`, partyID)
if errors.Is(err, sql.ErrNoRows) {
return ErrNotVisible
}
if err != nil {
return fmt.Errorf("lookup party parent: %w", err)
}
if _, err := s.projects.GetByID(ctx, userID, projectID); err != nil {
return err
}
if _, err := s.db.ExecContext(ctx,
`DELETE FROM paliad.parties WHERE id = $1`, partyID); err != nil {
return fmt.Errorf("delete party: %w", err)
}
return nil
}