Personenmodul hinzugefügt - schreibgeschützt

Mitarbeitermodul mit Abwesenheiten hinzugefügt
Einbau des Dokumententeils vereinfacht.
This commit is contained in:
2026-02-04 18:03:01 +01:00
parent 9a8c7d19eb
commit 8ae75cfc77
26 changed files with 1428 additions and 93 deletions

View File

@@ -310,6 +310,7 @@
<Compile Include="Controllers\EmployeeController.cs" />
<Compile Include="Controllers\ExternalSignatureController.cs" />
<Compile Include="Controllers\IAbsenceTimeController.cs" />
<Compile Include="Controllers\IDocumentsController.cs" />
<Compile Include="Controllers\PersonController.cs" />
<Compile Include="Controllers\SignableController.cs" />
<Compile Include="Controllers\ReportBaseController.cs" />
@@ -322,6 +323,7 @@
<Compile Include="Models\EmployeeSessionModel.cs" />
<Compile Include="Models\IAbsenceTimeModel.cs" />
<Compile Include="Models\IAbsenceTimeSessionModel.cs" />
<Compile Include="Models\IDocumentsModel.cs" />
<Compile Include="Models\MokZiel.cs" />
<Compile Include="Models\MokSupportConceptListObject.cs" />
<Compile Include="Models\MokMandator.cs" />
@@ -386,6 +388,7 @@
<Compile Include="Util\ReportUtils\Quittierungsbelegsunterschriftenobjekt.cs" />
<Compile Include="Util\ReportUtils\QuittierungsbelegsReportSettings.cs" />
<Compile Include="Util\ReportUtils\ReportTimeFrame.cs" />
<Compile Include="Util\JsonPerson.cs" />
<Compile Include="Util\ServiceRecord2Validation.cs" />
<Compile Include="Util\SignatureUtils.cs" />
<Compile Include="Util\SupportConceptStatisticsData.cs" />
@@ -639,7 +642,7 @@
<Content Include="Views\Report\Report.cshtml" />
<Content Include="Views\DevExpressScheduler\DevExpressScheduler.cshtml" />
<Content Include="Views\DevExpressScheduler\SchedulerPagePartial.cshtml" />
<Content Include="Views\Customer\DocumentPartial.cshtml" />
<Content Include="Views\Shared\DocumentPartial.cshtml" />
<Content Include="Scripts\moment\moment.min.js.map" />
<Content Include="Scripts\moment\moment-with-locales.min.js.map" />
<Content Include="Views\Shared\BeWoReportPartial.cshtml" />

View File

@@ -255,6 +255,7 @@ textarea {
--bewo-expires-in-three-months: #FF9400;
--bewo-group-booking: #f9e79f;
--bewo-customer-card-header: #AB4A07;
--bewo-person-card-header: #AB4A07;
--bewo-employee: #45993B;
--bewo-resource: #04B4D0;
--bewo-task-red: #993B3B;
@@ -2248,6 +2249,26 @@ pre code {
background-color: rgb(225.0281927711, 190.4178915663, 166.5118072289);
}
.table-bewo-person-card-header,
.table-bewo-person-card-header > th,
.table-bewo-person-card-header > td {
background-color: rgb(231.48, 204.32, 185.56);
}
.table-bewo-person-card-header th,
.table-bewo-person-card-header td,
.table-bewo-person-card-header thead th,
.table-bewo-person-card-header tbody + tbody {
border-color: rgb(211.32, 160.88, 126.04);
}
.table-hover .table-bewo-person-card-header:hover {
background-color: rgb(225.0281927711, 190.4178915663, 166.5118072289);
}
.table-hover .table-bewo-person-card-header:hover > td,
.table-hover .table-bewo-person-card-header:hover > th {
background-color: rgb(225.0281927711, 190.4178915663, 166.5118072289);
}
.table-bewo-employee,
.table-bewo-employee > th,
.table-bewo-employee > td {
@@ -3596,6 +3617,36 @@ fieldset:disabled a.btn {
box-shadow: 0 0 0 0.2rem rgba(183.6, 101.15, 44.2, 0.5);
}
.btn-bewo-person-card-header {
color: #fff;
background-color: #AB4A07;
border-color: #AB4A07;
}
.btn-bewo-person-card-header:hover {
color: #fff;
background-color: rgb(134.2542134831, 58.0983146067, 5.4957865169);
border-color: rgb(122.0056179775, 52.797752809, 4.9943820225);
}
.btn-bewo-person-card-header:focus, .btn-bewo-person-card-header.focus {
color: #fff;
background-color: rgb(134.2542134831, 58.0983146067, 5.4957865169);
border-color: rgb(122.0056179775, 52.797752809, 4.9943820225);
box-shadow: 0 0 0 0.2rem rgba(183.6, 101.15, 44.2, 0.5);
}
.btn-bewo-person-card-header.disabled, .btn-bewo-person-card-header:disabled {
color: #fff;
background-color: #AB4A07;
border-color: #AB4A07;
}
.btn-bewo-person-card-header:not(:disabled):not(.disabled):active, .btn-bewo-person-card-header:not(:disabled):not(.disabled).active, .show > .btn-bewo-person-card-header.dropdown-toggle {
color: #fff;
background-color: rgb(122.0056179775, 52.797752809, 4.9943820225);
border-color: rgb(109.7570224719, 47.4971910112, 4.4929775281);
}
.btn-bewo-person-card-header:not(:disabled):not(.disabled):active:focus, .btn-bewo-person-card-header:not(:disabled):not(.disabled).active:focus, .show > .btn-bewo-person-card-header.dropdown-toggle:focus {
box-shadow: 0 0 0 0.2rem rgba(183.6, 101.15, 44.2, 0.5);
}
.btn-bewo-employee {
color: #fff;
background-color: #45993B;
@@ -4316,6 +4367,31 @@ fieldset:disabled a.btn {
box-shadow: 0 0 0 0.2rem rgba(171, 74, 7, 0.5);
}
.btn-outline-bewo-person-card-header {
color: #AB4A07;
border-color: #AB4A07;
}
.btn-outline-bewo-person-card-header:hover {
color: #fff;
background-color: #AB4A07;
border-color: #AB4A07;
}
.btn-outline-bewo-person-card-header:focus, .btn-outline-bewo-person-card-header.focus {
box-shadow: 0 0 0 0.2rem rgba(171, 74, 7, 0.5);
}
.btn-outline-bewo-person-card-header.disabled, .btn-outline-bewo-person-card-header:disabled {
color: #AB4A07;
background-color: transparent;
}
.btn-outline-bewo-person-card-header:not(:disabled):not(.disabled):active, .btn-outline-bewo-person-card-header:not(:disabled):not(.disabled).active, .show > .btn-outline-bewo-person-card-header.dropdown-toggle {
color: #fff;
background-color: #AB4A07;
border-color: #AB4A07;
}
.btn-outline-bewo-person-card-header:not(:disabled):not(.disabled):active:focus, .btn-outline-bewo-person-card-header:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-bewo-person-card-header.dropdown-toggle:focus {
box-shadow: 0 0 0 0.2rem rgba(171, 74, 7, 0.5);
}
.btn-outline-bewo-employee {
color: #45993B;
border-color: #45993B;
@@ -6495,6 +6571,19 @@ a.badge-bewo-customer-card-header:focus, a.badge-bewo-customer-card-header.focus
box-shadow: 0 0 0 0.2rem rgba(171, 74, 7, 0.5);
}
.badge-bewo-person-card-header {
color: #fff;
background-color: #AB4A07;
}
a.badge-bewo-person-card-header:hover, a.badge-bewo-person-card-header:focus {
color: #fff;
background-color: rgb(122.0056179775, 52.797752809, 4.9943820225);
}
a.badge-bewo-person-card-header:focus, a.badge-bewo-person-card-header.focus {
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(171, 74, 7, 0.5);
}
.badge-bewo-employee {
color: #fff;
background-color: #45993B;
@@ -6880,6 +6969,18 @@ a.badge-bewo-report:focus, a.badge-bewo-report.focus {
color: rgb(39.9256179775, 17.277752809, 1.6343820225);
}
.alert-bewo-person-card-header {
color: rgb(88.92, 38.48, 3.64);
background-color: rgb(238.2, 218.8, 205.4);
border-color: rgb(231.48, 204.32, 185.56);
}
.alert-bewo-person-card-header hr {
border-top-color: rgb(225.0281927711, 190.4178915663, 166.5118072289);
}
.alert-bewo-person-card-header .alert-link {
color: rgb(39.9256179775, 17.277752809, 1.6343820225);
}
.alert-bewo-employee {
color: rgb(35.88, 79.56, 30.68);
background-color: rgb(217.8, 234.6, 215.8);
@@ -7509,6 +7610,20 @@ a.badge-bewo-report:focus, a.badge-bewo-report.focus {
border-color: rgb(88.92, 38.48, 3.64);
}
.list-group-item-bewo-person-card-header {
color: rgb(88.92, 38.48, 3.64);
background-color: rgb(231.48, 204.32, 185.56);
}
.list-group-item-bewo-person-card-header.list-group-item-action:hover, .list-group-item-bewo-person-card-header.list-group-item-action:focus {
color: rgb(88.92, 38.48, 3.64);
background-color: rgb(225.0281927711, 190.4178915663, 166.5118072289);
}
.list-group-item-bewo-person-card-header.list-group-item-action.active {
color: #fff;
background-color: rgb(88.92, 38.48, 3.64);
border-color: rgb(88.92, 38.48, 3.64);
}
.list-group-item-bewo-employee {
color: rgb(35.88, 79.56, 30.68);
background-color: rgb(202.92, 226.44, 200.12);
@@ -8554,6 +8669,16 @@ button.bg-bewo-customer-card-header:focus {
background-color: rgb(122.0056179775, 52.797752809, 4.9943820225) !important;
}
.bg-bewo-person-card-header {
background-color: #AB4A07 !important;
}
a.bg-bewo-person-card-header:hover, a.bg-bewo-person-card-header:focus,
button.bg-bewo-person-card-header:hover,
button.bg-bewo-person-card-header:focus {
background-color: rgb(122.0056179775, 52.797752809, 4.9943820225) !important;
}
.bg-bewo-employee {
background-color: #45993B !important;
}
@@ -8738,6 +8863,10 @@ button.bg-bewo-report:focus {
border-color: #AB4A07 !important;
}
.border-bewo-person-card-header {
border-color: #AB4A07 !important;
}
.border-bewo-employee {
border-color: #45993B !important;
}
@@ -11921,6 +12050,14 @@ a.text-bewo-customer-card-header:hover, a.text-bewo-customer-card-header:focus {
color: rgb(97.5084269663, 42.1966292135, 3.9915730337) !important;
}
.text-bewo-person-card-header {
color: #AB4A07 !important;
}
a.text-bewo-person-card-header:hover, a.text-bewo-person-card-header:focus {
color: rgb(97.5084269663, 42.1966292135, 3.9915730337) !important;
}
.text-bewo-employee {
color: #45993B !important;
}

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,28 @@
$theme-colors: ("bewo-archived-sc": #343A40, "bewo-deleted-sc": #F87F7C, "bewo-person": #B5662D, "bewo-organisation": #C8C000, "bewo-custom-goal": #4682B4, "service-record-marked": #c5e3ff, "bewo-teams": #565999, "bewo-goal-rating": #0000ff, "bewo-dev": #00cf7c, "primary": #FF5A00, "bewo-customers": #3b7799, "bewo-service-records": #c80000, "bewo-calendar": #04b4d0, "bewo-is-about-to-expire": #FF0000, "bewo-expires-in-three-months": #FF9400, "bewo-group-booking": #f9e79f, "bewo-customer-card-header": #AB4A07, "bewo-employee": #45993B, "bewo-resource": #04B4D0, "bewo-task-red": #993B3B, "bewo-report": #995E3B );
$theme-colors:
(
"bewo-archived-sc": #343A40,
"bewo-deleted-sc": #F87F7C,
"bewo-person": #B5662D,
"bewo-organisation": #C8C000,
"bewo-custom-goal": #4682B4,
"service-record-marked": #c5e3ff,
"bewo-teams": #565999,
"bewo-goal-rating": #0000ff,
"bewo-dev": #00cf7c,
"primary": #FF5A00,
"bewo-customers": #3b7799,
"bewo-service-records": #c80000,
"bewo-calendar": #04b4d0,
"bewo-is-about-to-expire": #FF0000,
"bewo-expires-in-three-months": #FF9400,
"bewo-group-booking": #f9e79f,
"bewo-customer-card-header": #AB4A07,
"bewo-person-card-header": #AB4A07,
"bewo-employee": #45993B,
"bewo-resource": #04B4D0,
"bewo-task-red": #993B3B,
"bewo-report": #995E3B
);
textarea {
min-height: 150px !important;

View File

@@ -18,7 +18,7 @@ using DevExpress.XtraReports.UI;
namespace BeWoPlanerMobil.Controllers
{
public class CustomerController : ReportBaseController, IAbsenceTimeController
public class CustomerController : ReportBaseController, IAbsenceTimeController, IDocumentsController
{
public CustomerController() { }
@@ -223,7 +223,7 @@ namespace BeWoPlanerMobil.Controllers
return PartialView("DocumentPartial");
}
if(Model.SelectedCustomerOid is null || fileOid is null)
if(Model.ObjectOid is null || fileOid is null)
{
TempData[TempDataConstants.ErrorAlertPopupMessage] = "Es ist leider ein unbekannter Fehler aufgetreten.<br/><br/>Bitte kontaktieren Sie einen Administrator.";
return PartialView("DocumentPartial", Model);
@@ -238,7 +238,7 @@ namespace BeWoPlanerMobil.Controllers
OperationsService.UpdateFileAttachment(file);
}
Model.FolderTree = OperationsService.GetFolderTree(TableID.Customer, Model.SelectedCustomerOid.Value);
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
return PartialView("DocumentPartial", Model);
}
@@ -266,14 +266,14 @@ namespace BeWoPlanerMobil.Controllers
return PartialView("DocumentPartial");
}
if(fileOid is null || fileVersion is null || Model.SelectedCustomerOid is null)
if(fileOid is null || fileVersion is null || Model.ObjectOid is null)
{
return PartialView("DocumentPartial", Model);
}
OperationsService.DeleteFileAttachment(fileOid.Value, fileVersion.Value);
Model.FolderTree = OperationsService.GetFolderTree(TableID.Customer, Model.SelectedCustomerOid.Value);
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
return PartialView("DocumentPartial", Model);
}
@@ -313,8 +313,8 @@ namespace BeWoPlanerMobil.Controllers
BeWoFolderOid = folderOid,
Description = description,
FileName = fileName,
ObjectTid = TableID.Customer,
ObjectOid = Model.SelectedCustomerOid,
ObjectTid = Model.ObjectTid,
ObjectOid = Model.ObjectOid,
BinaryData = fileData,
FileAttachmentType = FileAttachmentType.NotSpecified,
InsertedBy = $"{Model.LoggedInEmployee.FirstName} {Model.LoggedInEmployee.LastName}",
@@ -325,7 +325,7 @@ namespace BeWoPlanerMobil.Controllers
}
}
return RedirectToActionPermanent("Customer", new { id = Model.SelectedCustomerOid, model = Model });
return RedirectToActionPermanent("Customer", new { id = Model.ObjectOid, model = Model });
}
[Authorize]
@@ -344,11 +344,11 @@ namespace BeWoPlanerMobil.Controllers
var folder = OperationsService.GetFolderById(folderOid.Value);
if(folder?.BeWoFolderOid != null && folder.BeWoFolderVersion.HasValue && Model.SelectedCustomerOid.HasValue && folder.ObjectOid.Equals(Model.SelectedCustomerOid.Value) && folder.ObjectTid == TableID.Customer)
if(folder?.BeWoFolderOid != null && folder.BeWoFolderVersion.HasValue && Model.ObjectOid.HasValue && folder.ObjectOid.Equals(Model.ObjectOid.Value) && folder.ObjectTid == Model.ObjectTid)
{
OperationsService.DeleteFolder(folder.BeWoFolderOid.Value, folder.BeWoFolderVersion.Value);
Model.FolderTree = OperationsService.GetFolderTree(TableID.Customer, Model.SelectedCustomerOid.Value);
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
}
return PartialView("DocumentPartial", Model);
@@ -371,13 +371,13 @@ namespace BeWoPlanerMobil.Controllers
var folder2Rename = OperationsService.GetFolderById(folderOid);
if(folder2Rename != null && Model.SelectedCustomerOid.HasValue && folder2Rename.ObjectTid == TableID.Customer && folder2Rename.ObjectOid == Model.SelectedCustomerOid.Value)
if(folder2Rename != null && Model.ObjectOid.HasValue && folder2Rename.ObjectTid == Model.ObjectTid && folder2Rename.ObjectOid == Model.ObjectOid.Value)
{
folder2Rename.Name = newName;
OperationsService.UpdateFolder(folder2Rename);
Model.FolderTree = OperationsService.GetFolderTree(TableID.Customer, Model.SelectedCustomerOid.Value);
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
}
return PartialView("DocumentPartial", Model);
@@ -392,7 +392,7 @@ namespace BeWoPlanerMobil.Controllers
return PartialView("DocumentPartial");
}
if(Model.SelectedCustomerOid is null)
if(Model.ObjectOid is null)
{
return PartialView("DocumentPartial", Model);
}
@@ -405,14 +405,14 @@ namespace BeWoPlanerMobil.Controllers
var folder = new BeWoFolderDC
{
Name = folderName,
ObjectTid = TableID.Customer,
ObjectOid = Model.SelectedCustomerOid,
ObjectTid = Model.ObjectTid,
ObjectOid = Model.ObjectOid,
ParentBeWoFolderOid = parentFolderOid
};
OperationsService.InsertNewFolder(folder);
Model.FolderTree = OperationsService.GetFolderTree(TableID.Customer, Model.SelectedCustomerOid.Value);
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
return PartialView("DocumentPartial", Model);
}

View File

@@ -0,0 +1,29 @@
using System.Web.Mvc;
namespace BeWoPlanerMobil.Controllers
{
internal interface IDocumentsController
{
[Authorize]
ActionResult SaveDescriptionChange(string descriptionText, long? fileOid);
[Authorize]
FileResult DownloadFile(long fileOid);
[Authorize]
ActionResult DeleteFile(long? fileOid, long? fileVersion);
[Authorize]
[HttpPost]
ActionResult UploadFile(FormCollection formCollection);
[Authorize]
ActionResult DeleteFolder(long? folderOid);
[Authorize]
ActionResult RenameFolder(string newName, long folderOid);
[Authorize]
ActionResult AddNewFolder(string folderName, long? parentFolderOid);
}
}

View File

@@ -1,16 +1,23 @@
using BeWoPlanerMobil.Models;
using BeWoPlanerMobil.Service;
using DevExpress.Web.Internal.XmlProcessor;
using BeWoPlanerMobil.Util;
using BS.Shared;
using BS.Shared.DataContracts;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BeWoPlanerMobil.Controllers
{
public class PersonController : AbstractBaseController
public class PersonController : AbstractBaseController, IDocumentsController
{
[Authorize]
public override string SaveCollapsibleStatus(bool isOpen, string identifier)
{
return SaveCollapsibleStatusToModel(Model, isOpen, identifier);
}
public PersonController() { }
private PersonModel _Model;
@@ -66,6 +73,25 @@ namespace BeWoPlanerMobil.Controllers
return View(Model);
}
[Authorize]
[HttpPost]
public ActionResult SelectPerson(FormCollection formCollection)
{
if(!MobileSessionFacade.IsUserLoggedIn() || Request.Browser.Browser.Equals("InternetExplorer"))
{
TempData[TempDataConstants.DoLogoutKey] = true;
return Logout();
}
var selectedPersonOidString = formCollection["SelectedPersonOid"];
if(long.TryParse(selectedPersonOidString, out var selectedPersonOid))
{
LoadPersonToModel(selectedPersonOid);
}
return RedirectToAction("Person", Model);
}
private void InitViewModel()
{
if(!MobileSessionFacade.IsUserLoggedIn() || Request.Browser.Browser.Equals("InternetExplorer"))
@@ -74,20 +100,261 @@ namespace BeWoPlanerMobil.Controllers
return;
}
if(Model.PersonSessionModel.SelectedPersonOid.HasValue)
{
Model.SelectedPerson = CustomerService.LoadPerson(Model.PersonSessionModel.SelectedPersonOid.Value);
Model.Umfeld = CustomerService.GetEnvironmentCustomerForPerson(Model.PersonSessionModel.SelectedPersonOid.Value);
}
Model.Persons = CustomerService.GetAllAuthorizedCompactPersons();
if(Model.SelectedPerson?.PersonOid != null)
{
Model.FolderTree = OperationsService.GetFolderTree(TableID.Person, Model.SelectedPerson.PersonOid.Value);
}
}
private void LoadPersonToModel(long? personOid)
{
if(personOid is null || (personOid.Value > 0 && false == Model.Persons.Any(p => p.PersonOid.Equals(personOid.Value))))
if(personOid is null)
{
return;
}
if(personOid.Value > 0 && false == Model.Persons.Any(p => p.PersonOid.Equals(personOid.Value)))
{
return;
}
if(personOid == -1)
{
Model.PersonSessionModel.ResetValues();
}
else
{
if(Model.SelectedPersonOid?.Equals(personOid) ?? false)
{
Model.PersonSessionModel.ResetValues();
}
Model.Umfeld = CustomerService.GetEnvironmentCustomerForPerson(personOid.Value);
Model.SelectedPersonOid = personOid;
}
}
public override string SaveCollapsibleStatus(bool isOpen, string identifier)
#region Dokumente
[Authorize]
public ActionResult SaveDescriptionChange(string descriptionText, long? fileOid)
{
throw new NotImplementedException();
if(Model is null || !MobileSessionFacade.IsUserLoggedIn())
{
TempData[TempDataConstants.DoLogoutKey] = true;
return PartialView("DocumentPartial");
}
if(Model.ObjectOid is null || fileOid is null)
{
TempData[TempDataConstants.ErrorAlertPopupMessage] = "Es ist leider ein unbekannter Fehler aufgetreten.<br/><br/>Bitte kontaktieren Sie einen Administrator.";
return PartialView("DocumentPartial", Model);
}
var file = DownloadService.GetFileAttachmentForMobile(fileOid.Value, MobileSessionFacade.Tenant);
if(file != null)
{
file.Description = descriptionText;
OperationsService.UpdateFileAttachment(file);
}
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
var fileTemp = DownloadService.GetFileAttachmentForMobile(fileOid.Value, MobileSessionFacade.Tenant);
return PartialView("DocumentPartial", Model);
}
[Authorize]
public FileResult DownloadFile(long fileOid)
{
if(Model is null || !MobileSessionFacade.IsUserLoggedIn() || Request.Browser.Browser.Equals("InternetExplorer"))
{
TempData[TempDataConstants.DoLogoutKey] = true;
return null;
}
var fileAttachment = DownloadService.GetFileAttachmentForMobile(fileOid, MobileSessionFacade.Tenant);
return File(fileAttachment.BinaryData, "application/octet-stream", fileAttachment.FileName);
}
[Authorize]
public ActionResult DeleteFile(long? fileOid, long? fileVersion)
{
if(Model is null || !MobileSessionFacade.IsUserLoggedIn())
{
TempData[TempDataConstants.DoLogoutKey] = true;
return PartialView("DocumentPartial");
}
if(fileOid is null || fileVersion is null || Model.ObjectOid is null)
{
return PartialView("DocumentPartial", Model);
}
OperationsService.DeleteFileAttachment(fileOid.Value, fileVersion.Value);
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
return PartialView("DocumentPartial", Model);
}
[Authorize]
[HttpPost]
public ActionResult UploadFile(FormCollection formCollection)
{
if(Model is null || !MobileSessionFacade.IsUserLoggedIn() || Request.Browser.Browser.Equals("InternetExplorer"))
{
TempData[TempDataConstants.DoLogoutKey] = true;
return PartialView("DocumentPartial");
}
if(Request.Files.Count > 0)
{
var folderOidAsString = formCollection["folder-oid-input"];
var description = formCollection["file-description-input"];
var folderOid = string.IsNullOrWhiteSpace(folderOidAsString) ? null : (long?)long.Parse(folderOidAsString);
var file = Request.Files[0];
if(file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
byte[] fileData;
using(var binaryReader = new BinaryReader(file.InputStream))
{
fileData = binaryReader.ReadBytes(file.ContentLength);
}
var fileAttachment = new FileAttachmentDC
{
BeWoFolderOid = folderOid,
Description = description,
FileName = fileName,
ObjectTid = Model.ObjectTid,
ObjectOid = Model.ObjectOid,
BinaryData = fileData,
FileAttachmentType = FileAttachmentType.NotSpecified,
InsertedBy = $"{Model.LoggedInEmployee.FirstName} {Model.LoggedInEmployee.LastName}",
InsertedOn = DateTime.Now
};
OperationsService.InsertFileAttachment(fileAttachment);
}
}
return RedirectToActionPermanent("Person", new { id = Model.SelectedPersonOid, model = Model });
}
[Authorize]
public ActionResult DeleteFolder(long? folderOid)
{
if(Model is null || !MobileSessionFacade.IsUserLoggedIn() || Request.Browser.Browser.Equals("InternetExplorer"))
{
TempData[TempDataConstants.DoLogoutKey] = true;
return PartialView("DocumentPartial");
}
if(folderOid is null)
{
return PartialView("DocumentPartial", Model);
}
var folder = OperationsService.GetFolderById(folderOid.Value);
if(
folder?.BeWoFolderOid != null &&
folder.BeWoFolderVersion.HasValue &&
Model.ObjectOid.HasValue &&
folder.ObjectOid.Equals(Model.ObjectOid.Value) &&
folder.ObjectTid == Model.ObjectTid
)
{
OperationsService.DeleteFolder(folder.BeWoFolderOid.Value, folder.BeWoFolderVersion.Value);
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
}
return PartialView("DocumentPartial", Model);
}
[Authorize]
public ActionResult RenameFolder(string newName, long folderOid)
{
if(Model is null || !MobileSessionFacade.IsUserLoggedIn() || Request.Browser.Browser.Equals("InternetExplorer"))
{
TempData[TempDataConstants.DoLogoutKey] = true;
return PartialView("DocumentPartial");
}
if(string.IsNullOrWhiteSpace(newName))
{
TempData[TempDataConstants.ErrorMessageKey] = "Ungültiger Name";
return PartialView("DocumentPartial", Model);
}
var folder2Rename = OperationsService.GetFolderById(folderOid);
if(folder2Rename != null && Model.ObjectOid.HasValue && folder2Rename.ObjectTid == Model.ObjectTid && folder2Rename.ObjectOid == Model.ObjectOid.Value)
{
folder2Rename.Name = newName;
OperationsService.UpdateFolder(folder2Rename);
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
}
return PartialView("DocumentPartial", Model);
}
[Authorize]
public ActionResult AddNewFolder(string folderName, long? parentFolderOid)
{
if(Model is null || !MobileSessionFacade.IsUserLoggedIn() || Request.Browser.Browser.Equals("InternetExplorer"))
{
TempData[TempDataConstants.DoLogoutKey] = true;
return PartialView("DocumentPartial");
}
if(Model.ObjectOid is null)
{
return PartialView("DocumentPartial", Model);
}
if(string.IsNullOrWhiteSpace(folderName))
{
folderName = "Neuer Ordner";
}
var folder = new BeWoFolderDC
{
Name = folderName,
ObjectTid = Model.ObjectTid,
ObjectOid = Model.ObjectOid,
ParentBeWoFolderOid = parentFolderOid
};
OperationsService.InsertNewFolder(folder);
Model.FolderTree = OperationsService.GetFolderTree(Model.ObjectTid, Model.ObjectOid.Value);
return PartialView("DocumentPartial", Model);
}
#endregion
}
}

View File

@@ -286,5 +286,8 @@ namespace BeWoPlanerMobil.Models
public bool ShowBetrag { get; set; }
public bool IsServiceRecordGoalMandatory { get; set; }
public bool HasRightToViewPersonDocuments => CheckForAtLeastOneRight(new[] { UserRightType.ViewAll, UserRightType.DokumentePersonen });
public bool HasRightToViewPersonUmfeld => CheckForAtLeastOneRight(new[] { UserRightType.ViewAll, UserRightType.Person_FamilyView });
}
}

View File

@@ -1,19 +1,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
using BeWo.View.Navigation.Filter;
using BeWoPlanerMobil.Controllers;
using BeWoPlanerMobil.Service;
using BeWoPlanerMobil.Util;
using BS.Shared;
using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Compact;
using DevExpress.XtraReports.UI;
namespace BeWoPlanerMobil.Models
{
/// <summary>
/// Im SessionModel werden Oids in der Session gespeichert, um dann beim Neuladen (z.B. nach einer Post-Anfrage) ausgewählte Objekte wieder aus der Datenbank holen zu können.
/// </summary>
[Serializable]
public abstract class BaseSessionModel
{

View File

@@ -11,8 +11,10 @@ using DevExpress.XtraReports.UI;
namespace BeWoPlanerMobil.Models
{
public class CustomerModel : AbstractModel, ISignable, IAbsenceTimeModel
public class CustomerModel : AbstractModel, ISignable, IAbsenceTimeModel, IDocumentsModel
{
public long? ObjectOid => SelectedCustomerOid;
public TableID ObjectTid => TableID.Customer;
public string HtmlPrefix => "customer";
public IAbsenceTime AbsenceTimeObject

View File

@@ -0,0 +1,15 @@
using BS.Shared.DataContracts;
using System.Collections.Generic;
using BS.Shared;
namespace BeWoPlanerMobil.Models
{
public interface IDocumentsModel
{
string HtmlPrefix { get; }
List<BeWoFolderDC> FolderTree { get; set; }
Dictionary<string, bool> Collapsibles2IsShown { get; set; }
TableID ObjectTid { get; }
long? ObjectOid { get; }
}
}

View File

@@ -486,6 +486,7 @@ namespace BeWoPlanerMobil.Models
gruppenInfo = $"Gruppe ({serviceRecord.GroupPersonCount} Teilnehmer, {serviceRecord.GroupEmployeeCount} Betreuer) Dauer: {(end - start).TotalMinutes} {signatureToken}";
}
// LJ, 30.01.2026: Wird nicht gerundet!
return $"{GetServiceRecordTimeString(serviceRecord)} {gruppenInfo} {signatureToken}";
}
@@ -501,9 +502,10 @@ namespace BeWoPlanerMobil.Models
var gruppenInfo = string.Empty;
var dauerInfo = string.Empty;
var signatureToken = serviceRecord.SignatureOid.HasValue && isForServiceRecordsList ? "✔" : string.Empty;
// ToDo am 02.02.2026: Die Dauer der Gruppenbuchungen wird nicht korrekt angezeigt!
var dauer = serviceRecord.RoundedDuration; //serviceRecord.GroupOid.HasValue ? (decimal)(end - start).TotalMinutes : serviceRecord.RoundedDuration; //serviceRecord.GroupOid.HasValue ? (decimal) (end - start).TotalMinutes : MobileUtils.CalculateRoundedDuration(serviceRecord);
var dauer = (end - start).TotalMinutes;
if (serviceRecord.DurationInStunden.HasValue && serviceRecord.DurationInStunden == ZeiterfassungsDauer.Stunden)
if(serviceRecord.DurationInStunden.HasValue && serviceRecord.DurationInStunden == ZeiterfassungsDauer.Stunden)
{
dauer /= 60;
dauerInfo = $" ({dauer:0.0#} h)";
@@ -512,11 +514,11 @@ namespace BeWoPlanerMobil.Models
{
dauerInfo = $" ({dauer:0.##} min.)";
}
if (serviceRecord.GroupOid.HasValue)
if(serviceRecord.GroupOid.HasValue)
{
gruppenInfo = $" Gruppe ({serviceRecord.GroupPersonCount} Teilnehmer, {serviceRecord.GroupEmployeeCount} Betreuer)";
}
return $"{GetServiceRecordTimeString(serviceRecord)}{gruppenInfo}{dauerInfo} {signatureToken}";
}
@@ -528,10 +530,12 @@ namespace BeWoPlanerMobil.Models
return string.Empty;
}
var start = serviceRecord.Start.Value;
var end = serviceRecord.End.Value; //start.AddMinutes((int) serviceRecord.RoundedDuration), AB, 12.03.2025: Änderung vom 12.11.2024 rückgängig; Endzeit bleibt bei Gruppen- und Einzelbuchungen die tatäschliche Zeit - genau wie FAK
var isGroupBooking = serviceRecord.GroupOid.HasValue;
if (start.Second == 0)
var start = serviceRecord.Start.Value;
var end = isGroupBooking ? serviceRecord.End.Value : serviceRecord.Start.Value.AddMinutes((double) serviceRecord.RoundedDuration); //start.AddMinutes((int) serviceRecord.RoundedDuration), AB, 12.03.2025: Änderung vom 12.11.2024 rückgängig; Endzeit bleibt bei Gruppen- und Einzelbuchungen die tatäschliche Zeit - genau wie FAK
if(start.Second == 0)
{
return start.Date != end.Date ?
$"{start.ToShortDateString()} {start.ToShortTimeString()} - {end.ToShortDateString()} {end.ToShortTimeString()}" :
@@ -545,7 +549,7 @@ namespace BeWoPlanerMobil.Models
public static string GetServiceRecordTimeString(MokServiceRecord serviceRecord)
{
if (serviceRecord?.Start is null || serviceRecord.End is null)
if(serviceRecord?.Start is null || serviceRecord.End is null)
{
return string.Empty;
}
@@ -553,7 +557,7 @@ namespace BeWoPlanerMobil.Models
var start = serviceRecord.Start.Value;
var end = serviceRecord.End.Value; //start.AddMinutes((int) serviceRecord.RoundedDuration), AB, 12.03.2025: Änderung vom 12.11.2024 rückgängig; Endzeit bleibt bei Gruppen- und Einzelbuchungen die tatäschliche Zeit - genau wie FAK
if (start.Second == 0)
if(start.Second == 0)
{
return start.Date != end.Date ?
$"{start.ToShortDateString()} {start.ToShortTimeString()} - {end.ToShortDateString()} {end.ToShortTimeString()}" :

View File

@@ -1,13 +1,12 @@
using System;
using BeWoPlanerMobil.Util;
using BS.Shared;
using BS.Shared.DataContracts;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using BS.Shared.DataContracts;
namespace BeWoPlanerMobil.Models
{
public class PersonModel : AbstractModel
public class PersonModel : AbstractModel, IDocumentsModel
{
public PersonModel()
{
@@ -25,6 +24,12 @@ namespace BeWoPlanerMobil.Models
public PersonDC SelectedPerson { get; set; }
public List<CustomerPersonRelationDC> Umfeld { get; set; } = new List<CustomerPersonRelationDC>();
public JsonPerson SelectedJsonPerson => SelectedPerson is null ? null : new JsonPerson(SelectedPerson, Umfeld);
public long? ObjectOid => SelectedPersonOid;
public TableID ObjectTid => TableID.Person;
public string HtmlPrefix => "person";
public List<BeWoFolderDC> FolderTree { get; set; }
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace BeWoPlanerMobil.Models
{

View File

@@ -64,6 +64,12 @@ namespace BeWoPlanerMobil.Util
prependText = "Team";
modalTitleText = "Teamauswahl";
break;
case nameof(CompactPersonDC):
buttonColor = "btn-bewo-person";
buttonOutlineColor = "btn-outline-bewo-person";
prependText = "Person";
modalTitleText = "Personenauswahl";
break;
}
if(isButtonPrimaryColor)

View File

@@ -0,0 +1,132 @@
using BS.Shared;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using System.Collections.Generic;
using System.Web;
namespace BeWoPlanerMobil.Util
{
public class JsonPerson
{
#region Eigenschaften
public string Titel { get; set; }
public string Vorname { get; set; }
public string Nachname { get; set; }
public string Geburtstag { get; set; }
public Sex? PersonSex { get; set; }
public string Geschlecht { get; set; }
public string Familienstand { get; set; }
public string Funktion { get; set; }
public string Staatsangehoerigkeit { get; set; }
public string Migrationshintergrund { get; set; }
public string Aufenthaltsstatus { get; set; }
public string Archiviert { get; set; }
public string Notizen { get; set; }
#endregion
#region Adresse
public string Adresszusatz { get; set; }
public string Strasse { get; set; }
public string Postleitzahl { get; set; }
public string Ort { get; set; }
#endregion
#region Rechnungsadresse
public string RechnungsadresseStrasse { get; set; }
public string RechnungsadressePostleitzahl { get; set; }
public string RechnungsadresseOrt { get; set; }
#endregion
#region Kontakt
public string Handy { get; set; }
public string Telefon { get; set; }
public string Fax { get; set; }
public string EMail { get; set; }
public string Homepage { get; set; }
#endregion
public string GoogleMapsLink
{
get
{
var encodedStreet = HttpUtility.UrlEncode(Strasse);
var encodedCity = HttpUtility.UrlEncode(Ort);
return $"http://maps.google.de/maps?q={encodedStreet},+{Postleitzahl}+{encodedCity}&t=h&z=17";
}
}
public List<Organisation2PersonDC> Organisations { get; set; } = new List<Organisation2PersonDC>();
public List<Umfeldsperson> Umfeldpersonen { get; set; } = new List<Umfeldsperson>();
public JsonPerson(PersonDC person, List<CustomerPersonRelationDC> umfeld)
{
Titel = person.Title?.DisplayName;
Vorname = person.FirstName;
Nachname = person.LastName;
Geburtstag = person.DateOfBirth?.ToString("yyyy-MM-dd") ?? string.Empty;
PersonSex = person.Sex;
switch(person.Sex)
{
case Sex.Male:
Geschlecht = "Männlich";
break;
case Sex.Female:
Geschlecht = "Weiblich";
break;
case Sex.Divers:
Geschlecht = "Divers";
break;
default:
Geschlecht = string.Empty;
break;
}
Familienstand = person.FamilyStatus != null ? EnumTranslations.FamilyStatusTranslations[person.FamilyStatus] : string.Empty;
Funktion = person.Function?.DisplayName ?? string.Empty;
Staatsangehoerigkeit = person.Nationalitaet?.DisplayName ?? string.Empty;
Migrationshintergrund = person.Migrationshintergrund;
Aufenthaltsstatus = person.AufenthaltsStatus?.DisplayName ?? string.Empty;
Archiviert = person.ActivationType == ActivationTypeId.Archived ? "checked" : string.Empty;
Notizen = person.PersonNotice;
Adresszusatz = person.AddressLine1;
Strasse = person.Street;
Postleitzahl = person.PostalCode;
Ort = person.Town;
RechnungsadresseStrasse = person.InvoiceAddressStreet;
RechnungsadressePostleitzahl = person.InvoiceAddressPostalCode;
RechnungsadresseOrt = person.InvoiceAddressTown;
foreach(var contact in person.ContactInformations)
{
switch(contact.ContactType)
{
case ContactType.business_Mail:
EMail = contact.ContactValue;
break;
case ContactType.business_Fax:
Fax = contact.ContactValue;
break;
case ContactType.business_Phone:
Telefon = contact.ContactValue;
break;
case ContactType.business_MobilePhone:
Handy = contact.ContactValue;
break;
}
}
foreach(var rel in umfeld)
{
Umfeldpersonen.Add(new Umfeldsperson(rel));
}
Organisations = person.OrganisationRelations;
}
}
}

View File

@@ -11,7 +11,7 @@ using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Compact;
using BS.Shared.Extensions;
using BS.Shared.Services;
using Newtonsoft.Json;
using RestSharp;
using static System.Int32;
@@ -500,5 +500,36 @@ namespace BeWoPlanerMobil.Util
return stringBuilder.ToString();
}
public static decimal CalculateRoundedDuration(ServiceRecordDC serviceRecord)
{
if(serviceRecord.End is null || serviceRecord.Start is null)
{
return 0m;
}
var roundedDuration = (decimal) serviceRecord.End.Value.Subtract(serviceRecord.Start.Value).TotalMinutes;
if(serviceRecord.CostBearer2SupportConceptOid.HasValue && (serviceRecord.ServiceDescription?.Category?.IsBillable ?? false))
{
var calculations = Calculations.GetInstance(serviceRecord.CostBearer.CostBearerID);
roundedDuration = calculations.GetRoundedDuration(serviceRecord.CostBearer, serviceRecord.Start.Value, roundedDuration);
}
return roundedDuration;
}
public static string GetRoundedEndDate(ServiceRecordDC serviceRecord)
{
if(serviceRecord.End is null || serviceRecord.Start is null)
{
return Empty;
}
var duration = (double) CalculateRoundedDuration(serviceRecord);
return $"{(serviceRecord.Start.Value.AddMinutes(duration))}";
}
}
}

View File

@@ -19,8 +19,11 @@ namespace BeWoPlanerMobil.Util
public string Notice { get; set; }
public long? Customer2PersonOid { get; }
public string CustomerName { get; }
public Umfeldsperson(CustomerPersonRelationDC umfeldspersonDC)
{
CustomerName = umfeldspersonDC.Customer?.DetailDescription;
Customer2PersonOid = umfeldspersonDC.Customer2PersonOid;
var person = umfeldspersonDC.Person;
Rolle = umfeldspersonDC.RelationRole?.ToString() ?? string.Empty;

View File

@@ -15,7 +15,13 @@
$(".collapse").on("hidden.bs.collapse", function() {
window.saveCollapsibleState($(this).attr("id"), "@Url.Action("SaveCollapsibleStatus")");
});
});
resizeCustomerPrependElements();
});
$(document).resize(() => {
resizeCustomerPrependElements();
});
function resizeCustomerPrependElements() {
@@ -184,7 +190,7 @@
var inputDisabled = !Model.HasRightToEditCustomers ? "disabled" : string.Empty;
<div class="row" id="stammdaten-container">
<div class="col-sm-6 col-md-6 col-lg-6 col-xl-3 mt-2 px-1">
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 mt-2 px-1">
<div class="card">
<div class="card-body pb-0">
<div class="card-title text-bewo-customer-card-header">
@@ -310,7 +316,7 @@
</div>
</div>
</div>
<div class="col-sm-6 col-md-6 col-lg-6 col-xl-3 mt-2 px-1">
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 mt-2 px-1">
<div class="card">
<div class="card-body pb-0">
<div class="card-title text-bewo-customer-card-header">
@@ -367,7 +373,7 @@
</div>
</div>
</div>
<div class="col-sm-6 col-md-6 col-lg-6 col-xl-3 mt-2 px-1">
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 mt-2 px-1">
<div class="card">
<div class="card-body pb-0">
<div class="card-title text-bewo-customer-card-header">
@@ -410,7 +416,7 @@
</div>
</div>
<div class="row">
<div class="form-group w-100 mb-1">
<div class="form-group w-100 mt-1 mb-1">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text customer-prepend-text-rechnungsadresse">
@@ -424,7 +430,7 @@
</div>
</div>
</div>
<div class="col-sm-6 col-md-6 col-lg-6 col-xl-3 mt-2 px-1">
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 mt-2 px-1">
<div class="card">
<div class="card-body pb-0">
<div class="card-title text-bewo-customer-card-header">
@@ -497,7 +503,7 @@
</div>
</div>
<div class="col-sm-12 col-md-6 col-lg-12 mt-2 px-1">
<div class="col-sm-12 col-md-12 col-lg-12 mt-2 px-1">
<div class="card w-100 h-100">
<div class="card-header">
<div class="clearfix">

View File

@@ -1 +1,663 @@
@model BeWoPlanerMobil.Models.PersonModel
@using BeWoPlanerMobil.Util
@using BS.Shared
@model BeWoPlanerMobil.Models.PersonModel
<script type="text/javascript">
$(document).ready(function() {
$(".collapse").on("shown.bs.collapse", function () {
window.saveCollapsibleState($(this).attr("id"), "@Url.Action("SaveCollapsibleStatus")");
});
$(".collapse").on("hidden.bs.collapse", function() {
window.saveCollapsibleState($(this).attr("id"), "@Url.Action("SaveCollapsibleStatus")");
});
resizePersonPrependElements();
});
$(document).resize(() => {
resizePersonPrependElements();
});
function resizePersonPrependElements() {
try {
let width = window.getMaxWidth("person-prepend-text-eigenschaften");
if(width > 0) {
$(".person-prepend-text-eigenschaften").width(width);
}
width = window.getMaxWidth("person-prepend-text-adresse");
if(width > 0) {
$(".person-prepend-text-adresse").width(width);
}
width = window.getMaxWidth("person-prepend-text-rechnungsadresse");
if(width > 0) {
$(".person-prepend-text-rechnungsadresse").width(width);
}
width = window.getMaxWidth("person-prepend-text-kontakt");
if(width > 0) {
$(".person-prepend-text-kontakt").width(width);
}
} catch(error) {
window.showErrorPopup(error);
}
}
function selectPerson(element, personOid) {
try {
$("#person-oid-input").val(personOid);
$("#person-modal-popup").modal("hide");
showSpinner();
$("#select-person-form").submit();
} catch(error) {
window.showErrorPopup(error);
}
}
function resetPersonSearch() {
try {
$("#person-search-input").val("");
$("#person-dropdown-menu .dropdown-item").show();
const e = window.event;
e.cancelBubble = true;
if(e.stopPropagation) {
e.stopPropagation();
}
} catch(error) {
window.showErrorPopup(error);
}
}
function onPersonSearchInput() {
try {
var searchText = $("#person-search-input").val();
if(searchText === null || searchText === undefined || searchText.length === 0) {
$("#person-dropdown-menu .dropdown-item").show();
return;
}
const menuItems = $("#person-dropdown-menu .dropdown-item");
menuItems.hide();
$.each(menuItems,
function(index, item) {
const itemText = $(item).text().toLowerCase();
if(itemText.includes(searchText.toLowerCase())) {
$(item).show();
}
});
} catch(error) {
window.showErrorPopup(error);
}
}
</script>
<div class="container mt-3 lg-max-width">
<div class="row mb-3">
<div class="col-md">
@using(Html.BeginForm("SelectPerson", "Person", FormMethod.Post, new { id = "select-person-form" }))
{
var personName = Model.SelectedPerson != null ? $"{Model.SelectedPerson.LastName}, {Model.SelectedPerson.FirstName}" : string.Empty;
@Html.PopupWithSearchForIFilterables(Model.Persons, "selectPerson", null, personName, "person-modal-popup", false)
<input type="hidden" name="SelectedPersonOid" id="person-oid-input" />
}
</div>
</div>
</div>
@*
ToDo: Rechteabfragen implementieren!
*@
@if(Model.SelectedJsonPerson != null)
{
<div class="container-fluid mt-1 px-0 lg-max-width" id="person-container">
<div class="col-12">
<div class="card w-100 h-100 my-1">
<div class="card-header" onclick="cardHeaderClick2(this, resizePersonPrependElements)">
<div class="d-inline-flex">
<i class="fas fa-chevron-down h-100 text-bewo-person-card-header"></i>
</div>
<div class="d-inline d-inline-flex text-bewo-person-card-header h-100">
<button type="button" class="btn btn-link text-bewo-person-card-header" style="font-size: 20px;">
Details
</button>
</div>
</div>
<div class="collapse @Html.GetCollapsibleClass(Model.Collapsibles2IsShown, "person-details-collapsible")" id="person-details-collapsible">
<div class="card-body py-0">
@{
const string inputDisabled = "disabled";//!Model.HasRightToEditPersons ? "disabled" : string.Empty;
}
<div class="row" id="stammdaten-container">
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 mt-2 px-1">
<div class="card">
<div class="card-body pb-0">
<div class="card-title text-bewo-person-card-header">
Eigenschaften
</div>
<div class="row">
<div class="form-group w-100 mt-1 my-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-eigenschaften">
Titel
</div>
</div>
<input required @inputDisabled type="text" autocomplete="on" class="form-control" id="title" name="title" value="@Model.SelectedJsonPerson.Titel" />
<div class="invalid-feedback">
Dieses Feld darf nicht leer sein!
</div>
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 my-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-eigenschaften">
Vorname
</div>
</div>
<input required @inputDisabled type="text" autocomplete="on" class="form-control" id="vorname" name="vorname" value="@Model.SelectedJsonPerson.Vorname" />
<div class="invalid-feedback">
Dieses Feld darf nicht leer sein!
</div>
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 my-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-eigenschaften">
Nachname
</div>
</div>
<input required @inputDisabled type="text" autocomplete="on" class="form-control" id="nachname" name="nachname" value="@Model.SelectedJsonPerson.Nachname" />
<div class="invalid-feedback">
Dieses Feld darf nicht leer sein!
</div>
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 my-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-eigenschaften">
Geburtstag
</div>
</div>
<input type="date" @inputDisabled id="date-of-birth-input"
autocomplete="on"
name="geburtstag" class="form-control"
value="@Model.SelectedJsonPerson.Geburtstag" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 my-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-eigenschaften">
Geschlecht
</div>
</div>
@{
var selectedMale = Model.SelectedJsonPerson.PersonSex == Sex.Male ? "selected" : string.Empty;
var selectedFemale = Model.SelectedJsonPerson.PersonSex == Sex.Female ? "selected" : string.Empty;
var selectedDiverse = Model.SelectedJsonPerson.PersonSex == Sex.Divers ? "selected" : string.Empty;
var selectedNull = Model.SelectedJsonPerson.PersonSex is null ? "selected" : string.Empty;
}
<select class="custom-select" name="geschlecht" @inputDisabled>
<option value="-1" @selectedNull></option>
<option value="0" @selectedMale>M&auml;nnlich</option>
<option value="1" @selectedFemale>Weiblich</option>
<option value="2" @selectedDiverse>Divers</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
Familienstand
</div>
</div>
<input @inputDisabled maxlength="45" type="text" autocomplete="on" class="form-control" id="familienstand" name="familienstand" value="@Model.SelectedJsonPerson.Familienstand" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
Funktion
</div>
</div>
<input @inputDisabled maxlength="45" type="text" autocomplete="on" class="form-control" id="funktion" name="funktion" value="@Model.SelectedJsonPerson.Funktion" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 my-1">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
Staatsangehörigkeit
</div>
</div>
<input @inputDisabled maxlength="16" type="text" autocomplete="on" class="form-control" id="staatsangehoerigkeit" name="staatsangehoerigkeit" value="@Model.SelectedJsonPerson.Staatsangehoerigkeit" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 my-1">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
Migrationshintergrund
</div>
</div>
<input @inputDisabled maxlength="16" type="text" autocomplete="on" class="form-control" id="migrationshintergrund" name="migrationshintergrund" value="@Model.SelectedJsonPerson.Migrationshintergrund" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 my-1">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
Aufenthaltsstatus
</div>
</div>
<input @inputDisabled maxlength="16" type="text" autocomplete="on" class="form-control" id="aufenthaltsstatus" name="aufenthaltsstatus" value="@Model.SelectedJsonPerson.Aufenthaltsstatus" />
</div>
</div>
</div>
<div class="row">
<div class="form-group form-check w-100 my-1">
<input @inputDisabled type="checkbox" class="form-check-input" id="archiviert" name="archiviert" @Model.SelectedJsonPerson.Archiviert />
<label class="form-check-label" for="archiviert">Archiviert</label>
</div>
</div>
</div>
</div>
</div>
@* ---------- Adresse ----------**@
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 mt-2 px-1">
<div class="card">
<div class="card-body pb-0">
<div class="card-title text-bewo-person-card-header">
<a href="@Model.SelectedJsonPerson.GoogleMapsLink" target="_blank" class="text-bewo-person-card-header">Adresse<i class="fas fa-map-marker-alt ml-3"></i></a>
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-adresse">
Adresszusatz
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="adresszusatz" name="adresszusatz" value="@Model.SelectedJsonPerson.Adresszusatz" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-adresse">
Stra&szlig;e
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="strasse" name="strasse" value="@Model.SelectedJsonPerson.Strasse" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-adresse">
PLZ
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="plz" name="plz" value="@Model.SelectedJsonPerson.Postleitzahl" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 my-1">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-adresse">
Ort
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="ort" name="ort" value="@Model.SelectedJsonPerson.Ort" />
</div>
</div>
</div>
</div>
</div>
</div>
@* ---------- /Adresse ----------**@
@* ---------- Rechnungsadresse ----------**@
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 mt-2 px-1">
<div class="card">
<div class="card-body pb-0">
<div class="card-title text-bewo-person-card-header">
Rechnungsadresse
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-rechnungsadresse">
Stra&szlig;e
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="strasse-rechnungsadresse" name="strasse-rechnungsadresse" value="@Model.SelectedJsonPerson.RechnungsadresseStrasse" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-rechnungsadresse">
PLZ
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="plz-rechnungsadresse" name="plz-rechnungsadresse" value="@Model.SelectedJsonPerson.RechnungsadressePostleitzahl" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-1">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-rechnungsadresse">
Ort
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="ort-rechnungsadresse" name="ort-rechnungsadresse" value="@Model.SelectedJsonPerson.RechnungsadresseOrt" />
</div>
</div>
</div>
</div>
</div>
</div>
@* ---------- /Rechnungsadresse ----------**@
@* ---------- Kontakt ----------**@
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 mt-2 px-1">
<div class="card">
<div class="card-body pb-0">
<div class="card-title text-bewo-person-card-header">
Kontakt
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-kontakt">
Handy
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="handy" name="handy" value="@Model.SelectedJsonPerson.Handy" />
<div class="input-group-append">
<button type="button" class="btn btn-primary px-2" onclick="window.open('tel:@Model.SelectedJsonPerson.Handy')">
<i class="fas fa-phone"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-kontakt">
Telefon
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="telefon" name="telefon" value="@Model.SelectedJsonPerson.Telefon" />
<div class="input-group-append">
<button type="button" class="btn btn-primary px-2" onclick="window.open('tel:@Model.SelectedJsonPerson.Telefon')">
<i class="fas fa-phone"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 mt-1 mb-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-kontakt">
Fax
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="fax" name="fax" value="@Model.SelectedJsonPerson.Fax" />
</div>
</div>
</div>
<div class="row">
<div class="form-group w-100 my-1">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text person-prepend-text-kontakt">
Mail
</div>
</div>
<input type="text" autocomplete="on" @inputDisabled class="form-control" id="mail" name="mail" value="@Model.SelectedJsonPerson.EMail" />
<div class="input-group-append">
<button type="button" class="btn btn-primary px-2" onclick="window.open('mailto:@Model.SelectedJsonPerson.EMail')">
<i class="fas fa-envelope"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@* ---------- /Kontakt ----------**@
<div class="col-sm-12 col-md-6 col-lg-12 mt-2 px-1">
<div class="card w-100 h-100">
<div class="card-header">
<div class="clearfix">
<h5 class="text-bewo-person-card-header d-inline float-left">Kommentar</h5>
</div>
</div>
<div class="card-body m-1 p-1">
<div class="container-fluid m-1 p-1">
<textarea id="notice-textarea" autocomplete="on" @inputDisabled name="notiz" class="w-100 m-1 p-1" style="resize: vertical;">@Model.SelectedJsonPerson.Notizen</textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card w-100 h-100 my-1">
<div class="card-header" onclick="cardHeaderClick2(this, null)">
<div class="d-inline-flex">
<i class="fas fa-chevron-down h-100 text-bewo-person-card-header"></i>
</div>
<div class="d-inline d-inline-flex text-bewo-person-card-header h-100">
<button type="button" class="btn btn-link text-bewo-person-card-header" style="font-size: 20px;">
Organisationen
</button>
</div>
</div>
<div class="collapse @Html.GetCollapsibleClass(Model.Collapsibles2IsShown, "person-organisations-collapsible")" id="person-organisations-collapsible">
<div class="card-body px-1 pt-0 pb-1">
@foreach(var organisation in Model.SelectedJsonPerson.Organisations)
{
var rolle = organisation.RolleInOrganisation?.DisplayName ?? string.Empty;
<div class="card w-100 mt-1">
<div class="card-header" onclick="cardHeaderClick2(this)">
<div class="d-inline-flex">
<i class="fas fa-chevron-down h-100 text-primary" id="chevron-organisaiton-@organisation.Organisation2PersonOid"></i>
</div>
<div class="p2 d-inline-flex text-primary h-100">
<button type="button" class="btn btn-link">
@organisation.Organisation.Name
</button>
</div>
</div>
<div class="colapse @Html.GetCollapsibleClass(Model.Collapsibles2IsShown, $"collapse-organisation-{organisation.Organisation2PersonOid}")" id="collapse-organisation-@organisation.Organisation2PersonOid">
<div class="card-body">
<table class="table table-sm table-striped service-record-table">
<tr>
<td class="text-secondary">
Rolle
</td>
<td>
@rolle
</td>
</tr>
<tr>
<td class="text-secondary">
Mail
</td>
<td>
<a href="mailto:@organisation.Mail">
@organisation.Mail
</a>
</td>
</tr>
<tr>
<td class="text-secondary">
Tel.
</td>
<td>
<a href="tel:@organisation.Phone">
@organisation.Phone
</a>
</td>
</tr>
<tr>
<td class="text-secondary">
Fax
</td>
<td>
@organisation.Fax
</td>
</tr>
<tr>
<td class="text-secondary">
Notiz
</td>
<td>
@organisation.Notice
</td>
</tr>
</table>
</div>
</div>
</div>
}
</div>
</div>
</div>
@if(Model.HasRightToViewPersonUmfeld)
{
<div class="card w-100 h-100 my-1">
<div class="card-header" onclick="cardHeaderClick2(this, null)">
<div class="d-inline-flex">
<i class="fas fa-chevron-down h-100 text-bewo-person-card-header"></i>
</div>
<div class="d-inline d-inline-flex text-bewo-person-card-header h-100">
<button type="button" class="btn btn-link text-bewo-person-card-header" style="font-size: 20px;">
Umfeld
</button>
</div>
</div>
<div class="collapse @Html.GetCollapsibleClass(Model.Collapsibles2IsShown, "person-umfeld-collapsible")" id="person-umfeld-collapsible">
<div class="card-body px-1 pt-0 pb-1">
@foreach(var umfeldPerson in Model.SelectedJsonPerson.Umfeldpersonen)
{
<div class="card w-100 mt-1">
<div class="card-header" onclick="cardHeaderClick2(this)">
<div class="d-inline-flex">
<i class="fas fa-chevron-down h-100 text-primary" id="chevron-umfeldperson-@umfeldPerson.Customer2PersonOid"></i>
</div>
<div class="p2 d-inline-flex text-primary h-100">
<button type="button" class="btn btn-link">
@umfeldPerson.CustomerName
</button>
</div>
</div>
<div class="collapse @Html.GetCollapsibleClass(Model.Collapsibles2IsShown, $"collapse-umfeldperson-{umfeldPerson.Customer2PersonOid}")" id="collapse-umfeldperson-@umfeldPerson.Customer2PersonOid">
<div class="card-body">
<table class="table table-sm table-striped service-record-table">
<tr>
<td class="text-secondary">
Rolle
</td>
<td>
@umfeldPerson.Rolle
</td>
</tr>
<tr>
<td class="text-secondary">
Notiz
</td>
<td>
@umfeldPerson.Notice
</td>
</tr>
</table>
</div>
</div>
</div>
}
</div>
</div>
</div>
}
@if(Model.HasRightToViewPersonDocuments)
{
<div class="card w-100 h-100 my-1">
<div class="card-header" onclick="cardHeaderClick2(this, null)">
<div class="d-inline-flex">
<i class="fas fa-chevron-down h-100 text-bewo-person-card-header"></i>
</div>
<div class="d-inline d-inline-flex text-bewo-person-card-header h-100">
<button type="button" class="btn btn-link text-bewo-person-card-header" style="font-size: 20px;">
Dokumente
</button>
</div>
</div>
<div class="collapse @Html.GetCollapsibleClass(Model.Collapsibles2IsShown, "person-documents-collapsible")" id="person-documents-collapsible">
<div class="card-body py-0 px-1">
<div class="container-fluid px-0 pb-1" id="person-document-container">
@Html.Partial("DocumentPartial", Model)
</div>
</div>
</div>
</div>
}
</div>
</div>
}

View File

@@ -1,10 +1,10 @@
@using BeWoPlanerMobil.Models
@using BeWoPlanerMobil.Util
@using BS.Shared.Extensions
@model BeWoPlanerMobil.Models.IAbsenceTimeModel
@model IAbsenceTimeModel
@{
var controllerName = Model is CustomerModel ? "Customer" : "Employee";
var controllerName = Model.HtmlPrefix.FirstCharToUpper();
}
<script type="text/javascript">

View File

@@ -1,7 +1,12 @@
@using BeWoPlanerMobil.Models;
@using BeWoPlanerMobil.Util
@using BS.Shared.DataContracts
@model CustomerModel
@using BS.Shared.Extensions
@model IDocumentsModel
@{
var controllerName = Model.HtmlPrefix.FirstCharToUpper();
}
<script type="text/javascript">
$(document).ready(() => {
@@ -45,7 +50,7 @@
function saveDescriptionChange(fileOid) {
const descriptionText = $(`#description-${fileOid}`).val();
$("#customer-document-container").load("@Url.Action("SaveDescriptionChange")", {descriptionText: descriptionText, fileOid: fileOid});
$("#@Model.HtmlPrefix-document-container").load("@Url.Action("SaveDescriptionChange", controllerName)", {descriptionText: descriptionText, fileOid: fileOid});
}
function uploadFile(folderOid) {
@@ -69,7 +74,7 @@
newFolderName = "Neuer Ordner";
}
$("#customer-document-container").load("@Url.Action("AddNewFolder")", {folderName: newFolderName, parentFolderOid: folderOid});
$("#@Model.HtmlPrefix-document-container").load("@Url.Action("AddNewFolder", controllerName)", {folderName: newFolderName, parentFolderOid: folderOid});
$("#new-folder-popup").modal("hide");
});
}
@@ -82,7 +87,7 @@
$("#folder-popup-save-btn").off("click");
$("#folder-popup-save-btn").on("click", () => {
$("#customer-document-container").load("@Url.Action("RenameFolder")", {newName: $("#new-folder-name").val(), folderOid: folderOid});
$("#@Model.HtmlPrefix-document-container").load("@Url.Action("RenameFolder", controllerName)", {newName: $("#new-folder-name").val(), folderOid: folderOid});
$("#new-folder-popup").modal("hide");
});
}
@@ -91,7 +96,7 @@
showAlertMessageBox("Ordner löschen",
`ACHTUNG: Das Löschen von Ordnern kann nicht rückgängig gemacht werden.<br />Alle enthaltenen Dokumente bleiben erhalten und werden in den obersten Ordner "<span class='font-weight-bold'>Alle Ordner</span>" verschoben.<br /><br />Wollen Sie den Ordner "<span class='font-weight-bold'>${folderName}</span>" wirklich löschen?`,
() => {
$("#customer-document-container").load("@Url.Action("DeleteFolder")", {folderOid: folderOid});
$("#@Model.HtmlPrefix-document-container").load("@Url.Action("DeleteFolder", controllerName)", {folderOid: folderOid});
},
false);
}
@@ -110,7 +115,7 @@
showAlertMessageBox("Datei löschen",
`ACHTUNG: Das Löschen von Dateien kann nicht rückgängig gemacht werden!<br />Wollen Sie das Dokument '<span class='font-weight-bold'>${fileName}</span>' wirklich löschen?`,
() => {
$("#customer-document-container").load("@Url.Action("DeleteFile")", {fileOid: fileOid, fileVersion: fileVersion});
$("#@Model.HtmlPrefix-document-container").load("@Url.Action("DeleteFile", controllerName)", {fileOid: fileOid, fileVersion: fileVersion});
},
false);
}
@@ -118,6 +123,8 @@
@helper BuildFolderTreeBranch(BeWoFolderDC folder)
{
var controllerName = Model.HtmlPrefix.FirstCharToUpper();
<div class="card w-100 mt-2">
<div class="card-header p-0 border-0 d-flex">
<div class="input-group">
@@ -174,7 +181,7 @@
<i class="fas fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu">
<a href="@Url.Action("DownloadFile", new { fileOid = file.FileAttachmentOid })" class="btn btn-primary dropdown-item text-primary" role="button">
<a href="@Url.Action("DownloadFile", controllerName, new { fileOid = file.FileAttachmentOid })" class="btn btn-primary dropdown-item text-primary" role="button">
<i class="fas fa-download"></i>
Herunterladen
</a>
@@ -291,7 +298,7 @@
</button>
</div>
<div class="modal-body">
@using(Html.BeginForm("UploadFile", "Customer", FormMethod.Post, new { id="new-file-form", enctype="multipart/form-data" }))
@using(Html.BeginForm("UploadFile", controllerName, FormMethod.Post, new { id="new-file-form", enctype="multipart/form-data" }))
{
<div class="row">
<div class="col">

View File

@@ -397,21 +397,21 @@
</button>
</li>
}
if(Model.IsAllowedToSeePersons)
{
using(Html.BeginForm("RedirectToPersons", "Main", FormMethod.Post))
{
<li class="nav-item">
<button class="btn btn-link text-light no-underline" onclick="showSpinner()">
<i class="fas fa-user text-bewo-person"></i>
Personen
</button>
</li>
}
}
}
@if(Model.IsAllowedToSeePersons)
{
using(Html.BeginForm("RedirectToPersons", "Main", FormMethod.Post))
{
<li class="nav-item">
<button class="btn btn-link text-light no-underline" onclick="showSpinner()">
<i class="fas fa-user text-bewo-person"></i>
Personen
</button>
</li>
}
}
@if(Model.HasRightToViewOwnEmployee)
{
using(Html.BeginForm("RedirectToEmployees", "Main", FormMethod.Post))
@@ -461,7 +461,6 @@
}
}
@if (Model.HasRightToViewQuittierungsbelege)
{
using (Html.BeginForm("RedirectToReportView", "Main", FormMethod.Post))

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using BeWo.Data.Entities;
using BeWo.Data.Entities;
using BeWo.Service.DCEntityMapper;
using BS.Shared.Core;
using BS.Shared.DataContracts;

View File

@@ -1,11 +1,12 @@
using System.Diagnostics;
using BS.Shared.DataContracts.ClientPartials;
using System.Diagnostics;
using System.Windows.Media;
namespace BS.Shared.DataContracts.Compact
{
[DebuggerDisplay("{FullName}")]
public partial class CompactPersonDC : IFilterableDC
public partial class CompactPersonDC : IFilterableDC, IMoKSearchableDC
{
public string AddressString
{
@@ -252,5 +253,7 @@ namespace BS.Shared.DataContracts.Compact
return tfn;
}
public long EntityOid => PersonOid;
}
}

View File

@@ -133,5 +133,15 @@ namespace BS.Shared.Extensions
public static string SenderAddressToString(this IInvoiceBase invoiceBase)
=> $"{invoiceBase.SenderStreet}, {invoiceBase.SenderTown}, {invoiceBase.SenderPostCode}";
public static string FirstCharToUpper(this string input)
{
if(string.IsNullOrWhiteSpace(input))
{
return input;
}
return char.ToUpper(input[0]) + input.Substring(1);
}
}
}