migration to new Repo
This commit is contained in:
commit
405c7b3a6d
363
.gitignore
vendored
Normal file
363
.gitignore
vendored
Normal file
@ -0,0 +1,363 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
11
LSFE.Domain/Contracts/Account/INavigationService.cs
Normal file
11
LSFE.Domain/Contracts/Account/INavigationService.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using LSFE.Infrastructure.Dto.Account;
|
||||
|
||||
namespace LSFE.Domain.Contracts
|
||||
{
|
||||
public interface INavigationService
|
||||
{
|
||||
Task<NavigationResponseDto> GetUserNavigationAsync(string userId);
|
||||
Task<List<NavigationItemDto>> GetNavigationByRoleIdsAsync(List<string> roleIds);
|
||||
Task<List<NavigationItemDto>> GetNavigationByRoleAsync(string roleId);
|
||||
}
|
||||
}
|
||||
24
LSFE.Domain/Contracts/Doctor/IDoctorRepo.cs
Normal file
24
LSFE.Domain/Contracts/Doctor/IDoctorRepo.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using LSFE.Infrastructure.Dto.Doctor;
|
||||
using LSFE.Infrastructure.Entities.Doctor;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace LSFE.Domain.Contracts.Doctor
|
||||
{
|
||||
public interface IDoctorRepo
|
||||
{
|
||||
Task<Response> SoftDeleteAsync(int doctorId);
|
||||
Task<List<DoctorDto>> GetDoctor(DoctorDto dto);
|
||||
Task<List<DoctorDto>> GetMyDoctor(DoctorDto dto);
|
||||
Task<List<Infrastructure.Entities.Doctor.Doctor>> GetAllDoctor(DoctorDto dto);
|
||||
Task<List<DoctorCoverageToday>> GetDoctorCoverageToday(DoctorDto dto);
|
||||
Task<Response> PostPutDoctor(DoctorDto dto);
|
||||
Task<Response> ApproveDoctor(int doctorId, byte newStatus,string userName);
|
||||
Task<List<ForDoctorApprovalDto>> GetPendingApprovals(DoctorDto doctor);
|
||||
}
|
||||
}
|
||||
28
LSFE.Domain/Contracts/IAccountService.cs
Normal file
28
LSFE.Domain/Contracts/IAccountService.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using LSFE.Infrastructure.Dto;
|
||||
using LSFE.Infrastructure.Entities;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Contracts
|
||||
{
|
||||
public interface IAccountService
|
||||
{
|
||||
Task<List<RoleDto>> GetUserRole();
|
||||
Task<List<UserWithRoleDto>> GetUsers(string company);
|
||||
Task<List<Departments>> GetDepartment();
|
||||
Task<AppsUsers?> GetUserById(string userId);
|
||||
Task<string> CreateToken(AppsUsers user);
|
||||
Task<Response> PostPutDepartmentAsync(Departments dto);
|
||||
Task<Response> RegisterUserAsync(RegisterUserDto dto);
|
||||
Task<Response> UpdateUserAsync(UpdateUserDto dto);
|
||||
Task<Response> AddUserToRoleAsync(string userId, string roleName);
|
||||
Task<Response> AddClaimToUserAsync(string userId, Claim claim);
|
||||
}
|
||||
|
||||
}
|
||||
17
LSFE.Domain/Contracts/IGenericRepository.cs
Normal file
17
LSFE.Domain/Contracts/IGenericRepository.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Contracts
|
||||
{
|
||||
public interface IGenericRepository<T> where T : class
|
||||
{
|
||||
Task<T?> GetByIdAsync(int id);
|
||||
Task<IEnumerable<T>> GetAllAsync();
|
||||
Task AddAsync(T entity);
|
||||
void Update(T entity);
|
||||
void Delete(T entity);
|
||||
}
|
||||
}
|
||||
46
LSFE.Domain/Contracts/IUnitOfWork.cs
Normal file
46
LSFE.Domain/Contracts/IUnitOfWork.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using LSFE.Domain.Contracts.Doctor;
|
||||
using LSFE.Domain.Contracts.Maintenance;
|
||||
using LSFE.Domain.Contracts.Planning;
|
||||
using LSFE.Domain.Contracts.Reports;
|
||||
using LSFE.Domain.Contracts.TimeKeeping;
|
||||
using LSFE.Domain.Services;
|
||||
using LSFE.Domain.Services.TimeKeeping;
|
||||
using LSFE.Infrastructure.Entities.Admin;
|
||||
using LSFE.Infrastructure.Entities.Doctor;
|
||||
using LSFE.Infrastructure.Entities.Maintenance;
|
||||
using LSFE.Infrastructure.Entities.Planning;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Contracts
|
||||
{
|
||||
public interface IUnitOfWork : IDisposable
|
||||
{
|
||||
IErrorLogger ErrorLogger { get; }
|
||||
IDoctorRepo Doctors { get; }
|
||||
IAttendanceRepo Attendances { get; }
|
||||
IPlanningRepo Planning { get; }
|
||||
IReportsRepo Reports { get; }
|
||||
IMaintenanceRepo<Areas> Areas { get; }
|
||||
IMaintenanceRepo<Districts> Districts { get; }
|
||||
IMaintenanceRepo<Divisions> Divisions { get; }
|
||||
IMaintenanceRepo<Territories> Territories { get; }
|
||||
IMaintenanceRepo<InstitutionCategory> InstitutionCategories { get; }
|
||||
IMaintenanceRepo<Institutions> Institutions { get; }
|
||||
IMaintenanceRepo<Products> Products { get; }
|
||||
IMaintenanceRepo<Specialization> Specializations { get; }
|
||||
IMaintenanceRepo<Inventory> Inventory { get; }
|
||||
IMaintenanceRepo<InventoryTransaction> InventoryTransactions { get; }
|
||||
IMaintenanceRepo<ProductTransaction> ProductTransactions { get; }
|
||||
IMaintenanceRepo<MRRawPlan> MRRawPlans { get; }
|
||||
IMaintenanceRepo<CatchUpCall> CatchUpCalls { get; }
|
||||
IMaintenanceRepo<MRPlanDetail> MRPlanDetails { get; }
|
||||
IMaintenanceRepo<MRPlanHeader> MRPlanHeaders { get; }
|
||||
IMaintenanceRepo<UserDevice> UserDevices { get; }
|
||||
|
||||
Task<int> SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
25
LSFE.Domain/Contracts/Maintenance/IMaintenanceRepo.cs
Normal file
25
LSFE.Domain/Contracts/Maintenance/IMaintenanceRepo.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using LSFE.Infrastructure.Model;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Contracts.Maintenance
|
||||
{
|
||||
public interface IMaintenanceRepo<TEntity> where TEntity : class
|
||||
{
|
||||
Task<List<TEntity>> GetAllAsync(params Expression<Func<TEntity, object>>[] includeProperties);
|
||||
Task<TEntity?> GetByIdAsync(object id);
|
||||
Task<TEntity?> FindAsync(Expression<Func<TEntity, bool>> predicate);
|
||||
Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate);
|
||||
IQueryable<TEntity> Query();
|
||||
Task<IEnumerable<TEntity>> FindAllAsync(Expression<Func<TEntity, bool>> predicate);
|
||||
Task AddRangeAsync(IEnumerable<TEntity> entities);
|
||||
Task<Response> AddOrUpdateAsync(TEntity entity);
|
||||
Task<Response> SoftDeleteAsync(int id);
|
||||
Task<Response> DeleteAsync(Guid id);
|
||||
}
|
||||
}
|
||||
33
LSFE.Domain/Contracts/Planning/IPlanningRepo.cs
Normal file
33
LSFE.Domain/Contracts/Planning/IPlanningRepo.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using LSFE.Infrastructure.Dto;
|
||||
using LSFE.Infrastructure.Dto.Doctor;
|
||||
using LSFE.Infrastructure.Dto.Planning;
|
||||
using LSFE.Infrastructure.Entities.Maintenance;
|
||||
using LSFE.Infrastructure.Entities.Planning;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Contracts.Planning
|
||||
{
|
||||
public interface IPlanningRepo
|
||||
{
|
||||
Task<Response> SoftDeleteAsync(int doctorId);
|
||||
Task<List<MRRawPlan>> GetPlan(PlanningDto dto);
|
||||
Task<List<DoctorById>> GetDoctorById(DoctorDto dto);
|
||||
Task<List<DoctorWithPlan>> GetDoctorWithPlan(DoctorDto dto);
|
||||
Task<List<PendingApproval>> GetPendingApprovals(PlanningDto dto);
|
||||
Task<List<MRRawPlan>> GetAllMissReschedule(CredentialDto dto);
|
||||
Task<List<MRRawPlan>> GetAllAppointmentToday(CredentialDto dto);
|
||||
Task<List<MRRawPlansDto>> GetMRRawPlans(PlanByUserIdRequest dto);
|
||||
|
||||
Task<List<InventoryTransaction>> GetSampleTransaction(Guid mrRawPlanId);
|
||||
Task<List<ProductTransaction>> GetProductTransaction(Guid mrRawPlanId);
|
||||
|
||||
Task<Response> PostPutPlan(PlanningDto dto);
|
||||
Task<Response> CopyMonthlyMRRawPlans(PlanningDto dto);
|
||||
Task<Response> ApprovePlan(ApprovePlanDto dto);
|
||||
}
|
||||
}
|
||||
19
LSFE.Domain/Contracts/Reports/IReportsRepo.cs
Normal file
19
LSFE.Domain/Contracts/Reports/IReportsRepo.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using LSFE.Infrastructure.Dto.Reports;
|
||||
using LSFE.Infrastructure.Entities.Reports;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Contracts.Reports
|
||||
{
|
||||
public interface IReportsRepo
|
||||
{
|
||||
Task<List<CallRateReport>> GetCallRateReport(ReportsDto dto);
|
||||
Task<List<CallReachReport>> GetCallReachReport(ReportsDto dto);
|
||||
Task<List<CatchUpCallReport>> GetCatchUpCallReport(ReportsDto dto);
|
||||
Task<List<ActualCoverageReport>> GetActualCoverageReport(ReportsDto dto);
|
||||
Task<bool> PutMissedCall(ReportsDto dto);
|
||||
}
|
||||
}
|
||||
21
LSFE.Domain/Contracts/TimeKeeping/IAttendanceRepo.cs
Normal file
21
LSFE.Domain/Contracts/TimeKeeping/IAttendanceRepo.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using DocumentFormat.OpenXml.Office2010.ExcelAc;
|
||||
using LSFE.Infrastructure.Dto;
|
||||
using LSFE.Infrastructure.Dto.TimeKeeping;
|
||||
using LSFE.Infrastructure.Entities.TimeKeeping;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Contracts.TimeKeeping
|
||||
{
|
||||
public interface IAttendanceRepo
|
||||
{
|
||||
Task<List<Infrastructure.Entities.TimeKeeping.AttendanceDto>>
|
||||
GetAttendance(Infrastructure.Dto.TimeKeeping.AttendanceDto dto);
|
||||
Task<List<Attendance>> GetAttendanceToday(CredentialDto dto);
|
||||
Task<Response> PostPutAttendance(Infrastructure.Dto.TimeKeeping.AttendanceDto dto);
|
||||
}
|
||||
}
|
||||
47
LSFE.Domain/Helpers/ContentTypeHelper.cs
Normal file
47
LSFE.Domain/Helpers/ContentTypeHelper.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Helpers
|
||||
{
|
||||
public static class ContentTypeHelper
|
||||
{
|
||||
public static string GetContentType(string fileName)
|
||||
{
|
||||
var extension = Path.GetExtension(fileName).ToLowerInvariant();
|
||||
return extension switch
|
||||
{
|
||||
".pdf" => "application/pdf",
|
||||
".doc" => "application/msword",
|
||||
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
".xls" => "application/vnd.ms-excel",
|
||||
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
".png" => "image/png",
|
||||
".jpg" or ".jpeg" => "image/jpeg",
|
||||
".gif" => "image/gif",
|
||||
".txt" => "text/plain",
|
||||
_ => "application/octet-stream"
|
||||
};
|
||||
}
|
||||
public static string ValidateFile(string filePath, string uploadsPath)
|
||||
{
|
||||
var message = "";
|
||||
// Security: Prevent directory traversal attacks
|
||||
var fullPath = Path.GetFullPath(filePath);
|
||||
|
||||
if (!fullPath.StartsWith(Path.GetFullPath(uploadsPath)))
|
||||
{
|
||||
message="Invalid file path";
|
||||
}
|
||||
|
||||
if (!System.IO.File.Exists(filePath))
|
||||
{
|
||||
message="File not found";
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
LSFE.Domain/Helpers/ExcelTemplateValidator.cs
Normal file
40
LSFE.Domain/Helpers/ExcelTemplateValidator.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using ClosedXML.Excel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Helpers
|
||||
{
|
||||
public class ExcelTemplateValidator
|
||||
{
|
||||
private static readonly string[] REQUIRED_HEADERS = new[]
|
||||
{
|
||||
"BRAND", "January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December",
|
||||
"Year", "Medrep_Name"
|
||||
};
|
||||
|
||||
public static (bool isValid, List<string> errors) ValidateHeaders(IXLWorksheet worksheet)
|
||||
{
|
||||
var errors = new List<string>();
|
||||
var isValid = true;
|
||||
|
||||
for (int col = 1; col <= REQUIRED_HEADERS.Length; col++)
|
||||
{
|
||||
var headerValue = worksheet.Cell(1, col).Value.ToString()?.Trim();
|
||||
var expectedHeader = REQUIRED_HEADERS[col - 1];
|
||||
|
||||
if (string.IsNullOrEmpty(headerValue) ||
|
||||
!headerValue.Equals(expectedHeader, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
errors.Add($"Column {col}: Expected '{expectedHeader}' but found '{headerValue}'");
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return (isValid, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
LSFE.Domain/LSFE.Domain.csproj
Normal file
19
LSFE.Domain/LSFE.Domain.csproj
Normal file
@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ClosedXML" Version="0.105.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.20" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.20" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LSFE.Infrastructure\LSFE.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
170
LSFE.Domain/Services/Account/NavigationService.cs
Normal file
170
LSFE.Domain/Services/Account/NavigationService.cs
Normal file
@ -0,0 +1,170 @@
|
||||
using LSFE.Domain.Contracts;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using LSFE.Infrastructure.Dto.Account;
|
||||
using LSFE.Infrastructure.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Data;
|
||||
|
||||
namespace LSFE.Domain.Services.Account
|
||||
{
|
||||
public class NavigationService : INavigationService
|
||||
{
|
||||
private readonly LSFEDbContext _context;
|
||||
private readonly UserManager<AppsUsers> _userManager;
|
||||
|
||||
public NavigationService(LSFEDbContext context, UserManager<AppsUsers> userManager)
|
||||
{
|
||||
_context = context;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task<NavigationResponseDto> GetUserNavigationAsync(string userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return new NavigationResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "User not found or inactive",
|
||||
Data = new List<NavigationItemDto>()
|
||||
};
|
||||
}
|
||||
|
||||
// Get user roles using UserManager
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
|
||||
if (!roles.Any())
|
||||
{
|
||||
return new NavigationResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "User has no assigned roles",
|
||||
Data = new List<NavigationItemDto>()
|
||||
};
|
||||
}
|
||||
|
||||
// Get role IDs from role names
|
||||
var roleIds = await _context.Roles
|
||||
.Where(r => roles.Contains(r.Name))
|
||||
.Select(r => r.Id)
|
||||
.ToListAsync();
|
||||
|
||||
// Get navigation for all user roles
|
||||
var navigationItems = await GetNavigationByRoleIdsAsync(roleIds);
|
||||
|
||||
return new NavigationResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Data = navigationItems,
|
||||
Message = "Navigation retrieved successfully"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new NavigationResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Error retrieving navigation: {ex.Message}",
|
||||
Data = new List<NavigationItemDto>()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<NavigationItemDto>> GetNavigationByRoleIdsAsync(List<string> roleIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
var roleIdsParam = string.Join(",", roleIds);
|
||||
|
||||
using (var command = _context.Database.GetDbConnection().CreateCommand())
|
||||
{
|
||||
command.CommandText = "GetUserNavigation";
|
||||
command.CommandType = CommandType.StoredProcedure;
|
||||
command.Parameters.Add(new SqlParameter("@RoleIds", roleIdsParam));
|
||||
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
|
||||
using (var result = await command.ExecuteReaderAsync())
|
||||
{
|
||||
var rootModules = new List<NavigationItemDto>();
|
||||
var childModules = new List<NavigationItemDto>();
|
||||
|
||||
// Read root modules
|
||||
while (await result.ReadAsync())
|
||||
{
|
||||
rootModules.Add(new NavigationItemDto
|
||||
{
|
||||
Id = result["Id"].ToString(),
|
||||
Label = result["Label"].ToString(),
|
||||
Icon = result["Icon"].ToString(),
|
||||
Route = result["Route"].ToString(),
|
||||
Badge = result["Badge"]?.ToString(),
|
||||
Permissions = new PermissionsDto
|
||||
{
|
||||
CanView = Convert.ToBoolean(result["CanView"]),
|
||||
CanCreate = Convert.ToBoolean(result["CanCreate"]),
|
||||
CanEdit = Convert.ToBoolean(result["CanEdit"]),
|
||||
CanDelete = Convert.ToBoolean(result["CanDelete"])
|
||||
},
|
||||
Children = new List<NavigationItemDto>()
|
||||
});
|
||||
}
|
||||
|
||||
// Read child modules
|
||||
if (await result.NextResultAsync())
|
||||
{
|
||||
while (await result.ReadAsync())
|
||||
{
|
||||
childModules.Add(new NavigationItemDto
|
||||
{
|
||||
Id = result["Id"].ToString(),
|
||||
Label = result["Label"].ToString(),
|
||||
Icon = result["Icon"].ToString(),
|
||||
Route = result["Route"].ToString(),
|
||||
ParentModuleId = result["ParentModuleId"] != DBNull.Value
|
||||
? Convert.ToInt32(result["ParentModuleId"])
|
||||
: (int?)null,
|
||||
Permissions = new PermissionsDto
|
||||
{
|
||||
CanView = Convert.ToBoolean(result["CanView"]),
|
||||
CanCreate = Convert.ToBoolean(result["CanCreate"]),
|
||||
CanEdit = Convert.ToBoolean(result["CanEdit"]),
|
||||
CanDelete = Convert.ToBoolean(result["CanDelete"])
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Attach children to parents
|
||||
foreach (var parent in rootModules)
|
||||
{
|
||||
var children = childModules
|
||||
.Where(c => c.ParentModuleId.ToString() == parent.Id)
|
||||
.ToList();
|
||||
|
||||
parent.Children = children.Any() ? children : null;
|
||||
}
|
||||
|
||||
return rootModules;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error in GetNavigationByRoleIdsAsync: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
// Optional: Add method to get single role navigation (for backwards compatibility)
|
||||
public async Task<List<NavigationItemDto>> GetNavigationByRoleAsync(string roleId)
|
||||
{
|
||||
return await GetNavigationByRoleIdsAsync(new List<string> { roleId });
|
||||
}
|
||||
}
|
||||
}
|
||||
459
LSFE.Domain/Services/AccountService.cs
Normal file
459
LSFE.Domain/Services/AccountService.cs
Normal file
@ -0,0 +1,459 @@
|
||||
using LSFE.Domain.Contracts;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using LSFE.Infrastructure.Dto;
|
||||
using LSFE.Infrastructure.Entities;
|
||||
using LSFE.Infrastructure.Entities.Auth;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Services
|
||||
{
|
||||
public class AccountService : IAccountService
|
||||
{
|
||||
private readonly UserManager<AppsUsers> _userManager;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly RoleManager<Role> _roleManager;
|
||||
private readonly LSFEDbContext _context;
|
||||
public AccountService(UserManager<AppsUsers> userManager, IConfiguration configuration,
|
||||
RoleManager<Role> roleManager , LSFEDbContext context )
|
||||
{
|
||||
_userManager = userManager;
|
||||
_configuration = configuration;
|
||||
_roleManager = roleManager;
|
||||
_context = context;
|
||||
}
|
||||
public async Task<Response> AddUserToRoleAsync(string userId, string roleName)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = "User not found",
|
||||
MessCode = 0
|
||||
};
|
||||
|
||||
// Ensure role exists
|
||||
if (!await _roleManager.RoleExistsAsync(roleName))
|
||||
{
|
||||
var roleResult = await _roleManager.CreateAsync(new Role());
|
||||
if (!roleResult.Succeeded)
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = roleResult.ToString(),
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
await _userManager.AddToRoleAsync(user, roleName);
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = "Add user role successfully!",
|
||||
MessCode = 1
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<Response> AddClaimToUserAsync(string userId, Claim claim)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = "User not found",
|
||||
MessCode = 0
|
||||
};
|
||||
|
||||
await _userManager.AddClaimAsync(user, claim);
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = "Success",
|
||||
MessCode = 1
|
||||
};
|
||||
}
|
||||
public async Task<Response> RegisterUserAsync(RegisterUserDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 🔍 1. Validate required fields
|
||||
if (string.IsNullOrWhiteSpace(dto.UserName) ||
|
||||
string.IsNullOrWhiteSpace(dto.Email) ||
|
||||
string.IsNullOrWhiteSpace(dto.Password))
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = "Username, Email, and Password are required.",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
// 🧩 2. Check if username or email already exists
|
||||
var existingUserByName = await _userManager.FindByNameAsync(dto.UserName);
|
||||
if (existingUserByName != null)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Username '{dto.UserName}' is already taken.",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
var existingUserByEmail = await _userManager.FindByEmailAsync(dto.Email);
|
||||
if (existingUserByEmail != null)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Email '{dto.Email}' is already registered.",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
// 🏢 3. Optional: Validate Department
|
||||
if (dto.DepartmentId.HasValue)
|
||||
{
|
||||
var departmentExists = await _context.Departments
|
||||
.AnyAsync(d => d.DepartmentId == dto.DepartmentId && d.IsActive);
|
||||
|
||||
if (!departmentExists)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = "Invalid Department selected.",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 🧍♂️ 4. Create new user
|
||||
var user = new AppsUsers
|
||||
{
|
||||
UserName = dto.UserName,
|
||||
Email = dto.Email,
|
||||
FullName = dto.FullName,
|
||||
EmployeeId = dto.EmployeeId,
|
||||
Company = dto.Company,
|
||||
DepartmentId = dto.DepartmentId,
|
||||
Address = dto.Address,
|
||||
PhoneNumber = dto.PhoneNumber,
|
||||
CreatedDate = DateTime.UtcNow,
|
||||
UpdatedDate = DateTime.UtcNow,
|
||||
CreatedBy = dto.UpdatedBy,
|
||||
UpdatedBy = dto.UpdatedBy,
|
||||
EmailConfirmed = true // optional: mark as verified by admin
|
||||
};
|
||||
|
||||
var createResult = await _userManager.CreateAsync(user, dto.Password);
|
||||
if (!createResult.Succeeded)
|
||||
{
|
||||
var errors = string.Join("; ", createResult.Errors.Select(e => e.Description));
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Failed to create user: {errors}",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
// 🛡️ 5. Assign Role if provided
|
||||
if (!string.IsNullOrWhiteSpace(dto.RoleId))
|
||||
{
|
||||
var role = await _roleManager.FindByIdAsync(dto.RoleId);
|
||||
if (role == null)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Role with ID '{dto.RoleId}' does not exist.",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
await _userManager.AddToRoleAsync(user, role.Name);
|
||||
|
||||
// optional: add user claim for role
|
||||
await _userManager.AddClaimAsync(user, new Claim("RoleName", role.Name));
|
||||
}
|
||||
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = "User registered successfully!",
|
||||
MessCode = 1
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Unexpected error: {ex.Message}",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
public async Task<string> CreateToken(AppsUsers user)
|
||||
{
|
||||
var authClaims = await BuildClaims(user);
|
||||
|
||||
var authSigningKey = new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: _configuration["JWT:ValidIssuer"],
|
||||
audience: _configuration["JWT:ValidAudience"],
|
||||
expires: DateTime.UtcNow.AddMinutes(30),
|
||||
claims: authClaims,
|
||||
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
|
||||
);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
private async Task<List<Claim>> BuildClaims(AppsUsers user)
|
||||
{
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.Name, user.UserName),
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
||||
new Claim("FullName", user.FullName ?? ""),
|
||||
new Claim("Company", user.Company ?? ""),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
};
|
||||
|
||||
claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));
|
||||
return claims;
|
||||
}
|
||||
|
||||
public async Task<AppsUsers?> GetUserById(string userId)
|
||||
{
|
||||
return await _userManager.Users.FirstOrDefaultAsync(u => u.Id == userId);
|
||||
}
|
||||
|
||||
public async Task<Response> UpdateUserAsync(UpdateUserDto dto)
|
||||
{
|
||||
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(dto.Id.ToString());
|
||||
if (user == null)
|
||||
return new Response { Success = false, Message = "User not found.", MessCode = 0 };
|
||||
|
||||
// --- Update basic info ---
|
||||
user.Email = dto.Email;
|
||||
user.FullName = dto.FullName;
|
||||
user.EmployeeId = dto.EmployeeId;
|
||||
user.Company = dto.Company;
|
||||
user.DepartmentId = dto.DepartmentId;
|
||||
user.Address = dto.Address;
|
||||
user.PhoneNumber = dto.PhoneNumber;
|
||||
user.UpdatedBy = dto.UpdatedBy;
|
||||
user.UpdatedDate = DateTime.UtcNow;
|
||||
|
||||
// --- Update password (if provided) ---
|
||||
if (!string.IsNullOrWhiteSpace(dto.Password))
|
||||
{
|
||||
var passwordHasher = new PasswordHasher<AppsUsers>();
|
||||
user.PasswordHash = passwordHasher.HashPassword(user, dto.Password);
|
||||
}
|
||||
|
||||
var updateResult = await _userManager.UpdateAsync(user);
|
||||
if (!updateResult.Succeeded)
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = string.Join(", ", updateResult.Errors.Select(e => e.Description)),
|
||||
MessCode = 0
|
||||
};
|
||||
|
||||
// --- Update roles ---
|
||||
if (!string.IsNullOrEmpty(dto.RoleName))
|
||||
{
|
||||
var currentRoles = await _userManager.GetRolesAsync(user);
|
||||
if (currentRoles.Any())
|
||||
await _userManager.RemoveFromRolesAsync(user, currentRoles);
|
||||
|
||||
if (await _roleManager.RoleExistsAsync(dto.RoleName))
|
||||
await _userManager.AddToRoleAsync(user, dto.RoleName);
|
||||
else
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Role '{dto.RoleName}' not found.",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
// --- Update claims ---
|
||||
if (!string.IsNullOrEmpty(dto.RoleName))
|
||||
{
|
||||
var existingClaims = await _userManager.GetClaimsAsync(user);
|
||||
var existingRoleClaim = existingClaims.FirstOrDefault(c => c.Type == "Role");
|
||||
if (existingRoleClaim != null)
|
||||
await _userManager.RemoveClaimAsync(user, existingRoleClaim);
|
||||
|
||||
await _userManager.AddClaimAsync(user, new Claim("Role", dto.RoleName));
|
||||
}
|
||||
|
||||
await transaction.CommitAsync();
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = "User updated successfully!",
|
||||
MessCode = 1
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Updating user failed: {ex.Message}",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<UserWithRoleDto>> GetUsers(string company)
|
||||
{
|
||||
try
|
||||
{
|
||||
var users = await _userManager.Users
|
||||
.Where(u=>u.Company== company)
|
||||
.Include(u => u.Department)
|
||||
.ToListAsync();
|
||||
|
||||
var roles = await _roleManager.Roles.ToListAsync(); // cache all roles once
|
||||
|
||||
var result = new List<UserWithRoleDto>();
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
var roleNames = await _userManager.GetRolesAsync(user);
|
||||
|
||||
// Map role names to RoleId + RoleName
|
||||
var userRoles = roles
|
||||
.Where(r => roleNames.Contains(r.Name))
|
||||
.Select(r => new RoleDto
|
||||
{
|
||||
RoleId = r.Id,
|
||||
RoleName = r.Name!
|
||||
})
|
||||
.ToList();
|
||||
|
||||
result.Add(new UserWithRoleDto
|
||||
{
|
||||
Id = user.Id,
|
||||
EmployeeId = user.EmployeeId,
|
||||
Company = user.Company,
|
||||
Address = user.Address,
|
||||
UpdatedDate = user.UpdatedDate,
|
||||
UpdatedBy = user.UpdatedBy,
|
||||
Email = user.Email,
|
||||
FullName = user.FullName,
|
||||
RoleName = userRoles[0].RoleName,
|
||||
CreatedBy = user.CreatedBy,
|
||||
CreatedDate = user.CreatedDate,
|
||||
UserName = user.UserName,
|
||||
DepartmentId = user.DepartmentId,
|
||||
RoleId = userRoles[0].RoleId,
|
||||
Department = user.Department,
|
||||
AccessFailedCount = user.AccessFailedCount,
|
||||
PhoneNumber = user.PhoneNumber,
|
||||
LockoutEnd = user.LockoutEnd,
|
||||
LockoutEnabled = user.LockoutEnabled,
|
||||
UserRoles = userRoles
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.ToString();
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<Response> PostPutDepartmentAsync(Departments dto)
|
||||
{
|
||||
var existing = await _context.Departments
|
||||
.FirstOrDefaultAsync(d => d.DepartmentId == dto.DepartmentId);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
var duplicate = await _context.Departments
|
||||
.AnyAsync(d => d.DepartmentName == dto.DepartmentName
|
||||
|| d.DepartmentCode == dto.DepartmentCode);
|
||||
|
||||
if (duplicate)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = "Department name or code already exists.",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
_context.Departments.Add(dto);
|
||||
await _context.SaveChangesAsync();
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = "Department created successfully.",
|
||||
MessCode = 1
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
existing.DepartmentName = dto.DepartmentName;
|
||||
existing.DepartmentCode = dto.DepartmentCode;
|
||||
existing.IsActive = dto.IsActive;
|
||||
|
||||
_context.Departments.Update(existing);
|
||||
await _context.SaveChangesAsync();
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = "Department updated successfully.",
|
||||
MessCode = 1
|
||||
};
|
||||
}
|
||||
}
|
||||
public async Task<List<Departments>> GetDepartment()
|
||||
{
|
||||
return await _context.Departments.ToListAsync();
|
||||
}
|
||||
public async Task<List<RoleDto>> GetUserRole()
|
||||
{
|
||||
return await _roleManager.Roles
|
||||
.Select(r => new RoleDto
|
||||
{
|
||||
RoleId = r.Id,
|
||||
RoleName = r.Name
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
489
LSFE.Domain/Services/Doctor/DoctorRepo.cs
Normal file
489
LSFE.Domain/Services/Doctor/DoctorRepo.cs
Normal file
@ -0,0 +1,489 @@
|
||||
using LSFE.Domain.Contracts.Doctor;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using LSFE.Infrastructure.Dto.Doctor;
|
||||
using LSFE.Infrastructure.Entities.Doctor;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace LSFE.Domain.Services.Doctor
|
||||
{
|
||||
public class DoctorRepo : GenericRepository<Infrastructure.Entities.Doctor.Doctor>, IDoctorRepo
|
||||
{
|
||||
private readonly LSFEDbContext _context;
|
||||
public DoctorRepo(LSFEDbContext context) : base(context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Response> SoftDeleteAsync(int doctorId)
|
||||
{
|
||||
var response = new Response();
|
||||
|
||||
try
|
||||
{
|
||||
var existingDoctor = await GetByIdAsync(doctorId);
|
||||
|
||||
if (existingDoctor == null)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = $"Doctor with ID {doctorId} not found.";
|
||||
response.MessCode = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
existingDoctor.IsActive = false;
|
||||
|
||||
Update(existingDoctor);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
response.Success = true;
|
||||
response.Data = existingDoctor;
|
||||
response.Message = "Doctor deactivated successfully.";
|
||||
response.MessCode = 3; // code for delete/soft delete
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = ex.Message;
|
||||
response.MessCode = 0;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
public async Task<List<Infrastructure.Entities.Doctor.Doctor>> GetAllDoctor(DoctorDto dto)
|
||||
{
|
||||
var allItems = await _context.Doctors
|
||||
.FromSqlRaw("EXEC GetAllDoctor @UserId",
|
||||
new SqlParameter("@UserId", dto.UserId))
|
||||
.ToListAsync();
|
||||
|
||||
return allItems ?? new List<Infrastructure.Entities.Doctor.Doctor>();
|
||||
}
|
||||
public async Task<List<DoctorDto>> GetDoctor(DoctorDto dto)
|
||||
{
|
||||
var query =
|
||||
from d in _context.Doctors
|
||||
join i in _context.Institutions
|
||||
on d.InstitutionId equals i.InstitutionId into di
|
||||
from i in di.DefaultIfEmpty()
|
||||
join s in _context.Specializations
|
||||
on d.SpecializationId equals s.SpecializationId into si
|
||||
from s in si.DefaultIfEmpty()
|
||||
join dis in _context.Districts
|
||||
on d.DistrictId equals dis.DistrictId into dist
|
||||
from dis in dist.DefaultIfEmpty()
|
||||
join fda in _context.ForDoctorApprovals
|
||||
on d.DoctorId equals fda.DoctorId
|
||||
join u in _context.Users
|
||||
on d.UserId equals u.Id
|
||||
where d.UserId == dto.UserId
|
||||
&& (dto.DoctorId == 0 || d.DoctorId == dto.DoctorId)
|
||||
select new DoctorDto
|
||||
{
|
||||
DoctorId = d.DoctorId,
|
||||
FirstName = d.FirstName,
|
||||
MiddleInitial = d.MiddleInitial,
|
||||
LastName = d.LastName,
|
||||
EmailAddress = d.EmailAddress,
|
||||
PhoneNo = d.PhoneNo,
|
||||
SpecializationId = d.SpecializationId,
|
||||
LicenseNo = d.LicenseNo,
|
||||
Address = d.Address,
|
||||
BirthDate = d.BirthDate,
|
||||
MaxVisit = d.MaxVisit,
|
||||
MedRepName=u.UserName,
|
||||
Status = fda.Status,
|
||||
IsTerritorial= d.IsTerritorial,
|
||||
InstitutionId = fda.InstitutionId,
|
||||
InstitutionName = i != null ? i.InstitutionName : null,
|
||||
SpecializationName = s != null ? s.SpecializationName : null,
|
||||
DistrictId = fda != null ? dis.DistrictId : 0,
|
||||
DistrictName = dis != null ? dis.DistrictName : null
|
||||
};
|
||||
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<DoctorDto>> GetMyDoctor(DoctorDto dto)
|
||||
{
|
||||
var query =
|
||||
from d in _context.Doctors
|
||||
join i in _context.Institutions
|
||||
on d.InstitutionId equals i.InstitutionId into di
|
||||
from i in di.DefaultIfEmpty()
|
||||
join s in _context.Specializations
|
||||
on d.SpecializationId equals s.SpecializationId into si
|
||||
from s in si.DefaultIfEmpty()
|
||||
join dis in _context.Districts
|
||||
on d.DistrictId equals dis.DistrictId into dist
|
||||
from dis in dist.DefaultIfEmpty()
|
||||
join u in _context.Users
|
||||
on d.UserId equals u.Id
|
||||
where d.UserId == dto.UserId
|
||||
select new DoctorDto
|
||||
{
|
||||
DoctorId = d.DoctorId,
|
||||
FirstName = d.FirstName,
|
||||
MiddleInitial = d.MiddleInitial,
|
||||
LastName = d.LastName,
|
||||
EmailAddress = d.EmailAddress,
|
||||
PhoneNo = d.PhoneNo,
|
||||
SpecializationId = d.SpecializationId,
|
||||
LicenseNo = d.LicenseNo,
|
||||
Address = d.Address,
|
||||
BirthDate = d.BirthDate,
|
||||
MaxVisit = d.MaxVisit,
|
||||
Status = d.Status,
|
||||
MedRepName = u.UserName,
|
||||
IsTerritorial = d.IsTerritorial,
|
||||
InstitutionId = d.InstitutionId,
|
||||
InstitutionName = i != null ? i.InstitutionName : null,
|
||||
SpecializationName = s != null ? s.SpecializationName : null,
|
||||
DistrictId = dis != null ? dis.DistrictId : 0,
|
||||
DistrictName = dis != null ? dis.DistrictName : null
|
||||
};
|
||||
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
public async Task<Response> PostPutDoctor(DoctorDto dto)
|
||||
{
|
||||
var response = new Response();
|
||||
|
||||
try
|
||||
{
|
||||
Infrastructure.Entities.Doctor.Doctor? existingDoctor = null;
|
||||
ForDoctorApproval? existingApproval = null;
|
||||
|
||||
// Check if DoctorId exists
|
||||
if (dto.DoctorId > 0)
|
||||
{
|
||||
existingDoctor = await GetByIdAsync(dto.DoctorId);
|
||||
existingApproval = await _context.ForDoctorApprovals
|
||||
.FirstOrDefaultAsync(x => x.DoctorId == dto.DoctorId);
|
||||
}
|
||||
|
||||
int doctorId = dto.DoctorId;
|
||||
|
||||
// Step 1: Insert/Update in main Doctors table first to get DoctorId
|
||||
if (existingDoctor == null)
|
||||
{
|
||||
// CREATE new doctor in main table with Status = 1 (For DSM Approval)
|
||||
var newDoctor = MapToNewDoctor(dto);
|
||||
newDoctor.Status = 1; // Always start with "For DSM Approval"
|
||||
newDoctor.IsActive = false; // Not active until approved
|
||||
|
||||
await AddAsync(newDoctor);
|
||||
await _context.SaveChangesAsync(); // Save to get DoctorId
|
||||
|
||||
doctorId = newDoctor.DoctorId; // Get the auto-generated DoctorId
|
||||
}
|
||||
else
|
||||
{
|
||||
// UPDATE existing doctor - keep current status, don't change it here
|
||||
// The doctor table will only be updated when status = 3 (Approved)
|
||||
// For now, we just ensure it exists
|
||||
doctorId = existingDoctor.DoctorId;
|
||||
}
|
||||
|
||||
// Step 2: Insert/Update in ForDoctorApproval staging table
|
||||
if (existingApproval == null)
|
||||
{
|
||||
// INSERT into ForDoctorApproval
|
||||
var newApproval = new ForDoctorApproval
|
||||
{
|
||||
ForDoctorApprovalId = Guid.NewGuid(),
|
||||
DoctorId = doctorId, // Use the DoctorId from main table
|
||||
FirstName = dto.FirstName,
|
||||
MiddleInitial = dto.MiddleInitial,
|
||||
LastName = dto.LastName,
|
||||
MaxVisit = dto.MaxVisit,
|
||||
SpecializationId = dto.SpecializationId,
|
||||
UserId = dto.UserId,
|
||||
StageLevelId = dto.StageLevelId,
|
||||
DistrictId = dto.DistrictId,
|
||||
BirthDate = dto.BirthDate,
|
||||
EmailAddress = dto.EmailAddress,
|
||||
Address = dto.Address,
|
||||
PhoneNo = dto.PhoneNo,
|
||||
LicenseNo = dto.LicenseNo,
|
||||
InstitutionId = dto.InstitutionId,
|
||||
IsTerritorial = dto.IsTerritorial,
|
||||
Status = 1, // Always "For DSM Approval" when submitted
|
||||
CreatedDate = DateTime.UtcNow,
|
||||
UpdatedDate = DateTime.UtcNow,
|
||||
CreatedBy = dto.UserName
|
||||
};
|
||||
|
||||
await _context.ForDoctorApprovals.AddAsync(newApproval);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Data = new { DoctorId = doctorId, Approval = newApproval },
|
||||
Message = "Doctor submitted for approval successfully.",
|
||||
MessCode = 1
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// UPDATE existing ForDoctorApproval
|
||||
existingApproval.FirstName = dto.FirstName;
|
||||
existingApproval.MiddleInitial = dto.MiddleInitial;
|
||||
existingApproval.LastName = dto.LastName;
|
||||
existingApproval.MaxVisit = dto.MaxVisit;
|
||||
existingApproval.DistrictId = dto.DistrictId;
|
||||
existingApproval.SpecializationId = dto.SpecializationId;
|
||||
existingApproval.BirthDate = dto.BirthDate;
|
||||
existingApproval.EmailAddress = dto.EmailAddress;
|
||||
existingApproval.Address = dto.Address;
|
||||
existingApproval.PhoneNo = dto.PhoneNo;
|
||||
existingApproval.LicenseNo = dto.LicenseNo;
|
||||
existingApproval.InstitutionId = dto.InstitutionId;
|
||||
existingApproval.IsTerritorial = dto.IsTerritorial;
|
||||
existingApproval.UserId = dto.UserId;
|
||||
existingApproval.StageLevelId = dto.StageLevelId;
|
||||
existingApproval.Status = 1; // Reset to "For DSM Approval"
|
||||
existingApproval.UpdatedDate = DateTime.UtcNow;
|
||||
existingApproval.UpdatedBy = dto.UpdatedBy;
|
||||
|
||||
_context.ForDoctorApprovals.Update(existingApproval);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Data = new { DoctorId = doctorId, Approval = existingApproval },
|
||||
Message = "Doctor information updated and resubmitted for approval.",
|
||||
MessCode = 2
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = ex.Message,
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Response> ApproveDoctor(int doctorId, byte newStatus, string updatedBy)
|
||||
{
|
||||
var response = new Response();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the approval record from staging table
|
||||
var approval = await _context.ForDoctorApprovals
|
||||
.FirstOrDefaultAsync(x => x.DoctorId == doctorId);
|
||||
|
||||
if (approval == null)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = "Approval record not found for this doctor.",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
// Get the doctor from main table
|
||||
var doctor = await GetByIdAsync(doctorId);
|
||||
|
||||
if (doctor == null)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = "Doctor not found in main table.",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
// Validate status progression
|
||||
// Status flow: 1 (For DSM) -> 2 (For NSM) -> 3 (Approved) or 0 (Denied at any step)
|
||||
if (newStatus != 0 && newStatus != 2 && newStatus != 3)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = "Invalid status. Use 0 (Denied), 2 (For NSM Approval), or 3 (Approved).",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
// Update status in ForDoctorApproval staging table
|
||||
approval.Status = newStatus;
|
||||
approval.UpdatedDate = DateTime.UtcNow;
|
||||
_context.ForDoctorApprovals.Update(approval);
|
||||
|
||||
// Handle different status scenarios
|
||||
if (newStatus==3) // APPROVED - Copy from staging to main table
|
||||
{
|
||||
// Apply all changes from ForDoctorApproval to Doctors table
|
||||
doctor.FirstName = approval.FirstName;
|
||||
doctor.MiddleInitial = approval.MiddleInitial;
|
||||
doctor.LastName = approval.LastName;
|
||||
doctor.MaxVisit = approval.MaxVisit;
|
||||
doctor.DistrictId = approval.DistrictId;
|
||||
doctor.SpecializationId = approval.SpecializationId;
|
||||
doctor.StageLevelId = approval.StageLevelId;
|
||||
doctor.BirthDate = approval.BirthDate;
|
||||
doctor.EmailAddress = approval.EmailAddress;
|
||||
doctor.Address = approval.Address;
|
||||
doctor.PhoneNo = approval.PhoneNo;
|
||||
doctor.LicenseNo = approval.LicenseNo;
|
||||
doctor.InstitutionId = approval.InstitutionId;
|
||||
doctor.IsTerritorial = approval.IsTerritorial;
|
||||
doctor.Status = 3;
|
||||
doctor.IsActive = true; // Activate the doctor
|
||||
doctor.UpdatedDate = DateTime.UtcNow;
|
||||
doctor.UpdatedBy = updatedBy;
|
||||
|
||||
Update(doctor);
|
||||
|
||||
// Optionally, you can delete or archive the approval record
|
||||
// _context.ForDoctorApprovals.Remove(approval);
|
||||
}
|
||||
else if (newStatus == 0) // DENIED
|
||||
{
|
||||
// Update doctor status to denied
|
||||
doctor.Status = 0;
|
||||
doctor.IsActive = false;
|
||||
doctor.UpdatedDate= DateTime.UtcNow;
|
||||
doctor.UpdatedBy = updatedBy;
|
||||
Update(doctor);
|
||||
}
|
||||
else if (newStatus == 2) // FOR NSM APPROVAL
|
||||
{
|
||||
// Just update the staging table status, keep doctor status as is
|
||||
doctor.Status = 2; // Update main table to show it's in NSM review
|
||||
doctor.UpdatedDate = DateTime.UtcNow;
|
||||
doctor.UpdatedBy = updatedBy;
|
||||
Update(doctor);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
string statusMessage = newStatus switch
|
||||
{
|
||||
0 => "denied",
|
||||
2 => "forwarded to NSM for approval",
|
||||
3 => "approved and activated in the system",
|
||||
_ => "updated"
|
||||
};
|
||||
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Data = new { Doctor = doctor, Approval = approval },
|
||||
Message = $"Doctor has been {statusMessage}.",
|
||||
MessCode = 2
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = ex.Message,
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<ForDoctorApprovalDto>> GetPendingApprovals(DoctorDto dto)
|
||||
{
|
||||
var query =
|
||||
from fda in _context.ForDoctorApprovals
|
||||
join d in _context.Doctors
|
||||
on fda.DoctorId equals d.DoctorId into fdaDoc
|
||||
from d in fdaDoc.DefaultIfEmpty()
|
||||
join i in _context.Institutions
|
||||
on fda.InstitutionId equals i.InstitutionId into fdai
|
||||
from i in fdai.DefaultIfEmpty()
|
||||
join dis in _context.Districts
|
||||
on fda.DistrictId equals dis.DistrictId into fdaDist
|
||||
from dis in fdaDist.DefaultIfEmpty()
|
||||
join u in _context.Users
|
||||
on d.UserId equals u.Id into user
|
||||
from u in user.DefaultIfEmpty()
|
||||
where fda.Status == 1 || u.Company == dto.Company
|
||||
select new ForDoctorApprovalDto
|
||||
{
|
||||
ForDoctorApprovalId = fda.ForDoctorApprovalId,
|
||||
DoctorId = fda.DoctorId,
|
||||
FirstName = fda.FirstName,
|
||||
MiddleInitial = fda.MiddleInitial,
|
||||
LastName = fda.LastName,
|
||||
EmailAddress = fda.EmailAddress,
|
||||
PhoneNo = fda.PhoneNo,
|
||||
SpecializationId = fda.SpecializationId,
|
||||
LicenseNo = fda.LicenseNo,
|
||||
Address = fda.Address,
|
||||
BirthDate = fda.BirthDate,
|
||||
MaxVisit = fda.MaxVisit,
|
||||
MedRepName=u.UserName,
|
||||
Status = fda.Status,
|
||||
InstitutionId = fda.InstitutionId,
|
||||
InstitutionName = i != null ? i.InstitutionName : null,
|
||||
DistrictId = fda.DistrictId,
|
||||
DistrictName = dis != null ? dis.DistrictName : null,
|
||||
UserId = fda.UserId,
|
||||
StageLevelId = fda.StageLevelId,
|
||||
IsTerritorial = fda.IsTerritorial
|
||||
};
|
||||
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
private Infrastructure.Entities.Doctor.Doctor MapToNewDoctor(DoctorDto dto)
|
||||
{
|
||||
return new Infrastructure.Entities.Doctor.Doctor
|
||||
{
|
||||
FirstName = dto.FirstName,
|
||||
MiddleInitial = dto.MiddleInitial,
|
||||
LastName = dto.LastName,
|
||||
MaxVisit = dto.MaxVisit,
|
||||
SpecializationId = dto.SpecializationId,
|
||||
UserId = dto.UserId,
|
||||
StageLevelId = dto.StageLevelId,
|
||||
DistrictId = dto.DistrictId,
|
||||
BirthDate = dto.BirthDate,
|
||||
EmailAddress = dto.EmailAddress,
|
||||
Address = dto.Address,
|
||||
PhoneNo = dto.PhoneNo,
|
||||
LicenseNo = dto.LicenseNo,
|
||||
InstitutionId = dto.InstitutionId,
|
||||
IsTerritorial = dto.IsTerritorial,
|
||||
Status = dto.Status,
|
||||
IsActive = true,
|
||||
CreatedDate = DateTime.UtcNow,
|
||||
UpdatedDate = DateTime.UtcNow,
|
||||
CreatedBy = dto.UserName,
|
||||
UpdatedBy = dto.UserName,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<List<DoctorCoverageToday>> GetDoctorCoverageToday(DoctorDto dto)
|
||||
{
|
||||
var allItems = await _context.DoctorCoverageTodays
|
||||
.FromSqlRaw("EXEC GetDoctorCoverageToday @UserId,@MRPlanDate",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@MRPlanDate", dto.MRPlanDate))
|
||||
.ToListAsync();
|
||||
|
||||
return allItems ?? new List<DoctorCoverageToday>();
|
||||
}
|
||||
}
|
||||
}
|
||||
31
LSFE.Domain/Services/ErrorLogger.cs
Normal file
31
LSFE.Domain/Services/ErrorLogger.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using LSFE.Domain.Contracts.Doctor;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using LSFE.Infrastructure.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Services
|
||||
{
|
||||
public interface IErrorLogger
|
||||
{
|
||||
Task LogErrorAsync(ErrorLogs error);
|
||||
}
|
||||
|
||||
public class ErrorLogger : GenericRepository<Infrastructure.Entities.ErrorLogs>, IErrorLogger
|
||||
{
|
||||
private readonly LSFEDbContext _context;
|
||||
public ErrorLogger(LSFEDbContext context) : base(context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
public async Task LogErrorAsync(ErrorLogs error)
|
||||
{
|
||||
await _dbContext.AddAsync(error);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
632
LSFE.Domain/Services/ExcelInventoryService.cs
Normal file
632
LSFE.Domain/Services/ExcelInventoryService.cs
Normal file
@ -0,0 +1,632 @@
|
||||
using Azure.Core;
|
||||
using ClosedXML.Excel;
|
||||
using DocumentFormat.OpenXml.Office2016.Excel;
|
||||
using LSFE.Domain.Contracts;
|
||||
using LSFE.Domain.Helpers;
|
||||
using LSFE.Infrastructure.Entities;
|
||||
using LSFE.Infrastructure.Entities.Maintenance;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Services
|
||||
{
|
||||
public class ExcelInventoryService
|
||||
{
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly ILogger<ExcelInventoryService> _logger;
|
||||
private const int BATCH_SIZE = 100;
|
||||
public readonly UserManager<AppsUsers> _userManager;
|
||||
|
||||
public ExcelInventoryService(IUnitOfWork unitOfWork, ILogger<ExcelInventoryService> logger,
|
||||
UserManager<AppsUsers> userManager)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
}
|
||||
public async Task<ExcelUploadResponse> ProcessExcelFileAsync(IFormFile file, string userId, string userName)
|
||||
{
|
||||
var response = new ExcelUploadResponse();
|
||||
|
||||
try
|
||||
{
|
||||
// Use ArrayPool for better memory management
|
||||
var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent((int)file.Length);
|
||||
try
|
||||
{
|
||||
using var fileStream = file.OpenReadStream();
|
||||
await fileStream.ReadAsync(buffer.AsMemory(0, (int)file.Length));
|
||||
|
||||
using var stream = new MemoryStream(buffer, 0, (int)file.Length, writable: false);
|
||||
using var workbook = new XLWorkbook(stream);
|
||||
|
||||
var worksheet = workbook.Worksheets.FirstOrDefault();
|
||||
if (worksheet == null)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "No worksheet found in the Excel file";
|
||||
response.MessCode = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
// Validate headers
|
||||
var (isValid, errors) = ExcelTemplateValidator.ValidateHeaders(worksheet);
|
||||
if (!isValid)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Invalid Excel template. Please use the correct template.";
|
||||
response.Errors = errors;
|
||||
response.MessCode = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
var rowCount = worksheet.LastRowUsed()?.RowNumber() ?? 0;
|
||||
response.TotalRows = rowCount - 1;
|
||||
|
||||
// Calculate current year and month once for performance
|
||||
var currentDate = DateTime.Now;
|
||||
var currentYear = currentDate.Year;
|
||||
var currentMonth = currentDate.Month;
|
||||
|
||||
// Pre-load existing inventory data to avoid N+1 query problem
|
||||
var existingInventory = await LoadExistingInventoryAsync(worksheet, rowCount);
|
||||
|
||||
// Pre-load and validate all MedRep names from the Excel file
|
||||
var validMedReps = await ValidateMedRepNamesAsync(worksheet, rowCount, response);
|
||||
if (!validMedReps.Any())
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "No valid MedRep users found in the Excel file.";
|
||||
response.MessCode = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
// Process in batches
|
||||
var inventoriesToInsert = new List<Inventory>();
|
||||
var summaryDict = new Dictionary<string, InventorySummary>();
|
||||
|
||||
for (int row = 2; row <= rowCount; row++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var brandName = worksheet.Cell(row, 1).Value.ToString()?.Trim();
|
||||
var year = worksheet.Cell(row, 14).Value.ToString()?.Trim();
|
||||
var medRepName = worksheet.Cell(row, 15).Value.ToString()?.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(brandName))
|
||||
{
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate MedRep Name exists in the system
|
||||
if (string.IsNullOrWhiteSpace(medRepName))
|
||||
{
|
||||
response.Errors.Add($"Row {row}: MedRep Name is required.");
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!validMedReps.Contains(medRepName.ToLower()))
|
||||
{
|
||||
response.Errors.Add($"Row {row}: Invalid MedRep User Name '{medRepName}'. User does not exist in the system.");
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!int.TryParse(year, out int cycleYear))
|
||||
{
|
||||
response.Errors.Add($"Row {row}: Invalid year '{year}'");
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate year is not in the past
|
||||
if (cycleYear < currentYear)
|
||||
{
|
||||
response.Errors.Add($"Row {row}: Year {cycleYear} is in the past. Only current month onwards is allowed.");
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process each month
|
||||
for (byte month = 1; month <= 12; month++)
|
||||
{
|
||||
var cellValue = worksheet.Cell(row, month + 1).Value.ToString()?.Trim();
|
||||
|
||||
if (!int.TryParse(cellValue, out int qty) || qty <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate date is not in the past (current month or future only)
|
||||
if (cycleYear == currentYear && month < currentMonth)
|
||||
{
|
||||
_logger.LogDebug($"Row {row}, Month {month}: Skipping past date - {month}/{cycleYear}");
|
||||
response.Errors.Add($"Row {row}, Month {month}: Date {month}/{cycleYear} is in the past. Only {currentMonth}/{currentYear} onwards is allowed.");
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if record exists using in-memory lookup (much faster)
|
||||
var key = $"{brandName}|{month}|{cycleYear}|{medRepName}";
|
||||
if (existingInventory.Contains(key))
|
||||
{
|
||||
_logger.LogDebug($"Row {row}, Month {month}: Skipping duplicate - {key}");
|
||||
continue; // Skip duplicates
|
||||
}
|
||||
|
||||
// Also check if we already added this in current batch
|
||||
if (inventoriesToInsert.Any(x =>
|
||||
x.Description == brandName &&
|
||||
x.CycleMonth == month &&
|
||||
x.CycleYear == cycleYear &&
|
||||
x.MedRepName == medRepName))
|
||||
{
|
||||
_logger.LogDebug($"Row {row}, Month {month}: Skipping - already in current batch");
|
||||
continue;
|
||||
}
|
||||
|
||||
var inventory = new Inventory
|
||||
{
|
||||
InventoryId = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
Description = brandName,
|
||||
QtyIn = qty,
|
||||
QtyOut = 0,
|
||||
QtyBalance = qty,
|
||||
CycleMonth = month,
|
||||
CycleYear = cycleYear,
|
||||
MedRepName = medRepName,
|
||||
CreatedBy = userName,
|
||||
CreatedDate = DateTime.Now,
|
||||
IsActive = true
|
||||
};
|
||||
|
||||
inventoriesToInsert.Add(inventory);
|
||||
response.ProcessedRows++;
|
||||
|
||||
// Update summary
|
||||
var summaryKey = $"{brandName}|{medRepName}";
|
||||
if (!summaryDict.TryGetValue(summaryKey, out var summary))
|
||||
{
|
||||
summary = new InventorySummary
|
||||
{
|
||||
Brand = brandName,
|
||||
TotalQty = 0,
|
||||
MedRepName = medRepName
|
||||
};
|
||||
summaryDict[summaryKey] = summary;
|
||||
}
|
||||
summary.TotalQty += qty;
|
||||
}
|
||||
|
||||
// Batch insert to reduce database round trips
|
||||
if (inventoriesToInsert.Count >= BATCH_SIZE)
|
||||
{
|
||||
await _unitOfWork.Inventory.AddRangeAsync(inventoriesToInsert);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
inventoriesToInsert.Clear();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Errors.Add($"Row {row}: {ex.Message}");
|
||||
response.SkippedRows++;
|
||||
_logger.LogError(ex, $"Error processing row {row}");
|
||||
}
|
||||
}
|
||||
|
||||
// Insert remaining items
|
||||
if (inventoriesToInsert.Any())
|
||||
{
|
||||
await _unitOfWork.Inventory.AddRangeAsync(inventoriesToInsert);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
}
|
||||
|
||||
response.InsertedItems = summaryDict.Values.ToList();
|
||||
response.Success = true;
|
||||
response.MessCode = 1;
|
||||
response.Message = $"Successfully processed {response.ProcessedRows} inventory records. Skipped {response.SkippedRows} duplicates/invalid rows.";
|
||||
|
||||
_logger.LogInformation($"Excel processing completed: {response.ProcessedRows} processed, {response.SkippedRows} skipped, {response.TotalRows} total rows");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Return buffer to pool
|
||||
System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = $"Error processing Excel file: {ex.Message}";
|
||||
response.MessCode = 0;
|
||||
response.Errors.Add(ex.ToString());
|
||||
_logger.LogError(ex, "Error processing Excel file");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates all MedRep names from the Excel file against the UserManager
|
||||
/// Returns a HashSet of valid (lowercase) usernames for O(1) lookup during processing
|
||||
/// </summary>
|
||||
private async Task<HashSet<string>> ValidateMedRepNamesAsync(IXLWorksheet worksheet, int rowCount, ExcelUploadResponse response)
|
||||
{
|
||||
var validMedReps = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
var uniqueMedRepNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Collect unique MedRep names from Excel
|
||||
for (int row = 2; row <= rowCount; row++)
|
||||
{
|
||||
var medRepName = worksheet.Cell(row, 15).Value.ToString()?.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(medRepName))
|
||||
{
|
||||
uniqueMedRepNames.Add(medRepName);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate each unique MedRep name against UserManager
|
||||
foreach (var medRepName in uniqueMedRepNames)
|
||||
{
|
||||
var user = await _userManager.FindByNameAsync(medRepName.ToLower());
|
||||
if (user != null)
|
||||
{
|
||||
validMedReps.Add(medRepName.ToLower());
|
||||
_logger.LogDebug($"MedRep '{medRepName}' validated successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"MedRep '{medRepName}' not found in the system");
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Validated {validMedReps.Count} out of {uniqueMedRepNames.Count} unique MedRep names");
|
||||
|
||||
return validMedReps;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pre-loads existing inventory records from database to avoid N+1 queries
|
||||
/// This validates against ALL records in the inventory table (from all users)
|
||||
/// to prevent duplicate uploads
|
||||
/// </summary>
|
||||
private async Task<HashSet<string>> LoadExistingInventoryAsync(IXLWorksheet worksheet, int rowCount)
|
||||
{
|
||||
var brandNames = new HashSet<string>();
|
||||
var years = new HashSet<int>();
|
||||
var medRepNames = new HashSet<string>();
|
||||
var months = new HashSet<byte> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
|
||||
|
||||
// Collect unique brands, years, and med rep names from Excel file being uploaded
|
||||
for (int row = 2; row <= rowCount; row++)
|
||||
{
|
||||
var brandName = worksheet.Cell(row, 1).Value.ToString()?.Trim();
|
||||
var yearStr = worksheet.Cell(row, 14).Value.ToString()?.Trim();
|
||||
var medRepName = worksheet.Cell(row, 15).Value.ToString()?.Trim();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(brandName))
|
||||
{
|
||||
brandNames.Add(brandName);
|
||||
}
|
||||
|
||||
if (int.TryParse(yearStr, out int year))
|
||||
{
|
||||
years.Add(year);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(medRepName))
|
||||
{
|
||||
medRepNames.Add(medRepName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!brandNames.Any() || !years.Any())
|
||||
{
|
||||
return new HashSet<string>();
|
||||
}
|
||||
|
||||
// Single query to load ALL matching records from inventory table
|
||||
var existingRecords = await _unitOfWork.Inventory
|
||||
.FindAllAsync(inv =>
|
||||
brandNames.Contains(inv.Description) &&
|
||||
months.Contains(inv.CycleMonth) &&
|
||||
years.Contains(inv.CycleYear) &&
|
||||
medRepNames.Contains(inv.MedRepName) &&
|
||||
inv.IsActive == true);
|
||||
|
||||
// Create lookup set with composite key: Brand|Month|Year|MedRepName
|
||||
var existingKeys = new HashSet<string>(
|
||||
existingRecords.Select(inv =>
|
||||
$"{inv.Description}|{inv.CycleMonth}|{inv.CycleYear}|{inv.MedRepName}"));
|
||||
|
||||
_logger.LogInformation($"Loaded {existingKeys.Count} existing inventory records for validation");
|
||||
|
||||
return existingKeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*#region
|
||||
public async Task<ExcelUploadResponse> ProcessExcelFileAsync(IFormFile file, string userId, string userName)
|
||||
{
|
||||
var response = new ExcelUploadResponse();
|
||||
|
||||
try
|
||||
{
|
||||
// Use ArrayPool for better memory management
|
||||
var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent((int)file.Length);
|
||||
try
|
||||
{
|
||||
using var fileStream = file.OpenReadStream();
|
||||
await fileStream.ReadAsync(buffer.AsMemory(0, (int)file.Length));
|
||||
|
||||
using var stream = new MemoryStream(buffer, 0, (int)file.Length, writable: false);
|
||||
using var workbook = new XLWorkbook(stream);
|
||||
|
||||
var worksheet = workbook.Worksheets.FirstOrDefault();
|
||||
if (worksheet == null)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "No worksheet found in the Excel file";
|
||||
response.MessCode = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
// Validate headers
|
||||
var (isValid, errors) = ExcelTemplateValidator.ValidateHeaders(worksheet);
|
||||
if (!isValid)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Invalid Excel template. Please use the correct template.";
|
||||
response.Errors = errors;
|
||||
response.MessCode = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
var user = await _userManager.FindByNameAsync(request.UserName.ToLower());
|
||||
if (user == null)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Invalid MedRep User Name.";
|
||||
response.Errors = errors;
|
||||
response.MessCode = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
var rowCount = worksheet.LastRowUsed()?.RowNumber() ?? 0;
|
||||
response.TotalRows = rowCount - 1;
|
||||
|
||||
// Calculate current year and month once for performance
|
||||
var currentDate = DateTime.Now;
|
||||
var currentYear = currentDate.Year;
|
||||
var currentMonth = currentDate.Month;
|
||||
|
||||
// Pre-load existing inventory data to avoid N+1 query problem
|
||||
var existingInventory = await LoadExistingInventoryAsync(worksheet, rowCount);
|
||||
|
||||
// Process in batches
|
||||
var inventoriesToInsert = new List<Inventory>();
|
||||
var summaryDict = new Dictionary<string, InventorySummary>();
|
||||
|
||||
for (int row = 2; row <= rowCount; row++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var brandName = worksheet.Cell(row, 1).Value.ToString()?.Trim();
|
||||
var year = worksheet.Cell(row, 14).Value.ToString()?.Trim();
|
||||
var medRepName = worksheet.Cell(row, 15).Value.ToString()?.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(brandName))
|
||||
{
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!int.TryParse(year, out int cycleYear))
|
||||
{
|
||||
response.Errors.Add($"Row {row}: Invalid year '{year}'");
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate year is not in the past
|
||||
if (cycleYear < currentYear)
|
||||
{
|
||||
response.Errors.Add($"Row {row}: Year {cycleYear} is in the past. Only current month onwards is allowed.");
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process each month
|
||||
for (byte month = 1; month <= 12; month++)
|
||||
{
|
||||
var cellValue = worksheet.Cell(row, month + 1).Value.ToString()?.Trim();
|
||||
|
||||
if (!int.TryParse(cellValue, out int qty) || qty <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate date is not in the past (current month or future only)
|
||||
if (cycleYear == currentYear && month < currentMonth)
|
||||
{
|
||||
_logger.LogDebug($"Row {row}, Month {month}: Skipping past date - {month}/{cycleYear}");
|
||||
response.Errors.Add($"Row {row}, Month {month}: Date {month}/{cycleYear} is in the past. Only {currentMonth}/{currentYear} onwards is allowed.");
|
||||
response.SkippedRows++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if record exists using in-memory lookup (much faster)
|
||||
// Include MedRepName in key because same brand can be distributed by different reps
|
||||
var key = $"{brandName}|{month}|{cycleYear}|{medRepName}";
|
||||
if (existingInventory.Contains(key))
|
||||
{
|
||||
_logger.LogDebug($"Row {row}, Month {month}: Skipping duplicate - {key}");
|
||||
continue; // Skip duplicates
|
||||
}
|
||||
|
||||
// Also check if we already added this in current batch (prevent duplicates within same file)
|
||||
if (inventoriesToInsert.Any(x =>
|
||||
x.Description == brandName &&
|
||||
x.CycleMonth == month &&
|
||||
x.CycleYear == cycleYear &&
|
||||
x.MedRepName == medRepName))
|
||||
{
|
||||
_logger.LogDebug($"Row {row}, Month {month}: Skipping - already in current batch");
|
||||
continue;
|
||||
}
|
||||
|
||||
var inventory = new Inventory
|
||||
{
|
||||
InventoryId = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
Description = brandName,
|
||||
QtyIn = qty,
|
||||
QtyOut = 0,
|
||||
QtyBalance = qty,
|
||||
CycleMonth = month,
|
||||
CycleYear = cycleYear,
|
||||
MedRepName = medRepName,
|
||||
CreatedBy = userName,
|
||||
CreatedDate = DateTime.Now,
|
||||
IsActive = true
|
||||
};
|
||||
|
||||
inventoriesToInsert.Add(inventory);
|
||||
response.ProcessedRows++;
|
||||
|
||||
// Update summary
|
||||
var summaryKey = $"{brandName}|{medRepName}";
|
||||
if (!summaryDict.TryGetValue(summaryKey, out var summary))
|
||||
{
|
||||
summary = new InventorySummary
|
||||
{
|
||||
Brand = brandName,
|
||||
TotalQty = 0,
|
||||
MedRepName = medRepName
|
||||
};
|
||||
summaryDict[summaryKey] = summary;
|
||||
}
|
||||
summary.TotalQty += qty;
|
||||
}
|
||||
|
||||
// Batch insert to reduce database round trips
|
||||
if (inventoriesToInsert.Count >= BATCH_SIZE)
|
||||
{
|
||||
await _unitOfWork.Inventory.AddRangeAsync(inventoriesToInsert);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
inventoriesToInsert.Clear();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Errors.Add($"Row {row}: {ex.Message}");
|
||||
response.SkippedRows++;
|
||||
_logger.LogError(ex, $"Error processing row {row}");
|
||||
}
|
||||
}
|
||||
|
||||
// Insert remaining items
|
||||
if (inventoriesToInsert.Any())
|
||||
{
|
||||
await _unitOfWork.Inventory.AddRangeAsync(inventoriesToInsert);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
}
|
||||
|
||||
response.InsertedItems = summaryDict.Values.ToList();
|
||||
response.Success = true;
|
||||
response.MessCode = 1;
|
||||
response.Message = $"Successfully processed {response.ProcessedRows} inventory records. Skipped {response.SkippedRows} duplicates/invalid rows.";
|
||||
|
||||
_logger.LogInformation($"Excel processing completed: {response.ProcessedRows} processed, {response.SkippedRows} skipped, {response.TotalRows} total rows");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Return buffer to pool
|
||||
System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = $"Error processing Excel file: {ex.Message}";
|
||||
response.MessCode = 0;
|
||||
response.Errors.Add(ex.ToString());
|
||||
_logger.LogError(ex, "Error processing Excel file");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
/// <summary>
|
||||
/// Pre-loads existing inventory records from database to avoid N+1 queries
|
||||
/// This validates against ALL records in the inventory table (from all users)
|
||||
/// to prevent duplicate uploads
|
||||
/// </summary>
|
||||
private async Task<HashSet<string>> LoadExistingInventoryAsync(IXLWorksheet worksheet, int rowCount)
|
||||
{
|
||||
var brandNames = new HashSet<string>();
|
||||
var years = new HashSet<int>();
|
||||
var medRepNames = new HashSet<string>();
|
||||
var months = new HashSet<byte> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; // All possible months
|
||||
|
||||
// Collect unique brands, years, and med rep names from Excel file being uploaded
|
||||
for (int row = 2; row <= rowCount; row++)
|
||||
{
|
||||
var brandName = worksheet.Cell(row, 1).Value.ToString()?.Trim();
|
||||
var yearStr = worksheet.Cell(row, 14).Value.ToString()?.Trim();
|
||||
var medRepName = worksheet.Cell(row, 15).Value.ToString()?.Trim();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(brandName))
|
||||
{
|
||||
brandNames.Add(brandName);
|
||||
}
|
||||
|
||||
if (int.TryParse(yearStr, out int year))
|
||||
{
|
||||
years.Add(year);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(medRepName))
|
||||
{
|
||||
medRepNames.Add(medRepName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!brandNames.Any() || !years.Any())
|
||||
{
|
||||
return new HashSet<string>();
|
||||
}
|
||||
|
||||
// Single query to load ALL matching records from inventory table
|
||||
// This includes records uploaded by ANY user (current or previous uploads)
|
||||
// Filters by Brand, Month, Year, and MedRepName to ensure precise duplicate detection
|
||||
var existingRecords = await _unitOfWork.Inventory
|
||||
.FindAllAsync(inv =>
|
||||
brandNames.Contains(inv.Description) &&
|
||||
months.Contains(inv.CycleMonth) &&
|
||||
years.Contains(inv.CycleYear) &&
|
||||
medRepNames.Contains(inv.MedRepName) &&
|
||||
inv.IsActive == true); // Only check active records
|
||||
|
||||
// Create lookup set with composite key: Brand|Month|Year|MedRepName
|
||||
// This allows O(1) existence checks during row processing
|
||||
// MedRepName is included because the same brand can be handled by different reps
|
||||
var existingKeys = new HashSet<string>(
|
||||
existingRecords.Select(inv =>
|
||||
$"{inv.Description}|{inv.CycleMonth}|{inv.CycleYear}|{inv.MedRepName}"));
|
||||
|
||||
_logger.LogInformation($"Loaded {existingKeys.Count} existing inventory records for validation");
|
||||
|
||||
return existingKeys;
|
||||
}
|
||||
#endregion*/
|
||||
40
LSFE.Domain/Services/GenericRepository.cs
Normal file
40
LSFE.Domain/Services/GenericRepository.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using LSFE.Domain.Contracts;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Services
|
||||
{
|
||||
public class GenericRepository<TEntity> where TEntity : class
|
||||
{
|
||||
protected readonly LSFEDbContext _dbContext;
|
||||
|
||||
public GenericRepository(LSFEDbContext context)
|
||||
{
|
||||
_dbContext = context;
|
||||
}
|
||||
|
||||
public async Task<TEntity?> GetByIdAsync(int id) => await _dbContext.Set<TEntity>().FindAsync(id);
|
||||
|
||||
public async Task<List<TEntity>> GetAllAsync() => await _dbContext.Set<TEntity>().ToListAsync();
|
||||
|
||||
public async Task AddAsync(TEntity entity)
|
||||
{
|
||||
await _dbContext.Set<TEntity>().AddAsync(entity);
|
||||
}
|
||||
|
||||
public void Update(TEntity entity)
|
||||
{
|
||||
_dbContext.Entry(entity).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public void Remove(TEntity entity)
|
||||
{
|
||||
_dbContext.Set<TEntity>().Remove(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
135
LSFE.Domain/Services/Maintenance/MaintenanceRepo.cs
Normal file
135
LSFE.Domain/Services/Maintenance/MaintenanceRepo.cs
Normal file
@ -0,0 +1,135 @@
|
||||
using LSFE.Domain.Contracts.Maintenance;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using LSFE.Infrastructure.Dto.Maintenance;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Services.Maintenance
|
||||
{
|
||||
public class MaintenanceRepo<TEntity> : IMaintenanceRepo<TEntity> where TEntity : class
|
||||
{
|
||||
private readonly LSFEDbContext _context;
|
||||
private readonly DbSet<TEntity> _dbSet;
|
||||
public MaintenanceRepo(LSFEDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
_dbSet = _context.Set<TEntity>();
|
||||
}
|
||||
public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate)
|
||||
{
|
||||
return await _dbSet.AnyAsync(predicate);
|
||||
}
|
||||
public IQueryable<TEntity> Query()
|
||||
{
|
||||
return _context.Set<TEntity>().AsQueryable();
|
||||
}
|
||||
public async Task<List<TEntity>> GetAllAsync(params Expression<Func<TEntity, object>>[] includeProperties)
|
||||
{
|
||||
IQueryable<TEntity> query = _context.Set<TEntity>();
|
||||
|
||||
if (includeProperties != null)
|
||||
{
|
||||
foreach (var includeProperty in includeProperties)
|
||||
{
|
||||
query = query.Include(includeProperty);
|
||||
}
|
||||
}
|
||||
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Response> AddOrUpdateAsync(TEntity entity)
|
||||
{
|
||||
try
|
||||
{
|
||||
var keyProperty = _context.Model.FindEntityType(typeof(TEntity))?
|
||||
.FindPrimaryKey()?.Properties.First();
|
||||
|
||||
if (keyProperty == null)
|
||||
throw new InvalidOperationException($"No key defined for {typeof(TEntity).Name}");
|
||||
|
||||
var keyValue = keyProperty.PropertyInfo?.GetValue(entity);
|
||||
|
||||
var existing = await _context.Set<TEntity>().FindAsync(keyValue);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
await _context.Set<TEntity>().AddAsync(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Entry(existing).CurrentValues.SetValues(entity);
|
||||
_context.Entry(existing).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
return new Response { Success = true, MessCode = 1 };
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = ex.ToString(),
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Response> SoftDeleteAsync(int id)
|
||||
{
|
||||
var entity = await _context.Set<TEntity>().FindAsync(id);
|
||||
if (entity == null)
|
||||
return new Response { Success = false, Message = "Not found" };
|
||||
|
||||
var prop = typeof(TEntity).GetProperty("IsActive");
|
||||
if (prop != null)
|
||||
prop.SetValue(entity, false);
|
||||
|
||||
return new Response { Success = true };
|
||||
}
|
||||
public async Task<TEntity?> FindAsync(Expression<Func<TEntity, bool>> predicate)
|
||||
{
|
||||
return await _context.Set<TEntity>().FirstOrDefaultAsync(predicate);
|
||||
}
|
||||
|
||||
public async Task<TEntity?> GetByIdAsync(object id)
|
||||
{
|
||||
return await _context.Set<TEntity>().FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TEntity>> FindAllAsync(Expression<Func<TEntity, bool>> predicate)
|
||||
{
|
||||
return await _context.Set<TEntity>()
|
||||
.Where(predicate)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
}
|
||||
public async Task AddRangeAsync(IEnumerable<TEntity> entities)
|
||||
{
|
||||
if (entities == null || !entities.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _context.Set<TEntity>().AddRangeAsync(entities);
|
||||
}
|
||||
public async Task<Response> DeleteAsync(Guid id)
|
||||
{
|
||||
var entity = await _context.Set<TEntity>().FindAsync(id);
|
||||
|
||||
if (entity == null)
|
||||
return new Response { Success = false, Message = "Not found" };
|
||||
|
||||
_context.Set<TEntity>().Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return new Response { Success = true };
|
||||
}
|
||||
}
|
||||
}
|
||||
27
LSFE.Domain/Services/OutputParamMessage.cs
Normal file
27
LSFE.Domain/Services/OutputParamMessage.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Services
|
||||
{
|
||||
public static class OutputParamMessage
|
||||
{
|
||||
public static (SqlParameter MessCode, SqlParameter Message) CreateOutputParams()
|
||||
{
|
||||
var messCode = new SqlParameter("@MessCode", SqlDbType.TinyInt) { Direction = ParameterDirection.Output };
|
||||
var message = new SqlParameter("@Message", SqlDbType.VarChar, 8000) { Direction = ParameterDirection.Output };
|
||||
return (messCode, message);
|
||||
}
|
||||
public static (SqlParameter MessCode, SqlParameter Message, SqlParameter planId) CreateOutputParamsPlanId()
|
||||
{
|
||||
var messCode = new SqlParameter("@MessCode", SqlDbType.TinyInt) { Direction = ParameterDirection.Output };
|
||||
var message = new SqlParameter("@Message", SqlDbType.VarChar, 8000) { Direction = ParameterDirection.Output };
|
||||
var planId = new SqlParameter("@MRRawPlanIdOutPut", SqlDbType.VarChar, 8000) { Direction = ParameterDirection.Output };
|
||||
return (messCode, message, planId);
|
||||
}
|
||||
}
|
||||
}
|
||||
667
LSFE.Domain/Services/Planning/PlanningRepo.cs
Normal file
667
LSFE.Domain/Services/Planning/PlanningRepo.cs
Normal file
@ -0,0 +1,667 @@
|
||||
using LSFE.Domain.Contracts.Planning;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using LSFE.Infrastructure.Dto;
|
||||
using LSFE.Infrastructure.Dto.Doctor;
|
||||
using LSFE.Infrastructure.Dto.Planning;
|
||||
using LSFE.Infrastructure.Entities.Maintenance;
|
||||
using LSFE.Infrastructure.Entities.Planning;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.VisualBasic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static LSFE.Domain.Services.OutputParamMessage;
|
||||
|
||||
namespace LSFE.Domain.Services.Planning
|
||||
{
|
||||
public class PlanningRepo : GenericRepository<MRRawPlan>, IPlanningRepo
|
||||
{
|
||||
private readonly LSFEDbContext _context;
|
||||
public PlanningRepo(LSFEDbContext context) : base(context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
#region Post Put
|
||||
public async Task<Response> SoftDeleteAsync(int doctorId)
|
||||
{
|
||||
var response = new Response();
|
||||
|
||||
try
|
||||
{
|
||||
var existingDoctor = await GetByIdAsync(doctorId);
|
||||
|
||||
if (existingDoctor == null)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = $"Doctor with ID {doctorId} not found.";
|
||||
response.MessCode = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
Update(existingDoctor);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
response.Success = true;
|
||||
response.Data = existingDoctor;
|
||||
response.Message = "Doctor deactivated successfully.";
|
||||
response.MessCode = 3; // code for delete/soft delete
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = ex.Message;
|
||||
response.MessCode = 0;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
public async Task<Response> PostPutPlan(PlanningDto dto)
|
||||
{
|
||||
// Input validation
|
||||
if (dto == null)
|
||||
return new Response { Success = false, Message = "Invalid request data", MessCode = 0 };
|
||||
|
||||
var (messCode, message, planId) = CreateOutputParamsPlanId();
|
||||
|
||||
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
|
||||
try
|
||||
{
|
||||
// Execute the stored procedure
|
||||
await _context.Database.ExecuteSqlRawAsync(
|
||||
"EXEC PostPutPlan @UserId,@DoctorId,@CallDate,@CallType,@SLP," +
|
||||
"@IsJoinCall,@IsLiterature,@MRPlanDetailId,@MRRawPlanId,@IsUpdateDelete,@MessCode OUTPUT,@Message OUTPUT,@MRRawPlanIdOutPut OUTPUT",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@DoctorId", dto.DoctorId),
|
||||
new SqlParameter("@CallDate", dto.CallDate),
|
||||
new SqlParameter("@CallType", dto.SLP ?? "N/A"),
|
||||
new SqlParameter("@SLP", dto.SLP ?? "N/A"),
|
||||
new SqlParameter("@IsJoinCall", false),
|
||||
new SqlParameter("@IsLiterature", dto.IsLiterature),
|
||||
new SqlParameter("@MRPlanDetailId",
|
||||
string.IsNullOrEmpty(dto.MRPlanDetailId) || dto.MRPlanDetailId == "N/A"
|
||||
? (object)DBNull.Value
|
||||
: Guid.Parse(dto.MRPlanDetailId)),
|
||||
new SqlParameter("@MRRawPlanId",
|
||||
string.IsNullOrEmpty(dto.MRRawPlanId) || dto.MRRawPlanId == "N/A"
|
||||
? (object)DBNull.Value
|
||||
: Guid.Parse(dto.MRRawPlanId)),
|
||||
new SqlParameter("@IsUpdateDelete", dto.IsUpdateDelete),
|
||||
messCode,
|
||||
message,
|
||||
planId);
|
||||
|
||||
var code = (byte)(messCode.Value ?? 0);
|
||||
var msg = message.Value?.ToString() ?? "No response message.";
|
||||
var mrRawPlanId = planId.Value?.ToString() ?? string.Empty;
|
||||
var success = code != 0;
|
||||
|
||||
// If stored procedure failed, rollback and return
|
||||
if (!success)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
return new Response { Success = false, Message = msg, MessCode = code };
|
||||
}
|
||||
|
||||
// Validate that we have a valid plan ID for insert/update operations
|
||||
if (string.IsNullOrEmpty(mrRawPlanId) && dto.IsUpdateDelete != 1)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
return new Response { Success = false, Message = "You cannot create more than one visit in a week!", MessCode = 0 };
|
||||
}
|
||||
|
||||
// Get username for audit trail
|
||||
var username = await _context.Users
|
||||
.Where(u => u.Id == dto.UserId)
|
||||
.Select(u => u.UserName)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(username))
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
return new Response { Success = false, Message = "User not found", MessCode = 0 };
|
||||
}
|
||||
|
||||
var now = DateTime.Now;
|
||||
DateOnly today = DateOnly.FromDateTime(DateTime.Now);
|
||||
|
||||
// Handle inventory based on operation type
|
||||
if (dto.IsUpdateDelete == 1 || dto.IsUpdateDelete == 2) // UPDATE/DELETE
|
||||
{
|
||||
|
||||
if (!IsSameMonth(dto.CallDate, today))
|
||||
{
|
||||
msg = await HandleInventoryDelete(dto, msg);
|
||||
msg = await HandlePromosDelete(dto, msg);
|
||||
|
||||
msg = await HandleInventoryInsert(dto, msg, username, now, mrRawPlanId);
|
||||
msg = await HandlePromosInsert(dto, msg, username, now, mrRawPlanId);
|
||||
}
|
||||
}
|
||||
else if (dto.IsUpdateDelete == 2) // UPDATE
|
||||
{
|
||||
if (!IsSameMonth(dto.CallDate, today))
|
||||
{
|
||||
msg = await HandleInventoryUpdate(dto, msg, username, now);
|
||||
msg = await HandlePromosUpdate(dto, msg, username, now, mrRawPlanId);
|
||||
}
|
||||
}
|
||||
else // INSERT (new plan)
|
||||
{
|
||||
msg = await HandleInventoryInsert(dto, msg, username, now, mrRawPlanId);
|
||||
msg = await HandlePromosInsert(dto, msg, username, now, mrRawPlanId);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
await transaction.CommitAsync();
|
||||
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = msg,
|
||||
MessCode = code
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
return new Response
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Error processing plan: {ex.Message}",
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
private bool IsSameMonth(DateOnly date, DateOnly reference)
|
||||
{
|
||||
return date.Year == reference.Year && date.Month == reference.Month;
|
||||
}
|
||||
|
||||
private async Task<string> HandleInventoryDelete(PlanningDto dto, string msg)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dto.MRRawPlanId))
|
||||
return msg + " Cannot delete inventory: Missing MRRawPlanId.";
|
||||
|
||||
var mrRawPlanGuid = Guid.Parse(dto.MRRawPlanId);
|
||||
|
||||
// Get all inventory transactions (ONE query only)
|
||||
var transactions = await _context.InventoryTransactions
|
||||
.Where(t => t.MRRawPlanId == mrRawPlanGuid)
|
||||
.ToListAsync();
|
||||
|
||||
if (transactions.Count == 0)
|
||||
return msg + " No linked inventory transactions found.";
|
||||
|
||||
// Collect inventory IDs for batch query
|
||||
var inventoryIds = transactions
|
||||
.Select(t => t.InventoryId)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
// Load inventories in ONE query
|
||||
var inventories = await _context.Inventory
|
||||
.Where(i => inventoryIds.Contains(i.InventoryId))
|
||||
.ToDictionaryAsync(i => i.InventoryId);
|
||||
|
||||
// Process each transaction update
|
||||
foreach (var tx in transactions)
|
||||
{
|
||||
if (!inventories.TryGetValue(tx.InventoryId, out var inventory))
|
||||
continue; // if missing, skip safely
|
||||
|
||||
inventory.QtyOut -= tx.QtyIn; // revert qty out
|
||||
inventory.QtyBalance += tx.QtyIn; // restore qty
|
||||
|
||||
if (inventory.QtyOut < 0)
|
||||
inventory.QtyOut = 0; // safety check
|
||||
|
||||
_context.Inventory.Update(inventory);
|
||||
}
|
||||
|
||||
// Remove all transaction records
|
||||
_context.InventoryTransactions.RemoveRange(transactions);
|
||||
|
||||
return msg + $" Deleted {transactions.Count} record(s).";
|
||||
}
|
||||
|
||||
private async Task<string> HandleInventoryUpdate(PlanningDto dto, string msg,
|
||||
string username, DateTime now)
|
||||
{
|
||||
if (dto.SelectedSamples == null || !dto.SelectedSamples.Any())
|
||||
return msg;
|
||||
|
||||
if (string.IsNullOrEmpty(dto.MRRawPlanId))
|
||||
{
|
||||
return msg + " Cannot update inventory: Missing MRRawPlanId.";
|
||||
}
|
||||
|
||||
var mrRawPlanGuid = Guid.Parse(dto.MRRawPlanId);
|
||||
|
||||
foreach (var sample in dto.SelectedSamples)
|
||||
{
|
||||
var inventoryTransaction = await _context.InventoryTransactions
|
||||
.FirstOrDefaultAsync(i => i.InventoryId == sample.InventoryId
|
||||
&& i.MRRawPlanId == mrRawPlanGuid);
|
||||
|
||||
var inventory = await _context.Inventory
|
||||
.FirstOrDefaultAsync(i => i.InventoryId == sample.InventoryId);
|
||||
|
||||
if (inventory == null)
|
||||
{
|
||||
msg += $" {sample.InventoryId}: Inventory not found.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inventoryTransaction == null)
|
||||
{
|
||||
msg += $" {inventory.Description}: No existing transaction found.";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate quantity difference
|
||||
int newQtyBalance = inventory.QtyIn - sample.SelectedQty;
|
||||
|
||||
//Update the new qty balance
|
||||
inventory.QtyBalance = newQtyBalance;
|
||||
|
||||
// Validate balance
|
||||
if (inventory.QtyBalance < 0)
|
||||
{
|
||||
msg += $" {inventory.Description}: Insufficient stock for update.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inventory.QtyBalance > inventory.QtyIn)
|
||||
{
|
||||
msg += $" {inventory.Description}: Balance exceeds original quantity.";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update transaction
|
||||
inventoryTransaction.AppointmentDate = dto.CallDate;
|
||||
inventoryTransaction.QtyOut = sample.SelectedQty;
|
||||
inventoryTransaction.QtyIn = sample.SelectedQty;
|
||||
inventoryTransaction.UpdatedBy = username;
|
||||
inventoryTransaction.UpdatedDate = now;
|
||||
|
||||
_context.InventoryTransactions.Update(inventoryTransaction);
|
||||
_context.Inventory.Update(inventory);
|
||||
|
||||
msg += $" {inventory.Description}: Updated from {sample.OriginalQty} to {sample.SelectedQty} units.";
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
private async Task<string> HandleInventoryInsert(PlanningDto dto, string msg,
|
||||
string username, DateTime now, string mrRawPlanId)
|
||||
{
|
||||
if (dto.SelectedSamples == null || !dto.SelectedSamples.Any())
|
||||
return msg;
|
||||
|
||||
if (!Guid.TryParse(mrRawPlanId, out var planGuid))
|
||||
{
|
||||
return msg + " Invalid plan ID for inventory insert.";
|
||||
}
|
||||
|
||||
var inventoryWarnings = new List<string>();
|
||||
|
||||
foreach (var sample in dto.SelectedSamples)
|
||||
{
|
||||
var inventory = await _context.Inventory
|
||||
.FirstOrDefaultAsync(i => i.InventoryId == sample.InventoryId);
|
||||
|
||||
if (inventory == null)
|
||||
{
|
||||
inventoryWarnings.Add($"{sample.InventoryId}: Not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate quantity difference
|
||||
int newQtyBalance = inventory.QtyIn - sample.SelectedQty;
|
||||
|
||||
|
||||
// Create transaction
|
||||
var inventoryTransaction = new InventoryTransaction
|
||||
{
|
||||
InventoryId = sample.InventoryId,
|
||||
DoctorId = dto.DoctorId,
|
||||
Description = sample.Description ?? inventory.Description ?? "Sample",
|
||||
MRRawPlanId = planGuid,
|
||||
AppointmentDate = dto.CallDate,
|
||||
CreatedDate = now,
|
||||
QtyIn = sample.SelectedQty,
|
||||
QtyOut = sample.SelectedQty,
|
||||
UserId = dto.UserId,
|
||||
CreatedBy = username,
|
||||
IsActive = true
|
||||
};
|
||||
_context.InventoryTransactions.Add(inventoryTransaction);
|
||||
|
||||
// Deduct from inventory
|
||||
inventory.QtyBalance = newQtyBalance;
|
||||
inventory.QtyOut = sample.SelectedQty;
|
||||
inventory.UpdatedBy = username;
|
||||
inventory.UpdatedDate = now;
|
||||
_context.Inventory.Update(inventory);
|
||||
}
|
||||
|
||||
if (inventoryWarnings.Any())
|
||||
{
|
||||
msg += $" (Inventory warnings: {string.Join("; ", inventoryWarnings)})";
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
private async Task<string> HandlePromosDelete(PlanningDto dto, string msg)
|
||||
{
|
||||
if (dto.SelectedPromos == null || !dto.SelectedPromos.Any())
|
||||
return msg;
|
||||
|
||||
if (string.IsNullOrEmpty(dto.MRRawPlanId))
|
||||
{
|
||||
return msg + " Cannot delete promos: Missing MRRawPlanId.";
|
||||
}
|
||||
|
||||
var mrRawPlanGuid = Guid.Parse(dto.MRRawPlanId);
|
||||
|
||||
var promosToDelete = await _context.ProductTransactions
|
||||
.Where(pt => pt.MRRawPlanId == mrRawPlanGuid)
|
||||
.ToListAsync();
|
||||
|
||||
_context.ProductTransactions.RemoveRange(promosToDelete);
|
||||
|
||||
return msg + $" Deleted {promosToDelete.Count} promo(s).";
|
||||
}
|
||||
|
||||
private async Task<string> HandlePromosUpdate(PlanningDto dto, string msg,
|
||||
string username, DateTime now, string mrRawPlanId)
|
||||
{
|
||||
if (dto.SelectedPromos == null || !dto.SelectedPromos.Any())
|
||||
return msg;
|
||||
|
||||
if (string.IsNullOrEmpty(dto.MRRawPlanId))
|
||||
{
|
||||
return msg + " Cannot update promos: Missing MRRawPlanId.";
|
||||
}
|
||||
|
||||
var mrRawPlanGuid = Guid.Parse(dto.MRRawPlanId);
|
||||
|
||||
// Delete existing promos
|
||||
var existingPromos = await _context.ProductTransactions
|
||||
.Where(pt => pt.MRRawPlanId == mrRawPlanGuid)
|
||||
.ToListAsync();
|
||||
|
||||
_context.ProductTransactions.RemoveRange(existingPromos);
|
||||
|
||||
// Insert new promos
|
||||
foreach (var product in dto.SelectedPromos)
|
||||
{
|
||||
var productTransaction = new ProductTransaction
|
||||
{
|
||||
ProductId = product.ProductId,
|
||||
DoctorId = dto.DoctorId,
|
||||
ProductName = product.ProductName,
|
||||
AppointmentDate = dto.CallDate,
|
||||
MRRawPlanId = mrRawPlanGuid,
|
||||
CreatedDate = now,
|
||||
CreatedBy = username,
|
||||
UserId = dto.UserId,
|
||||
IsActive = true
|
||||
};
|
||||
|
||||
_context.ProductTransactions.Add(productTransaction);
|
||||
}
|
||||
|
||||
return msg + $" Updated promos: removed {existingPromos.Count}, added {dto.SelectedPromos.Count}.";
|
||||
}
|
||||
|
||||
private async Task<string> HandlePromosInsert(PlanningDto dto, string msg,
|
||||
string username, DateTime now, string mrRawPlanId)
|
||||
{
|
||||
if (dto.SelectedPromos == null || !dto.SelectedPromos.Any())
|
||||
return msg;
|
||||
|
||||
if (!Guid.TryParse(mrRawPlanId, out var planGuid))
|
||||
{
|
||||
return msg + " Invalid plan ID for promo insert.";
|
||||
}
|
||||
|
||||
foreach (var product in dto.SelectedPromos)
|
||||
{
|
||||
var productTransaction = new ProductTransaction
|
||||
{
|
||||
ProductId = product.ProductId,
|
||||
DoctorId = dto.DoctorId,
|
||||
ProductName= product.ProductName,
|
||||
AppointmentDate = dto.CallDate,
|
||||
MRRawPlanId = planGuid,
|
||||
CreatedDate = now,
|
||||
CreatedBy = username,
|
||||
UserId = dto.UserId,
|
||||
IsActive = true
|
||||
};
|
||||
|
||||
_context.ProductTransactions.Add(productTransaction);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
public async Task<Response> ApprovePlan(ApprovePlanDto dto)
|
||||
{
|
||||
if (dto == null || dto.AppointmentIds == null || !dto.AppointmentIds.Any())
|
||||
return new Response { Success = false, Message = "No appointments selected", MessCode = 0 };
|
||||
|
||||
// Filter out empty GUIDs
|
||||
var guidList = dto.AppointmentIds.Where(id => id != Guid.Empty).ToList();
|
||||
|
||||
if (!guidList.Any())
|
||||
return new Response { Success = false, Message = "No valid appointment ids", MessCode = 0 };
|
||||
|
||||
using var tx = await _context.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
var plans = await _context.MRRawPlans
|
||||
.Where(p => guidList.Contains(p.MRRawPlanId))
|
||||
.ToListAsync();
|
||||
|
||||
if (!plans.Any())
|
||||
return new Response { Success = false, Message = "No matching appointments found", MessCode = 0 };
|
||||
|
||||
int approvedCount = 0;
|
||||
int alreadyApproved = 0;
|
||||
var now = DateTime.UtcNow;
|
||||
var userIdOrSystem = dto.UpdatedBy ?? "System";
|
||||
|
||||
foreach (var plan in plans)
|
||||
{
|
||||
if (plan.IsApproved == true)
|
||||
{
|
||||
alreadyApproved++;
|
||||
continue;
|
||||
}
|
||||
plan.IsApproved = true;
|
||||
plan.ApprovedBy = userIdOrSystem;
|
||||
plan.ApprovedDate = now;
|
||||
approvedCount++;
|
||||
}
|
||||
|
||||
if (approvedCount > 0)
|
||||
{
|
||||
_context.MRRawPlans.UpdateRange(plans.Where(p => p.IsApproved == true));
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await tx.CommitAsync();
|
||||
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = $"{approvedCount} approved, {alreadyApproved} already approved.",
|
||||
MessCode = 1
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await tx.RollbackAsync();
|
||||
return new Response { Success = false, Message = $"Error: {ex.Message}", MessCode = 0 };
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Response> CopyMonthlyMRRawPlans(PlanningDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (messCode, message) = CreateOutputParams();
|
||||
|
||||
// Execute the stored procedure
|
||||
await _context.Database.ExecuteSqlRawAsync(
|
||||
"EXEC CopyMonthlyMRRawPlans @MonthPlanCopy,@NewMonthPlan,@UserId,@MessCode OUTPUT,@Message OUTPUT",
|
||||
new SqlParameter("@MonthPlanCopy", dto.MonthPlanCopy),
|
||||
new SqlParameter("@NewMonthPlan", dto.NewMonthPlan),
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
messCode,
|
||||
message);
|
||||
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = message.Value.ToString(),
|
||||
MessCode = (byte)(messCode.Value ?? 0)
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
return new Response
|
||||
{
|
||||
Success = true,
|
||||
Message = ex.ToString() ?? ex.InnerException.ToString(),
|
||||
MessCode = 0
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
public async Task<List<DoctorWithPlan>> GetDoctorWithPlan(DoctorDto dto)
|
||||
{
|
||||
string spName = dto.IsPlanning
|
||||
? "GetDoctorWithPlan"
|
||||
: "GetDoctorWithPlanStatus";
|
||||
|
||||
var allItems = await _context.DoctorWithPlan
|
||||
.FromSqlRaw(
|
||||
$"EXEC {spName} @UserId, @MRPlanDate",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@MRPlanDate", dto.MRPlanDate))
|
||||
.ToListAsync();
|
||||
|
||||
return allItems ?? new List<DoctorWithPlan>();
|
||||
}
|
||||
|
||||
public async Task<List<MRRawPlan>> GetPlan(PlanningDto dto)
|
||||
{
|
||||
var allItems = await _context.MRRawPlans
|
||||
.FromSqlRaw("EXEC GetPlan @UserId",
|
||||
new SqlParameter("@UserId", dto.UserId))
|
||||
.ToListAsync();
|
||||
|
||||
return allItems ?? new List<MRRawPlan>();
|
||||
}
|
||||
public async Task<List<DoctorById>> GetDoctorById(DoctorDto dto)
|
||||
{
|
||||
var allItems = await _context.DoctorByIds
|
||||
.FromSqlRaw("EXEC GetDoctorById @UserId,@DoctorId,@MRPlanDate",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@DoctorId", dto.DoctorId),
|
||||
new SqlParameter("@MRPlanDate", dto.MRPlanDate))
|
||||
.ToListAsync();
|
||||
|
||||
return allItems ?? new List<DoctorById>();
|
||||
}
|
||||
public async Task<List<PendingApproval>> GetPendingApprovals(PlanningDto dto)
|
||||
{
|
||||
var mrPlanDate = GetValidDate(dto.MRPlanDate);
|
||||
|
||||
var allItems = await _context.PendingApprovals
|
||||
.FromSqlRaw(
|
||||
"EXEC GetPendingApprovals @UserId,@DoctorId,@MRPlanDate",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@DoctorId", dto.DoctorId),
|
||||
new SqlParameter("@MRPlanDate", mrPlanDate)
|
||||
)
|
||||
.ToListAsync();
|
||||
|
||||
return allItems ?? new List<PendingApproval>();
|
||||
}
|
||||
private static DateTime GetValidDate(object? input)
|
||||
{
|
||||
if (input is DateTime dt)
|
||||
return dt;
|
||||
|
||||
if (input is string s && DateTime.TryParse(s, out var parsed))
|
||||
return parsed;
|
||||
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public async Task<List<MRRawPlan>> GetAllAppointmentToday(CredentialDto dto)
|
||||
{
|
||||
var allItems = await _context.MRRawPlans
|
||||
.FromSqlRaw("EXEC GetAllAppointmentToday @UserId",
|
||||
new SqlParameter("@UserId", dto.UserId))
|
||||
.ToListAsync();
|
||||
|
||||
return allItems ?? new List<MRRawPlan>();
|
||||
}
|
||||
public async Task<List<MRRawPlan>> GetAllMissReschedule(CredentialDto dto)
|
||||
{
|
||||
var allItems = await _context.MRRawPlans
|
||||
.FromSqlRaw("EXEC GetAllMissReschedule @UserId,@IsMissed,@IsReschedule",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@IsMissed", dto.IsMissed),
|
||||
new SqlParameter("@IsReschedule", dto.IsReschedule))
|
||||
.ToListAsync();
|
||||
|
||||
return allItems ?? new List<MRRawPlan>();
|
||||
}
|
||||
|
||||
public async Task<List<InventoryTransaction>> GetSampleTransaction(Guid mrRawPlanId)
|
||||
{
|
||||
var allItems = await _context.InventoryTransactions
|
||||
.FromSqlRaw("EXEC GetSampleTransaction @MRRawPlanId",
|
||||
new SqlParameter("@MRRawPlanId", mrRawPlanId)).ToListAsync();
|
||||
|
||||
return allItems ?? new List<InventoryTransaction>();
|
||||
}
|
||||
|
||||
public async Task<List<ProductTransaction>> GetProductTransaction(Guid mrRawPlanId)
|
||||
{
|
||||
var allItems = await _context.ProductTransactions
|
||||
.FromSqlRaw("EXEC GetProductTransaction @MRRawPlanId",
|
||||
new SqlParameter("@MRRawPlanId", mrRawPlanId)).ToListAsync();
|
||||
|
||||
return allItems ?? new List<ProductTransaction>();
|
||||
}
|
||||
|
||||
public async Task<List<MRRawPlansDto>> GetMRRawPlans(PlanByUserIdRequest dto)
|
||||
{
|
||||
var plans = await _context.MRRawPlansDto
|
||||
.FromSqlRaw("EXEC GetMRRawPlans @UserId",
|
||||
new SqlParameter("@UserId", dto.UserId)).ToListAsync();
|
||||
|
||||
return plans ?? new List<MRRawPlansDto>();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
130
LSFE.Domain/Services/Reports/ReportsRepo.cs
Normal file
130
LSFE.Domain/Services/Reports/ReportsRepo.cs
Normal file
@ -0,0 +1,130 @@
|
||||
using LSFE.Domain.Contracts.Reports;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using LSFE.Infrastructure.Dto.Reports;
|
||||
using LSFE.Infrastructure.Entities.Reports;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Services.Reports
|
||||
{
|
||||
public class ReportsRepo : IReportsRepo
|
||||
{
|
||||
private readonly LSFEDbContext _context;
|
||||
public ReportsRepo(LSFEDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
protected Task<(DateTime StartDate, DateTime EndDate)>
|
||||
GetCurrentWholeMonth()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var startDate = new DateTime(
|
||||
now.Year,
|
||||
now.Month,
|
||||
1,
|
||||
0, 0, 0,
|
||||
DateTimeKind.Utc
|
||||
);
|
||||
|
||||
var endDate = startDate.AddMonths(1);
|
||||
|
||||
return Task.FromResult((startDate, endDate));
|
||||
}
|
||||
public async Task<List<CallRateReport>> GetCallRateReport(ReportsDto dto)
|
||||
{
|
||||
var (startDate, endDate) = await GetCurrentWholeMonth();
|
||||
|
||||
var allItems = await _context.CallRateReports
|
||||
.FromSqlRaw(
|
||||
"EXEC GetCallRateReport @UserId,@StartDate,@EndDate,@Period",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@StartDate", dto.StartDate ?? startDate),
|
||||
new SqlParameter("@EndDate", dto.EndDate ?? endDate),
|
||||
new SqlParameter("@Period", dto.Period ?? "monthly")).ToListAsync();
|
||||
|
||||
return allItems ?? new List<CallRateReport>();
|
||||
}
|
||||
|
||||
public async Task<List<CallReachReport>> GetCallReachReport(ReportsDto dto)
|
||||
{
|
||||
var (startDate, endDate) = await GetCurrentWholeMonth();
|
||||
|
||||
var allItems = await _context.CallReachReports
|
||||
.FromSqlRaw(
|
||||
"EXEC GetCallReachReport @UserId,@StartDate,@EndDate,@Period",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@StartDate", dto.StartDate ?? startDate),
|
||||
new SqlParameter("@EndDate", dto.EndDate ?? endDate),
|
||||
new SqlParameter("@Period", dto.Period ?? "monthly")).ToListAsync();
|
||||
|
||||
return allItems ?? new List<CallReachReport>();
|
||||
}
|
||||
|
||||
public async Task<List<CatchUpCallReport>> GetCatchUpCallReport(ReportsDto dto)
|
||||
{
|
||||
var (startDate, endDate) = await GetCurrentWholeMonth();
|
||||
|
||||
var allItems = await _context.CatchUpCallReports
|
||||
.FromSqlRaw(
|
||||
"EXEC GetCatchUpCallReport @UserId,@StartDate,@EndDate,@Period",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@StartDate", dto.StartDate ?? startDate),
|
||||
new SqlParameter("@EndDate", dto.EndDate ?? endDate),
|
||||
new SqlParameter("@Period", dto.Period ?? "monthly")).ToListAsync();
|
||||
|
||||
return allItems ?? new List<CatchUpCallReport>();
|
||||
}
|
||||
public async Task<List<ActualCoverageReport>> GetActualCoverageReport(ReportsDto dto)
|
||||
{
|
||||
var (startDate, endDate) = await GetCurrentWholeMonth();
|
||||
|
||||
var allItems = await _context.ActualCoverageReports
|
||||
.FromSqlRaw(
|
||||
"EXEC GetActualCoverageReport @UserId,@StartDate,@EndDate,@Period",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@StartDate", dto.StartDate ?? startDate),
|
||||
new SqlParameter("@EndDate", dto.EndDate ?? endDate),
|
||||
new SqlParameter("@Period", dto.Period ?? "monthly")).ToListAsync();
|
||||
|
||||
return allItems ?? new List<ActualCoverageReport>();
|
||||
}
|
||||
public async Task<bool> PutMissedCall(ReportsDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var catchUp = await _context.CatchUpCalls
|
||||
.SingleOrDefaultAsync(c => c.MRRawPlanId == dto.MRRawPlanId);
|
||||
|
||||
if (catchUp != null)
|
||||
{
|
||||
catchUp.MissedReason = dto.MissedReason;
|
||||
}
|
||||
|
||||
var plan = await _context.MRRawPlans
|
||||
.SingleOrDefaultAsync(c => c.MRRawPlanId == dto.MRRawPlanId);
|
||||
|
||||
if (plan != null)
|
||||
{
|
||||
plan.MissedReason = dto.MissedReason;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return plan?.MissedReason != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.ToString();
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
177
LSFE.Domain/Services/Security/RoleAuthorizationCache.cs
Normal file
177
LSFE.Domain/Services/Security/RoleAuthorizationCache.cs
Normal file
@ -0,0 +1,177 @@
|
||||
using LSFE.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace LSFE.Domain.Services.Security
|
||||
{
|
||||
public interface IRoleAuthorizationCache
|
||||
{
|
||||
Task<List<string>> GetAllowedRoleIdsAsync(string controller);
|
||||
Task<bool> UserHasAccessAsync(string userId, string controller);
|
||||
Task<List<string>> GetUserRoleIdsAsync(string userId);
|
||||
void InvalidateCache(string controller);
|
||||
void InvalidateUserCache(string userId);
|
||||
void InvalidateAllCache();
|
||||
}
|
||||
|
||||
public class RoleAuthorizationCache : IRoleAuthorizationCache
|
||||
{
|
||||
private readonly IMemoryCache _cache;
|
||||
private readonly LSFEDbContext _dbContext;
|
||||
private readonly ILogger<RoleAuthorizationCache> _logger;
|
||||
|
||||
private const string CONTROLLER_ROLES_PREFIX = "controller_roles_";
|
||||
private const string USER_ROLES_PREFIX = "user_roles_";
|
||||
private const int CACHE_DURATION_MINUTES = 30;
|
||||
|
||||
public RoleAuthorizationCache(
|
||||
IMemoryCache cache,
|
||||
LSFEDbContext dbContext,
|
||||
ILogger<RoleAuthorizationCache> logger)
|
||||
{
|
||||
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
|
||||
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all role IDs that have access to a specific controller (CACHED)
|
||||
/// </summary>
|
||||
public async Task<List<string>> GetAllowedRoleIdsAsync(string controller)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(controller))
|
||||
throw new ArgumentException("Controller name cannot be null or empty", nameof(controller));
|
||||
|
||||
var cacheKey = $"{CONTROLLER_ROLES_PREFIX}{controller}";
|
||||
|
||||
return await _cache.GetOrCreateAsync(cacheKey, async entry =>
|
||||
{
|
||||
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CACHE_DURATION_MINUTES);
|
||||
entry.SetPriority(CacheItemPriority.High);
|
||||
|
||||
_logger.LogInformation("Cache MISS: Loading role permissions for controller: {Controller}", controller);
|
||||
|
||||
var roleIds = await _dbContext.AuthorizeRoles
|
||||
.Where(ar => ar.IsActive && ar.Controller == controller)
|
||||
.Select(ar => ar.RoleId)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
_logger.LogInformation("Cached {Count} roles for controller: {Controller}", roleIds.Count, controller);
|
||||
|
||||
return roleIds ?? new List<string>();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all role IDs for a specific user (CACHED)
|
||||
/// </summary>
|
||||
public async Task<List<string>> GetUserRoleIdsAsync(string userId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
throw new ArgumentException("User ID cannot be null or empty", nameof(userId));
|
||||
|
||||
var cacheKey = $"{USER_ROLES_PREFIX}{userId}";
|
||||
|
||||
return await _cache.GetOrCreateAsync(cacheKey, async entry =>
|
||||
{
|
||||
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CACHE_DURATION_MINUTES);
|
||||
entry.SetPriority(CacheItemPriority.High);
|
||||
|
||||
_logger.LogInformation("Cache MISS: Loading roles for user: {UserId}", userId);
|
||||
|
||||
var userRoleIds = await _dbContext.UserRoles
|
||||
.Where(ur => ur.UserId == userId)
|
||||
.Select(ur => ur.RoleId)
|
||||
.ToListAsync();
|
||||
|
||||
_logger.LogInformation("Cached {Count} roles for user: {UserId}", userRoleIds.Count, userId);
|
||||
|
||||
return userRoleIds ?? new List<string>();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a specific user has access to a controller (FULLY CACHED)
|
||||
/// </summary>
|
||||
public async Task<bool> UserHasAccessAsync(string userId, string controller)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
throw new ArgumentException("User ID cannot be null or empty", nameof(userId));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(controller))
|
||||
throw new ArgumentException("Controller name cannot be null or empty", nameof(controller));
|
||||
|
||||
try
|
||||
{
|
||||
// Get allowed role IDs from cache (or database if cache miss)
|
||||
var allowedRoleIds = await GetAllowedRoleIdsAsync(controller);
|
||||
|
||||
if (!allowedRoleIds.Any())
|
||||
{
|
||||
_logger.LogWarning("No roles configured for controller: {Controller}", controller);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get user's role IDs from cache (or database if cache miss)
|
||||
var userRoleIds = await GetUserRoleIdsAsync(userId);
|
||||
|
||||
if (!userRoleIds.Any())
|
||||
{
|
||||
_logger.LogWarning("User {UserId} has no roles assigned", userId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user has any of the required roles (IN-MEMORY operation, no database!)
|
||||
var hasAccess = userRoleIds.Any(userRole => allowedRoleIds.Contains(userRole));
|
||||
|
||||
_logger.LogDebug("User {UserId} access to {Controller}: {HasAccess}", userId, controller, hasAccess);
|
||||
|
||||
return hasAccess;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error checking access for user {UserId} on controller {Controller}",
|
||||
userId, controller);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidate cache for a specific controller
|
||||
/// </summary>
|
||||
public void InvalidateCache(string controller)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(controller))
|
||||
return;
|
||||
|
||||
var cacheKey = $"{CONTROLLER_ROLES_PREFIX}{controller}";
|
||||
_cache.Remove(cacheKey);
|
||||
|
||||
_logger.LogInformation("Cache invalidated for controller: {Controller}", controller);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidate cache for a specific user (call this when user roles change)
|
||||
/// </summary>
|
||||
public void InvalidateUserCache(string userId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
return;
|
||||
|
||||
var cacheKey = $"{USER_ROLES_PREFIX}{userId}";
|
||||
_cache.Remove(cacheKey);
|
||||
|
||||
_logger.LogInformation("Cache invalidated for user: {UserId}", userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidate all controller role caches
|
||||
/// </summary>
|
||||
public void InvalidateAllCache()
|
||||
{
|
||||
_logger.LogWarning("All cache invalidation requested - consider implementing a cache key tracking system");
|
||||
}
|
||||
}
|
||||
}
|
||||
108
LSFE.Domain/Services/TimeKeeping/AttendanceRepo.cs
Normal file
108
LSFE.Domain/Services/TimeKeeping/AttendanceRepo.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using LSFE.Domain.Contracts.Planning;
|
||||
using LSFE.Domain.Contracts.TimeKeeping;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using LSFE.Infrastructure.Dto;
|
||||
using LSFE.Infrastructure.Dto.TimeKeeping;
|
||||
using LSFE.Infrastructure.Entities.Planning;
|
||||
using LSFE.Infrastructure.Entities.TimeKeeping;
|
||||
using LSFE.Infrastructure.Model;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Services.TimeKeeping
|
||||
{
|
||||
public class AttendanceRepo : GenericRepository<Attendance>, IAttendanceRepo
|
||||
{
|
||||
private readonly LSFEDbContext _context;
|
||||
public AttendanceRepo(LSFEDbContext context) : base(context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
public async Task<List<Infrastructure.Entities.TimeKeeping.AttendanceDto>>
|
||||
GetAttendance(Infrastructure.Dto.TimeKeeping.AttendanceDto dto)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var startDate = new DateTime(now.Year,now.Month,1,0, 0, 0,DateTimeKind.Utc);
|
||||
var endDate = startDate.AddMonths(1);
|
||||
|
||||
|
||||
var attendance = await _context.AttendanceDto
|
||||
.FromSqlRaw(
|
||||
"EXEC GetAttendance @UserId,@StartDate,@EndDate,@Period",
|
||||
new SqlParameter("@UserId", dto.UserId),
|
||||
new SqlParameter("@StartDate", dto.StartDate ?? startDate),
|
||||
new SqlParameter("@EndDate", dto.EndDate ?? endDate),
|
||||
new SqlParameter("@Period", dto.Period ?? "monthly")).ToListAsync();
|
||||
|
||||
return attendance ?? new List<Infrastructure.Entities.TimeKeeping.AttendanceDto>();
|
||||
}
|
||||
|
||||
public async Task<List<Attendance>> GetAttendanceToday(CredentialDto dto)
|
||||
{
|
||||
var attendance = await _context.Attendances
|
||||
.FromSqlRaw("EXEC GetAttendanceToday @UserId",
|
||||
new SqlParameter("@UserId", dto.UserId))
|
||||
.ToListAsync();
|
||||
|
||||
return attendance ?? new List<Attendance>();
|
||||
}
|
||||
|
||||
public async Task<Response> PostPutAttendance(Infrastructure.Dto.TimeKeeping.AttendanceDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Query by AppAttendanceId (the mobile app's ID), not AttendanceId (the DB auto-increment)
|
||||
var logs = await _context.Attendances
|
||||
.FirstOrDefaultAsync(a => a.UserId == dto.UserId &&
|
||||
a.AppAttendanceId == dto.AppAttendanceId);
|
||||
|
||||
if (logs == null)
|
||||
{
|
||||
// Create new record - don't set AttendanceId, let the DB auto-increment it
|
||||
var attendance = new Attendance
|
||||
{
|
||||
AppAttendanceId = dto.AppAttendanceId,
|
||||
TimeIn = dto.TimeIn,
|
||||
UserId = dto.UserId,
|
||||
TimeOut = dto.TimeOut,
|
||||
TimeInLocation = dto.TimeInLocation,
|
||||
TimeOutLocation = dto.TimeOutLocation,
|
||||
SignatureFileNameIn = dto.SignatureFileNameIn,
|
||||
};
|
||||
await _context.AddAsync(attendance);
|
||||
await _context.SaveChangesAsync(); // MUST SAVE!
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update existing record
|
||||
logs.TimeIn = dto.TimeIn;
|
||||
logs.TimeOut = dto.TimeOut;
|
||||
logs.TimeInLocation = dto.TimeInLocation;
|
||||
logs.TimeOutLocation = dto.TimeOutLocation;
|
||||
logs.SignatureFileNameOut = dto.SignatureFileNameOut;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return new Response
|
||||
{
|
||||
Message = "Sync Successfully",
|
||||
MessCode = 1,
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the actual error
|
||||
Debug.WriteLine($"PostPutAttendance Error: {ex.ToString()}");
|
||||
throw; // Re-throw so the controller can log it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
LSFE.Domain/Services/UnitOfWork.cs
Normal file
86
LSFE.Domain/Services/UnitOfWork.cs
Normal file
@ -0,0 +1,86 @@
|
||||
using LSFE.Domain.Contracts;
|
||||
using LSFE.Domain.Contracts.Doctor;
|
||||
using LSFE.Domain.Contracts.Maintenance;
|
||||
using LSFE.Domain.Contracts.Planning;
|
||||
using LSFE.Domain.Contracts.Reports;
|
||||
using LSFE.Domain.Contracts.TimeKeeping;
|
||||
using LSFE.Domain.Services.Doctor;
|
||||
using LSFE.Domain.Services.Maintenance;
|
||||
using LSFE.Domain.Services.Planning;
|
||||
using LSFE.Domain.Services.Reports;
|
||||
using LSFE.Domain.Services.TimeKeeping;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using LSFE.Infrastructure.Entities.Admin;
|
||||
using LSFE.Infrastructure.Entities.Doctor;
|
||||
using LSFE.Infrastructure.Entities.Maintenance;
|
||||
using LSFE.Infrastructure.Entities.Planning;
|
||||
using LSFE.Infrastructure.Entities.TimeKeeping;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Domain.Services
|
||||
{
|
||||
public class UnitOfWork : IUnitOfWork
|
||||
{
|
||||
private readonly LSFEDbContext _context;
|
||||
public IErrorLogger ErrorLogger { get; }
|
||||
public IPlanningRepo Planning { get; }
|
||||
public IDoctorRepo Doctors { get; }
|
||||
public IAttendanceRepo Attendances {get; }
|
||||
public IMaintenanceRepo<Areas> Areas { get; }
|
||||
public IMaintenanceRepo<Districts> Districts { get; }
|
||||
public IMaintenanceRepo<Divisions> Divisions { get; }
|
||||
public IMaintenanceRepo<Territories> Territories { get; }
|
||||
public IMaintenanceRepo<Institutions> Institutions { get; }
|
||||
public IMaintenanceRepo<InstitutionCategory> InstitutionCategories { get; }
|
||||
public IMaintenanceRepo<Products> Products { get; }
|
||||
public IMaintenanceRepo<Specialization> Specializations { get; }
|
||||
public IMaintenanceRepo<Inventory> Inventory { get; }
|
||||
public IMaintenanceRepo<InventoryTransaction> InventoryTransactions { get; }
|
||||
public IMaintenanceRepo<ProductTransaction> ProductTransactions { get; }
|
||||
public IMaintenanceRepo<MRRawPlan> MRRawPlans { get; }
|
||||
public IMaintenanceRepo<CatchUpCall> CatchUpCalls { get; }
|
||||
public IMaintenanceRepo<MRPlanDetail> MRPlanDetails { get; }
|
||||
public IMaintenanceRepo<MRPlanHeader> MRPlanHeaders { get; }
|
||||
public IMaintenanceRepo<UserDevice> UserDevices { get; }
|
||||
public IReportsRepo Reports { get; }
|
||||
public UnitOfWork(LSFEDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
ErrorLogger = new ErrorLogger(_context);
|
||||
Doctors = new DoctorRepo(_context);
|
||||
Planning = new PlanningRepo(_context);
|
||||
Reports = new ReportsRepo(_context);
|
||||
Attendances = new AttendanceRepo(_context);
|
||||
Areas = new MaintenanceRepo<Areas>(_context);
|
||||
Districts = new MaintenanceRepo<Districts>(_context);
|
||||
Divisions = new MaintenanceRepo<Divisions>(_context);
|
||||
Territories = new MaintenanceRepo<Territories>(_context);
|
||||
Institutions = new MaintenanceRepo<Institutions>(_context);
|
||||
InstitutionCategories = new MaintenanceRepo<InstitutionCategory>(_context);
|
||||
Products = new MaintenanceRepo<Products>(_context);
|
||||
Specializations = new MaintenanceRepo<Specialization>(_context);
|
||||
Inventory = new MaintenanceRepo<Inventory>(_context);
|
||||
InventoryTransactions = new MaintenanceRepo<InventoryTransaction>(_context);
|
||||
ProductTransactions = new MaintenanceRepo<ProductTransaction>(_context);
|
||||
MRRawPlans= new MaintenanceRepo<MRRawPlan>(_context);
|
||||
CatchUpCalls = new MaintenanceRepo<CatchUpCall>(_context);
|
||||
MRPlanDetails = new MaintenanceRepo<MRPlanDetail>(_context);
|
||||
MRPlanHeaders = new MaintenanceRepo<MRPlanHeader>(_context);
|
||||
UserDevices = new MaintenanceRepo<UserDevice>(_context);
|
||||
|
||||
}
|
||||
|
||||
public async Task<int> SaveChangesAsync()
|
||||
{
|
||||
return await _context.SaveChangesAsync();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
_context.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
48
LSFE.Infrastructure/Converter/GuidListConverter.cs
Normal file
48
LSFE.Infrastructure/Converter/GuidListConverter.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Converter
|
||||
{
|
||||
public class GuidListConverter : JsonConverter<List<Guid>>
|
||||
{
|
||||
public override List<Guid> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var list = new List<Guid>();
|
||||
|
||||
if (reader.TokenType != JsonTokenType.StartArray)
|
||||
throw new JsonException();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.EndArray)
|
||||
return list;
|
||||
|
||||
if (reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
var str = reader.GetString();
|
||||
if (Guid.TryParse(str, out var guid))
|
||||
list.Add(guid);
|
||||
}
|
||||
else if (reader.TokenType == JsonTokenType.PropertyName)
|
||||
{
|
||||
// Handle object format if needed
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, List<Guid> value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStartArray();
|
||||
foreach (var guid in value)
|
||||
writer.WriteStringValue(guid.ToString());
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
154
LSFE.Infrastructure/Database/LSFEDbContext.cs
Normal file
154
LSFE.Infrastructure/Database/LSFEDbContext.cs
Normal file
@ -0,0 +1,154 @@
|
||||
using LSFE.Infrastructure.Dto.Planning;
|
||||
using LSFE.Infrastructure.Entities;
|
||||
using LSFE.Infrastructure.Entities.Admin;
|
||||
using LSFE.Infrastructure.Entities.Auth;
|
||||
using LSFE.Infrastructure.Entities.Doctor;
|
||||
using LSFE.Infrastructure.Entities.Maintenance;
|
||||
using LSFE.Infrastructure.Entities.Planning;
|
||||
using LSFE.Infrastructure.Entities.Reports;
|
||||
using LSFE.Infrastructure.Entities.TimeKeeping;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Database
|
||||
{
|
||||
public class LSFEDbContext : IdentityDbContext<AppsUsers, Role, string>
|
||||
{
|
||||
public LSFEDbContext(DbContextOptions<LSFEDbContext> options) : base(options) { }
|
||||
public DbSet<Doctor> Doctors { get; set; }
|
||||
public DbSet<ForDoctorApproval> ForDoctorApprovals { get; set; }
|
||||
public DbSet<Departments> Departments { get; set; }
|
||||
public DbSet<Attachment> Attachments { get; set; }
|
||||
public DbSet<Areas> Areas { get; set; }
|
||||
public DbSet<Districts> Districts { get; set; }
|
||||
public DbSet<Institutions> Institutions { get; set; }
|
||||
public DbSet<Specialization> Specializations { get; set; }
|
||||
public DbSet<InstitutionCategory> InstitutionCategories { get; set; }
|
||||
public DbSet<Products> Products { get; set; }
|
||||
public DbSet<ProductTransaction> ProductTransactions { get; set; }
|
||||
public DbSet<InventoryTransaction> InventoryTransactions { get; set; }
|
||||
public DbSet<Territories> Territories { get; set; }
|
||||
public DbSet<Divisions> Divisions { get; set; }
|
||||
public DbSet<MRRawPlan> MRRawPlans { get; set; }
|
||||
public DbSet<MRRawPlansDto> MRRawPlansDto { get; set; }
|
||||
public DbSet<CatchUpCall> CatchUpCalls { get; set; }
|
||||
public DbSet<Inventory> Inventory { get; set; }
|
||||
public DbSet<CatchUpCallReport> CatchUpCallReports { get; set; }
|
||||
public DbSet<CallRateReport> CallRateReports { get; set; }
|
||||
public DbSet<CallReachReport> CallReachReports { get; set; }
|
||||
public DbSet<ActualCoverageReport> ActualCoverageReports { get; set; }
|
||||
public virtual DbSet<DoctorWithPlan> DoctorWithPlan { get; set; }
|
||||
public DbSet<DoctorCoverageToday> DoctorCoverageTodays { get; set; }
|
||||
public virtual DbSet<PendingApproval> PendingApprovals { get; set; }
|
||||
public virtual DbSet<DoctorById> DoctorByIds { get; set; }
|
||||
public DbSet<MRPlanDetail> MRPlanDetails { get; set; }
|
||||
public DbSet<MRPlanHeader> MRPlanHeaders { get; set; }
|
||||
public DbSet<Attendance> Attendances { get; set; }
|
||||
public virtual DbSet<Entities.TimeKeeping.AttendanceDto> AttendanceDto { get; set; }
|
||||
public DbSet<ErrorLogs> ErrorLogs { get; set; }
|
||||
public DbSet<UserDevice> UserDevices { get; set; }
|
||||
public DbSet<AuthorizeRoles> AuthorizeRoles { get; internal set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
modelBuilder.Entity<ActualCoverageReport>(entity =>
|
||||
{
|
||||
entity.ToTable("ActualCoverageReport");
|
||||
entity.HasNoKey();
|
||||
});
|
||||
modelBuilder.Entity<Areas>()
|
||||
.Property(d => d.AreaId)
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
modelBuilder.Entity<Divisions>()
|
||||
.Property(d => d.DivisionId)
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
modelBuilder.Entity<Doctor>()
|
||||
.HasOne(d => d.Institution)
|
||||
.WithMany(i => i.Doctors)
|
||||
.HasForeignKey(d => d.InstitutionId);
|
||||
|
||||
modelBuilder.Entity<Doctor>()
|
||||
.HasOne(d => d.Districts)
|
||||
.WithMany(i => i.Doctors)
|
||||
.HasForeignKey(d => d.DistrictId);
|
||||
|
||||
modelBuilder.Entity<Districts>(b =>
|
||||
{
|
||||
b.ToTable("Districts");
|
||||
b.HasOne(u => u.Areas)
|
||||
.WithMany()
|
||||
.HasForeignKey(u => u.AreaId)
|
||||
.IsRequired(false);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Institutions>(b =>
|
||||
{
|
||||
b.ToTable("Institutions");
|
||||
b.HasOne(u => u.Territories)
|
||||
.WithMany()
|
||||
.HasForeignKey(u => u.TerritoryId)
|
||||
.IsRequired(false);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Territories>(b =>
|
||||
{
|
||||
b.ToTable("Territories");
|
||||
b.HasOne(u => u.Districts)
|
||||
.WithMany()
|
||||
.HasForeignKey(u => u.DistrictId)
|
||||
.IsRequired(false);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<AppsUsers>(b =>
|
||||
{
|
||||
b.ToTable("Users");
|
||||
b.HasOne(u => u.Attachment)
|
||||
.WithMany()
|
||||
.HasForeignKey(u => u.AttachmentId)
|
||||
.IsRequired(false);
|
||||
|
||||
b.HasOne(u => u.Department)
|
||||
.WithMany()
|
||||
.HasForeignKey(u => u.DepartmentId)
|
||||
.IsRequired(false);
|
||||
});
|
||||
modelBuilder.Entity<Attachment>(b =>
|
||||
{
|
||||
b.ToTable("Attachment");
|
||||
|
||||
b.Property(d => d.AttachmentId)
|
||||
.ValueGeneratedOnAdd();
|
||||
});
|
||||
modelBuilder.Entity<Departments>(b =>
|
||||
{
|
||||
b.ToTable("Departments");
|
||||
});
|
||||
modelBuilder.Entity<IdentityUserClaim<string>>(b =>
|
||||
{
|
||||
b.ToTable("UserClaims");
|
||||
});
|
||||
modelBuilder.Entity<IdentityUserToken<string>>(b =>
|
||||
{
|
||||
b.ToTable("UserTokens");
|
||||
});
|
||||
modelBuilder.Entity<Role>(b =>
|
||||
{
|
||||
b.ToTable("Roles");
|
||||
});
|
||||
modelBuilder.Entity<IdentityUserRole<string>>(b =>
|
||||
{
|
||||
b.ToTable("UserRoles");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
29
LSFE.Infrastructure/Dto/Account/NavigationItemDto.cs
Normal file
29
LSFE.Infrastructure/Dto/Account/NavigationItemDto.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System.Security;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Account
|
||||
{
|
||||
public class NavigationItemDto
|
||||
{
|
||||
public required string Id { get; set; }
|
||||
public required string Label { get; set; }
|
||||
public required string Icon { get; set; }
|
||||
public string? Badge { get; set; }
|
||||
public required string Route { get; set; }
|
||||
public PermissionsDto? Permissions { get; set; }
|
||||
public List<NavigationItemDto>? Children { get; set; }
|
||||
public int? ParentModuleId { get; set; }
|
||||
}
|
||||
public class PermissionsDto
|
||||
{
|
||||
public bool CanView { get; set; }
|
||||
public bool CanCreate { get; set; }
|
||||
public bool CanEdit { get; set; }
|
||||
public bool CanDelete { get; set; }
|
||||
}
|
||||
public class NavigationResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public List<NavigationItemDto>? Data { get; set; }
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
}
|
||||
17
LSFE.Infrastructure/Dto/Account/UserClaimsDto.cs
Normal file
17
LSFE.Infrastructure/Dto/Account/UserClaimsDto.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Account
|
||||
{
|
||||
public class UserClaimsDto
|
||||
{
|
||||
public string UserId { get; init; } = default!;
|
||||
public string UserName { get; init; } = default!;
|
||||
public string FullName { get; init; } = default!;
|
||||
public string Company { get; init; } = default!;
|
||||
public IReadOnlyList<string> Roles { get; init; } = [];
|
||||
}
|
||||
}
|
||||
21
LSFE.Infrastructure/Dto/Admin/DeviceInfo.cs
Normal file
21
LSFE.Infrastructure/Dto/Admin/DeviceInfo.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Admin
|
||||
{
|
||||
public class DeviceInfoDto
|
||||
{
|
||||
public string? DeviceId { get; set; }
|
||||
public string? DeviceModel { get; set; }
|
||||
public string? Platform { get; set; }
|
||||
public string? Version { get; set; }
|
||||
public string? Manufacturer { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? DeviceType { get; set; }
|
||||
public string? VersionString { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
}
|
||||
}
|
||||
19
LSFE.Infrastructure/Dto/ApprovePlanDto.cs
Normal file
19
LSFE.Infrastructure/Dto/ApprovePlanDto.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using LSFE.Infrastructure.Converter;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto
|
||||
{
|
||||
public class ApprovePlanDto
|
||||
{
|
||||
[JsonConverter(typeof(GuidListConverter))]
|
||||
public List<Guid> AppointmentIds { get; set; } = new();
|
||||
public string? UserId { get; set; }
|
||||
public string? MRPlanDate { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
}
|
||||
}
|
||||
17
LSFE.Infrastructure/Dto/CredentialDto.cs
Normal file
17
LSFE.Infrastructure/Dto/CredentialDto.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto
|
||||
{
|
||||
public class CredentialDto
|
||||
{
|
||||
public string? UserRole { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public bool IsMissed { get; set; }
|
||||
public bool IsReschedule { get; set; }
|
||||
}
|
||||
}
|
||||
14
LSFE.Infrastructure/Dto/Doctor/CoverageDto.cs
Normal file
14
LSFE.Infrastructure/Dto/Doctor/CoverageDto.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Doctor
|
||||
{
|
||||
public class CoverageDto
|
||||
{
|
||||
public string? UpdatedBy { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
}
|
||||
}
|
||||
40
LSFE.Infrastructure/Dto/Doctor/DoctorDto.cs
Normal file
40
LSFE.Infrastructure/Dto/Doctor/DoctorDto.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Doctor
|
||||
{
|
||||
public class DoctorDto
|
||||
{
|
||||
public int DoctorId { get; set; } = 0;
|
||||
public string? MiddleInitial { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public byte MaxVisit { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public byte StageLevelId { get; set; }
|
||||
public DateTime? BirthDate { get; set; }
|
||||
public string? EmailAddress { get; set; }
|
||||
public int InstitutionId { get; set; }
|
||||
public byte Status { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public string? PhoneNo { get; set; } = "N/A";
|
||||
public string? MedRepName { get; set; }
|
||||
public byte SpecializationId { get; set; }
|
||||
public string? SpecializationName { get; set; }
|
||||
public string? LicenseNo { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? InstitutionName { get; set; }
|
||||
public int? DistrictId { get; set; }
|
||||
public string? DistrictName { get; set; }
|
||||
public string? MRPlanDate { get; set; }
|
||||
public bool IsTerritorial { get; set; }
|
||||
public bool IsPlanning { get; set; }
|
||||
public bool IsCatchUpCall { get; set; }=false;
|
||||
public string? UserName { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
}
|
||||
}
|
||||
33
LSFE.Infrastructure/Dto/Doctor/ForDoctorApprovalDto.cs
Normal file
33
LSFE.Infrastructure/Dto/Doctor/ForDoctorApprovalDto.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Doctor
|
||||
{
|
||||
public class ForDoctorApprovalDto
|
||||
{
|
||||
public Guid ForDoctorApprovalId { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? MiddleInitial { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? EmailAddress { get; set; }
|
||||
public string? PhoneNo { get; set; }
|
||||
public byte SpecializationId { get; set; }
|
||||
public string? LicenseNo { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public DateTime? BirthDate { get; set; }
|
||||
public byte MaxVisit { get; set; }
|
||||
public byte Status { get; set; }
|
||||
public int InstitutionId { get; set; }
|
||||
public string? InstitutionName { get; set; }
|
||||
public int? DistrictId { get; set; }
|
||||
public string? DistrictName { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public byte StageLevelId { get; set; }
|
||||
public bool IsTerritorial { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
}
|
||||
}
|
||||
22
LSFE.Infrastructure/Dto/Maintenance/InventoryDto.cs
Normal file
22
LSFE.Infrastructure/Dto/Maintenance/InventoryDto.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Maintenance
|
||||
{
|
||||
public class InventoryDto
|
||||
{
|
||||
public Guid InventoryId { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
public string? UserRole { get; set; }
|
||||
public int QtyOut { get; set; }
|
||||
public int QtyBalance { get; set; }
|
||||
public byte CycleMonth { get; set; }
|
||||
public int CycleYear { get; set; }
|
||||
public DateTime UpdatedDate { get; set; }
|
||||
}
|
||||
}
|
||||
16
LSFE.Infrastructure/Dto/Maintenance/ProductDto.cs
Normal file
16
LSFE.Infrastructure/Dto/Maintenance/ProductDto.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Maintenance
|
||||
{
|
||||
public class ProductDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
public string? UserRole { get; set; }
|
||||
}
|
||||
}
|
||||
23
LSFE.Infrastructure/Dto/Maintenance/ProductTransactionDto.cs
Normal file
23
LSFE.Infrastructure/Dto/Maintenance/ProductTransactionDto.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Maintenance
|
||||
{
|
||||
public class ProductTransactionDto
|
||||
{
|
||||
public Guid ProductTransId { get; set; }
|
||||
public int ProductId { get; set; }
|
||||
public string? ProductName { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public DateOnly? AppointmentDate { get; set; }
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public string? CreatedBy { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public string? ProductLink { get; set; }
|
||||
}
|
||||
}
|
||||
43
LSFE.Infrastructure/Dto/Planning/MRRawPlansDto.cs
Normal file
43
LSFE.Infrastructure/Dto/Planning/MRRawPlansDto.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Planning
|
||||
{
|
||||
[Keyless]
|
||||
public class MRRawPlansDto
|
||||
{
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public Guid? MRPlanDetailId { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public byte? VisitCount { get; set; }
|
||||
public DateOnly? CallDate { get; set; }
|
||||
public DateOnly? ActualCallDate { get; set; }
|
||||
public DateTime? StartTime { get; set; }
|
||||
public DateTime? EndTime { get; set; }
|
||||
public string? Longitude { get; set; }
|
||||
public string? Latitude { get; set; }
|
||||
public string? Location { get; set; }
|
||||
public bool IsSignature { get; set; }
|
||||
public string? SignatureFileName { get; set; }
|
||||
public bool IsMissed { get; set; }
|
||||
public string? MissedReason { get; set; }
|
||||
public bool IsDoctorProxy { get; set; }
|
||||
public bool IsReschedule { get; set; }
|
||||
public string? RescheduleReason { get; set; }
|
||||
public string? CallType { get; set; }
|
||||
public bool IsJoinCall { get; set; }
|
||||
public byte? CycleId { get; set; }
|
||||
public bool IsApproved { get; set; }
|
||||
public bool IsRecovered { get; set; }
|
||||
public string? SLP { get; set; }
|
||||
public string? ApprovedBy { get; set; }
|
||||
public DateTime? ApprovedDate { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public byte Status { get; set; }
|
||||
public bool IsCatchUpCall { get; set; } = false;
|
||||
}
|
||||
}
|
||||
14
LSFE.Infrastructure/Dto/Planning/PlanByUserIdRequest.cs
Normal file
14
LSFE.Infrastructure/Dto/Planning/PlanByUserIdRequest.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Planning
|
||||
{
|
||||
public class PlanByUserIdRequest
|
||||
{
|
||||
public string? UserId { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Planning.Request
|
||||
{
|
||||
public class PlanForApprovalRequest
|
||||
{
|
||||
}
|
||||
}
|
||||
56
LSFE.Infrastructure/Dto/PlanningDto.cs
Normal file
56
LSFE.Infrastructure/Dto/PlanningDto.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto
|
||||
{
|
||||
public class PlanningDto
|
||||
{
|
||||
public class SelectedSampleDto
|
||||
{
|
||||
public Guid InventoryId { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int MaxQty { get; set; }
|
||||
public int OriginalQty { get; set; }
|
||||
public int QtyBalance { get; set; }
|
||||
public int SelectedQty { get; set; }
|
||||
}
|
||||
public class SelectedPromoDto
|
||||
{
|
||||
public int ProductId { get; set; }
|
||||
public string? ProductName { get; set; }
|
||||
}
|
||||
|
||||
public List<SelectedSampleDto> SelectedSamples { get; set; } = new();
|
||||
public List<SelectedPromoDto> SelectedPromos { get; set; } = new();
|
||||
public string? MRPlanDetailId { get; set; }=null;
|
||||
public int DoctorId { get; set; }
|
||||
[DataType(DataType.Date)]
|
||||
public DateOnly PlanDate { get; set; }
|
||||
public int DivisionId { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public string? MRRawPlanId { get; set; } = null;
|
||||
public int ProductId { get; set; }
|
||||
[DataType(DataType.Date)]
|
||||
public DateOnly CallDate { get; set; }
|
||||
public string? RescheduleReason { get; set; }
|
||||
public string? CallType { get; set; }
|
||||
public byte CycleId { get; set; }
|
||||
public bool IsApproved { get; set; } = false;
|
||||
public bool IsRecovered { get; set; } = false;
|
||||
public bool IsLiterature { get; set; }
|
||||
public string? SLP { get; set; }
|
||||
public byte IsUpdateDelete { get; set; }
|
||||
public string? MRPlanDate { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public byte CycleMonth { get; set; }
|
||||
public int CycleYear { get; set; }
|
||||
public DateOnly MonthPlanCopy { get; set; }
|
||||
public DateOnly NewMonthPlan { get; set; }
|
||||
}
|
||||
}
|
||||
25
LSFE.Infrastructure/Dto/RegisterUserDto.cs
Normal file
25
LSFE.Infrastructure/Dto/RegisterUserDto.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto
|
||||
{
|
||||
public class RegisterUserDto
|
||||
{
|
||||
public string? UserName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public int? DepartmentId { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public int? AttachmentId { get; set; } = 1;
|
||||
public string? EmployeeId { get; set; }
|
||||
public string? RoleId { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
public string? RoleName { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
}
|
||||
}
|
||||
22
LSFE.Infrastructure/Dto/Reports/ReportsDto.cs
Normal file
22
LSFE.Infrastructure/Dto/Reports/ReportsDto.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.Reports
|
||||
{
|
||||
public class ReportsDto
|
||||
{
|
||||
public required string UserId { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public DateOnly CallMonth { get; set; }
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public string? MissedReason { get; set; }
|
||||
public string? Period { get; set; }
|
||||
public DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
}
|
||||
}
|
||||
14
LSFE.Infrastructure/Dto/RoleDto.cs
Normal file
14
LSFE.Infrastructure/Dto/RoleDto.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto
|
||||
{
|
||||
public class RoleDto
|
||||
{
|
||||
public string? RoleId { get; set; }
|
||||
public string? RoleName { get; set; }
|
||||
}
|
||||
}
|
||||
27
LSFE.Infrastructure/Dto/TimeKeeping/AttendanceDto.cs
Normal file
27
LSFE.Infrastructure/Dto/TimeKeeping/AttendanceDto.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto.TimeKeeping
|
||||
{
|
||||
public class AttendanceDto
|
||||
{
|
||||
public int AttendanceId { get; set; }
|
||||
public string? AppAttendanceId { get; set; }
|
||||
public DateTime? TimeIn { get; set; }
|
||||
public DateTime? TimeOut { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? TimeInLocation { get; set; }
|
||||
public string? TimeOutLocation { get; set; }
|
||||
public string? SignatureFileNameIn { get; set; }
|
||||
public string? SignatureFileNameOut { get; set; }
|
||||
public string? SignatureBase64 { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
public DateTime? StartDate { get; set; }
|
||||
public string? Period { get; set; }
|
||||
}
|
||||
}
|
||||
25
LSFE.Infrastructure/Dto/UpdateUserDto.cs
Normal file
25
LSFE.Infrastructure/Dto/UpdateUserDto.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto
|
||||
{
|
||||
public class UpdateUserDto
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
public string? RoleId { get; set; }
|
||||
public string? RoleName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public int? DepartmentId { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? EmployeeId { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
}
|
||||
}
|
||||
18
LSFE.Infrastructure/Dto/UserWithRoleDto.cs
Normal file
18
LSFE.Infrastructure/Dto/UserWithRoleDto.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using LSFE.Infrastructure.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Dto
|
||||
{
|
||||
public class UserWithRoleDto : AppsUsers
|
||||
{
|
||||
public string? RoleName { get; set; }
|
||||
public Departments? Departments { get; set; }
|
||||
public List<RoleDto>? UserRoles { get; set; }
|
||||
public string? RoleId { get; set; }
|
||||
}
|
||||
}
|
||||
35
LSFE.Infrastructure/Entities/Admin/UserDevice.cs
Normal file
35
LSFE.Infrastructure/Entities/Admin/UserDevice.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using LSFE.Infrastructure.Model;
|
||||
using LSFE.Infrastructure.Model.Admin;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Admin
|
||||
{
|
||||
[Table("UserDevices")]
|
||||
public class UserDevice
|
||||
{
|
||||
[Key]
|
||||
public int UserDeviceId { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? DeviceId { get; set; }
|
||||
public string? DeviceModel { get; set; }
|
||||
public string? DevicePlatform { get; set; }
|
||||
public string? DeviceManufacturer { get; set; }
|
||||
public string? DeviceVersion { get; set; }
|
||||
public string? AppVersion { get; set; }
|
||||
public bool IsAuthorized { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public DateTime? DeactivatedDate { get; set; }
|
||||
public string? DeactivatedBy { get; set; }
|
||||
public string? DeactivationReason { get; set; }
|
||||
public DateTime RegisteredDate { get; set; }
|
||||
public string? RegisteredBy { get; set; }
|
||||
//public DateTime LastLoginDate { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
}
|
||||
32
LSFE.Infrastructure/Entities/AppsUsers.cs
Normal file
32
LSFE.Infrastructure/Entities/AppsUsers.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Net.Mail;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities
|
||||
{
|
||||
public class AppsUsers : IdentityUser
|
||||
{
|
||||
public string? FullName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public int? DepartmentId { get; set; }
|
||||
|
||||
[ForeignKey("DepartmentId")]
|
||||
public Departments? Department { get; set; }
|
||||
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public DateTime UpdatedDate { get; set; }
|
||||
public string? CreatedBy { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public int? AttachmentId { get; set; }
|
||||
|
||||
[ForeignKey("AttachmentId")]
|
||||
public Attachment? Attachment { get; set; }
|
||||
public string? EmployeeId { get; set; }
|
||||
}
|
||||
}
|
||||
19
LSFE.Infrastructure/Entities/Attachment.cs
Normal file
19
LSFE.Infrastructure/Entities/Attachment.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities
|
||||
{
|
||||
[Table("Attachments")]
|
||||
public class Attachment
|
||||
{
|
||||
[Key]
|
||||
public int AttachmentId { get; set; }
|
||||
public string? URL { get; set; }
|
||||
public string? FileName { get; set; }
|
||||
}
|
||||
}
|
||||
16
LSFE.Infrastructure/Entities/Auth/AuthorizeRoles.cs
Normal file
16
LSFE.Infrastructure/Entities/Auth/AuthorizeRoles.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Auth
|
||||
{
|
||||
public class AuthorizeRoles
|
||||
{
|
||||
[Key]
|
||||
public int AuthorizeRoleId { get; set; }
|
||||
public string? RoleId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Controller { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public string? CreatedBy { get; set; }
|
||||
}
|
||||
}
|
||||
50
LSFE.Infrastructure/Entities/Auth/Module.cs
Normal file
50
LSFE.Infrastructure/Entities/Auth/Module.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Auth
|
||||
{
|
||||
[Table("Modules")]
|
||||
public class Module
|
||||
{
|
||||
[Key]
|
||||
public int ModuleId { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
public required string ModuleName { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
public required string ModuleKey { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
public required string IconName { get; set; }
|
||||
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
// CHANGE: Make this nullable for root modules
|
||||
public int? ParentModuleId { get; set; }
|
||||
|
||||
[MaxLength(200)]
|
||||
public required string RouteURL { get; set; }
|
||||
|
||||
[MaxLength(200)]
|
||||
public string? Description { get; set; }
|
||||
|
||||
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[ForeignKey("ParentModuleId")]
|
||||
public virtual Module? ParentModule { get; set; }
|
||||
|
||||
public virtual ICollection<Module>? ChildModules { get; set; }
|
||||
|
||||
public virtual ICollection<RoleModulePermission>? RoleModulePermissions { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
10
LSFE.Infrastructure/Entities/Auth/Role.cs
Normal file
10
LSFE.Infrastructure/Entities/Auth/Role.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Auth
|
||||
{
|
||||
public class Role : IdentityRole
|
||||
{
|
||||
public virtual ICollection<RoleModulePermission> RoleModulePermissions { get; set; }
|
||||
= new List<RoleModulePermission>();
|
||||
}
|
||||
}
|
||||
40
LSFE.Infrastructure/Entities/Auth/RoleModulePermission.cs
Normal file
40
LSFE.Infrastructure/Entities/Auth/RoleModulePermission.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Auth
|
||||
{
|
||||
[Table("RoleModulePermissions")]
|
||||
public class RoleModulePermission
|
||||
{
|
||||
[Key]
|
||||
public int PermissionId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string RoleId { get; set; } = default!;
|
||||
|
||||
[Required]
|
||||
public int ModuleId { get; set; }
|
||||
|
||||
public bool CanView { get; set; } = false;
|
||||
|
||||
public bool CanCreate { get; set; } = false;
|
||||
|
||||
public bool CanEdit { get; set; } = false;
|
||||
|
||||
public bool CanDelete { get; set; } = false;
|
||||
|
||||
[MaxLength(500)]
|
||||
public string? BadgeQuery { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Navigation Properties
|
||||
[ForeignKey("RoleId")]
|
||||
public virtual Role? Role { get; set; }
|
||||
|
||||
[ForeignKey("ModuleId")]
|
||||
public virtual Module? Module { get; set; }
|
||||
}
|
||||
}
|
||||
16
LSFE.Infrastructure/Entities/BaseLogEntity.cs
Normal file
16
LSFE.Infrastructure/Entities/BaseLogEntity.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities
|
||||
{
|
||||
public class BaseLogEntity
|
||||
{
|
||||
public DateTime CreatedDate { get; set; }= DateTime.Now;
|
||||
public DateTime UpdatedDate { get; set; }
|
||||
public string? CreatedBy { get; set; }
|
||||
public string? UpdatedBy { get;set; }
|
||||
}
|
||||
}
|
||||
20
LSFE.Infrastructure/Entities/Departments.cs
Normal file
20
LSFE.Infrastructure/Entities/Departments.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities
|
||||
{
|
||||
[Table("Departments")]
|
||||
public class Departments
|
||||
{
|
||||
[Key]
|
||||
public int DepartmentId { get; set; }
|
||||
public string? DepartmentCode { get; set; }
|
||||
public string? DepartmentName { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
39
LSFE.Infrastructure/Entities/Doctor/Doctor.cs
Normal file
39
LSFE.Infrastructure/Entities/Doctor/Doctor.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using LSFE.Infrastructure.Entities.Maintenance;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Doctor
|
||||
{
|
||||
[Table("Doctors")]
|
||||
public class Doctor : BaseLogEntity
|
||||
{
|
||||
[Key]
|
||||
public int DoctorId { get; set; }
|
||||
public string? MiddleInitial { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public byte MaxVisit { get; set; }
|
||||
public byte SpecializationId { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public byte StageLevelId { get; set; }
|
||||
public DateTime? BirthDate { get; set; }
|
||||
public string? EmailAddress { get; set; }
|
||||
public string? LicenseNo { get; set; }
|
||||
public string? PhoneNo { get; set; }
|
||||
public int InstitutionId { get; set; }
|
||||
[ForeignKey(nameof(InstitutionId))]
|
||||
public Institutions? Institution { get; set; }
|
||||
public byte Status { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool IsTerritorial { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public int? DistrictId { get; set; }
|
||||
[ForeignKey(nameof(DistrictId))]
|
||||
public Districts? Districts { get; set; }
|
||||
}
|
||||
}
|
||||
32
LSFE.Infrastructure/Entities/Doctor/DoctorCoverageToday.cs
Normal file
32
LSFE.Infrastructure/Entities/Doctor/DoctorCoverageToday.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Doctor
|
||||
{
|
||||
public class DoctorCoverageToday
|
||||
{
|
||||
[Key]
|
||||
public int DoctorId { get; set; }
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public string? MiddleInitial { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public byte MaxVisit { get; set; }
|
||||
public string? SpecializationName { get; set; }
|
||||
public DateTime BirthDate { get; set; }
|
||||
public string? EmailAddress { get; set; }
|
||||
public string? LicenseNo { get; set; }
|
||||
public string? PhoneNo { get; set; }
|
||||
public int InstitutionId { get; set; }
|
||||
public string? InstitutionName { get; set; }
|
||||
public byte Status { get; set; }
|
||||
public bool IsTerritorial { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? DistrictName { get; set; }
|
||||
public string? AreaName { get; set; }
|
||||
}
|
||||
}
|
||||
39
LSFE.Infrastructure/Entities/Doctor/ForDoctorApproval.cs
Normal file
39
LSFE.Infrastructure/Entities/Doctor/ForDoctorApproval.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using LSFE.Infrastructure.Entities.Maintenance;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Doctor
|
||||
{
|
||||
[Table("ForDoctorApproval")]
|
||||
public class ForDoctorApproval : BaseLogEntity
|
||||
{
|
||||
[Key]
|
||||
public Guid ForDoctorApprovalId { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public string? MiddleInitial { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public byte MaxVisit { get; set; }
|
||||
public byte SpecializationId { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public byte StageLevelId { get; set; }
|
||||
public DateTime? BirthDate { get; set; }
|
||||
public string? EmailAddress { get; set; }
|
||||
public string? LicenseNo { get; set; }
|
||||
public string? PhoneNo { get; set; }
|
||||
public int InstitutionId { get; set; }
|
||||
[ForeignKey(nameof(InstitutionId))]
|
||||
public Institutions? Institution { get; set; }
|
||||
public bool IsTerritorial { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public byte Status { get; set; }
|
||||
public int? DistrictId { get; set; }
|
||||
[ForeignKey(nameof(DistrictId))]
|
||||
public Districts? Districts { get; set; }
|
||||
}
|
||||
}
|
||||
20
LSFE.Infrastructure/Entities/Doctor/Specialization.cs
Normal file
20
LSFE.Infrastructure/Entities/Doctor/Specialization.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Doctor
|
||||
{
|
||||
[Table("Specialization")]
|
||||
public class Specialization
|
||||
{
|
||||
[Key]
|
||||
public byte SpecializationId { get; set; }
|
||||
public string? SpecializationName { get; set; }
|
||||
public int CompanyId { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
21
LSFE.Infrastructure/Entities/ErrorLogs.cs
Normal file
21
LSFE.Infrastructure/Entities/ErrorLogs.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities
|
||||
{
|
||||
[Table("ErrorLogs")]
|
||||
public class ErrorLogs
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
public string? Message { get; set; }
|
||||
public string? CreatedBy { get; set; }
|
||||
public string? Application { get; set; }
|
||||
public DateTime CreatedDate { get; set; }= DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
19
LSFE.Infrastructure/Entities/Maintenance/Areas.cs
Normal file
19
LSFE.Infrastructure/Entities/Maintenance/Areas.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
[Table("Areas")]
|
||||
public class Areas
|
||||
{
|
||||
[Key]
|
||||
public int AreaId { get; set; }
|
||||
public string? AreaName { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
23
LSFE.Infrastructure/Entities/Maintenance/Districts.cs
Normal file
23
LSFE.Infrastructure/Entities/Maintenance/Districts.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
[Table("Districts")]
|
||||
public class Districts
|
||||
{
|
||||
[Key]
|
||||
public int DistrictId { get; set; }
|
||||
public string? DistrictName { get; set; }
|
||||
public int AreaId { get; set; }
|
||||
[ForeignKey("AreaId")]
|
||||
public Areas? Areas { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public ICollection<Doctor.Doctor> Doctors { get; set; } = new List<Doctor.Doctor>();
|
||||
}
|
||||
}
|
||||
19
LSFE.Infrastructure/Entities/Maintenance/Divisions.cs
Normal file
19
LSFE.Infrastructure/Entities/Maintenance/Divisions.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
[Table("Divisions")]
|
||||
public class Divisions
|
||||
{
|
||||
[Key]
|
||||
public int DivisionId { get; set; }
|
||||
public string? DivisionName { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
[Table("InstitutionCategory")]
|
||||
public class InstitutionCategory
|
||||
{
|
||||
[Key]
|
||||
public int InstitutionCategoryId { get; set; }
|
||||
public string? InstitutionCategoryCode { get; set; }
|
||||
public string? InstitutionCategoryName { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
26
LSFE.Infrastructure/Entities/Maintenance/Institutions.cs
Normal file
26
LSFE.Infrastructure/Entities/Maintenance/Institutions.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
[Table("Institutions")]
|
||||
public class Institutions
|
||||
{
|
||||
[Key]
|
||||
public int InstitutionId { get; set; }
|
||||
public string? InstitutionName { get; set; }
|
||||
public int TerritoryId { get; set; }
|
||||
[ForeignKey("TerritoryId")]
|
||||
public Territories? Territories { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public int InstitutionCategoryId { get; set; }
|
||||
[ForeignKey("InstitutionCategoryId")]
|
||||
public InstitutionCategory? InstitutionCategory { get; set; }
|
||||
public ICollection<Doctor.Doctor> Doctors { get; set; } = new List<Doctor.Doctor>();
|
||||
}
|
||||
}
|
||||
31
LSFE.Infrastructure/Entities/Maintenance/Inventory.cs
Normal file
31
LSFE.Infrastructure/Entities/Maintenance/Inventory.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
|
||||
[Table("Inventory")]
|
||||
public class Inventory
|
||||
{
|
||||
[Key]
|
||||
public Guid InventoryId { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int QtyIn { get; set; }
|
||||
public int QtyOut { get; set; }
|
||||
public int QtyBalance { get; set; }
|
||||
public byte CycleMonth { get; set; }
|
||||
public int CycleYear { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
public string? CreatedBy { get; set; }
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
public DateTime? UpdatedDate { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
using LSFE.Infrastructure.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
[Table("InventoryTransaction")]
|
||||
public class InventoryTransaction
|
||||
{
|
||||
[Key]
|
||||
public Guid InventoryTransId { get; set; }
|
||||
public Guid InventoryId { get; set; }
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public DateOnly? AppointmentDate { get; set; }
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public int QtyIn { get; set; }
|
||||
public int QtyOut { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? CreatedBy { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
public DateTime? UpdatedDate { get; set; }
|
||||
public byte IconEmojiId { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
[Table("ProductTransaction")]
|
||||
public class ProductTransaction
|
||||
{
|
||||
[Key]
|
||||
public Guid ProductTransId { get; set; }
|
||||
public int ProductId { get; set; }
|
||||
public string? ProductName { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public DateOnly AppointmentDate { get; set; }
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public string? CreatedBy { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
21
LSFE.Infrastructure/Entities/Maintenance/Products.cs
Normal file
21
LSFE.Infrastructure/Entities/Maintenance/Products.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
[Table("Products")]
|
||||
public class Products
|
||||
{
|
||||
[Key]
|
||||
public int ProductId { get; set; }
|
||||
public string? ProductName { get; set; }
|
||||
public string? ProductLink { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
22
LSFE.Infrastructure/Entities/Maintenance/Territories.cs
Normal file
22
LSFE.Infrastructure/Entities/Maintenance/Territories.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Maintenance
|
||||
{
|
||||
[Table("Territories")]
|
||||
public class Territories
|
||||
{
|
||||
[Key]
|
||||
public int TerritoryId { get; set; }
|
||||
public string? TerritoryName { get; set; }
|
||||
public int DistrictId { get; set; }
|
||||
[ForeignKey("DistrictId")]
|
||||
public Districts? Districts { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
43
LSFE.Infrastructure/Entities/Planning/CatchUpCall.cs
Normal file
43
LSFE.Infrastructure/Entities/Planning/CatchUpCall.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Planning
|
||||
{
|
||||
[Table("CatchUpCalls")]
|
||||
public class CatchUpCall
|
||||
{
|
||||
[Key]
|
||||
public Guid CatchUpCallId { get; set; }
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public Guid? MRPlanDetailId { get; set; }
|
||||
public byte? VisitCount { get; set; }
|
||||
public DateOnly? CallDate { get; set; }
|
||||
public DateOnly? ActualCallDate { get; set; }
|
||||
public DateTime? StartTime { get; set; }
|
||||
public DateTime? EndTime { get; set; }
|
||||
public string? Longitude { get; set; }
|
||||
public string? Latitude { get; set; }
|
||||
public string? Location { get; set; }
|
||||
public bool IsSignature { get; set; } = false;
|
||||
public string? SignatureFileName { get; set; }
|
||||
public bool IsMissed { get; set; } = false;
|
||||
public string? MissedReason { get; set; }
|
||||
public bool IsDoctorProxy { get; set; } = false;
|
||||
public bool IsReschedule { get; set; } = false;
|
||||
public string? RescheduleReason { get; set; }
|
||||
public string? CallType { get; set; }
|
||||
public bool IsJoinCall { get; set; } = false;
|
||||
public byte? CycleId { get; set; }
|
||||
public bool IsApproved { get; set; } = false;
|
||||
public bool IsRecovered { get; set; } = false;
|
||||
public bool IsLiterature { get; set; }
|
||||
public bool IsCatchUpCall { get; set; }
|
||||
public string? SLP { get; set; }
|
||||
public byte Status { get; set; }
|
||||
}
|
||||
}
|
||||
32
LSFE.Infrastructure/Entities/Planning/DoctorById.cs
Normal file
32
LSFE.Infrastructure/Entities/Planning/DoctorById.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Planning
|
||||
{
|
||||
public class DoctorById
|
||||
{
|
||||
[Key]
|
||||
public string? MRRawPlanId { get; set; }
|
||||
public Guid MRPlanDetailId { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public string? MRFullName { get; set; }
|
||||
public string? InstitutionName { get; set; }
|
||||
public Guid MRPlanHeaderId { get; set; }
|
||||
public string? MRPlanDate { get; set; }
|
||||
public string? DivisionName { get; set; }
|
||||
public int DivisionId { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? MiddleInitial { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? TerritoryName { get; set; }
|
||||
public string? SpecializationName { get; set; }
|
||||
public bool IsLiterature { get; set; }
|
||||
public bool IsApproved { get; set; }
|
||||
public byte MaxVisit { get; set; }
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
}
|
||||
30
LSFE.Infrastructure/Entities/Planning/DoctorWithPlan.cs
Normal file
30
LSFE.Infrastructure/Entities/Planning/DoctorWithPlan.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using LSFE.Infrastructure.Entities.Doctor;
|
||||
using LSFE.Infrastructure.Entities.Maintenance;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Planning
|
||||
{
|
||||
public class DoctorWithPlan
|
||||
{
|
||||
[Key]
|
||||
public int DoctorId { get; set; }
|
||||
public string? MRFullName { get; set; }
|
||||
public string? InstitutionName { get; set; }
|
||||
public Guid MRPlanHeaderId { get; set; }
|
||||
public Guid MRPlanDetailId { get; set; }
|
||||
public string? MRPlanDate { get; set; }
|
||||
public int TerritoryId { get; set; }
|
||||
public string? TerritoryName { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? MiddleInitial { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? SpecializationName { get; set; }
|
||||
public byte MaxVisit { get; set; }
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
}
|
||||
22
LSFE.Infrastructure/Entities/Planning/MRPlanDetail.cs
Normal file
22
LSFE.Infrastructure/Entities/Planning/MRPlanDetail.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Planning
|
||||
{
|
||||
[Table("MRPlanDetails")]
|
||||
public class MRPlanDetail
|
||||
{
|
||||
[Key]
|
||||
public Guid MRPlanDetailId { get; set; }
|
||||
public Guid MRPlanHeaderId { get; set; }
|
||||
public byte MaxVisit { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public int TerritoryId { get; set; }
|
||||
public int DivisionId { get; set; }
|
||||
}
|
||||
}
|
||||
19
LSFE.Infrastructure/Entities/Planning/MRPlanHeader.cs
Normal file
19
LSFE.Infrastructure/Entities/Planning/MRPlanHeader.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Planning
|
||||
{
|
||||
[Table("MRPlanHeader")]
|
||||
public class MRPlanHeader
|
||||
{
|
||||
[Key]
|
||||
public Guid MRPlanHeaderId { get; set; }
|
||||
public string? MRUserId { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
45
LSFE.Infrastructure/Entities/Planning/MRRawPlan.cs
Normal file
45
LSFE.Infrastructure/Entities/Planning/MRRawPlan.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Planning
|
||||
{
|
||||
[Table("MRRawPlans")]
|
||||
public class MRRawPlan
|
||||
{
|
||||
[Key]
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public Guid? MRPlanDetailId { get; set; }
|
||||
public byte? VisitCount { get; set; }
|
||||
public DateOnly? CallDate { get; set; }
|
||||
public DateOnly? ActualCallDate { get; set; }
|
||||
public DateTime? StartTime { get; set; }
|
||||
public DateTime? EndTime { get; set; }
|
||||
public string? Longitude { get; set; }
|
||||
public string? Latitude { get; set; }
|
||||
public string? Location { get; set; }
|
||||
public bool IsSignature { get; set; } = false;
|
||||
public string? SignatureFileName { get; set; }
|
||||
public bool IsMissed { get; set; } = false;
|
||||
public string? MissedReason { get; set; }
|
||||
public bool IsDoctorProxy { get; set; } = false;
|
||||
public bool IsReschedule { get; set; } = false;
|
||||
public string? RescheduleReason { get; set; }
|
||||
public string? CallType { get; set; }
|
||||
public bool IsJoinCall { get; set; } = false;
|
||||
public byte? CycleId { get; set; }
|
||||
public bool IsApproved { get; set; } = false;
|
||||
public bool IsRecovered { get; set; } = false;
|
||||
public bool IsLiterature { get; set; }
|
||||
public string? SLP { get; set; }
|
||||
public string? ApprovedBy { get; set; }
|
||||
public DateTime? ApprovedDate { get; set; }
|
||||
public byte Status { get; set; }
|
||||
public bool IsCatchUpCall { get; set; }
|
||||
}
|
||||
}
|
||||
30
LSFE.Infrastructure/Entities/Planning/PendingApproval.cs
Normal file
30
LSFE.Infrastructure/Entities/Planning/PendingApproval.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Planning
|
||||
{
|
||||
public class PendingApproval
|
||||
{
|
||||
[Key]
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public Guid MRPlanDetailId { get; set; }
|
||||
public int DoctorId { get; set; }
|
||||
public string? MRFullName { get; set; }
|
||||
public string? InstitutionName { get; set; }
|
||||
public Guid MRPlanHeaderId { get; set; }
|
||||
public string? MRPlanDate { get; set; }
|
||||
public string? DivisionName { get; set; }
|
||||
public int DivisionId { get; set; }
|
||||
public string? TerritoryName { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? MiddleInitial { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? SpecializationName { get; set; }
|
||||
public byte MaxVisit { get; set; }
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
}
|
||||
43
LSFE.Infrastructure/Entities/Reports/ActualCoverageReport.cs
Normal file
43
LSFE.Infrastructure/Entities/Reports/ActualCoverageReport.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Reports
|
||||
{
|
||||
[Keyless]
|
||||
public class ActualCoverageReport
|
||||
{
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public Guid? MRPlanDetailId { get; set; }
|
||||
public DateOnly? CallDate { get; set; }
|
||||
public DateOnly? ActualCallDate { get; set; }
|
||||
public DateTime? StartTime { get; set; }
|
||||
public DateTime? EndTime { get; set; }
|
||||
public string? Longitude { get; set; }
|
||||
public string? Latitude { get; set; }
|
||||
public string? Location { get; set; }
|
||||
public bool IsSignature { get; set; } = false;
|
||||
public string? SignatureFileName { get; set; }
|
||||
public bool IsMissed { get; set; } = false;
|
||||
public string? MissedReason { get; set; }
|
||||
public bool IsDoctorProxy { get; set; } = false;
|
||||
public bool IsReschedule { get; set; } = false;
|
||||
public string? RescheduleReason { get; set; }
|
||||
public string? CallType { get; set; }
|
||||
public bool IsApproved { get; set; } = false;
|
||||
public bool IsRecovered { get; set; } = false;
|
||||
public bool IsLiterature { get; set; }
|
||||
public string? SLP { get; set; }
|
||||
public string? ApprovedBy { get; set; }
|
||||
public DateTime? ApprovedDate { get; set; }
|
||||
public bool IsCatchUpCall { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public string? DoctorName { get; set; }
|
||||
public string? TerritoryName { get; set; }
|
||||
}
|
||||
}
|
||||
22
LSFE.Infrastructure/Entities/Reports/CallRateReport.cs
Normal file
22
LSFE.Infrastructure/Entities/Reports/CallRateReport.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Reports
|
||||
{
|
||||
public class CallRateReport
|
||||
{
|
||||
[Key]
|
||||
public required string MRUserId { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public int TotalTargetCall { get; set; }
|
||||
public int TotalActualCallRate { get; set; }
|
||||
public int TotalMissedCall { get; set; }
|
||||
public int TotalReschedule { get; set; }
|
||||
public decimal TotalCallRatePercentage { get; set; }
|
||||
}
|
||||
}
|
||||
20
LSFE.Infrastructure/Entities/Reports/CallReachReport.cs
Normal file
20
LSFE.Infrastructure/Entities/Reports/CallReachReport.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Reports
|
||||
{
|
||||
public class CallReachReport
|
||||
{
|
||||
[Key]
|
||||
public required string MRUserId { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public int TotalTargetCall { get; set; }
|
||||
public int TotalActualCallReach { get; set; }
|
||||
public decimal TotalCallReachPercentage { get; set; }
|
||||
}
|
||||
}
|
||||
30
LSFE.Infrastructure/Entities/Reports/CatchUpCallReport.cs
Normal file
30
LSFE.Infrastructure/Entities/Reports/CatchUpCallReport.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.Reports
|
||||
{
|
||||
public class CatchUpCallReport
|
||||
{
|
||||
[Key]
|
||||
public Guid CatchUpCallId { get; set; }
|
||||
public Guid MRRawPlanId { get; set; }
|
||||
public Guid? MRPlanDetailId { get; set; }
|
||||
public byte? VisitCount { get; set; }
|
||||
public DateOnly? ActualCallDate { get; set; }
|
||||
public DateTime? StartTime { get; set; }
|
||||
public DateTime? EndTime { get; set; }
|
||||
public string? Longitude { get; set; }
|
||||
public string? Latitude { get; set; }
|
||||
public string? Location { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
public string? DoctorName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public string? MissedReason { get; set; }
|
||||
public DateOnly OriginalCallDate { get; set; }
|
||||
public DateOnly CatchUpCallDate { get; set; }
|
||||
}
|
||||
}
|
||||
20
LSFE.Infrastructure/Entities/TimeKeeping/Attendance.cs
Normal file
20
LSFE.Infrastructure/Entities/TimeKeeping/Attendance.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.TimeKeeping
|
||||
{
|
||||
[Table("Attendance")]
|
||||
public class Attendance
|
||||
{
|
||||
[Key]
|
||||
public int AttendanceId { get; set; }
|
||||
public string? AppAttendanceId { get; set; }
|
||||
public DateTime? TimeIn { get; set; }
|
||||
public DateTime? TimeOut { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? TimeInLocation { get; set; }
|
||||
public string? TimeOutLocation { get; set; }
|
||||
public string? SignatureFileNameIn { get; set; }
|
||||
public string? SignatureFileNameOut { get; set; }
|
||||
}
|
||||
}
|
||||
24
LSFE.Infrastructure/Entities/TimeKeeping/AttendanceDto.cs
Normal file
24
LSFE.Infrastructure/Entities/TimeKeeping/AttendanceDto.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Entities.TimeKeeping
|
||||
{
|
||||
public class AttendanceDto
|
||||
{
|
||||
[Key]
|
||||
public int AttendanceId { get; set; }
|
||||
public string? AppAttendanceId { get; set; }
|
||||
public DateTime? TimeIn { get; set; }
|
||||
public DateTime? TimeOut { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? TimeInLocation { get; set; }
|
||||
public string? TimeOutLocation { get; set; }
|
||||
public string? SignatureFileNameIn { get; set; }
|
||||
public string? SignatureFileNameOut { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
}
|
||||
}
|
||||
29
LSFE.Infrastructure/LSFE.Infrastructure.csproj
Normal file
29
LSFE.Infrastructure/LSFE.Infrastructure.csproj
Normal file
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.23" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.20" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.20" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.20">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.20" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.20">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Features" Version="8.0.23" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Dto\Planning\Response\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
363
LSFE.Infrastructure/Migrations/20250920072412_iniitialCreate.Designer.cs
generated
Normal file
363
LSFE.Infrastructure/Migrations/20250920072412_iniitialCreate.Designer.cs
generated
Normal file
@ -0,0 +1,363 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LSFE.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(LSFEDbContext))]
|
||||
[Migration("20250920072412_iniitialCreate")]
|
||||
partial class iniitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.20")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("LSFE.Infrastructure.Entities.AppsUsers", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Address")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("AttachmentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Company")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("CreatedDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<int?>("DepartmentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("FullName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("UpdatedBy")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("UpdatedDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AttachmentId");
|
||||
|
||||
b.HasIndex("DepartmentId");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("Users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LSFE.Infrastructure.Entities.Attachment", b =>
|
||||
{
|
||||
b.Property<int>("AttachmentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("AttachmentId"));
|
||||
|
||||
b.Property<string>("FileName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("URL")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("AttachmentId");
|
||||
|
||||
b.ToTable("Attachment", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LSFE.Infrastructure.Entities.Departments", b =>
|
||||
{
|
||||
b.Property<int>("DepartmentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("DepartmentId"));
|
||||
|
||||
b.Property<string>("Department")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.HasKey("DepartmentId");
|
||||
|
||||
b.ToTable("Departments", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("Roles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("UserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("UserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("UserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LSFE.Infrastructure.Entities.AppsUsers", b =>
|
||||
{
|
||||
b.HasOne("LSFE.Infrastructure.Entities.Attachment", "Attachment")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttachmentId");
|
||||
|
||||
b.HasOne("LSFE.Infrastructure.Entities.Departments", "Department")
|
||||
.WithMany()
|
||||
.HasForeignKey("DepartmentId");
|
||||
|
||||
b.Navigation("Attachment");
|
||||
|
||||
b.Navigation("Department");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("LSFE.Infrastructure.Entities.AppsUsers", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("LSFE.Infrastructure.Entities.AppsUsers", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LSFE.Infrastructure.Entities.AppsUsers", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("LSFE.Infrastructure.Entities.AppsUsers", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
287
LSFE.Infrastructure/Migrations/20250920072412_iniitialCreate.cs
Normal file
287
LSFE.Infrastructure/Migrations/20250920072412_iniitialCreate.cs
Normal file
@ -0,0 +1,287 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LSFE.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class iniitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Attachment",
|
||||
columns: table => new
|
||||
{
|
||||
AttachmentId = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
URL = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
FileName = table.Column<string>(type: "nvarchar(max)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Attachment", x => x.AttachmentId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Departments",
|
||||
columns: table => new
|
||||
{
|
||||
DepartmentId = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Department = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Departments", x => x.DepartmentId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Roles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "nvarchar(max)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Roles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
FullName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Company = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DepartmentId = table.Column<int>(type: "int", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
UpdatedDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
UpdatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Address = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
AttachmentId = table.Column<int>(type: "int", nullable: true),
|
||||
UserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(type: "bit", nullable: false),
|
||||
PasswordHash = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
SecurityStamp = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
PhoneNumber = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(type: "bit", nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(type: "bit", nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(type: "bit", nullable: false),
|
||||
AccessFailedCount = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Users_Attachment_AttachmentId",
|
||||
column: x => x.AttachmentId,
|
||||
principalTable: "Attachment",
|
||||
principalColumn: "AttachmentId");
|
||||
table.ForeignKey(
|
||||
name: "FK_Users_Departments_DepartmentId",
|
||||
column: x => x.DepartmentId,
|
||||
principalTable: "Departments",
|
||||
principalColumn: "DepartmentId");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "RoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
RoleId = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "nvarchar(max)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_RoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_RoleClaims_Roles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "Roles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
ProviderKey = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
UserId = table.Column<string>(type: "nvarchar(450)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_UserLogins_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
UserId = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "nvarchar(max)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_UserClaims_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
RoleId = table.Column<string>(type: "nvarchar(450)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_UserRoles_Roles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "Roles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_UserRoles_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
LoginProvider = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
Value = table.Column<string>(type: "nvarchar(max)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_UserTokens_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_RoleClaims_RoleId",
|
||||
table: "RoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UserLogins_UserId",
|
||||
table: "UserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "Roles",
|
||||
column: "NormalizedName",
|
||||
unique: true,
|
||||
filter: "[NormalizedName] IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UserClaims_UserId",
|
||||
table: "UserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UserRoles_RoleId",
|
||||
table: "UserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "Users",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Users_AttachmentId",
|
||||
table: "Users",
|
||||
column: "AttachmentId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Users_DepartmentId",
|
||||
table: "Users",
|
||||
column: "DepartmentId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "Users",
|
||||
column: "NormalizedUserName",
|
||||
unique: true,
|
||||
filter: "[NormalizedUserName] IS NOT NULL");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "RoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Roles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Attachment");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Departments");
|
||||
}
|
||||
}
|
||||
}
|
||||
360
LSFE.Infrastructure/Migrations/LSFEDbContextModelSnapshot.cs
Normal file
360
LSFE.Infrastructure/Migrations/LSFEDbContextModelSnapshot.cs
Normal file
@ -0,0 +1,360 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LSFE.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LSFE.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(LSFEDbContext))]
|
||||
partial class LSFEDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.20")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("LSFE.Infrastructure.Entities.AppsUsers", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Address")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("AttachmentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Company")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("CreatedDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<int?>("DepartmentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("FullName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("UpdatedBy")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("UpdatedDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AttachmentId");
|
||||
|
||||
b.HasIndex("DepartmentId");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("Users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LSFE.Infrastructure.Entities.Attachment", b =>
|
||||
{
|
||||
b.Property<int>("AttachmentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("AttachmentId"));
|
||||
|
||||
b.Property<string>("FileName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("URL")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("AttachmentId");
|
||||
|
||||
b.ToTable("Attachment", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LSFE.Infrastructure.Entities.Departments", b =>
|
||||
{
|
||||
b.Property<int>("DepartmentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("DepartmentId"));
|
||||
|
||||
b.Property<string>("Department")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.HasKey("DepartmentId");
|
||||
|
||||
b.ToTable("Departments", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("Roles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("UserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("UserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("UserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LSFE.Infrastructure.Entities.AppsUsers", b =>
|
||||
{
|
||||
b.HasOne("LSFE.Infrastructure.Entities.Attachment", "Attachment")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttachmentId");
|
||||
|
||||
b.HasOne("LSFE.Infrastructure.Entities.Departments", "Department")
|
||||
.WithMany()
|
||||
.HasForeignKey("DepartmentId");
|
||||
|
||||
b.Navigation("Attachment");
|
||||
|
||||
b.Navigation("Department");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("LSFE.Infrastructure.Entities.AppsUsers", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("LSFE.Infrastructure.Entities.AppsUsers", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LSFE.Infrastructure.Entities.AppsUsers", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("LSFE.Infrastructure.Entities.AppsUsers", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
43
LSFE.Infrastructure/Model/Admin/AdminVerificationRequest.cs
Normal file
43
LSFE.Infrastructure/Model/Admin/AdminVerificationRequest.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Model.Admin
|
||||
{
|
||||
public class AdminVerificationRequest
|
||||
{
|
||||
public string? AdminUserName { get; set; }
|
||||
public string? AdminPassword { get; set; }
|
||||
}
|
||||
public class RevokeDeviceRequest
|
||||
{
|
||||
public string? AdminPassword { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? DeviceId { get; set; }
|
||||
public string? Reason { get; set; }
|
||||
}
|
||||
|
||||
/*public class UserDevice
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? DeviceId { get; set; }
|
||||
public string? DeviceModel { get; set; }
|
||||
public string? DevicePlatform { get; set; }
|
||||
public string? DeviceVersion { get; set; }
|
||||
public string? DeviceManufacturer { get; set; }
|
||||
public string? AppVersion { get; set; }
|
||||
public bool IsAuthorized { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public DateTime RegisteredDate { get; set; }
|
||||
public string? RegisteredBy { get; set; }
|
||||
public DateTime? LastLoginDate { get; set; }
|
||||
public DateTime? DeactivatedDate { get; set; }
|
||||
public string? DeactivatedBy { get; set; }
|
||||
public string? DeactivationReason { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}*/
|
||||
}
|
||||
16
LSFE.Infrastructure/Model/Admin/AdminVerificationResponse.cs
Normal file
16
LSFE.Infrastructure/Model/Admin/AdminVerificationResponse.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Model.Admin
|
||||
{
|
||||
public class AdminVerificationResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Data { get; set; }
|
||||
public string? Message { get; set; }
|
||||
public int MessCode { get; set; }
|
||||
}
|
||||
}
|
||||
21
LSFE.Infrastructure/Model/Admin/DeviceInfo.cs
Normal file
21
LSFE.Infrastructure/Model/Admin/DeviceInfo.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Model.Admin
|
||||
{
|
||||
public class DeviceInfo
|
||||
{
|
||||
public string? DeviceId { get; set; }
|
||||
public string? DeviceModel { get; set; }
|
||||
public string? Platform { get; set; }
|
||||
public string? Version { get; set; }
|
||||
public string? Manufacturer { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? DeviceType { get; set; }
|
||||
public string? VersionString { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
}
|
||||
}
|
||||
20
LSFE.Infrastructure/Model/Admin/DeviceRegistrationRequest.cs
Normal file
20
LSFE.Infrastructure/Model/Admin/DeviceRegistrationRequest.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Model.Admin
|
||||
{
|
||||
public class DeviceRegistrationRequest
|
||||
{
|
||||
public string? MedRepUsername { get; set; }
|
||||
public string? MedRepPassword { get; set; }
|
||||
public string? DeviceId { get; set; }
|
||||
public string? DeviceModel { get; set; }
|
||||
public string? DevicePlatform { get; set; }
|
||||
public string? DeviceVersion { get; set; }
|
||||
public string? DeviceManufacturer { get; set; }
|
||||
public string? AppVersion { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Model.Admin
|
||||
{
|
||||
public class DeviceRegistrationResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Message { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? DeviceId { get; set; }
|
||||
public DateTime RegisteredDate { get; set; }
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
}
|
||||
}
|
||||
17
LSFE.Infrastructure/Model/ExcelUploadResponse.cs
Normal file
17
LSFE.Infrastructure/Model/ExcelUploadResponse.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Model
|
||||
{
|
||||
public class ExcelUploadResponse : Response
|
||||
{
|
||||
public int TotalRows { get; set; }
|
||||
public int ProcessedRows { get; set; }
|
||||
public int SkippedRows { get; set; }
|
||||
public List<string> Errors { get; set; } = new List<string>();
|
||||
public List<InventorySummary> InsertedItems { get; set; } = new List<InventorySummary>();
|
||||
}
|
||||
}
|
||||
15
LSFE.Infrastructure/Model/InventorySummary.cs
Normal file
15
LSFE.Infrastructure/Model/InventorySummary.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Model
|
||||
{
|
||||
public class InventorySummary
|
||||
{
|
||||
public string? Brand { get; set; }
|
||||
public int TotalQty { get; set; }
|
||||
public string? MedRepName { get; set; }
|
||||
}
|
||||
}
|
||||
16
LSFE.Infrastructure/Model/Response.cs
Normal file
16
LSFE.Infrastructure/Model/Response.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Model
|
||||
{
|
||||
public class Response
|
||||
{
|
||||
public object? Data { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string? Message { get; set; }
|
||||
public byte MessCode { get; set; }
|
||||
}
|
||||
}
|
||||
15
LSFE.Infrastructure/Model/Users.cs
Normal file
15
LSFE.Infrastructure/Model/Users.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LSFE.Infrastructure.Model
|
||||
{
|
||||
public class Users
|
||||
{
|
||||
public string? UserName { get; set; }
|
||||
public string? Password { get; set; }
|
||||
}
|
||||
}
|
||||
22
LSFE.MobApp/App.xaml
Normal file
22
LSFE.MobApp/App.xaml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version = "1.0" encoding = "UTF-8" ?>
|
||||
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:converters="clr-namespace:LSFE.MobApp.Converters"
|
||||
xmlns:local="clr-namespace:LSFE.MobApp"
|
||||
x:Class="LSFE.MobApp.App">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
|
||||
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<converters:InverseBoolConverter x:Key="InverseBoolConverter" />
|
||||
<converters:InverseBoolConverter x:Key="IsNotNullConverter" />
|
||||
<converters:InverseBoolConverter x:Key="IsNotNullOrEmptyConverter" />
|
||||
<converters:InverseBoolConverter x:Key="ColorToLightColorConverter" />
|
||||
|
||||
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user