package repository import ( "context" "encoding/json" "errors" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5" ) // MembershipPlanRow is a configurable growth membership SKU. type MembershipPlanRow struct { Code string `json:"code"` Title string `json:"title"` DurationDays int `json:"duration_days"` AmountCents int `json:"amount_cents"` Active bool `json:"active"` UpdatedAt time.Time `json:"updated_at"` } // ListMembershipPlans returns all plans ordered by code. func (r *AdminRepo) ListMembershipPlans(ctx context.Context) ([]MembershipPlanRow, error) { rows, err := r.Pool.Query(ctx, ` SELECT code, title, duration_days, amount_cents, active, updated_at FROM membership_plans ORDER BY code`) if err != nil { return nil, err } defer rows.Close() var out []MembershipPlanRow for rows.Next() { var p MembershipPlanRow if err := rows.Scan(&p.Code, &p.Title, &p.DurationDays, &p.AmountCents, &p.Active, &p.UpdatedAt); err != nil { return nil, err } out = append(out, p) } return out, rows.Err() } // GetMembershipPlan loads one plan by code. func (r *AdminRepo) GetMembershipPlan(ctx context.Context, code string) (*MembershipPlanRow, error) { var p MembershipPlanRow err := r.Pool.QueryRow(ctx, ` SELECT code, title, duration_days, amount_cents, active, updated_at FROM membership_plans WHERE code=$1`, code, ).Scan(&p.Code, &p.Title, &p.DurationDays, &p.AmountCents, &p.Active, &p.UpdatedAt) if errors.Is(err, pgx.ErrNoRows) { return nil, nil } if err != nil { return nil, err } return &p, nil } // UpdateMembershipPlanWithAudit updates mutable fields and audits. func (r *AdminRepo) UpdateMembershipPlanWithAudit( ctx context.Context, adminID uuid.UUID, code, title string, days, amountCents int, active bool, meta json.RawMessage, ) error { tx, err := r.Pool.Begin(ctx) if err != nil { return err } defer tx.Rollback(ctx) tag, err := tx.Exec(ctx, ` UPDATE membership_plans SET title=$2, duration_days=$3, amount_cents=$4, active=$5, updated_at=now() WHERE code=$1`, code, title, days, amountCents, active) if err != nil { return err } if tag.RowsAffected() == 0 { return errString("plan not found") } if meta == nil { meta = json.RawMessage(`{}`) } if _, err := tx.Exec(ctx, ` INSERT INTO admin_audit_logs(admin_id, action, target_type, target_id, meta) VALUES ($1,'membership.plans.update','membership_plan',$2,$3)`, adminID, code, meta, ); err != nil { return err } return tx.Commit(ctx) }