using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore; using System.Linq; using AyaNova.Models; using AyaNova.Api.ControllerHelpers; using AyaNova.Biz; using System.Collections.Generic; using System.Text; using System; namespace AyaNova.Api.Controllers { [ApiController] [ApiVersion("8.0")] [Route("api/v{version:apiVersion}/unit")] [Produces("application/json")] [Authorize] public class UnitController : ControllerBase { private readonly AyContext ct; private readonly ILogger log; private readonly ApiServerState serverState; /// /// ctor /// /// /// /// public UnitController(AyContext dbcontext, ILogger logger, ApiServerState apiServerState) { ct = dbcontext; log = logger; serverState = apiServerState; } /// /// Create Unit /// /// /// From route path /// [HttpPost] public async Task PostUnit([FromBody] Unit newObject, ApiVersion apiVersion) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); UnitBiz biz = UnitBiz.GetBiz(ct, HttpContext); if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); Unit o = await biz.CreateAsync(newObject); if (o == null) return BadRequest(new ApiErrorResponse(biz.Errors)); else return CreatedAtAction(nameof(UnitController.GetUnit), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o)); } /// /// Get Unit /// /// /// Unit [HttpGet("{id}")] public async Task GetUnit([FromRoute] long id) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); UnitBiz biz = UnitBiz.GetBiz(ct, HttpContext); if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); var o = await biz.GetAsync(id); if (o == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); return Ok(ApiOkResponse.Response(o)); } /// /// Update Unit /// /// /// [HttpPut] public async Task PutUnit([FromBody] Unit updatedObject) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); UnitBiz biz = UnitBiz.GetBiz(ct, HttpContext); if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); var o = await biz.PutAsync(updatedObject); if (o == null) { if (biz.Errors.Exists(z => z.Code == ApiErrorCode.CONCURRENCY_CONFLICT)) return StatusCode(409, new ApiErrorResponse(biz.Errors)); else return BadRequest(new ApiErrorResponse(biz.Errors)); } return Ok(ApiOkResponse.Response(new { Concurrency = o.Concurrency })); ; } /// /// Delete Unit /// /// /// NoContent [HttpDelete("{id}")] public async Task DeleteUnit([FromRoute] long id) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); UnitBiz biz = UnitBiz.GetBiz(ct, HttpContext); if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!await biz.DeleteAsync(id)) return BadRequest(new ApiErrorResponse(biz.Errors)); return NoContent(); } /// /// Get service address for this Unit /// /// /// Service address [HttpGet("address/{id}")] public async Task GetUnitServiceAddress([FromRoute] long id) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Unit)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); var unt = await ct.Unit.AsNoTracking().Where(x => x.Id == id).FirstOrDefaultAsync(); if (unt == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); return Ok(ApiOkResponse.Response(new { unit = new AddressRecord(unt.Serial, unt.Address, unt.City, unt.Region, unt.Country, unt.Latitude, unt.Longitude) })); } /// /// Get Unit list by tag and optionally customer /// /// /// List of units [HttpPost("bulk-add-selection-list")] public async Task GetBulkAddList([FromBody] UnitListByTagParams searchParam) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Part)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); if (searchParam.tags.Count == 0) //note: this error only applies to api users so not translated return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, "tags", "tags are required")); //build query //select id,serial,customerid from aunit where aunit.tags @> array['red','blue'::varchar(255)] AND customerid=19 /* select aunit.id,aunit.serial, acustomer.name from aunit left join acustomer on aunit.customerid=acustomer.id where aunit.tags @> array['red','blue'::varchar(255)] AND customerid=19 */ StringBuilder sbQuery = new StringBuilder(); sbQuery.Append("select aunit.id,aunit.serial, acustomer.name from aunit left join acustomer on aunit.customerid=acustomer.id where aunit.tags "); StringBuilder sb = new StringBuilder(); sb.Append("@> array["); foreach (string s in searchParam.tags) sb.Append($"'{s}',"); sbQuery.Append(sb.ToString().TrimEnd(',')); sbQuery.Append("::varchar(255)]"); if (searchParam.restrictToCustomerId != null && searchParam.restrictToCustomerId != 0) sbQuery.Append($" and customerid={searchParam.restrictToCustomerId}"); List slist = new List(); using (var cmd = ct.Database.GetDbConnection().CreateCommand()) { await ct.Database.OpenConnectionAsync(); cmd.CommandText = sbQuery.ToString(); using (var dr = await cmd.ExecuteReaderAsync()) { while (dr.Read()) { slist.Add(new InternalUnitListForSorting(dr.GetString(2), dr.GetString(1), dr.GetInt64(0))); } } } var sorted = slist.OrderBy(z => z.CustomerName).ThenBy(z => z.UnitSerial); //return Ok(ApiOkResponse.Response(sorted.Select(z => new NameIdItem { Id = z.unitid, Name = $"{z.serial} {z.customername}" }).ToList())); return Ok(ApiOkResponse.Response(sorted)); } public record UnitListByTagParams(List tags, long? restrictToCustomerId); private record InternalUnitListForSorting(string CustomerName, string UnitSerial, long UnitId); //------------ /// /// Get Unit service and warranty info /// /// /// Service and warranty info [HttpGet("service-warranty-info/{id}")] public async Task GetUnitServiceWarrantyInfo([FromRoute] long id) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Unit)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); var u = await ct.Unit.AsNoTracking().Where(x => x.Id == id).FirstOrDefaultAsync(); if (u == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); UnitServiceWarrantyInfo ret = new UnitServiceWarrantyInfo(); ret.PurchaseReceiptNumber = u.Receipt; ret.PurchaseDate = u.PurchasedDate; ret.PurchaseFromVendorId = u.PurchasedFromVendorId; if (u.PurchasedFromVendorId != null) ret.PurchasedFromVendor = await ct.Vendor.AsNoTracking().Where(x => x.Id == u.PurchasedFromVendorId).Select(x => x.Name).FirstOrDefaultAsync(); //Warranty terms //WarrantyLength is in months //UnitModel warranty has precedence always except when Unit OverrideModelWarranty is true or there is no unitmodel set UnitModel unitModel = null; if (u.UnitModelId != null) unitModel = await ct.UnitModel.AsNoTracking().Where(x => x.Id == u.UnitModelId).FirstOrDefaultAsync(); if (u.OverrideModelWarranty || unitModel == null) { //get warranty terms from the unit itself ret.LifeTimeWarranty = u.LifeTimeWarranty; ret.WarrantyTerms = u.WarrantyTerms; if (!u.LifeTimeWarranty && u.WarrantyLength != null && u.PurchasedDate != null) ret.WarrantyExpiryDate = ((DateTime)u.PurchasedDate).AddMonths((int)u.WarrantyLength); } else { //get warranty terms from the unit model ret.LifeTimeWarranty = unitModel.LifeTimeWarranty; ret.WarrantyTerms = unitModel.WarrantyTerms; if (!unitModel.LifeTimeWarranty && unitModel.WarrantyLength != null && u.PurchasedDate != null) ret.WarrantyExpiryDate = ((DateTime)u.PurchasedDate).AddMonths((int)unitModel.WarrantyLength); } //Recent workorders //arbitrarily returning last 3 work orders to account for one of them being the current active work order and not wanting to try to filter in closed status workorders for reasons //limiting to three because there is already an all workorders option from unit itself if they are really researching the history and it's about three clicks away from this info //List RecentWorkOrders = new List(); string q = "select distinct(aworkorder.serial), aworkorder.id, aworkorder.servicedate from aworkorder " + "left join aworkorderitem on aworkorder.id=aworkorderitem.workorderid " + "left join aworkorderitemunit on aworkorderitemunit.workorderitemid=aworkorderitem.id " + $"where aworkorderitemunit.unitid={id} " + "order by aworkorder.serial DESC " + "limit 3"; using (var cmd = ct.Database.GetDbConnection().CreateCommand()) { await ct.Database.OpenConnectionAsync(); cmd.CommandText = q; using (var dr = await cmd.ExecuteReaderAsync()) { while (dr.Read()) { ret.RecentWorkOrders.Add(new RecentWorkOrder(dr.GetInt64(0), dr.GetInt64(1), dr.GetDateTime(2))); } } } // var lastWoItemIds = await ct.WorkOrderItemUnit.AsNoTracking().Where(z => z.UnitId == id).OrderByDescending(z => z.Id).Take(10).Select(z => z.WorkOrderItemId).ToListAsync(); // foreach (long woitemid in lastWoItemIds) // { // var woid = await ct.WorkOrderItem.AsNoTracking().Where(x => x.Id == woitemid).OrderByDescending(x => x.WorkOrderId).Select(x => x.WorkOrderId).FirstOrDefaultAsync(); // ret.RecentWorkOrders.Add( // await ct.WorkOrder.AsNoTracking().Where(x => x.Id == woid).Select(x => new RecentWorkOrder(x.Serial, x.Id, x.ServiceDate)).FirstOrDefaultAsync() // ); // } return Ok(ApiOkResponse.Response(ret)); } public record RecentWorkOrder(long? Serial, long? Id, DateTime? ServiceDate); public class UnitServiceWarrantyInfo { public UnitServiceWarrantyInfo() { RecentWorkOrders = new List(); } public List RecentWorkOrders { get; set; } public DateTime? PurchaseDate { get; set; } public string PurchasedFromVendor { get; set; } public long? PurchaseFromVendorId { get; set; } public string PurchaseReceiptNumber { get; set; } public bool LifeTimeWarranty { get; set; } public DateTime? WarrantyExpiryDate { get; set; } public string WarrantyTerms { get; set; } } /// /// Get Unit info required for service workorder on selection /// /// UnitId /// Contract name and Id, meter reading status [HttpGet("work-order-info/{id}")] public async Task GetUnitActiveContract([FromRoute] long id) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Unit)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); //Get contract info var retContract = new NameIdItem { Name = string.Empty, Id = 0 }; var UnitInfo = await ct.Unit.AsNoTracking().Where(x => x.Id == id).Select(x => new { x.ContractId, x.ContractExpires, x.Metered }).FirstOrDefaultAsync(); if (UnitInfo != null && UnitInfo.ContractExpires >= DateTime.UtcNow)//has valid contract { var c = await ct.Contract.AsNoTracking().FirstOrDefaultAsync(x => x.Id == UnitInfo.ContractId); if (c != null && c.Active != false) { retContract.Name = c.Name; retContract.Id = c.Id; } } return Ok(ApiOkResponse.Response(new{contract=retContract,IsMetered=UnitInfo.Metered})); } /// /// Create Unit Meter Reading entry /// /// /// From route path /// [HttpPost("meter-reading")] public async Task PostUnitMeterReading([FromBody] UnitMeterReading newObject, ApiVersion apiVersion) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); UnitMeterReadingBiz biz = UnitMeterReadingBiz.GetBiz(ct, HttpContext); if (!Authorized.HasCreateRole(HttpContext.Items, AyaType.Unit)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); UnitMeterReading o = await biz.CreateAsync(newObject); if (o == null) return BadRequest(new ApiErrorResponse(biz.Errors)); else return CreatedAtAction(nameof(UnitController.GetUnitMeterReading), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o)); } /// /// Get Unit Meter Reading entry /// /// /// Unit [HttpGet("meter-reading/{id}")] public async Task GetUnitMeterReading([FromRoute] long id) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); UnitMeterReadingBiz biz = UnitMeterReadingBiz.GetBiz(ct, HttpContext); if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Unit)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); var o = await biz.GetAsync(id); if (o == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); return Ok(ApiOkResponse.Response(o)); } }//eoc }//eons