public JsonResult SaveVote(int personId, int voteId, int count, Guid invalidReason)
    {
      var currentElectionGuid = UserSession.CurrentElectionGuid;
      var isSingleName = UserSession.CurrentElection.IsSingleNameElection;

      if (voteId != 0)
      {
        // update existing record

        // find info about the existing Vote
        var voteInfo =
          Db.vVoteInfoes.SingleOrDefault(vi => vi.VoteId == voteId && vi.ElectionGuid == currentElectionGuid);

        if (voteInfo == null)
        {
          // problem... client has a vote number, but we didn't find...
          return new { Updated = false, Error = "Invalid vote id" }.AsJsonResult();
        }

        Db.Detach(voteInfo);

        var rawVote = Db.Votes.Single(v => v.C_RowId == voteInfo.VoteId);

        voteInfo.SingleNameElectionCount = count;
        rawVote.SingleNameElectionCount = count;

        rawVote.PersonCombinedInfo = voteInfo.PersonCombinedInfo;

        DetermineInvalidReasonGuid(invalidReason, rawVote);

        Db.SaveChanges();

        var ballotAnalyzer = new BallotAnalyzer();
        var ballotStatusInfo = ballotAnalyzer.UpdateBallotStatus(CurrentRawBallot(), VoteInfosForCurrentBallot());
        var sum = isSingleName
                    ? Db.vVoteInfoes.Where(vi => vi.LocationId == voteInfo.LocationId).Sum(vi => vi.SingleNameElectionCount)
                    : Db.vBallotInfoes.Count(b => b.LocationId == voteInfo.LocationId);

        return new
                 {
                   Updated = true,
                   BallotStatus = ballotStatusInfo.Status.Value,
                   BallotStatusText = ballotStatusInfo.Status.DisplayText,
                   ballotStatusInfo.SpoiledCount,
                   LocationBallotsEntered = sum
                 }.AsJsonResult();
      }

      var ballot = GetCurrentBallotInfo();
      if (ballot == null)
      {
        return new { Updated = false, Error = "Invalid ballot" }.AsJsonResult();
      }

      // don't have an active Ballot!
      // make a new Vote record

      var invalidReasonGuid = DetermineInvalidReasonGuid(invalidReason);

      var person = Db.People.SingleOrDefault(p => p.C_RowId == personId && p.ElectionGuid == currentElectionGuid);

      var ok = person != null || invalidReasonGuid != Guid.Empty;

      if (ok)
      {
        var nextVoteNum = 1 + Db.Votes.Where(v => v.BallotGuid == ballot.BallotGuid)
                                .OrderByDescending(v => v.PositionOnBallot)
                                .Take(1)
                                .Select(b => b.PositionOnBallot)
                                .SingleOrDefault();

        var vote = new Vote
                     {
                       BallotGuid = ballot.BallotGuid,
                       PositionOnBallot = nextVoteNum,
                       StatusCode = VoteHelper.VoteStatusCode.Ok,
                       SingleNameElectionCount = count
                     };
        if (person != null)
        {
          vote.PersonGuid = person.PersonGuid;
          vote.PersonCombinedInfo = person.CombinedInfo;
          vote.InvalidReasonGuid = VoteHelperLocal.IneligibleToReceiveVotes(person.IneligibleReasonGuid, person.CanReceiveVotes);
        }
        if (invalidReasonGuid != Guid.Empty)
        {
          vote.InvalidReasonGuid = invalidReasonGuid.AsNullableGuid();
        }
        Db.Votes.Add(vote);
        Db.SaveChanges();

        var ballotAnalyzer = new BallotAnalyzer();
        var ballotStatusInfo = ballotAnalyzer.UpdateBallotStatus(CurrentRawBallot(), VoteInfosForCurrentBallot());

        var sum = isSingleName
                    ? Db.vVoteInfoes.Where(vi => vi.LocationId == ballot.LocationId).Sum(vi => vi.SingleNameElectionCount)
                    : Db.vBallotInfoes.Count(b => b.LocationId == ballot.LocationId);

        return new
                 {
                   Updated = true,
                   VoteId = vote.C_RowId,
                   pos = vote.PositionOnBallot,
                   BallotStatus = ballotStatusInfo.Status.Value,
                   BallotStatusText = ballotStatusInfo.Status.DisplayText,
                   ballotStatusInfo.SpoiledCount,
                   LocationBallotsEntered = sum
                 }.AsJsonResult();
      }

      // don't recognize person id
      return new { Updated = false, Error = "Invalid person" }.AsJsonResult();
    }
    public JsonResult DeleteVote(int vid)
    {
      var voteInfo =
        Db.vVoteInfoes.SingleOrDefault(vi => vi.ElectionGuid == UserSession.CurrentElectionGuid && vi.VoteId == vid);
      if (voteInfo == null)
      {
        return new { Message = "Not found" }.AsJsonResult();
      }

      var vote = Db.Votes.Single(v => v.C_RowId == vid);
      Db.Votes.Remove(vote);
      Db.SaveChanges();

      UpdateVotePositions(voteInfo.BallotGuid);

      var ballotAnalyzer = new BallotAnalyzer();
      var ballotStatusInfo = ballotAnalyzer.UpdateBallotStatus(CurrentRawBallot(), VoteInfosForCurrentBallot());
      var isSingleName = UserSession.CurrentElection.IsSingleNameElection;
      var sum = isSingleName
            ? Db.vVoteInfoes.Where(vi => vi.LocationId == voteInfo.LocationId).Sum(vi => vi.SingleNameElectionCount)
            : Db.vBallotInfoes.Count(b => b.LocationId == voteInfo.LocationId);

      return new
               {
                 Deleted = true,
                 Votes = CurrentVotesForJs(),
                 BallotStatus = ballotStatusInfo.Status.Value,
                 BallotStatusText = ballotStatusInfo.Status.DisplayText,
                 ballotStatusInfo.SpoiledCount,
                 LocationBallotsEntered = sum
               }.AsJsonResult();
    }
    public JsonResult SetNeedsReview(bool needsReview)
    {
      var ballot = CurrentRawBallot();

      ballot.StatusCode = needsReview ? BallotStatusEnum.Review : BallotStatusEnum.Ok;

      var ballotAnalyzer = new BallotAnalyzer();
      var ballotStatusInfo = ballotAnalyzer.UpdateBallotStatus(ballot, VoteInfosForCurrentBallot());

      Db.SaveChanges();

      return new
               {
                 BallotStatus = ballotStatusInfo.Status.Value,
                 BallotStatusText = ballotStatusInfo.Status.DisplayText,
                 ballotStatusInfo.SpoiledCount
               }.AsJsonResult();
    }