Step 5 of 4712 (security review F16, staff side; contacts are already refused by step 1). schedule/svc and schedule/user had no role check, so any login got the whole service schedule (work order numbers, customer names, technicians). They now need read rights on work orders, quotes or PMs, the test the client uses to show the Schedule menu, so no role that sees the schedule loses it; only Inventory and OpsAdmin roles (no menu) are refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
568 lines
26 KiB
C#
568 lines
26 KiB
C#
using System.Threading.Tasks;
|
|
using System;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.Extensions.Logging;
|
|
using AyaNova.Models;
|
|
using AyaNova.Api.ControllerHelpers;
|
|
using AyaNova.Biz;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using AyaNova.Util;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
namespace AyaNova.Api.Controllers
|
|
{
|
|
[ApiController]
|
|
[Asp.Versioning.ApiVersion("8.0")]
|
|
[Route("api/v{version:apiVersion}/schedule")]
|
|
[Produces("application/json")]
|
|
[Authorize]
|
|
public class ScheduleController : ControllerBase
|
|
{
|
|
private const string WHITE_HEXA = "#FFFFFFFF";
|
|
private const string BLACK_HEXA = "#000000FF";
|
|
private const string GRAY_NEUTRAL_HEXA = "#CACACAFF";
|
|
private readonly AyContext ct;
|
|
private readonly ILogger<ScheduleController> log;
|
|
private readonly ApiServerState serverState;
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
/// <param name="dbcontext"></param>
|
|
/// <param name="logger"></param>
|
|
/// <param name="apiServerState"></param>
|
|
public ScheduleController(AyContext dbcontext, ILogger<ScheduleController> logger, ApiServerState apiServerState)
|
|
{
|
|
ct = dbcontext;
|
|
log = logger;
|
|
serverState = apiServerState;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Get service management schedule for parameters specified
|
|
/// time zone UTC offset in minutes is required to be passed in
|
|
/// timestamps returned are in Unix Epoch milliseconds converted for local time display
|
|
/// </summary>
|
|
/// <param name="p">Service schedule parameters</param>
|
|
/// <param name="apiVersion">From route path</param>
|
|
/// <returns></returns>
|
|
[HttpPost("svc")]
|
|
public async Task<IActionResult> PostServiceSchedule([FromBody] ServiceScheduleParams p, Asp.Versioning.ApiVersion apiVersion)
|
|
{
|
|
if (!serverState.IsOpen)
|
|
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
|
if (!CanSeeServiceSchedule())
|
|
return StatusCode(403, new ApiNotAuthorizedResponse());
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(new ApiErrorResponse(ModelState));
|
|
|
|
List<ServiceScheduleListItem> r = new List<ServiceScheduleListItem>();
|
|
|
|
//Note: query will return records that fall within viewed range even if they start or end outside of it
|
|
//However in month view (only, rest are as is) we can see up to 6 days before or after the month so in the interest of filling those voids:
|
|
//Adjust query dates to encompass actual potential view range
|
|
DateTime ViewStart = p.Start;
|
|
DateTime ViewEnd = p.End;
|
|
//this covers the largest possible window that could display due to nearly a week of the last or next month showing
|
|
if (p.View == ScheduleView.Month)
|
|
{
|
|
ViewStart = p.Start.AddDays(-6);
|
|
ViewEnd = p.End.AddDays(6);
|
|
}
|
|
|
|
|
|
//Tags to Users
|
|
List<NameIdItem> Users = null;
|
|
|
|
if (p.Tags.Count == 0)
|
|
Users = await ct.User.AsNoTracking()
|
|
.Where(x => x.Active == true && (x.UserType == UserType.ServiceContractor || x.UserType == UserType.Service))
|
|
.OrderBy(x => x.Name)
|
|
.Select(x => new NameIdItem { Name = x.Name, Id = x.Id })
|
|
.ToListAsync();
|
|
else
|
|
{
|
|
Users = new List<NameIdItem>();
|
|
//add users that match any of the tags, to match they must have at least one of the tags
|
|
//iterate available users
|
|
var availableUsers = await ct.User.AsNoTracking().Where(x => x.Active == true && (x.UserType == UserType.ServiceContractor || x.UserType == UserType.Service)).OrderBy(x => x.Name).Select(x => new { x.Name, x.Id, x.Tags }).ToListAsync();
|
|
//if user has any of the tags in the list then include them
|
|
foreach (var u in availableUsers)
|
|
{
|
|
//any of the inclusive tags in contact tags?
|
|
if (p.Tags.Intersect(u.Tags).Any())
|
|
Users.Add(new NameIdItem { Name = u.Name, Id = u.Id });
|
|
}
|
|
}
|
|
List<long?> userIdList = Users.Select(x => x.Id as long?).ToList();
|
|
userIdList.Add(null);
|
|
//WORKORDERS
|
|
{
|
|
//Note: query for *overlapping* ranges, not *contained* entirely in view range
|
|
r.AddRange(await ct.ViewScheduleWorkOrder.Where(x => userIdList.Contains(x.SchedUserId) && ViewStart <= x.StopDate && x.StartDate <= ViewEnd)
|
|
.Select(x => MakeServiceWOSchedItem(x, p))
|
|
.ToListAsync());
|
|
}
|
|
var HasUnAssigned = r.Any(x => x.UserId == 0);
|
|
return Ok(ApiOkResponse.Response(new { items = r, users = Users, hasUnassigned = HasUnAssigned }));
|
|
}
|
|
|
|
public class ServiceScheduleParams
|
|
{
|
|
[Required]
|
|
public ScheduleView View { get; set; }
|
|
[Required]
|
|
public DateTime Start { get; set; }
|
|
[Required]
|
|
public DateTime End { get; set; }
|
|
[Required]
|
|
public ScheduleWorkOrderColorSource WisuColorSource { get; set; }
|
|
// [Required]
|
|
// public List<long> Users { get; set; }//user id's to display and in order to display
|
|
[Required]
|
|
public List<string> Tags { get; set; }
|
|
[Required]
|
|
public bool Dark { get; set; }//indicate if Client is set to dark mode or not, used for colorless types to display as black or white
|
|
}
|
|
|
|
|
|
|
|
//###############################################################
|
|
//USER - svc-schedule-user
|
|
//###############################################################
|
|
|
|
/// <summary>
|
|
/// Get User schedule for parameters specified
|
|
/// This is called when drilling down into specific user from service schedule form and is not the personal schedule
|
|
/// time zone UTC offset in minutes is required to be passed in
|
|
/// timestamps returned are in Unix Epoch milliseconds converted for local time display
|
|
/// </summary>
|
|
/// <param name="p">User schedule parameters</param>
|
|
/// <param name="apiVersion">From route path</param>
|
|
/// <returns></returns>
|
|
[HttpPost("user")]
|
|
public async Task<IActionResult> PostUserSchedule([FromBody] PersonalScheduleParams p, Asp.Versioning.ApiVersion apiVersion)
|
|
{
|
|
if (!serverState.IsOpen)
|
|
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
|
if (!CanSeeServiceSchedule())
|
|
return StatusCode(403, new ApiNotAuthorizedResponse());
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(new ApiErrorResponse(ModelState));
|
|
|
|
List<PersonalScheduleListItem> r = new List<PersonalScheduleListItem>();
|
|
|
|
//user options color source should never be empty here but just in case
|
|
if (p.WisuColorSource == null)
|
|
{
|
|
p.WisuColorSource = ScheduleWorkOrderColorSource.WorkOrderStatus;
|
|
}
|
|
|
|
|
|
//Note: query will return records that fall within viewed range even if they start or end outside of it
|
|
//However in month view (only, rest are as is) we can see up to 6 days before or after the month so in the interest of filling those voids:
|
|
//Adjust query dates to encompass actual potential view range
|
|
DateTime ViewStart = p.Start;
|
|
DateTime ViewEnd = p.End;
|
|
//this covers the largest possible window that could display due to nearly a week of the last or next month showing
|
|
if (p.View == ScheduleView.Month)
|
|
{
|
|
ViewStart = p.Start.AddDays(-6);
|
|
ViewEnd = p.End.AddDays(6);
|
|
}
|
|
|
|
long? actualUserId = p.UserId == 0 ? null : p.UserId;
|
|
|
|
|
|
|
|
//WORKORDERS
|
|
{
|
|
//Note: query for *overlapping* ranges, not *contained* entirely in view range
|
|
r.AddRange(await ct.ViewScheduleWorkOrder.Where(x => x.SchedUserId == actualUserId && ViewStart <= x.StopDate && x.StartDate <= ViewEnd)
|
|
.Select(x => MakePersonalWOSchedItem(x, p, false))//this is the drill down by service manager route so assume they can edit here
|
|
.ToListAsync());
|
|
}
|
|
|
|
|
|
return Ok(ApiOkResponse.Response(r));
|
|
}
|
|
|
|
|
|
//###############################################################
|
|
//PERSONAL
|
|
//###############################################################
|
|
|
|
/// <summary>
|
|
/// Get personal schedule for parameters specified
|
|
/// time zone UTC offset in minutes is required to be passed in
|
|
/// timestamps returned are in Unix Epoch milliseconds converted for local time display
|
|
/// </summary>
|
|
/// <param name="p">Personal schedule parameters</param>
|
|
/// <param name="apiVersion">From route path</param>
|
|
/// <returns></returns>
|
|
[HttpPost("personal")]
|
|
public async Task<IActionResult> PostPersonalSchedule([FromBody] PersonalScheduleParams p, Asp.Versioning.ApiVersion apiVersion)
|
|
{
|
|
if (!serverState.IsOpen)
|
|
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(new ApiErrorResponse(ModelState));
|
|
|
|
List<PersonalScheduleListItem> r = new List<PersonalScheduleListItem>();
|
|
|
|
|
|
//user options will be empty here if route called from dashboard so fetch here
|
|
if (p.Wisu && p.WisuColorSource == null)
|
|
{
|
|
//default if not set yet
|
|
p.WisuColorSource = ScheduleWorkOrderColorSource.WorkOrderStatus;
|
|
|
|
//not provided so fetch it here at the server
|
|
//this is a stupid work around due to close to release date and not wanting to change front end areas but to support dashboard widgets to lower amount of fetches
|
|
FormUserOptionsBiz uoptbiz = FormUserOptionsBiz.GetBiz(ct, HttpContext);
|
|
var o = await uoptbiz.GetAsync("home-schedule");
|
|
if (o != null)
|
|
{
|
|
JObject formData = JObject.Parse(o.Options);
|
|
p.WisuColorSource = formData["wisuColorSource"].ToObject<ScheduleWorkOrderColorSource>();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var UserId = UserIdFromContext.Id(HttpContext.Items);
|
|
var UType = UserTypeFromContext.Type(HttpContext.Items);
|
|
|
|
//Need to make sure send not editable for wisu events if restricted user
|
|
WorkOrderBiz biz = WorkOrderBiz.GetBiz(ct, HttpContext);
|
|
bool UserIsRestrictedType = biz.UserIsRestrictedType;
|
|
|
|
//Note: query will return records that fall within viewed range even if they start or end outside of it
|
|
//However in month view (only, rest are as is) we can see up to 6 days before or after the month so in the interest of filling those voids:
|
|
//Adjust query dates to encompass actual potential view range
|
|
DateTime ViewStart = p.Start;
|
|
DateTime ViewEnd = p.End;
|
|
//this covers the largest possible window that could display due to nearly a week of the last or next month showing
|
|
if (p.View == ScheduleView.Month)
|
|
{
|
|
ViewStart = p.Start.AddDays(-6);
|
|
ViewEnd = p.End.AddDays(6);
|
|
}
|
|
|
|
//WORKORDERS
|
|
if (p.Wisu && (UType == UserType.Service || UType == UserType.ServiceContractor))
|
|
{
|
|
//Note: query for *overlapping* ranges, not *contained* entirely in view range
|
|
r.AddRange(await ct.ViewScheduleWorkOrder.Where(x => x.SchedUserId == UserId && ViewStart <= x.StopDate && x.StartDate <= ViewEnd)
|
|
.Select(x => MakePersonalWOSchedItem(x, p, UserIsRestrictedType))
|
|
.ToListAsync());
|
|
}
|
|
|
|
//REMINDERS
|
|
if (p.Reminders)
|
|
{
|
|
r.AddRange(await ct.Reminder.Where(x => x.UserId == UserId && ViewStart <= x.StopDate && x.StartDate <= ViewEnd).Select(x => MakeReminderSchedItem(x, p)).ToListAsync());
|
|
}
|
|
|
|
//REVIEWS
|
|
if (p.Reviews)
|
|
{
|
|
r.AddRange(await ct.Review.Where(x => x.UserId == UserId && ViewStart <= x.ReviewDate && x.ReviewDate <= ViewEnd).Select(x => MakeReviewSchedItem(x, p)).ToListAsync());
|
|
}
|
|
return Ok(ApiOkResponse.Response(r));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adjust a schedule item's start / end timestamp
|
|
/// </summary>
|
|
/// <param name="ad">Adjustment parameters parameters</param>
|
|
/// <param name="apiVersion">From route path</param>
|
|
/// <returns>Error or OK response</returns>
|
|
[HttpPost("adjust")]
|
|
public async Task<IActionResult> AdjustSchedule([FromBody] ScheduleItemAdjustParams ad, Asp.Versioning.ApiVersion apiVersion)
|
|
{
|
|
if (!serverState.IsOpen)
|
|
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(new ApiErrorResponse(ModelState));
|
|
|
|
switch (ad.Type)
|
|
{
|
|
case AyaType.WorkOrderItemScheduledUser:
|
|
{
|
|
WorkOrderBiz biz = WorkOrderBiz.GetBiz(ct, HttpContext);
|
|
if (!Authorized.HasModifyRole(HttpContext.Items, AyaType.WorkOrderItemScheduledUser) || biz.UserIsRestrictedType)
|
|
return StatusCode(403, new ApiNotAuthorizedResponse());
|
|
//svc-schedule-user uses 0 to mean unassigned user but here it's null
|
|
//and it can't be a required parameter and provide null for some reason so zero is the token indicator
|
|
if (ad.UserId == 0)
|
|
ad.UserId = null;
|
|
var o = await biz.ScheduledUserPutNewScheduleTimeAsync(ad);
|
|
if (o == false)
|
|
{
|
|
if (biz.Errors.Exists(z => z.Code == ApiErrorCode.CONCURRENCY_CONFLICT))
|
|
return StatusCode(409, new ApiErrorResponse(biz.Errors));
|
|
else
|
|
return BadRequest(new ApiErrorResponse(biz.Errors));
|
|
}
|
|
}
|
|
break;
|
|
case AyaType.Reminder:
|
|
{
|
|
ReminderBiz biz = ReminderBiz.GetBiz(ct, HttpContext);
|
|
var o = await biz.PutNewScheduleTimeAsync(ad);
|
|
if (o == false)
|
|
{
|
|
if (biz.Errors.Exists(z => z.Code == ApiErrorCode.CONCURRENCY_CONFLICT))
|
|
return StatusCode(409, new ApiErrorResponse(biz.Errors));
|
|
else
|
|
return BadRequest(new ApiErrorResponse(biz.Errors));
|
|
}
|
|
}
|
|
break;
|
|
case AyaType.Review:
|
|
{
|
|
ReviewBiz biz = ReviewBiz.GetBiz(ct, HttpContext);
|
|
var o = await biz.PutNewScheduleTimeAsync(ad);
|
|
if (o == false)
|
|
{
|
|
if (biz.Errors.Exists(z => z.Code == ApiErrorCode.CONCURRENCY_CONFLICT))
|
|
return StatusCode(409, new ApiErrorResponse(biz.Errors));
|
|
else
|
|
return BadRequest(new ApiErrorResponse(biz.Errors));
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, "Type", "Type not supported for adjustment"));
|
|
}
|
|
|
|
//a-ok response
|
|
return Ok(ApiOkResponse.Response(true));
|
|
}
|
|
|
|
|
|
|
|
//#### UTILITY METHODS ##############
|
|
|
|
private static ServiceScheduleListItem MakeServiceWOSchedItem(ViewScheduleWorkOrder v, ServiceScheduleParams p)
|
|
{
|
|
var s = new ServiceScheduleListItem();
|
|
s.Id = v.WoItemSchedUserId;
|
|
s.Color = ColorFromWOItem(v, p.WisuColorSource);
|
|
s.TextColor = TextColor(s.Color);
|
|
s.Start = (DateTime)v.StartDate;
|
|
s.End = (DateTime)v.StopDate;
|
|
s.Type = AyaType.WorkOrderItemScheduledUser;
|
|
s.Name = NameFromWOItem(v);
|
|
s.Editable = v.WorkOrderStatusCompleted != true && v.WorkOrderStatusLocked != true;//could be null as well which we'll consider open as it's no status set
|
|
s.UserId = v.SchedUserId ?? 0;//0 signifies to client schedule that it's an unassigned woitem
|
|
return s;
|
|
}
|
|
|
|
private static PersonalScheduleListItem MakePersonalWOSchedItem(ViewScheduleWorkOrder v, PersonalScheduleParams p, bool userIsRestrictedType)
|
|
{
|
|
bool canEdit = true;
|
|
if (userIsRestrictedType)
|
|
canEdit = false;
|
|
if (v.WorkOrderStatusCompleted == true)
|
|
{
|
|
canEdit = false;
|
|
}
|
|
if (v.WorkOrderStatusLocked == true)
|
|
{
|
|
canEdit = false;
|
|
}
|
|
|
|
var s = new PersonalScheduleListItem();
|
|
s.Id = v.WoItemSchedUserId;
|
|
s.Color = ColorFromWOItem(v, (ScheduleWorkOrderColorSource)p.WisuColorSource);
|
|
s.TextColor = TextColor(s.Color);
|
|
s.Start = (DateTime)v.StartDate;
|
|
s.End = (DateTime)v.StopDate;
|
|
s.Type = AyaType.WorkOrderItemScheduledUser;
|
|
s.Name = NameFromWOItem(v);
|
|
s.Editable = canEdit;
|
|
return s;
|
|
}
|
|
|
|
private static PersonalScheduleListItem MakeReminderSchedItem(Reminder v, PersonalScheduleParams p)
|
|
{
|
|
var s = new PersonalScheduleListItem();
|
|
s.Id = v.Id;
|
|
s.Color = v.Color;
|
|
s.TextColor = TextColor(v.Color);
|
|
s.Start = (DateTime)v.StartDate;
|
|
s.End = (DateTime)v.StopDate;
|
|
s.Type = AyaType.Reminder;
|
|
s.Name = v.Name;
|
|
s.Editable = true;//personal reminders are always editable
|
|
return s;
|
|
}
|
|
|
|
private static PersonalScheduleListItem MakeReviewSchedItem(Review v, PersonalScheduleParams p)
|
|
{
|
|
var s = new PersonalScheduleListItem();
|
|
s.Id = v.Id;
|
|
s.Color = p.Dark ? WHITE_HEXA : BLACK_HEXA;
|
|
s.TextColor = p.Dark ? "black" : "white";
|
|
s.Start = (DateTime)v.ReviewDate;
|
|
s.End = (DateTime)v.ReviewDate.AddMinutes(30);//just something to show in schedule as not supporting all day or unscheduled type stuff
|
|
s.Type = AyaType.Review;
|
|
s.Name = v.Name;
|
|
s.Editable = v.CompletedDate == null;//not completed yet so can still be changed
|
|
return s;
|
|
}
|
|
|
|
|
|
|
|
private static string ColorFromWOItem(ViewScheduleWorkOrder v, ScheduleWorkOrderColorSource src)
|
|
{
|
|
|
|
switch (src)
|
|
{
|
|
case ScheduleWorkOrderColorSource.None:
|
|
return GRAY_NEUTRAL_HEXA;
|
|
case ScheduleWorkOrderColorSource.WorkOrderStatus:
|
|
return string.IsNullOrWhiteSpace(v.WorkOrderStatusColor) ? GRAY_NEUTRAL_HEXA : v.WorkOrderStatusColor;
|
|
case ScheduleWorkOrderColorSource.WorkOrderItemStatus:
|
|
return string.IsNullOrWhiteSpace(v.WorkOrderItemStatusColor) ? GRAY_NEUTRAL_HEXA : v.WorkOrderItemStatusColor;
|
|
case ScheduleWorkOrderColorSource.WorkOrderItemPriority:
|
|
return string.IsNullOrWhiteSpace(v.WorkOrderItemPriorityColor) ? GRAY_NEUTRAL_HEXA : v.WorkOrderItemPriorityColor;
|
|
|
|
}
|
|
return GRAY_NEUTRAL_HEXA;
|
|
}
|
|
|
|
private static string TextColor(string hexcolor)
|
|
{
|
|
//Note: we use HEXA format which is 8 hex digits
|
|
//this here works even though it's considering as 6 digits because in hexA the last two
|
|
//digits are the opacity which this can ignore
|
|
if (string.IsNullOrWhiteSpace(hexcolor) || hexcolor.Length < 6) return GRAY_NEUTRAL_HEXA;//gray neutral
|
|
hexcolor = hexcolor.Replace("#", "");
|
|
var r = StringUtil.HexToInt(hexcolor.Substring(0, 2));
|
|
var g = StringUtil.HexToInt(hexcolor.Substring(2, 2));
|
|
var b = StringUtil.HexToInt(hexcolor.Substring(4, 2));
|
|
var yiq = (r * 299 + g * 587 + b * 114) / 1000;
|
|
//return yiq >= 128 ? WHITE_HEXA : BLACK_HEXA;
|
|
return yiq >= 128 ? "black" : "white";//<---NOTE: this MUST be a named color due to how the style is applied at client
|
|
}
|
|
|
|
private static string NameFromWOItem(ViewScheduleWorkOrder v)
|
|
{
|
|
// Name=[wonumber customername]
|
|
return v.Serial.ToString() + " " + v.CustomerName;
|
|
}
|
|
|
|
//case 4712: the service schedule (and its per user drill down) needs the same rights the client
|
|
//uses to show the Schedule menu: open work orders, quotes or PMs
|
|
private bool CanSeeServiceSchedule()
|
|
{
|
|
return Authorized.HasReadFullRole(HttpContext.Items, AyaType.WorkOrder)
|
|
|| Authorized.HasReadFullRole(HttpContext.Items, AyaType.Quote)
|
|
|| Authorized.HasReadFullRole(HttpContext.Items, AyaType.PM);
|
|
}
|
|
|
|
|
|
public enum ScheduleWorkOrderColorSource : int
|
|
{
|
|
None = 0,
|
|
WorkOrderStatus = 2,
|
|
WorkOrderItemStatus = 3,
|
|
WorkOrderItemPriority = 4
|
|
}
|
|
|
|
public enum ScheduleView : int
|
|
{
|
|
Day = 1,
|
|
Week = 2,
|
|
Month = 3,
|
|
Day4 = 4,
|
|
Category = 5
|
|
}
|
|
|
|
public class PersonalScheduleParams
|
|
{
|
|
[Required]
|
|
public ScheduleView View { get; set; }
|
|
[Required]
|
|
public DateTime Start { get; set; }
|
|
[Required]
|
|
public DateTime End { get; set; }
|
|
public ScheduleWorkOrderColorSource? WisuColorSource { get; set; }
|
|
[Required]
|
|
public bool Wisu { get; set; }
|
|
[Required]
|
|
public bool Reviews { get; set; }
|
|
[Required]
|
|
public bool Reminders { get; set; }
|
|
[Required]
|
|
public bool Dark { get; set; }//indicate if Client is set to dark mode or not, used for colorless types to display as black or white
|
|
[Required]
|
|
public long UserId { get; set; }//required due to dual use from home-schedule and svc-schedule-user if it's a 0 zero then it's actually meant to be null not assigned userid
|
|
}
|
|
|
|
public class PersonalScheduleListItem
|
|
{
|
|
//Never be null dates in here even though source records might be have null dates because they are not queried for and
|
|
//can't be displayed on a calendar anyway
|
|
//user can simply filter a data table by null dates to see them
|
|
//we shouldn't have allowed null dates in the first place in v7 but here we are :)
|
|
public DateTime Start { get; set; }
|
|
public DateTime End { get; set; }
|
|
public string Name { get; set; }
|
|
public string Color { get; set; }
|
|
public string TextColor { get; set; }
|
|
public AyaType Type { get; set; }
|
|
public long Id { get; set; }
|
|
public bool Editable { get; set; }
|
|
}
|
|
|
|
public class ServiceScheduleListItem
|
|
{
|
|
//Never be null dates in here even though source records might be have null dates because they are not queried for and
|
|
//can't be displayed on a calendar anyway
|
|
//user can simply filter a data table by null dates to see them
|
|
//we shouldn't have allowed null dates in the first place in v7 but here we are :)
|
|
public DateTime Start { get; set; }
|
|
public DateTime End { get; set; }
|
|
public string Name { get; set; }
|
|
public string Color { get; set; }
|
|
public string TextColor { get; set; }
|
|
public AyaType Type { get; set; }
|
|
public long Id { get; set; }
|
|
public bool Editable { get; set; }
|
|
public long UserId { get; set; }
|
|
}
|
|
//------------
|
|
|
|
|
|
// deprecated as not used but by tag instead
|
|
// /// <summary>
|
|
// /// Get active Scheduleable user list
|
|
// /// </summary>
|
|
// /// <returns>List of all scheduleable users</returns>
|
|
// [HttpGet("scheduleable-user-list")]
|
|
// public async Task<IActionResult> GetScheduleableUserList()
|
|
// {
|
|
// if (!serverState.IsOpen)
|
|
// return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
|
// if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.WorkOrder))//WorkOrder right applies to all svc-schedule ops
|
|
// return StatusCode(403, new ApiNotAuthorizedResponse());
|
|
// var o = await ct.User.AsNoTracking().Where(x => x.Active == true && x.UserType == UserType.ServiceContractor || x.UserType == UserType.Service).OrderBy(x => x.Name).Select(x => new { x.Name, x.Id }).ToListAsync();
|
|
|
|
// return Ok(ApiOkResponse.Response(o));
|
|
// }
|
|
|
|
}//eoc
|
|
}//eons |