package services import ( "context" "database/sql" "encoding/json" "fmt" "mgit.msbls.de/m/KanzlAI-mGMT/internal/models" "github.com/google/uuid" "github.com/jmoiron/sqlx" ) type PartyService struct { db *sqlx.DB } func NewPartyService(db *sqlx.DB) *PartyService { return &PartyService{db: db} } type CreatePartyInput struct { Name string `json:"name"` Role *string `json:"role,omitempty"` Representative *string `json:"representative,omitempty"` ContactInfo json.RawMessage `json:"contact_info,omitempty"` } type UpdatePartyInput struct { Name *string `json:"name,omitempty"` Role *string `json:"role,omitempty"` Representative *string `json:"representative,omitempty"` ContactInfo json.RawMessage `json:"contact_info,omitempty"` } func (s *PartyService) ListByCase(ctx context.Context, tenantID, caseID uuid.UUID) ([]models.Party, error) { var parties []models.Party err := s.db.SelectContext(ctx, &parties, "SELECT * FROM parties WHERE case_id = $1 AND tenant_id = $2 ORDER BY name", caseID, tenantID) if err != nil { return nil, fmt.Errorf("listing parties: %w", err) } return parties, nil } func (s *PartyService) Create(ctx context.Context, tenantID, caseID uuid.UUID, userID uuid.UUID, input CreatePartyInput) (*models.Party, error) { // Verify case exists and belongs to tenant var exists bool err := s.db.GetContext(ctx, &exists, "SELECT EXISTS(SELECT 1 FROM cases WHERE id = $1 AND tenant_id = $2)", caseID, tenantID) if err != nil { return nil, fmt.Errorf("checking case: %w", err) } if !exists { return nil, sql.ErrNoRows } id := uuid.New() contactInfo := input.ContactInfo if contactInfo == nil { contactInfo = json.RawMessage("{}") } _, err = s.db.ExecContext(ctx, `INSERT INTO parties (id, tenant_id, case_id, name, role, representative, contact_info) VALUES ($1, $2, $3, $4, $5, $6, $7)`, id, tenantID, caseID, input.Name, input.Role, input.Representative, contactInfo) if err != nil { return nil, fmt.Errorf("creating party: %w", err) } // Log event desc := fmt.Sprintf("Party added: %s", input.Name) createEvent(ctx, s.db, tenantID, caseID, userID, "party_added", desc, nil) var party models.Party if err := s.db.GetContext(ctx, &party, "SELECT * FROM parties WHERE id = $1", id); err != nil { return nil, fmt.Errorf("fetching created party: %w", err) } return &party, nil } func (s *PartyService) Update(ctx context.Context, tenantID, partyID uuid.UUID, input UpdatePartyInput) (*models.Party, error) { // Verify party exists and belongs to tenant var current models.Party err := s.db.GetContext(ctx, ¤t, "SELECT * FROM parties WHERE id = $1 AND tenant_id = $2", partyID, tenantID) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, fmt.Errorf("fetching party: %w", err) } sets := []string{} args := []interface{}{} argIdx := 1 if input.Name != nil { sets = append(sets, fmt.Sprintf("name = $%d", argIdx)) args = append(args, *input.Name) argIdx++ } if input.Role != nil { sets = append(sets, fmt.Sprintf("role = $%d", argIdx)) args = append(args, *input.Role) argIdx++ } if input.Representative != nil { sets = append(sets, fmt.Sprintf("representative = $%d", argIdx)) args = append(args, *input.Representative) argIdx++ } if input.ContactInfo != nil { sets = append(sets, fmt.Sprintf("contact_info = $%d", argIdx)) args = append(args, input.ContactInfo) argIdx++ } if len(sets) == 0 { return ¤t, nil } query := fmt.Sprintf("UPDATE parties SET %s WHERE id = $%d AND tenant_id = $%d", joinStrings(sets, ", "), argIdx, argIdx+1) args = append(args, partyID, tenantID) if _, err := s.db.ExecContext(ctx, query, args...); err != nil { return nil, fmt.Errorf("updating party: %w", err) } var updated models.Party if err := s.db.GetContext(ctx, &updated, "SELECT * FROM parties WHERE id = $1", partyID); err != nil { return nil, fmt.Errorf("fetching updated party: %w", err) } return &updated, nil } func (s *PartyService) Delete(ctx context.Context, tenantID, partyID uuid.UUID) error { result, err := s.db.ExecContext(ctx, "DELETE FROM parties WHERE id = $1 AND tenant_id = $2", partyID, tenantID) if err != nil { return fmt.Errorf("deleting party: %w", err) } rows, _ := result.RowsAffected() if rows == 0 { return sql.ErrNoRows } return nil }