Projektdateien hinzufügen.

This commit is contained in:
Kilian Wirl 2025-04-28 09:18:17 +02:00
commit 4eee8ebdf9
1739 changed files with 1538111 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

387
.gitignore vendored Normal file
View File

@ -0,0 +1,387 @@
## 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
#Custom
[Ll]ogs/
[Ee]xport/
[Ss]ysLogs/
[Pp]erformanceLogs/
[Aa]pp_Files/[Dd]ocuments/**
[Aa]pp_Files/[Ss]yslogs
[Aa]pp_Files/[Tt]emp
[Aa]pp_Files/[Ss]hares
[Aa]pp_Data/jobs
node_modules
.vs/
gehGassi.Web/wwwroot/app_files/documents/**
gehGassi.Web/wwwroot/app_files/temp/**
gehGassi.Web/wwwroot/app_files/shares/**
*/wwwroot/scripts
*/wwwroot/css
*/wwwroot/lib
gehGassi.Web/tempkey.rsa
gehGassi.Web/keys
gehGassi.Web/TS/**/*.js
gehGassi.Web/TS/**/*.map

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,426 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Address_City" xml:space="preserve">
<value>Stadt</value>
</data>
<data name="Address_CountryCode" xml:space="preserve">
<value>Land</value>
</data>
<data name="Address_Zip" xml:space="preserve">
<value>PLZ</value>
</data>
<data name="Banner_ChangeSize_Question" xml:space="preserve">
<value>Möchten Sie wirklich die Größe des Banners ändern? Wenn Sie dies tun, werden die Bilder für den Banner zurückgesetzt. Sollten Sie die Änderungen im Anschluss nicht speichern, gehen die alten Bilder nicht verloren.</value>
</data>
<data name="Button_Add" xml:space="preserve">
<value>Hinzufügen</value>
</data>
<data name="Button_Cancel" xml:space="preserve">
<value>Abbrechen</value>
</data>
<data name="Button_No" xml:space="preserve">
<value>Nein</value>
</data>
<data name="Button_Ok" xml:space="preserve">
<value>Ok</value>
</data>
<data name="Button_Placeholder" xml:space="preserve">
<value>Platzhalter</value>
</data>
<data name="Button_Yes" xml:space="preserve">
<value>Ja</value>
</data>
<data name="Cart_Clear_Question" xml:space="preserve">
<value>Möchten Sie den gesamten Warenkorb wirklich leeren?</value>
</data>
<data name="Cart_Remove_Question" xml:space="preserve">
<value>Möchten Sie diesen Artikel wirklich aus dem Warenkorb entfernen?</value>
</data>
<data name="Commmon_Add" xml:space="preserve">
<value>Hinzufügen</value>
</data>
<data name="Common_Access_Forbidden" xml:space="preserve">
<value>Sie verfügen nicht über die notwendigen Rechte um auf diese Ressource zuzugreifen bzw. diese Funktion auszuführen.</value>
</data>
<data name="Common_Already_Exits" xml:space="preserve">
<value>Wird bereits verwendet</value>
</data>
<data name="Common_Cancel" xml:space="preserve">
<value>Abbrechen</value>
</data>
<data name="Common_Cancel_NoSuccess" xml:space="preserve">
<value>Stornierung nicht erfolgreich</value>
</data>
<data name="Common_Cancel_Question" xml:space="preserve">
<value>Möchten Sie diesen Datensatz wirklich stornieren?</value>
</data>
<data name="Common_Cancel_Question_Batch" xml:space="preserve">
<value>Wollen Sie diese Datensätze wirklich stornieren?</value>
</data>
<data name="Common_Cancel_Success" xml:space="preserve">
<value>Stornierung erfolgreich</value>
</data>
<data name="Common_CartTotal_Changed" xml:space="preserve">
<value>Die Gesamtsumme des Warenkorbes hat sich geändert.</value>
</data>
<data name="Common_ChangeState_Question" xml:space="preserve">
<value>Möchten Sie wirklich den Status dieser Entität ändern?</value>
</data>
<data name="Common_Completed" xml:space="preserve">
<value>Abgeschlossen</value>
</data>
<data name="Common_DeactivateTwoFactor_Question" xml:space="preserve">
<value>Möchten Sie die 2-Faktor Authentifizierung wirklich deaktiveren?</value>
</data>
<data name="Common_Delete" xml:space="preserve">
<value>Löschen</value>
</data>
<data name="Common_Delete_NoSuccess" xml:space="preserve">
<value>Löschen nicht erfolgreich</value>
</data>
<data name="Common_Delete_Question" xml:space="preserve">
<value>Möchten Sie diesen Eintrag wirklich löschen?</value>
</data>
<data name="Common_Delete_Question_Batch" xml:space="preserve">
<value>Möchten Sie diese Einträge wirklich löschen?</value>
</data>
<data name="Common_Delete_Success" xml:space="preserve">
<value>Löschen erfolgreich</value>
</data>
<data name="Common_Edit" xml:space="preserve">
<value>Bearbeiten</value>
</data>
<data name="Common_Export_NoSuccess" xml:space="preserve">
<value>Export gescheitert.</value>
</data>
<data name="Common_Export_Success" xml:space="preserve">
<value>Export erfolgreich</value>
</data>
<data name="Common_Failed" xml:space="preserve">
<value>Gescheitert</value>
</data>
<data name="Common_ForceDelete_Question" xml:space="preserve">
<value>Möchten Sie diesen Eintrag wirklich löschen? Achtung: die Daten werden physisch gelöscht!</value>
</data>
<data name="Common_ForceDelete_Question_Batch" xml:space="preserve">
<value>Möchten Sie diese Einträge wirklich löschen? Achtung: die Daten werden physisch gelöscht!</value>
</data>
<data name="Common_Locked" xml:space="preserve">
<value>Gesperrt</value>
</data>
<data name="Common_Locked_Reason" xml:space="preserve">
<value>Sperrgrund</value>
</data>
<data name="Common_Logout" xml:space="preserve">
<value>Abmelden</value>
</data>
<data name="Common_No" xml:space="preserve">
<value>Nein</value>
</data>
<data name="Common_Note" xml:space="preserve">
<value>Hinweis</value>
</data>
<data name="Common_Ok" xml:space="preserve">
<value>OK</value>
</data>
<data name="Common_ResetPassword_NoSuccess" xml:space="preserve">
<value>Das Email für das Zurücksetzen des Passwortes konnte nicht gesendet werden.</value>
</data>
<data name="Common_ResetPassword_Success" xml:space="preserve">
<value>Das Email für das Zurücksetzen des Passwortes wurde erfolgreich gesendet.</value>
</data>
<data name="Common_Save_NoSuccess" xml:space="preserve">
<value>Speichern nicht erfolgreich</value>
</data>
<data name="Common_Save_Success" xml:space="preserve">
<value>Speichern erfolgreich</value>
</data>
<data name="Common_Search" xml:space="preserve">
<value>Suchen</value>
</data>
<data name="Common_Select" xml:space="preserve">
<value>Auswählen...</value>
</data>
<data name="Common_Select_Status" xml:space="preserve">
<value>Wählen Sie einen Status</value>
</data>
<data name="Common_Send_NoSuccess" xml:space="preserve">
<value>Versand nicht erfolgreich</value>
</data>
<data name="Common_Send_Success" xml:space="preserve">
<value>Versand erfolgreich</value>
</data>
<data name="Common_TaxIncluded" xml:space="preserve">
<value>inkl. MwSt.</value>
</data>
<data name="Common_Unlock_NoSuccess" xml:space="preserve">
<value>Entsperren nicht erfolgreich.</value>
</data>
<data name="Common_Unlock_Question" xml:space="preserve">
<value>Möchten Sie diesen App-User wirklich entsperren?</value>
</data>
<data name="Common_Unlock_Success" xml:space="preserve">
<value>Entsperren erfolgreich.</value>
</data>
<data name="Common_User_Locked" xml:space="preserve">
<value>Der Benutzer wurde gesperrt.</value>
</data>
<data name="Common_Yes" xml:space="preserve">
<value>Ja</value>
</data>
<data name="CustomerType_Comment" xml:space="preserve">
<value>Anmerkungen</value>
</data>
<data name="CustomerType_Delete_Question" xml:space="preserve">
<value>Möchten Sie diesen Kundentypen wirklich löschen?</value>
</data>
<data name="CustomerType_Delete_Question_Batch" xml:space="preserve">
<value>Möchten Sie diese Kundentypen wirklich löschen?</value>
</data>
<data name="CustomerType_Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="CustomerType_TenantId" xml:space="preserve">
<value>Mandant</value>
</data>
<data name="Customer_CompanyName" xml:space="preserve">
<value>Name</value>
</data>
<data name="Customer_CustomerTypeName" xml:space="preserve">
<value>Typ</value>
</data>
<data name="Customer_Delete_Question" xml:space="preserve">
<value>Möchten Sie diesen Kunden wirklich löschen?</value>
</data>
<data name="Customer_Delete_Question_Batch" xml:space="preserve">
<value>Möchten Sie diese Kunden wirklich löschen?</value>
</data>
<data name="Customer_Number" xml:space="preserve">
<value>Nummer</value>
</data>
<data name="Customer_ResellerName" xml:space="preserve">
<value>Distributor</value>
</data>
<data name="Customer_Vat" xml:space="preserve">
<value>UID</value>
</data>
<data name="Err_Fido_AuthenticatorError" xml:space="preserve">
<value>Die Authentifizierung wurde abgebrochen.</value>
</data>
<data name="Err_Fido_Exists" xml:space="preserve">
<value>Konnte keine Anmeldeinformationen im Browser erstellen. Wahrscheinlich, weil der Benutzername bereits bei Ihrem Authentifikator registriert ist, oder kein Authifikator zur Verfügung steht.</value>
</data>
<data name="Err_Fido_RequestFailed" xml:space="preserve">
<value>Anfrage an Server fehlgeschlagen. Ihre Anmeldeinformationen können nicht authentifiziert werden.</value>
</data>
<data name="Err_Fido_VerificationFailed" xml:space="preserve">
<value>Ihre Anmeldedaten konnten nicht überprüft werden.</value>
</data>
<data name="Err_Forbidden" xml:space="preserve">
<value>Der Zugriff auf diese Ressource ist nicht gestattet (403).</value>
</data>
<data name="Err_Subscription_Denied" xml:space="preserve">
<value>Ihr Abo ist nicht (mehr) gültig.</value>
</data>
<data name="Err_Unauthorized" xml:space="preserve">
<value>Sie verfügen nicht über das Recht auf diese Ressource zuzugreifen (401).</value>
</data>
<data name="Fido_Create_Success" xml:space="preserve">
<value>Ihre FIDO2-Anmeldeinformationen wurden erfolgreich für diesen Authentifikator erstellt.</value>
</data>
<data name="Password_Change_NoSuccess" xml:space="preserve">
<value>Ihr Passwort konnte nicht geändert werden.</value>
</data>
<data name="Password_Change_Success" xml:space="preserve">
<value>Ihr Passwort wurde erfolgreich geändert.</value>
</data>
<data name="Password_Set_NoSuccess" xml:space="preserve">
<value>Ihr Passwort konnte nicht gesetzt werden.</value>
</data>
<data name="Password_Set_Success" xml:space="preserve">
<value>Ihr Passwort wurde erfolgreich gesetzt.</value>
</data>
<data name="Profile_Change_NoSuccess" xml:space="preserve">
<value>Ihr Profil konnte nicht aktualisiert werden.</value>
</data>
<data name="Profile_Change_Success" xml:space="preserve">
<value>Ihr Profil wurde erfolgreich aktualisiert.</value>
</data>
<data name="Session_Timout_Warning" xml:space="preserve">
<value>Sie werden bei Inaktivität in {0} Minuten abgemeldet.</value>
</data>
<data name="Session_Timout_Warning_Title" xml:space="preserve">
<value>Session timeout warning</value>
</data>
<data name="Settings_Delete_Question" xml:space="preserve">
<value>Möchten Sie diese Einstellung wirklich löschen?</value>
</data>
<data name="Settings_Delete_Question_Batch" xml:space="preserve">
<value>Möchten Sie diese Einstellungen wirklich löschen?</value>
</data>
<data name="Shop_Gdpr_NotAccepted" xml:space="preserve">
<value>Bitte bestätigen Sie, dass Sie die Datenschutzerklärung zur Kenntnis genommen haben.</value>
</data>
<data name="Shop_TermsAndConditions_NotAccepted" xml:space="preserve">
<value>Bitte stimmen Sie den AGB zu.</value>
</data>
<data name="SystemMessage_Send_Question" xml:space="preserve">
<value>Möchten Sie diese Systemnachricht wirklich senden?</value>
</data>
<data name="User_ConfirmEmail_Question" xml:space="preserve">
<value>Möchten Sie die E-Mail Adresse des gewählten Benutzers wirklich bestätigen?</value>
</data>
<data name="User_Delete_Question" xml:space="preserve">
<value>Möchten Sie diesen Benutzer wirklich löschen?</value>
</data>
<data name="User_Delete_Question_Batch" xml:space="preserve">
<value>Möchten Sie die gewählten Benutzer wirklich löschen?</value>
</data>
<data name="User_Lock_Question" xml:space="preserve">
<value>Möchten Sie diesen Benutzer wirklich sperren?</value>
</data>
<data name="User_Lock_Question_Batch" xml:space="preserve">
<value>Möchten Sie die gewählten Benutzer wirklich sperren?</value>
</data>
<data name="User_Permissions_Add_Question" xml:space="preserve">
<value>Möchten Sie die mit dieser Rolle verbundenen Berechtigungen hinzufügen?</value>
</data>
<data name="User_Permissions_NotLoaded" xml:space="preserve">
<value>Die mit dieser Rolle verbundenen Berechtigungen konnten nicht geladen werden.</value>
</data>
<data name="User_Permissions_Remove_Question" xml:space="preserve">
<value>Möchten Sie die mit dieser Rolle verbundenen Berechtigungen entfernen?</value>
</data>
<data name="User_ResetPassword_Question" xml:space="preserve">
<value>Möchten Sie wirklich das Passwort dieses Benutzers zurücksetzen?</value>
</data>
<data name="User_ResetPassword_Question_Batch" xml:space="preserve">
<value>Möchten Sie die Passwörter der gewählten Benutzer wirklich zurücksetzen?</value>
</data>
<data name="User_SendConfirmEmail_Question" xml:space="preserve">
<value>Möchten Sie wirklich eine E-Mail Adressen-Bestätigung an den gewählten Benutzer senden?</value>
</data>
<data name="User_Unlock_Question" xml:space="preserve">
<value>Möchten Sie diesen Benutzer wirklich entsperren?</value>
</data>
<data name="User_Unlock_Question_Batch" xml:space="preserve">
<value>Möchten Sie die gewählten Benutzer wirklich entsperren?</value>
</data>
</root>

View File

@ -0,0 +1,426 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Address_City" xml:space="preserve">
<value>City</value>
</data>
<data name="Address_CountryCode" xml:space="preserve">
<value>Country</value>
</data>
<data name="Address_Zip" xml:space="preserve">
<value>Zip</value>
</data>
<data name="Banner_ChangeSize_Question" xml:space="preserve">
<value>Do you really want to resize the banner? If you do, the images for the banner will be reset. If you do not save the changes afterwards, the old images will not be lost.</value>
</data>
<data name="Button_Add" xml:space="preserve">
<value>Add</value>
</data>
<data name="Button_Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Button_No" xml:space="preserve">
<value>No</value>
</data>
<data name="Button_Ok" xml:space="preserve">
<value>Ok</value>
</data>
<data name="Button_Placeholder" xml:space="preserve">
<value>Placeholder</value>
</data>
<data name="Button_Yes" xml:space="preserve">
<value>Yes</value>
</data>
<data name="Cart_Clear_Question" xml:space="preserve">
<value>Do you really want to empty the entire shopping cart?</value>
</data>
<data name="Cart_Remove_Question" xml:space="preserve">
<value>Do you really want to remove this item from the shopping cart?</value>
</data>
<data name="Commmon_Add" xml:space="preserve">
<value>Add</value>
</data>
<data name="Common_Access_Forbidden" xml:space="preserve">
<value>You do not have the necessary rights to access this resource or execute this function.</value>
</data>
<data name="Common_Already_Exits" xml:space="preserve">
<value>Is already in use</value>
</data>
<data name="Common_Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Common_Cancel_NoSuccess" xml:space="preserve">
<value>Cancellation not successful</value>
</data>
<data name="Common_Cancel_Question" xml:space="preserve">
<value>Do you really want to cancel this item?</value>
</data>
<data name="Common_Cancel_Question_Batch" xml:space="preserve">
<value>Do you really want to cancel these items?</value>
</data>
<data name="Common_Cancel_Success" xml:space="preserve">
<value>Cancellation successful</value>
</data>
<data name="Common_CartTotal_Changed" xml:space="preserve">
<value>The total amount of the shopping cart has changed.</value>
</data>
<data name="Common_ChangeState_Question" xml:space="preserve">
<value>Do you really want to change the state of this entity?</value>
</data>
<data name="Common_Completed" xml:space="preserve">
<value>Completed</value>
</data>
<data name="Common_DeactivateTwoFactor_Question" xml:space="preserve">
<value>Do you really want to disable two factor authentication?</value>
</data>
<data name="Common_Delete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Common_Delete_NoSuccess" xml:space="preserve">
<value>Delete failed</value>
</data>
<data name="Common_Delete_Question" xml:space="preserve">
<value>Do you really want to delete this item?</value>
</data>
<data name="Common_Delete_Question_Batch" xml:space="preserve">
<value>Do you really want to delete these items?</value>
</data>
<data name="Common_Delete_Success" xml:space="preserve">
<value>Deleted successfully</value>
</data>
<data name="Common_Edit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Common_Export_NoSuccess" xml:space="preserve">
<value>Export failed.</value>
</data>
<data name="Common_Export_Success" xml:space="preserve">
<value>Export successfull</value>
</data>
<data name="Common_Failed" xml:space="preserve">
<value>Failed</value>
</data>
<data name="Common_ForceDelete_Question" xml:space="preserve">
<value>Do you really want to delete this item? Attention: the data will be physically deleted!</value>
</data>
<data name="Common_ForceDelete_Question_Batch" xml:space="preserve">
<value>Do you really want to delete these items? Attention: the data will be physically deleted!</value>
</data>
<data name="Common_Locked" xml:space="preserve">
<value>Gesperrt</value>
</data>
<data name="Common_Locked_Reason" xml:space="preserve">
<value>Lock reason</value>
</data>
<data name="Common_Logout" xml:space="preserve">
<value>Logout</value>
</data>
<data name="Common_No" xml:space="preserve">
<value>No</value>
</data>
<data name="Common_Note" xml:space="preserve">
<value>Note</value>
</data>
<data name="Common_Ok" xml:space="preserve">
<value>OK</value>
</data>
<data name="Common_ResetPassword_NoSuccess" xml:space="preserve">
<value>The password reset email could not be sent.</value>
</data>
<data name="Common_ResetPassword_Success" xml:space="preserve">
<value>The password reset email has been sent successfully.</value>
</data>
<data name="Common_Save_NoSuccess" xml:space="preserve">
<value>Save failed</value>
</data>
<data name="Common_Save_Success" xml:space="preserve">
<value>Saved successfully</value>
</data>
<data name="Common_Search" xml:space="preserve">
<value>Search</value>
</data>
<data name="Common_Select" xml:space="preserve">
<value>Select...</value>
</data>
<data name="Common_Select_Status" xml:space="preserve">
<value>Select a status</value>
</data>
<data name="Common_Send_NoSuccess" xml:space="preserve">
<value>Sending failed</value>
</data>
<data name="Common_Send_Success" xml:space="preserve">
<value>Sending successful</value>
</data>
<data name="Common_TaxIncluded" xml:space="preserve">
<value>incl. tax</value>
</data>
<data name="Common_Unlock_NoSuccess" xml:space="preserve">
<value>Unlock not successfull.</value>
</data>
<data name="Common_Unlock_Question" xml:space="preserve">
<value>Do you really want to unlock this app-user?</value>
</data>
<data name="Common_Unlock_Success" xml:space="preserve">
<value>Unlock successfull.</value>
</data>
<data name="Common_User_Locked" xml:space="preserve">
<value>The user has been locked.</value>
</data>
<data name="Common_Yes" xml:space="preserve">
<value>Yes</value>
</data>
<data name="CustomerType_Comment" xml:space="preserve">
<value>Comment</value>
</data>
<data name="CustomerType_Delete_Question" xml:space="preserve">
<value>Do you really want to delete this customer type?</value>
</data>
<data name="CustomerType_Delete_Question_Batch" xml:space="preserve">
<value>Do you really want to delete these customer types?</value>
</data>
<data name="CustomerType_Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="CustomerType_TenantId" xml:space="preserve">
<value>Tenant</value>
</data>
<data name="Customer_CompanyName" xml:space="preserve">
<value>Name</value>
</data>
<data name="Customer_CustomerTypeName" xml:space="preserve">
<value>Type</value>
</data>
<data name="Customer_Delete_Question" xml:space="preserve">
<value>Do you really want to delete this customer?</value>
</data>
<data name="Customer_Delete_Question_Batch" xml:space="preserve">
<value>Do you really want to delete these customers?</value>
</data>
<data name="Customer_Number" xml:space="preserve">
<value>Number</value>
</data>
<data name="Customer_ResellerName" xml:space="preserve">
<value>Reseller</value>
</data>
<data name="Customer_Vat" xml:space="preserve">
<value>VAT</value>
</data>
<data name="Err_Fido_AuthenticatorError" xml:space="preserve">
<value>Authentication canceled.</value>
</data>
<data name="Err_Fido_Exists" xml:space="preserve">
<value>Could not create credentials in browser. Probably because the username is already registered with your authenticator, or no authenticator is available.</value>
</data>
<data name="Err_Fido_RequestFailed" xml:space="preserve">
<value>Request to server failed. Can not authenticate your credentials.</value>
</data>
<data name="Err_Fido_VerificationFailed" xml:space="preserve">
<value>Your credentials could not be verified.</value>
</data>
<data name="Err_Forbidden" xml:space="preserve">
<value>You are not allowed to access this resource (403).</value>
</data>
<data name="Err_Subscription_Denied" xml:space="preserve">
<value>Your subscription is not valid (anymore).</value>
</data>
<data name="Err_Unauthorized" xml:space="preserve">
<value>You do not have the right to access this resource (401).</value>
</data>
<data name="Fido_Create_Success" xml:space="preserve">
<value>Your FIDO2 credentials have been created successfully for this authenticator.</value>
</data>
<data name="Password_Change_NoSuccess" xml:space="preserve">
<value>Password could not be changed.</value>
</data>
<data name="Password_Change_Success" xml:space="preserve">
<value>Password has been changed successfully.</value>
</data>
<data name="Password_Set_NoSuccess" xml:space="preserve">
<value>Password could not be set.</value>
</data>
<data name="Password_Set_Success" xml:space="preserve">
<value>Password has been set successfully.</value>
</data>
<data name="Profile_Change_NoSuccess" xml:space="preserve">
<value>Your profile could not be updated.</value>
</data>
<data name="Profile_Change_Success" xml:space="preserve">
<value>Your profile has been updated successfully.</value>
</data>
<data name="Session_Timout_Warning" xml:space="preserve">
<value>You will be logged out in {0} minutes if inactive.</value>
</data>
<data name="Session_Timout_Warning_Title" xml:space="preserve">
<value>Session timeout warning</value>
</data>
<data name="Settings_Delete_Question" xml:space="preserve">
<value>Do you really want to delete this setting?</value>
</data>
<data name="Settings_Delete_Question_Batch" xml:space="preserve">
<value>Do you really want to delete these settings?</value>
</data>
<data name="Shop_Gdpr_NotAccepted" xml:space="preserve">
<value>Please confirm that you have read and understood the privacy policy.</value>
</data>
<data name="Shop_TermsAndConditions_NotAccepted" xml:space="preserve">
<value>Please agree to the terms and conditions.</value>
</data>
<data name="SystemMessage_Send_Question" xml:space="preserve">
<value>Do you really want to send this system message?</value>
</data>
<data name="User_ConfirmEmail_Question" xml:space="preserve">
<value>Do you really want to confirm the email address of the selected user?</value>
</data>
<data name="User_Delete_Question" xml:space="preserve">
<value>Do you really want to delete this user?</value>
</data>
<data name="User_Delete_Question_Batch" xml:space="preserve">
<value>Do you really want to delete the selected users?</value>
</data>
<data name="User_Lock_Question" xml:space="preserve">
<value>Do you really want to lock this user?</value>
</data>
<data name="User_Lock_Question_Batch" xml:space="preserve">
<value>Do you really want to lock the seleted users?</value>
</data>
<data name="User_Permissions_Add_Question" xml:space="preserve">
<value>Do you want to add the permissions associated with this role?</value>
</data>
<data name="User_Permissions_NotLoaded" xml:space="preserve">
<value>The permissions associated with this role could not be loaded.</value>
</data>
<data name="User_Permissions_Remove_Question" xml:space="preserve">
<value>Do you want to remove the permissions associated with this role?</value>
</data>
<data name="User_ResetPassword_Question" xml:space="preserve">
<value>Do you really want to reset this users password?</value>
</data>
<data name="User_ResetPassword_Question_Batch" xml:space="preserve">
<value>Do you really want to reset the selected users passwords?</value>
</data>
<data name="User_SendConfirmEmail_Question" xml:space="preserve">
<value>Do you really want to send an email confirmation to the selected user?</value>
</data>
<data name="User_Unlock_Question" xml:space="preserve">
<value>Do you really want to unlock this user?</value>
</data>
<data name="User_Unlock_Question_Batch" xml:space="preserve">
<value>Do you really want to unlock the selected users?</value>
</data>
</root>

View File

@ -0,0 +1,315 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace gehGassi.Common.Data
{
/// <summary>
/// Datamanager Erweiterungen
/// </summary>
public static class DataManagerExtensions
{
#region EF / Syncfusion Extensions
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values)
{
var resultExp = CreateMethodCallExpression(source, "OrderBy", ordering);
return source.Provider.CreateQuery<T>(resultExp);
}
public static IQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string ordering, params object[] values)
{
var resultExp = CreateMethodCallExpression(source, "OrderByDescending", ordering);
return source.Provider.CreateQuery<T>(resultExp);
}
public static IQueryable<T> ThenBy<T>(this IQueryable<T> source, string ordering, params object[] values)
{
var resultExp = CreateMethodCallExpression(source, "ThenBy", ordering);
return source.Provider.CreateQuery<T>(resultExp);
}
public static IQueryable<T> ThenByDescending<T>(this IQueryable<T> source, string ordering, params object[] values)
{
var resultExp = CreateMethodCallExpression(source, "ThenByDescending", ordering);
return source.Provider.CreateQuery<T>(resultExp);
}
private static MethodCallExpression CreateMethodCallExpression<T>(IQueryable<T> source, string methodName, string ordering)
{
var strings = ordering.Split('.');
var types = new List<Type>();
var properties = new List<PropertyInfo>();
var propertyAccesses = new List<MemberExpression>();
types.Add(typeof(T));
for (int i = 0; i < strings.Length; i++)
{
if (i != 0)
types.Add(properties[i - 1].PropertyType);
properties.Add(types[i].GetProperty(strings[i]));
}
var parameter = Expression.Parameter(types[0], "p");
for (int i = 0; i < properties.Count; i++)
{
propertyAccesses.Add(i == 0
? Expression.MakeMemberAccess(parameter, properties[i])
: Expression.MakeMemberAccess(propertyAccesses[i - 1], properties[i]));
}
var orderByExp = Expression.Lambda(propertyAccesses.Last(), parameter);
return Expression.Call(typeof(Queryable), methodName,
new Type[] { types.First(), properties.Last().PropertyType }, source.Expression, Expression.Quote(orderByExp));
}
#region WHERE
/// <summary>Generate expression from simple and complex property</summary>
/// <param name="propertyName"></param>
/// <param name="sourceType"></param>
/// <param name="paramExpression"></param>
/// <returns></returns>
public static Expression GetValueExpression(this ParameterExpression paramExpression, string propertyName, Type sourceType)
{
Expression expression1 = (Expression)null;
if (typeof(ICustomTypeDescriptor).IsAssignableFrom(sourceType))
{
Expression.Convert(paramExpression, typeof(ICustomTypeDescriptor));
return (Expression<Func<ICustomTypeDescriptor, object, object>>)((ICustomTypeDescriptor t, object o) => t.GetProperties()[propertyName].GetValue(o));
}
string str1 = propertyName;
char[] chArray = new char[1] { '.' };
foreach (string str2 in str1.Split(chArray))
{
if (expression1 != null)
{
int result;
expression1 = !int.TryParse(str2, out result) ? (Expression)Expression.PropertyOrField(expression1, str2) : (Expression)Expression.ArrayIndex(expression1, (Expression)Expression.Constant((object)result));
}
else
{
Expression expression2 = (Expression)paramExpression;
if (paramExpression.Type != sourceType)
expression2 = (Expression)Expression.Convert((Expression)paramExpression, sourceType);
expression1 = (Expression)Expression.PropertyOrField(expression2, str2);
}
}
return expression1;
}
public static IQueryable Filter(this IQueryable source, string propertyName, object value, FilterType filterType, bool isCaseSensitive, Type sourceType)
{
ParameterExpression paramExpression = Expression.Parameter(source.ElementType, sourceType.Name);
Expression valueExpression = paramExpression.GetValueExpression(propertyName, sourceType);
Type type = valueExpression.Type;
//if (Syncfusion.Linq.NullableHelperInternal.IsNullableType(valueExpression.Type))
// type = NullableHelperInternal.GetUnderlyingType(valueExpression.Type);
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = Nullable.GetUnderlyingType(type);
}
if (type == typeof(DateTimeOffset))
{
value = DateTimeOffset.Parse(value.ToString());
}
else
value = Convert.ChangeType((object)value, type);
if (filterType == FilterType.Equals || filterType == FilterType.NotEquals || (filterType == FilterType.LessThan || filterType == FilterType.LessThanOrEqual) || (filterType == FilterType.GreaterThan || filterType == FilterType.GreaterThanOrEqual))
{
BinaryExpression binaryExpression = (BinaryExpression)null;
switch (filterType)
{
case FilterType.LessThan:
binaryExpression = Expression.LessThan(valueExpression, (Expression)Expression.Constant(value, valueExpression.Type));
break;
case FilterType.LessThanOrEqual:
binaryExpression = Expression.LessThanOrEqual(valueExpression, (Expression)Expression.Constant(value, valueExpression.Type));
break;
case FilterType.Equals:
binaryExpression = !(type != typeof(string)) ? (!isCaseSensitive ? Expression.Equal((Expression)GetToLowerMethodCallExpression(valueExpression), (Expression)Expression.Constant(value == null ? value : (object)value.ToString().ToLower(), valueExpression.Type)) : Expression.Equal(valueExpression, (Expression)Expression.Constant(value, valueExpression.Type))) : Expression.Equal(valueExpression, (Expression)Expression.Constant(value, valueExpression.Type));
break;
case FilterType.NotEquals:
binaryExpression = !(type != typeof(string)) ? (!isCaseSensitive ? Expression.NotEqual((Expression)GetToLowerMethodCallExpression(valueExpression), (Expression)Expression.Constant(value == null ? value : (object)value.ToString().ToLower(), valueExpression.Type)) : Expression.NotEqual(valueExpression, (Expression)Expression.Constant(value, valueExpression.Type))) : Expression.NotEqual(valueExpression, (Expression)Expression.Constant(value, valueExpression.Type));
break;
case FilterType.GreaterThanOrEqual:
binaryExpression = Expression.GreaterThanOrEqual(valueExpression, (Expression)Expression.Constant(value, valueExpression.Type));
break;
case FilterType.GreaterThan:
binaryExpression = Expression.GreaterThan(valueExpression, (Expression)Expression.Constant(value, valueExpression.Type));
break;
}
LambdaExpression lambdaExpression = Expression.Lambda((Expression)binaryExpression, new ParameterExpression[1] { paramExpression });
return source.Provider.CreateQuery((Expression)Expression.Call(typeof(Queryable), "Where", new Type[1] { source.ElementType }, new Expression[2] { source.Expression, (Expression)lambdaExpression }));
}
MethodInfo method = ((IEnumerable<MethodInfo>)typeof(string).GetMethods()).Where<MethodInfo>((Func<MethodInfo, bool>)(m => m.Name == filterType.ToString())).FirstOrDefault<MethodInfo>();
Expression body;
if (isCaseSensitive)
body = (Expression)Expression.Call(valueExpression, method, new Expression[1]
{
(Expression) Expression.Constant(value, typeof (string))
});
else
body = (Expression)Expression.Call((Expression)GetToLowerMethodCallExpression(valueExpression), method, new Expression[1]
{
(Expression) Expression.Constant(value == null ? value : (object) value.ToString().ToLower(), typeof (string))
});
LambdaExpression lambdaExpression1 = Expression.Lambda(body, new ParameterExpression[1]
{
paramExpression
});
return source.Provider.CreateQuery((Expression)Expression.Call(typeof(Queryable), "Where", new Type[1] { source.ElementType }, new Expression[2]{source.Expression,(Expression) lambdaExpression1
}));
}
public static IQueryable Filter(this IQueryable source, string propertyName, object value, FilterType filterType, bool isCaseSensitive)
{
Type elementType = source.ElementType;
return source.Filter(propertyName, value, filterType, isCaseSensitive, elementType);
}
private static MethodCallExpression GetToLowerMethodCallExpression(Expression memExp)
{
MethodInfo method = ((IEnumerable<MethodInfo>)typeof(string).GetMethods()).FirstOrDefault<MethodInfo>((Func<MethodInfo, bool>)(m => m.Name == "ToLower"));
return Expression.Call(memExp, method, new Expression[0]);
}
#endregion
#region DataManager
public static void SetComplexProperties(this DataManager dm, List<ComplexProperty> properties)
{
foreach (var complexProperty in properties)
{
if (dm?.Sorted?.FirstOrDefault(c => c.Name == complexProperty.Original) != null) dm.Sorted.First(c => c.Name == complexProperty.Original).Name = complexProperty.Complex;
//if (dm?.Select?.FindIndex(ind => ind.Equals(complexProperty.Original)) > -1)
// dm.Select[dm.Select.FindIndex(ind => ind.Equals(complexProperty.Original))] = complexProperty.Complex;
if (dm?.Where?.FirstOrDefault(c => c.Field == complexProperty.Original) != null) dm.Where.First(c => c.Field == complexProperty.Original).Field = complexProperty.Complex;
if (dm?.Where != null)
{
foreach (var whereFilter in dm.Where)
{
SetComplexPropertiesForPredicate(whereFilter, complexProperty);
}
}
}
}
private static void SetComplexPropertiesForPredicate(WhereFilter whereFilter, ComplexProperty complexProperty)
{
if (whereFilter.predicates == null)
{
if (whereFilter.Field == complexProperty.Original)
whereFilter.Field = complexProperty.Complex;
}
else
{
foreach (var predicate in whereFilter.predicates)
{
SetComplexPropertiesForPredicate(predicate, complexProperty);
}
}
}
public static IQueryable<T> ApplySorting<T>(this DataManager dm, IQueryable<T> source)
{
if (dm.Sorted != null && dm.Sorted.Count > 0)
{
var count = 0;
foreach (var sort in dm.Sorted)
{
if (count == 0)
source = sort.Direction.ToLower() == "ascending" ? source.OrderBy(sort.Name.ToPascalCase()) : source.OrderByDescending(sort.Name.ToPascalCase());
else
source = sort.Direction.ToLower() == "ascending" ? source.ThenBy(sort.Name.ToPascalCase()) : source.ThenByDescending(sort.Name.ToPascalCase());
count += 1;
}
}
return source;
}
public static IQueryable<T> ApplyFiltering<T>(this DataManager dm, IQueryable<T> source, out int totalCount)
{
totalCount = 0;
if (dm.Where != null)
{
foreach (var whereFilter in dm.Where)
{
whereFilter.Field = whereFilter.Field.ToPascalCase();
source = HandlePredicate(whereFilter, source);
}
}
totalCount = source.Count();
return source;
}
public static IQueryable<T> HandlePredicate<T>(WhereFilter whereFilter, IQueryable<T> source)
{
if (whereFilter.predicates == null)
{
var filter = GetFilterOperator(whereFilter.Operator);
var filterType = (FilterType)Enum.Parse(typeof(FilterType), filter, true);
if (whereFilter.value != null)
source = (IQueryable<T>)source.Filter(whereFilter.Field, whereFilter.value, filterType, false);
}
else
{
foreach (var predicate in whereFilter.predicates)
{
source = HandlePredicate<T>(predicate, source);
}
}
return source;
}
private static string GetFilterOperator(string filter)
{
if (filter == "equal")
filter = "equals";
else if (filter == "notequal")
filter = "notequals";
return filter;
}
public static IQueryable<T> ApplyPaging<T>(this DataManager dm, IQueryable<T> source)
{
if (dm.Skip != 0)
{
source = (IQueryable<T>)source.Skip<T>(dm.Skip);
}
if (dm.Take != 0)
{
source = (IQueryable<T>)source.Take<T>(dm.Take);
}
return source;
}
#endregion
#endregion
#region Custom
#endregion
}
}

View File

@ -0,0 +1,107 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
namespace gehGassi.Common.Data
{
/// <summary>
/// Datamanager für die Übernahme der Such-Parameter von Syncfusion Komponenten
/// </summary>
[ExcludeFromCodeCoverage]
public class DataManager
{
[DefaultValue(null)]
public int Skip { get; set; }
[DefaultValue(null)]
public int Take { get; set; }
public string antiForgery { get; set; }
[DefaultValue(null)]
public bool RequiresCounts { get; set; }
public string Table { get; set; }
public List<string> Group { get; set; }
public List<string> Select { get; set; }
public List<string> Expand { get; set; }
public List<Sort> Sorted { get; set; }
public List<SearchFilter> Search { get; set; }
public List<WhereFilter> Where { get; set; }
public List<Aggregate> Aggregates { get; set; }
public string SearchValue { get; set; }
}
[ExcludeFromCodeCoverage]
public class Sort
{
public string Name { get; set; }
public string Direction { get; set; }
}
[ExcludeFromCodeCoverage]
public class SearchFilter
{
public List<string> Fields { get; set; }
public string Key { get; set; }
public string Operator { get; set; }
}
[ExcludeFromCodeCoverage]
public class WhereFilter
{
public string Field { get; set; }
public bool IgnoreCase { get; set; }
public bool IsComplex { get; set; }
public string Operator { get; set; }
public string Condition { get; set; }
public object value { get; set; }
public List<WhereFilter> predicates { get; set; }
}
[ExcludeFromCodeCoverage]
public class Aggregate
{
public string Field { get; set; }
public string Type { get; set; }
}
[ExcludeFromCodeCoverage]
public class ComplexProperty
{
public string Original { get; set; }
public string Complex { get; set; }
}
public enum FilterType
{
LessThan,
LessThanOrEqual,
Equals,
NotEquals,
GreaterThanOrEqual,
GreaterThan,
StartsWith,
EndsWith,
Contains,
Undefined,
Between,
}
}

View File

@ -0,0 +1,198 @@
using ProjNet.CoordinateSystems.Transformations;
using ProjNet.CoordinateSystems;
using ProjNet;
using System.Collections.Generic;
using System.Linq;
using NetTopologySuite.Geometries;
namespace gehGassi.Common.Data
{
/// <summary>
/// Erweiterungen
/// </summary>
public static class Extensions
{
public static string ToPascalCase(this string value)
{
// If there are 0 or 1 characters, just return the string.
if (value == null) return value;
if (value.Length < 2) return value.ToUpper();
var delimiter = new[] { ' ', '-', '_', '.' };
var charArray = new char[value.Length];
var newWord = true;
for (var i = 0; i < value.Length; ++i)
{
var currentChar = value[i];
if (char.IsLetter(currentChar))
{
if (newWord || char.IsUpper(currentChar))
{
newWord = false;
currentChar = char.ToUpper(currentChar);
}
else
{
currentChar = char.ToLower(currentChar);
}
}
else if (delimiter.Contains(currentChar))
{
newWord = true;
}
charArray[i] = currentChar;
}
return new string(charArray);
}
/// <summary>
/// Gibt einen Text gekürzt auf eine max. Zeichenzahl zurück.
/// Wenn der Text länger als maxLength war, wird "..." angehängt.
/// </summary>
/// <param name="text">Text</param>
/// <param name="maxLength">max. Länge</param>
/// <returns>Text gekürzt</returns>
public static string Ellipsis(this string text, int maxLength)
{
if (string.IsNullOrWhiteSpace(text))
return "";
if (maxLength > 0 && text.Length > maxLength)
{
return text.Substring(0, maxLength) + "...";
}
return text;
}
/// <summary>
/// Hilfsfunktion die aus einem String "gefährlichen" Text für SQL-Injection Attacken entfernt
/// </summary>
/// <param name="text">Text</param>
/// <param name="maxLength">Max. Länge des Textes</param>
/// <returns>Bereinigter Text</returns>
public static string SanitizeSqlInjection(this string text, int maxLength)
{
if (string.IsNullOrWhiteSpace(text))
return "";
if (text.Length > maxLength)
text = text.Substring(0, maxLength);
var result = text.Trim().Replace(";", "");
result = result.Replace("'", "");
result = result.Replace("--", "");
result = result.Replace("/*", "");
result = result.Replace("*/", "");
result = result.Replace("xp_", "");
return result;
}
/// <summary>
/// Hilfsfunktion die aus einem Text einen filter-Text für felxible Like-Abfragen erstellt.
/// * wird mit SQL-Wildcard % ersetzt
/// - wird mit SQL-Wildcard % ersetzt
/// " " wird mit SQL-Wildcard % ersetzt
/// </summary>
/// <param name="text">Text der bearbeitet werden soll</param>
/// <returns>SQL-Like angepasster Text</returns>
public static string SqlFilter(this string text)
{
if (string.IsNullOrWhiteSpace(text))
return "";
var result = text.Trim().Replace("*", "%");
result = result.Replace("-", "%");
result = result.Replace(" ", "%");
return $"%{result}%";
}
private static readonly CoordinateSystemServices _coordinateSystemServices
= new CoordinateSystemServices(
new Dictionary<int, string>
{
// Coordinate systems:
[4326] = GeographicCoordinateSystem.WGS84.WKT,
// This coordinate system covers the area of our data.
// Different data requires a different coordinate system.
[2855] =
@"
PROJCS[""NAD83(HARN) / Washington North"",
GEOGCS[""NAD83(HARN)"",
DATUM[""NAD83_High_Accuracy_Regional_Network"",
SPHEROID[""GRS 1980"",6378137,298.257222101,
AUTHORITY[""EPSG"",""7019""]],
AUTHORITY[""EPSG"",""6152""]],
PRIMEM[""Greenwich"",0,
AUTHORITY[""EPSG"",""8901""]],
UNIT[""degree"",0.01745329251994328,
AUTHORITY[""EPSG"",""9122""]],
AUTHORITY[""EPSG"",""4152""]],
PROJECTION[""Lambert_Conformal_Conic_2SP""],
PARAMETER[""standard_parallel_1"",48.73333333333333],
PARAMETER[""standard_parallel_2"",47.5],
PARAMETER[""latitude_of_origin"",47],
PARAMETER[""central_meridian"",-120.8333333333333],
PARAMETER[""false_easting"",500000],
PARAMETER[""false_northing"",0],
UNIT[""metre"",1,
AUTHORITY[""EPSG"",""9001""]],
AUTHORITY[""EPSG"",""2855""]]
",
[3035] =
@"
PROJCS[""ETRS89-extended / LAEA Europe"",
GEOGCS[""ETRS89"",
DATUM[""European_Terrestrial_Reference_System_1989"",
SPHEROID[""GRS 1980"",6378137,298.257222101],
TOWGS84[0,0,0,0,0,0,0]],
PRIMEM[""Greenwich"",0],
UNIT[""degree"",0.0174532925199433,
AUTHORITY[""EPSG"",""9122""]],
AUTHORITY[""EPSG"",""4258""]],
PROJECTION[""Lambert_Azimuthal_Equal_Area""],
PARAMETER[""latitude_of_center"",52],
PARAMETER[""longitude_of_center"",10],
PARAMETER[""false_easting"",4321000],
PARAMETER[""false_northing"",3210000],
UNIT[""metre"",1],
AUTHORITY[""EPSG"",""3035""]]
"
});
public static Geometry ProjectTo(this Geometry geometry, int srid)
{
var transformation = _coordinateSystemServices.CreateTransformation(geometry.SRID, srid);
var result = geometry.Copy();
result.Apply(new MathTransformFilter(transformation.MathTransform));
return result;
}
private class MathTransformFilter : ICoordinateSequenceFilter
{
private readonly MathTransform _transform;
public MathTransformFilter(MathTransform transform)
=> _transform = transform;
public bool Done => false;
public bool GeometryChanged => true;
public void Filter(CoordinateSequence seq, int i)
{
var x = seq.GetX(i);
var y = seq.GetY(i);
var z = seq.GetZ(i);
_transform.Transform(ref x, ref y, ref z);
seq.SetX(i, x);
seq.SetY(i, y);
seq.SetZ(i, z);
}
}
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace gehGassi.Common.Extensions
{
public static class OrderExtensions
{
public static IOrderedQueryable<T> SpecialOrderBy<T>(this IQueryable<T> source, string propertyName, SortDirection sortDirection = SortDirection.Ascending)
{
if (sortDirection == SortDirection.Ascending)
return source.OrderBy(ToLambda<T>(propertyName));
return source.OrderByDescending(ToLambda<T>(propertyName));
}
public static IOrderedQueryable<T> SpecialThenBy<T>(this IOrderedQueryable<T> source, string propertyName, SortDirection sortDirection = SortDirection.Ascending)
{
if (sortDirection == SortDirection.Ascending)
return source.ThenBy(ToLambda<T>(propertyName));
return source.ThenByDescending(ToLambda<T>(propertyName));
}
private static Expression<Func<T, object>> ToLambda<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, propertyName);
var convertedProperty = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<T, object>>(convertedProperty, parameter);
}
}
/// <summary>
/// Sortierrichtung
/// </summary>
public enum SortDirection
{
/// <summary>
/// Aufsteigend
/// </summary>
Ascending = 1,
/// <summary>
/// Absteigend
/// </summary>
Descending = 2
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Reflection;
using System.Resources;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace gehGassi.Common
{
/// <summary>
/// String-Localizer Factory die mit fixen Ressourcen in der Assembly "Localization" arbeitet.
/// Wenn nichts angegeben wird "Text" verwendet.
/// "Annotations" ist auch ein Sonderfall der definiert ist.
/// Soll eine andere Ressource in der Assembly "Localization" verwendet werden, dann muss z.B. IStringLocalizer<XXX> verwendet werden.
/// Dann muss die Ressource in "Localization" XXX heissen.
/// </summary>
public class FixedStringLocalizerFactory : IStringLocalizerFactory
{
private readonly ILogger _logger;
private readonly IResourceNamesCache _resourceNamesCache = (IResourceNamesCache)new ResourceNamesCache();
private readonly ConcurrentDictionary<string, ResourceManagerStringLocalizer> _localizerCache = new ConcurrentDictionary<string, ResourceManagerStringLocalizer>();
private readonly string _applicationName;
private readonly string _resourcesRelativePath;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="hostingEnvironment">Informationen über das Hosting Environment</param>
/// <param name="localizationOptions">Optionen</param>
/// <param name="logger">Instanz eines ILogger</param>
public FixedStringLocalizerFactory(IHostEnvironment hostingEnvironment, IOptions<LocalizationOptions> localizationOptions, ILogger<FixedStringLocalizerFactory> logger)
{
if (hostingEnvironment == null)
throw new ArgumentNullException("hostingEnvironment");
if (localizationOptions == null)
throw new ArgumentNullException("localizationOptions");
_logger = logger;
this._applicationName = hostingEnvironment.ApplicationName;
this._resourcesRelativePath = localizationOptions.Value.ResourcesPath ?? string.Empty;
if (string.IsNullOrEmpty(this._resourcesRelativePath))
return;
this._resourcesRelativePath = this._resourcesRelativePath.Replace(Path.AltDirectorySeparatorChar, '.').Replace(Path.DirectorySeparatorChar, '.') + ".";
}
/// <summary>
/// Erstellt einen StringLocalizer vom gesuchten Typ
/// </summary>
/// <param name="resourceSource">Typ. Name des Typs entspricht Name der Ressource-Datei</param>
/// <returns>StringLocalizer</returns>
public IStringLocalizer Create(Type resourceSource)
{
if ((object)resourceSource == null)
throw new ArgumentNullException("resourceSource");
var baseName = resourceSource.Name;
var location = _applicationName;
return Create(baseName, location);
}
/// <summary>
/// Erstellt einen StringLocalizer
/// </summary>
/// <param name="baseName">Basis-Name. Wenn nicht Annotations, dann wird hier "Text" verwendet</param>
/// <param name="location">Wird nicht verwendet. Ist immer "Localization"</param>
/// <returns>StringLocalizer</returns>
public IStringLocalizer Create(string baseName, string location)
{
if (baseName == null)
throw new ArgumentNullException("baseName");
if (baseName.ToUpperInvariant() != "ANNOTATIONS" && baseName.ToUpperInvariant() != "IDENTITYERROR" && baseName.ToUpperInvariant() != "CLIENT")
baseName = "Text";
location = "gehGassi.Common";
return (IStringLocalizer)this._localizerCache.GetOrAdd(string.Format("B={0},L={1}", (object)baseName, (object)location), (Func<string, ResourceManagerStringLocalizer>)(_ =>
{
Assembly assembly = Assembly.Load(new AssemblyName(location));
baseName = $"{location}.{baseName}";
return new ResourceManagerStringLocalizer(new ResourceManager(baseName, assembly), assembly, baseName, this._resourceNamesCache, _logger);
}));
}
}
}

View File

@ -0,0 +1,29 @@
namespace gehGassi.Common
{
/// <summary>
/// Einstellungen für die UID-Prüfung
/// </summary>
public class VatValidationSettings
{
/// <summary>
/// Ist aktiviert oder nicht
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// Adresse des WebService
/// </summary>
public string Address { get; set; }
/// <summary>
/// Semikolon getrennte Liste der EU-Länder
/// </summary>
public string Countries { get; set; }
/// <summary>
/// Semikolon getrennte Liste von Ländern für die keine Steuer berechnet wird
/// </summary>
public string NoTaxCountries { get; set; }
}
}

2068
gehGassi.Common/Text.de.resx Normal file

File diff suppressed because it is too large Load Diff

2070
gehGassi.Common/Text.en.resx Normal file

File diff suppressed because it is too large Load Diff

2037
gehGassi.Common/Text.resx Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Version>1.0.13</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.10" />
<PackageReference Include="NetTopologySuite" Version="2.5.0" />
<PackageReference Include="ProjNet" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Annotations.de.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Annotations.en.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Text.de.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Text.en.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -0,0 +1,39 @@
using System;
using Microsoft.AspNetCore.WebUtilities;
namespace gehGassi.Core.Common
{
internal static class Extensions
{
internal static string GetShareFromFileUrl(this string url)
{
if (!string.IsNullOrWhiteSpace(url))
{
var uri = new Uri(url);
var queryParameters = QueryHelpers.ParseQuery(uri.Query);
if (queryParameters.ContainsKey("share"))
return queryParameters["share"];
}
return string.Empty;
}
internal static string GetPathFromFileUrl(this string url)
{
if (!string.IsNullOrWhiteSpace(url))
{
var uri = new Uri(url);
var queryParameters = QueryHelpers.ParseQuery(uri.Query);
if (queryParameters.ContainsKey("path"))
{
var path = queryParameters["path"];
return path.ToString().TrimStart('/');
}
}
return string.Empty;
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Threading.Tasks;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen DBcontext der Auditing vornimmt
/// </summary>
public interface IAuditDbContext : IDisposable
{
/// <summary>
/// Speichern der Änderungen mit Auditing (wenn aktiviert) und Benutzername
/// </summary>
/// <param name="userName">Benutzer der die Änderungen vorgenommen hat</param>
/// <param name="noAudit">Gibt an, dass kein Audit-Eintrag erstellt werden soll, auch wenn Audit aktiviert ist</param>
/// <returns>Anzahl betroffener Datensätze</returns>
int SaveChanges(string userName, bool noAudit);
/// <summary>
/// Speichern der Änderungen mit Auditing (wenn aktiviert) und Benutzername
/// </summary>
/// <param name="userName">Benutzer der die Änderungen vorgenommen hat</param>
/// <param name="noAudit">Gibt an, dass kein Audit-Eintrag erstellt werden soll, auch wenn Audit aktiviert ist</param>
/// <returns>Anzahl betroffener Datensätze</returns>
Task<int> SaveChangesAsync(string userName, bool noAudit);
}
/// <summary>
/// Hilfsklasse - Einstellungen des AuditDbContext
/// </summary>
public class AuditOptions
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
public AuditOptions()
{
IsEnabled = true;
}
/// <summary>
/// Soll das auditing durchgeführt werden?
/// </summary>
public bool IsEnabled { get; set; }
}
}

View File

@ -0,0 +1,15 @@
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittsellenbeschreibung für einen Service der die Validierung von E-Mail Adressen ermöglicht
/// </summary>
public interface IEmailValidationService
{
/// <summary>
/// Gibt zurück ob es sich bei der E-Mail Adresse um eine Wegwerfadresse handelt
/// </summary>
/// <param name="emailAddress">E-Mail Adresse die geprüft werden soll</param>
/// <returns>true wenn Wegwerf-Email, false sonst</returns>
bool IsDisposable(string emailAddress);
}
}

View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der die Verwaltung von Dateien ermöglicht
/// </summary>
public interface IFileService
{
/// <summary>
/// Speichern einer Datei in einen Container
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <param name="file">Datei als Byte-Array</param>
/// <param name="overwrite">Überschreiben vorhandener Daten</param>
/// <returns>Absoluter Dateiname</returns>
Task<string> StoreAsync(string containerName, string filename, byte[] file, bool overwrite = true);
/// <summary>
/// Speichern einer Datei in einen Container
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <param name="fileStream">Datei als Stream</param>
/// <param name="overwrite">Überschreiben vorhandener Daten</param>
/// <returns>Absoluter Dateiname</returns>
Task<string> StoreAsync(string containerName, string filename, Stream fileStream, bool overwrite = true);
/// <summary>
/// Umbenennen einer Datei in einem Container
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <param name="newFilename">Neuer Dateiname</param>
Task RenameAsync(string containerName, string filename, string newFilename);
/// <summary>
/// Gibt eine Datei zurück
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <returns>Datei als Byte-Array</returns>
Task<byte[]> GetAsync(string containerName, string filename);
/// <summary>
/// Gibt eine Datei zurück
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <returns>Datei als Stream</returns>
Task<Stream> GetAsStreamAsync(string containerName, string filename);
/// <summary>
/// Löschen einer Datei in einem Container
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
Task DeleteAsync(string containerName, string filename);
/// <summary>
/// Gibt eine Liste aller Dateien eines Containers zurück die sich in einem Directory befinden
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="directory">Verzeichnis.</param>
/// <param name="fileNamePart">Teil eines Dateinamens für die suche</param>
/// <returns>Liste von Dateien</returns>
Task<List<string>> ListFilesAsync(string containerName, string directory = "", string fileNamePart = "");
/// <summary>
/// Gibt eine Liste aller Verzeichnise zurück die sich im Container befindne
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="root">wenn angegeben, wird von dem Verzeihnis als root ausgegangen</param>
/// <returns></returns>
Task<List<string>> ListDirectoriesAsync(string containerName, string root = "");
/// <summary>
/// Koperien von Dateinen eines Containers in einen anderen
/// </summary>
/// <param name="sourceContainer">Quellcontainer</param>
/// <param name="sourceDirectory">Quellpfad</param>
/// <param name="targetContainer">ZielContainer</param>
/// <param name="targetDirectory">ZielPfad</param>
Task CopyFilesAsync(string sourceContainer, string sourceDirectory, string targetContainer, string targetDirectory);
/// <summary>
/// Koperien von Dateien eines Pfades in einen anderen
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="sourceDirectory">Quellpfad</param>
/// <param name="targetDirectory">ZielPfad</param>
Task CopyFilesAsync(string containerName, string sourceDirectory, string targetDirectory);
/// <summary>
/// Verschieben von Dateien eines Containers in einen anderen
/// </summary>
/// <param name="sourceContainer">Quellcontainer</param>
/// <param name="sourceDirectory">Quellpfad</param>
/// <param name="targetContainer">ZielContainer</param>
/// <param name="targetDirectory">ZielPfad</param>
Task MoveFilesAsync(string sourceContainer, string sourceDirectory, string targetContainer, string targetDirectory);
/// <summary>
/// Verschieben von Dateien eines Pfades in einen anderen
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="sourceDirectory">Quellpfad</param>
/// <param name="targetDirectory">ZielPfad</param>
Task MoveFilesAsync(string containerName, string sourceDirectory, string targetDirectory);
/// <summary>
/// Löschen des Inhalts eines Verzeichnises
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="directory">Verzeichnis</param>
/// <param name="fileNamePart">Optional: Dateinamen Wildcard</param>
Task ClearDirectoryAsync(string containerName, string directory, string fileNamePart = "");
/// <summary>
/// Löscht einen Container und den Inhalt
/// </summary>
/// <param name="containerName">Name des Containers</param>
Task DeleteContainerAsync(string containerName);
/// <summary>
/// Löscht Dateien aus einem Ordner auf welche
/// vor dem Datum das letzte mal zugegriffen wurde
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="directory">Verzeichnis</param>
/// <param name="date">Datum</param>
Task DeleteOldFilesAsync(string containerName, string directory, DateTimeOffset date);
/// <summary>
/// Gibt den reinen Dateinamen eines Files zurück (Ohne Directories usw)
/// </summary>
/// <param name="fileName">Dateiname</param>
/// <returns>Reiner Dateiname</returns>
string GetFlatFilename(string fileName);
}
}

View File

@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der die Verwaltung von Dateien als share ermöglicht.
/// Alle Ordner und Dateinamen sind relativ anzugeben.
/// z.B. test/Ordner1/order2
/// z.B. test/Ordner1/test.png
/// </summary>
public interface IFileShareService
{
/// <summary>
/// Auflisten der Ordner in einem Ordner
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Liste der Ordner im Share</returns>
List<FolderResult> ListFolders(string shareName, string folder);
/// <summary>
/// Gibt Informationen über einen Ordner zurück
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Ordner oder null</returns>
FolderResult GetFolderInfo(string shareName, string folder);
/// <summary>
/// Auflisten der Dateien in einem Ordner
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Liste der Dateien im Ordner</returns>
List<FileResult> ListFiles(string shareName, string folder);
/// <summary>
/// Gibt Informationen über eine Datei zurück
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname relativ</param>
/// <returns>FileInfo oder null</returns>
FileResult GetFileInfo(string shareName, string fileName);
/// <summary>
/// Auflisten der Dateien und Ordner in einem Ordner
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Liste Dateien und Liste Ordner</returns>
(List<FileResult>, List<FolderResult>) ListFilesAndFolders(string shareName, string folder);
/// <summary>
/// Anlegen eines Ordners.
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="newfolderName">Neuer Ordner</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
bool CreateFolder(string shareName, string newfolderName);
/// <summary>
/// Speichern einer Datei
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <param name="file">Datei</param>
/// <param name="overwrite">true wenn überschrieben werden soll, false sonst</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
bool Store(string shareName, string fileName, byte[] file, bool overwrite = false);
/// <summary>
/// Holt eine Datei
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <returns>Datei oder null, wenn nicht gefunden</returns>
byte[] Get(string shareName, string fileName);
/// <summary>
/// Holt eine Datei als Stream
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <returns>Stream oder null, wenn nicht gefunden</returns>
Stream GetStream(string shareName, string fileName);
/// <summary>
/// Holt eine Datei als Stream
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <param name="start">Wo soll gestartet werden</param>
/// <param name="end">Ende</param>
/// <returns>Stream oder null, wenn nicht gefunden</returns>
Stream GetStream(string shareName, string fileName, long start, long end);
/// <summary>
/// Umbenennen einer Datei
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Aktueller Dateiname</param>
/// <param name="newFileName">Neuer Dateiname</param>
/// <param name="copy">Soll eine Kopie gemacht werden? Wenn nicht wird die Quelldatei gelöscht</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
bool RenameFile(string shareName, string fileName, string newFileName, bool copy);
/// <summary>
/// Umbenennen eines Ordners
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Aktueller Ordnername</param>
/// <param name="newFolder">Neuer Ordnername</param>
/// <param name="copy">Soll eine Kopie gemacht werden? Wenn nicht wird der QuellOrdner gelöscht</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
bool RenameFolder(string shareName, string folder, string newFolder, bool copy);
/// <summary>
/// Umbenennen eines Ordners - Ohne TransferManager
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Aktueller Ordnername</param>
/// <param name="newFolder">Neuer Ordnername</param>
/// <param name="copy">Soll eine Kopie gemacht werden? Wenn nicht wird der QuellOrdner gelöscht</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
bool RenameFolderEx(string shareName, string folder, string newFolder, bool copy);
/// <summary>
/// Löschen einer Datei
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
bool DeleteFile(string shareName, string fileName);
/// <summary>
/// Löschen eines Ordners
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
bool DeleteFolder(string shareName, string folder);
/// <summary>
/// Löschen eines Shares
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
bool DeleteShare(string shareName);
/// <summary>
/// Gibt die größe des Ordners (Dateien) als byte zurück
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Liste der Ordner im Share</returns>
long GetFolderSize(string shareName, string folder);
}
/// <summary>
/// Repräsentiert einen Ordner mit Infos
/// </summary>
[ExcludeFromCodeCoverage]
public class FolderResult
{
/// <summary>
/// Name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Datum letzte Bearbeitung
/// </summary>
public DateTimeOffset? LastModified { get; set; }
/// <summary>
/// Datum Erstellung
/// </summary>
public DateTimeOffset? Created { get; set; }
/// <summary>
/// Hat Unterordner?
/// </summary>
public bool HasSubfolders { get; set; }
}
/// <summary>
/// Repräsentiert eine Datei mit Info
/// </summary>
[ExcludeFromCodeCoverage]
public class FileResult
{
/// <summary>
/// Name der Datei
/// </summary>
public string Name { get; set; }
/// <summary>
/// Datum letzte Bearbeitung
/// </summary>
public DateTimeOffset? LastModified { get; set; }
/// <summary>
/// Datum Erstellung
/// </summary>
public DateTimeOffset? Created { get; set; }
/// <summary>
/// größe der Datei
/// </summary>
public long Size { get; set; }
}
}

View File

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der zu einer Adresse Längen- und Breitengrad ermittelt, oder zu Längen- und Breitengrad eine Adresse
/// </summary>
public interface IGeoLocationService
{
/// <summary>
/// Längen und Breitengrad zu einer Adresse suchen.
/// Zuerst wird versucht die Position mit Strasse, PLZ, Ort und Land zu finden.
/// Wenn das nicht klappt, dann mit PLZ, Ort, Land.
/// Wenn das nicht klappt, dann PLZ Land.
/// </summary>
/// <param name="address">Adresse</param>
/// <param name="language">Gewünschte Sprache ISO2</param>
/// <returns>Geo-Position</returns>
Task<GeoLocation> GetLocationAsync(Address address, string language);
/// <summary>
/// Längen und Breitengrad zu einer Adresse suchen.
/// </summary>
/// <param name="address">Adresse als Text-Zeile</param>
/// <param name="language">Gewünschte Sprache ISO2</param>
/// <returns>Geo-Position</returns>
Task<GeoLocation> GetLocationAsync(string address, string language);
/// <summary>
/// Längen und Breitengrad zu einer Adresse suchen. Adresse wird mit Details mitgeliefert.
/// Zuerst wird versucht die Position mit Strasse, PLZ, Ort und Land zu finden.
/// Wenn das nicht klappt, dann mit PLZ, Ort, Land.
/// Wenn das nicht klappt, dann PLZ Land.
/// </summary>
/// <param name="address">Adresse</param>
/// <param name="language">Gewünschte Sprache ISO2</param>
/// <returns>Geo-Position</returns>
Task<GeoLocationFill> GetLocationFillAsync(Address address, string language);
/// <summary>
/// Längen und Breitengrad zu einer Adresse suchen. Adresse wird mit Details mitgeliefert.
/// </summary>
/// <param name="address">Adresse als Text-Zeile</param>
/// <param name="language">Gewünschte Sprache ISO2</param>
/// <returns>Geo-Position</returns>
Task<GeoLocationFill> GetLocationFillAsync(string address, string language);
/// <summary>
/// Reverse-Geocoding
/// </summary>
/// <param name="lat">Breitengrad</param>
/// <param name="lng">Längengrad</param>
/// <param name="language">Gewünschte Sprache ISO2</param>
/// <returns>ReverseLocation</returns>
Task<ReverseLocation> GetReverseAsync(double lat, double lng, string language);
}
/// <summary>
/// Hilfsklasse - Ergebnis einer Geolokalisierung
/// </summary>
public class GeoLocation
{
/// <summary>
/// Breitengrad
/// </summary>
public double Latitude { get; set; }
/// <summary>
/// Längengrad
/// </summary>
public double Longitude { get; set; }
/// <summary>
/// Anfrage erfolgreich?
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Optionale Fehlermeldung wenn kein Erfolg
/// </summary>
public string ErrorMessage { get; set; }
}
/// <summary>
/// Hilfsklasse - Ergbenis einer Geolokalisierung um Adresse ergänzt
/// </summary>
public class GeoLocationFill : GeoLocation
{
/// <summary>
/// Adresse die gefunden wurde
/// </summary>
public Address Address { get; set; }
/// <summary>
/// Erstellt eine Instanz
/// </summary>
public GeoLocationFill()
{
Address = new Address();
}
}
/// <summary>
/// Hilfsklasse - Ergebnis einer Geolokalisierung nach Lat Long
/// </summary>
public class ReverseLocation
{
/// <summary>
/// Adresse die gefunden wurde
/// </summary>
public Address Address { get; set; }
/// <summary>
/// Anfrage erfolgreich?
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Optionale Fehlermeldung wenn kein Erfolg
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// Erstellt eine Instanz
/// </summary>
public ReverseLocation()
{
Address = new Address();
}
}
/// <summary>
/// Optionen für den Geolokalisierungs-Service
/// </summary>
public class GeoLocationOptions
{
/// <summary>
/// Basis-URI
/// </summary>
public string BaseUri { get; set; }
/// <summary>
/// Name des Useragents der im Header gesetzt werden soll
/// </summary>
public string UserAgentName { get; set; }
/// <summary>
/// Version des UserAgents der im Header gesetzt werden soll
/// </summary>
public string UserAgentVersion { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der eine Kombination aus private und public Key erstellen kann
/// </summary>
public interface IKeyGeneratorService
{
/// <summary>
/// Erstellt einen public und einen Private key mit Standard 1024 bit Verschlüsselung
/// </summary>
/// <returns>PrivateKey und Public Key</returns>
Tuple<string, string> GenerateKeys();
/// <summary>
/// Erstellt einen public und einen Private key
/// </summary>
/// <param name="keySize">Schlüssel Größe</param>
/// <returns>PrivateKey und Public Key</returns>
Tuple<string, string> GenerateKeys(int keySize);
/// <summary>
/// Erstellt einen public und einen Private key
/// </summary>
/// <param name="keySize">Schlüssel Größe</param>
/// <param name="seed">Seed</param>
/// <returns>PrivateKey und Public Key</returns>
Tuple<string, string> GenerateKeys(int keySize, byte[] seed);
}
}

View File

@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Klarna.Checkout;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung eines Service der alle benötigten Klarna-Funktionen zur Verfügung stellt
/// </summary>
public interface IKlarnaService
{
/// <summary>
/// Prüft ob die Klarna Credentials korrekt sind und eine Verbindung zur Api aufgenommen werden kann
/// </summary>
/// <param name="username">Klarna Benutzername</param>
/// <param name="password">Klarna Passwort</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> TestCredentialsAsync(string username, string password);
/// <summary>
/// Erstellen einer Klarna-Order
/// </summary>
/// <param name="orderId">Id der simple print Bestellung</param>
/// <param name="username">Klarna Benutzername</param>
/// <param name="password">Klarna Passwort</param>
/// <param name="termsUrl">Redirect-Url für AGB</param>
/// <param name="checkoutUrl">Url für Checkout</param>
/// <param name="confirmationUrl">Bestätigungs-URL</param>
/// <param name="pushUrl">URL für Push-Notifications</param>
/// <returns>Klarna Order oder null, wenn ein Fehler aufgetreten ist</returns>
Task<Order> CreateOrderAsync(long orderId, string username, string password, string termsUrl, string checkoutUrl, string confirmationUrl, string pushUrl);
/// <summary>
/// Abfragen einer Klarna-Order - Checkout API
/// </summary>
/// <param name="klarnaOrderId">Klarna Order ID</param>
/// <param name="username">Klarna Benutzername</param>
/// <param name="password">Klarna Passwort</param>
/// <returns>Klarna Order oder null, wenn nicht gefunden</returns>
Task<Order> GetOrderAsync(string klarnaOrderId, string username, string password);
/// <summary>
/// Abfragen einer Klarna-Order - OrderManagement API
/// </summary>
/// <param name="klarnaOrderId">Klarna Order ID</param>
/// <param name="username">Klarna Benutzername</param>
/// <param name="password">Klarna Passwort</param>
/// <returns>Klarna Order oder null, wenn nicht gefunden</returns>
Task<Klarna.OrderManagement.Order> GetManagementOrderAsync(string klarnaOrderId, string username, string password);
/// <summary>
/// Bestätigen einer Klarna-Order - OrderManagement API
/// </summary>
/// <param name="klarnaOrderId">Klarna Order ID</param>
/// <param name="username">Klarna Benutzername</param>
/// <param name="password">Klarna Passwort</param>
/// <returns>Klarna Order oder null, wenn nicht gefunden</returns>
Task<bool> AcknowledgeOrderAsync(string klarnaOrderId, string username, string password);
/// <summary>
/// Bestätigen des Versands einer Bestellung - OrderManagement API
/// </summary>
/// <param name="orderId">Order ID</param>
/// <param name="username">Klarna Benutzername</param>
/// <param name="password">Klarna Passwort</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<CaptureOrderResult> CaptureOrderAsync(long orderId, string username, string password);
}
/// <summary>
/// Rückgabe des Versuchs eine Order als Shipped einzutragen
/// </summary>
public enum CaptureOrderResult
{
/// <summary>
/// Erfolgreich
/// </summary>
Success = 1,
/// <summary>
/// Capture nicht erlaubt
/// </summary>
CaptureNotAllowed = 20,
/// <summary>
/// Der verfügbare Betrag ist 0
/// </summary>
RemainingAuthorizedAmount = 21,
/// <summary>
/// Bestellung nicht gefunden
/// </summary>
OrderNotFound = 30,
/// <summary>
/// Authorisierung gescheitert
/// </summary>
AuthorizationFailed = 40,
/// <summary>
/// Timeout bei der Kommunikation. Nochmal versuchen
/// </summary>
Timeout = 50
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der die Verwaltung von verfügbaren Sprachen realisiert
/// </summary>
public interface ILanguageService
{
/// <summary>
/// Gibt eine Sprache anhand des ISO2-Codes zurück
/// </summary>
/// <param name="iso2"></param>
/// <returns>Sprache oder null, wenn keine gefunden</returns>
Language Get(string iso2);
/// <summary>
/// Gibt die Standard-Sprache des Systems zurück
/// </summary>
/// <returns>Standard-Sprache</returns>
Language GetDefault();
/// <summary>
/// Gibt die Auflistung aller Sprachen des Systems zurück
/// </summary>
/// <returns>Liste von Sprachen</returns>
List<Language> GetAll();
/// <summary>
/// Gibt die Auflistung aller Sprachen des Systems zurück als ISO2 Liste
/// </summary>
/// <returns>Liste von Sprachen</returns>
List<string> GetAllIso2();
/// <summary>
/// Gibt eine gefilterte Liste von Sprachen zurück. Sucht nur im Namen
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kunden</returns>
List<Language> Search(string filter);
}
/// <summary>
/// Repräsentiert eine Sprache
/// </summary>
public class Language
{
/// <summary>
/// 2-Letter ISO Code der Sprache
/// </summary>
public string Iso2 { get; set; }
/// <summary>
/// Name der Sprache (Deutsch, Englisch usw..)
/// </summary>
public string Name { get; set; }
/// <summary>
/// Name der Sprache in englisch
/// </summary>
public string NameEnglish { get; set; }
/// <summary>
/// Gibt an ob es sich um die Standardsprache handelt
/// </summary>
public bool IsDefault { get; set; }
}
}

View File

@ -0,0 +1,554 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.SymbolStore;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
using gehGassi.Domain.Payment;
using Microsoft.VisualBasic.CompilerServices;
using Newtonsoft.Json.Converters;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der die Kommunikation mit MangoPay ermöglicht
/// </summary>
public interface IMangoPayService
{
/// <summary>
/// ruft eine Test-Funktion auf
/// </summary>
/// <returns>Task</returns>
Task TestAsync();
/// <summary>
/// Erstellen eines Mangopay-Benutzers für einen App-User.
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="suppressCommit">Soll das Commit unterdrückt werden?</param>
/// <returns>MangoPayResult mit der ID des Mangopay-Users</returns>
Task<MangoPayResult<string>> CreateOwnerAsync(string appUserId, bool suppressCommit);
/// <summary>
/// Aktualisieren eines Mangopay-Benutzers für einen App-User
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<MangoPayResult<bool>> UpdateOwnerAsync(string appUserId);
/// <summary>
/// Prüfen ob ein MangoPay-Benutzer existiert
/// </summary>
/// <param name="mangoPayUserId">Id des Mangopay-Benutzers</param>
/// <returns>true wenn existent, false sonst</returns>
Task<MangoPayResult<bool>> OwnerExistsAsync(string mangoPayUserId);
/// <summary>
/// Erstellt ein Wallet für einen AppUser mit einem bestimmten Typ.
/// Wenn das Wallet schon existiert, wird es zurückgegeben.
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="walletType">Typ des Wallets</param>
/// <returns>Wallet</returns>
Task<MangoPayResult<Wallet>> CreateWalletAsync(string appUserId, WalletType walletType);
/// <summary>
/// Aktualisiert den Kontostand eines Wallets und gibt diesen zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="walletType">Typ des Wallets</param>
/// <returns>Wallet mit neuem Kontostand</returns>
Task<MangoPayResult<Wallet>> GetWalletBalanceAsync(string appUserId, WalletType walletType);
/// <summary>
/// Aktualisiert den Kontostand eines Wallets und gibt diesen zurück
/// </summary>
/// <param name="walletId">Id des Wallets bei MangoPay</param>
/// <returns>Kontostand</returns>
Task<MangoPayResult<long>> GetWalletBalanceAsync(string walletId);
/// <summary>
/// Prüfen ob ein MangoPay-Wallet existiert
/// </summary>
/// <param name="walletId">Id des Mangopay-Wallets</param>
/// <returns>true wenn existent, false sonst</returns>
Task<MangoPayResult<bool>> WalletExistsAsync(string walletId);
/// <summary>
/// Erstellt ein Bankkonto für einen AppUser
/// </summary>
/// <param name="mangoPayUserId">Id des Benutzers bei MangoPay</param>
/// <param name="ownerName">Name des Inhabers des Bankkontos</param>
/// <param name="address">Adresse des Inhabers des Bankkontos</param>
/// <param name="iban">Iban des Bankkontos</param>
/// <param name="bic">BIC des Bankkontos</param>
/// <returns>MangoPayResult mit der ID des Bankkontos</returns>
Task<MangoPayResult<string>> CreateBankAccountAsync(string mangoPayUserId, string ownerName, Address address, string iban, string bic);
/// <summary>
/// Gibt ein Bankkonto für einen App-User zurück
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="mangoPayUserId">Id des Benutzers bei MangoPay</param>
/// <param name="bankAccountId">Id des Bankkontos bei MangoPay</param>
/// <returns>MangoPayResult mit Bankkonto oder null, wenn keines gefunden</returns>
Task<MangoPayResult<BankAccount>> GetBankAccountAsync(string appUserId, string mangoPayUserId, string bankAccountId);
/// <summary>
/// Deaktiveren eines Bankkontos für einen App-User
/// </summary>
/// <param name="mangoPayUserId">Id des Benutzers bei MangoPay</param>
/// <param name="bankAccountId">Id des Bankkontos bei MangoPay</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<MangoPayResult<bool>> DeactivateBankAccountAsync(string mangoPayUserId, string bankAccountId);
/// <summary>
/// Transferieren von Geld vom Guthaben-Konto zum Transaktions-Konto
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="ammount">Betrag der transferiert werden soll</param>
/// <param name="tag">Tag der zum Transfer abgelegt werden soll</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<MangoPayResult<bool>> TransferMoneyFromCreditToFeeAccountAsync(string appUserId, long ammount, string tag);
/// <summary>
/// Transferieren von Geld vom Transaktions-Konto zum Guthaben-Konto
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="ammount">Betrag der transferiert werden soll</param>
/// <param name="tag">Tag der zum Transfer abgelegt werden soll</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<MangoPayResult<bool>> TransferMoneyFromFeeToCreditAccountAsync(string appUserId, long ammount, string tag);
/// <summary>
/// Transferieren von Geld von einem Wallet zu einem anderen
/// </summary>
/// <param name="sourceAppUserId">Quell-App-UserId</param>
/// <param name="targetAppUserId">Ziel-App-UserId</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="ammount">Betrag der transferiert werden soll</param>
/// <param name="fees">Gebühren an gehGassi</param>
/// <param name="tag">Tag der zum Transfer abgelegt werden soll</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<MangoPayResult<bool>> TransferMoneyAsync(string sourceAppUserId, string targetAppUserId, string walkId, long ammount, long fees, string tag);
/// <summary>
/// Transferiert Geld von einem Wallet zu einem anderen
/// </summary>
/// <param name="sourceMangopayUserId">Id des Mangopayusers der zahlt</param>
/// <param name="sourceWalletId">Quellwallet Id</param>
/// <param name="targetMangopayUserId">Id des Mangopayusers der die Zahlung erhält</param>
/// <param name="targetWalletId">Zielwallet Id</param>
/// <param name="walkId">Id des Walks - oder leer</param>
/// <param name="ammount">Betrag</param>
/// <param name="fees">Optinal: Gebühren an gehGassi</param>
/// <param name="tag">Tag der zum Transfer abgelegt werden soll</param>
/// <param name="sourceAppUserId">Optional: Quell-App-UserId</param>
/// <param name="targetAppUserId">Optional: Ziel-App-UserId</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<MangoPayResult<bool>> TransferMoneyAsync(string sourceMangopayUserId, string sourceWalletId, string targetMangopayUserId, string targetWalletId, string walkId, long ammount, long fees, string tag, string sourceAppUserId = "", string targetAppUserId = "");
/// <summary>
/// Erstellen einer Kreditkarten-Einzahlung
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="ammount">Betrag der eingezahlt werden soll</param>
/// <param name="fees">Gebühren die an gehgassi abgeführt werden</param>
/// <param name="fromCredit">Betrag der zusätzlich vom Guthabenkonto genommen werden muss</param>
/// <param name="tag">Tag der zur Einzahlung abgelegt werden soll</param>
/// <param name="returnUrl">Return-Url für MangoPay</param>
/// <returns>MangopayResult mit einem string der eine ReturnUrl beinhaltet die die App öffnen muss</returns>
Task<MangoPayResult<string>> CreatePayInCardAsync(string appUserId, string walkId, long ammount, long fees, long fromCredit, string tag, string returnUrl);
/// <summary>
/// Erstellen einer Maestro-Einzahlung
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="ammount">Betrag der eingezahlt werden soll</param>
/// <param name="fees">Gebühren die an gehgassi abgeführt werden</param>
/// <param name="fromCredit">Betrag der zusätzlich vom Guthabenkonto genommen werden muss</param>
/// <param name="tag">Tag der zur Einzahlung abgelegt werden soll</param>
/// <param name="returnUrl">Return-Url für MangoPay</param>
/// <returns>MangopayResult mit einem string der eine ReturnUrl beinhaltet die die App öffnen muss</returns>
Task<MangoPayResult<string>> CreatePayInMaestroAsync(string appUserId, string walkId, long ammount, long fees, long fromCredit, string tag, string returnUrl);
/// <summary>
/// Erstellen einer Klarna-Einzahlung
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="ammount">Betrag der eingezahlt werden soll</param>
/// <param name="fees">Gebühren die an gehgassi abgeführt werden</param>
/// <param name="fromCredit">Betrag der zusätzlich vom Guthabenkonto genommen werden muss</param>
/// <param name="tag">Tag der zur Einzahlung abgelegt werden soll</param>
/// <param name="returnUrl">Return-Url für MangoPay</param>
/// <returns>MangopayResult mit einem string der eine ReturnUrl beinhaltet die die App öffnen muss</returns>
Task<MangoPayResult<string>> CreatePayInKlarnaAsync(string appUserId, string walkId, long ammount, long fees, long fromCredit, string tag, string returnUrl);
/// <summary>
/// Erstellen einer PayPal-Einzahlung
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="ammount">Betrag der eingezahlt werden soll</param>
/// <param name="fees">Gebühren die an gehgassi abgeführt werden</param>
/// <param name="fromCredit">Betrag der zusätzlich vom Guthabenkonto genommen werden muss</param>
/// <param name="tag">Tag der zur Einzahlung abgelegt werden soll</param>
/// <param name="returnUrl">Return-Url für MangoPay</param>
/// <returns>MangopayResult mit einem string der eine ReturnUrl beinhaltet die die App öffnen muss</returns>
Task<MangoPayResult<string>> CreatePayInPayPalAsync(string appUserId, string walkId, long ammount, long fees, long fromCredit, string tag, string returnUrl);
/// <summary>
/// Gibt eine Transaktion bei Mangopay zurück.
/// Kann zum Prüfen des Status einer Transaktion verwendet werden.
/// </summary>
/// <param name="mangoPayTransactionId">Id der Transaktion bei MangoPay</param>
/// <returns>MangoPayResult mit Transaktion</returns>
Task<MangoPayResult<MangoPayTransaction>> GetTransactionAsync(string mangoPayTransactionId);
/// <summary>
/// Erstellen eines Kyc-Dokuments
/// </summary>
/// <param name="mangoPayUserId">Id des Users bei MangoPay</param>
/// <param name="type">Typ des Dokumentes</param>
/// <param name="files">Liste von Dateien als byte-Array</param>
/// <returns>MangoPayResult mit der ID des Dokumentes</returns>
Task<MangoPayResult<string>> CreateKycDocumentAsync(string mangoPayUserId, IdentityDocumentType type, List<byte[]> files);
/// <summary>
/// Gibt ein Kyc-Dokument zurück
/// </summary>
/// <param name="documentId">Id des Dokumentes bei MangoPay</param>
/// <returns>Kyc-Dokument</returns>
Task<MangoPayResult<MangoPayKycDocument>> GetKycDocumentAsync(string documentId);
/// <summary>
/// Gibt eine Auszahlung zurück
/// </summary>
/// <param name="payoutId">Id der Auszalhung</param>
/// <returns>MangoPayResult mit Auszahlung oder null, wenn nicht vorhanden</returns>
Task<MangoPayResult<MangoPayPayout>> GetPayoutAsync(string payoutId);
/// <summary>
/// Erstellen einer Auszahlung
/// </summary>
/// <param name="mangoPayUserId">Id des Users bei Mangopay</param>
/// <param name="mangoPayWalletId">Id des Wallets bei MangoPay</param>
/// <param name="mangoPayBankId">Id der Bankverbindung bei MangoPay</param>
/// <param name="ammount">Summe die Ausgezahlt werden soll in der kleinsten Einheit</param>
/// <param name="tag">TAG mit Referenz auf gehgassi</param>
/// <returns>MangoPayResult mit erstellter Auszahlung</returns>
Task<MangoPayResult<MangoPayPayout>> CreatePayoutAsync(string mangoPayUserId, string mangoPayWalletId, string mangoPayBankId, long ammount, string tag);
/// <summary>
/// Gibt Infos zu einer Rückzahlung zurück
/// </summary>
/// <param name="refundId">Id der Rückzahlung</param>
/// <returns>MangoPayResult</returns>
Task<MangoPayResult<MangoPayRefund>> GetRefundAsync(string refundId);
/// <summary>
/// Erstellen eines Legal-Mangopay-Benutzers für einen Kunden
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="suppressCommit">Soll das Commit unterdrückt werden?</param>
/// <returns>MangoPayResult mit der ID des Mangopay-Users</returns>
Task<MangoPayResult<string>> CreateLegalOwnerAsync(long customerId, bool suppressCommit);
/// <summary>
/// Aktualisieren eines Mangopay-Benutzers für einen Kunden
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<MangoPayResult<bool>> UpdateLegalOwnerAsync(long customerId);
/// <summary>
/// Prüfen ob ein MangoPay-Benutzer für einen Kunden existiert
/// </summary>
/// <param name="mangoPayUserId">Id des Mangopay-Benutzers des Kunden</param>
/// <returns>true wenn existent, false sonst</returns>
Task<MangoPayResult<bool>> LegalOwnerExistsAsync(string mangoPayUserId);
/// <summary>
/// Erstellt ein Wallet für einen Kunden mit einem bestimmten Typ.
/// </summary>
/// <param name="customerUniqueId">Unique Id des Kunden</param>
/// <param name="walletType">Typ des Wallets</param>
/// <param name="description">Beschreibung</param>
/// <returns>Wallet</returns>
Task<MangoPayResult<Wallet>> CreateWalletForCustomerAsync(string customerUniqueId, WalletType walletType, string description);
}
/// <summary>
/// Ergebnis einer MangoPay-Operation
/// </summary>
public class MangoPayResult<T>
{
/// <summary>
/// Anfrage erfolgreich
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Wenn nicht erfolgreich, Fehlermeldung
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// Wenn nicht erfolgreich, Fehlertyp
/// </summary>
public string ErrorType { get; set; }
/// <summary>
/// Wenn nicht erfolgreich, Fehlermeldungen
/// </summary>
public Dictionary<string,string> ErrorMessages { get; set; }
/// <summary>
/// Rückgabe-Wert
/// </summary>
public T Value { get; set; }
/// <summary>
/// Erstellt eine Instanz
/// </summary>
public MangoPayResult()
{
Success = false;
ErrorMessage = string.Empty;
ErrorType = MangoPayErrorCodes.Err_None;
ErrorMessages = new Dictionary<string, string>();
Value = default(T);
}
}
/// <summary>
/// Fehlercodes bei MangoPay Operationen
/// </summary>
public static class MangoPayErrorCodes
{
public const string Err_None = "Err_None";
public const string Err_Unknown = "Err_Unknown";
public const string Err_AppUser_NotFound = "Err_AppUser_NotFound";
public const string Err_User_NotFound = "Err_User_NotFound";
public const string Err_User_Create_Exists = "Err_User_Create_Exists";
public const string Err_User_BirthDate = "Err_User_BirthDate";
public const string Err_User_Nationality = "Err_User_Nationality";
public const string Err_User_MainResidence = "Err_User_MainResidence";
public const string Err_User_Update_NotExists = "Err_User_Update_NotExists";
public const string Err_Wallet_NotExists = "Err_Wallet_NotExists";
public const string Err_Transfer_NotEnoughMoney = "Err_Transfer_NotEnoughMoney";
public const string Err_PayIn_Failed = "Err_PayIn_Failed";
public const string Err_Kyc_PageFailed = "Err_Kyc_PageFailed";
public const string Err_Kyc_DocumentFailed = "Err_Kyc_DocumentFailed";
public const string Err_PayOut_Failed = "Err_PayOut_Failed";
public const string Err_Refund_Failed = "Err_Refund_Failed";
public const string Err_Customer_DataMissing = "Err_Customer_DataMissing";
public const string Err_Customer_NotFound = "Err_Customer_NotFound";
}
/// <summary>
/// Repräsentiert eine Transaktion bei Mangopay
/// </summary>
public class MangoPayTransaction
{
/// <summary>
/// Transaktions-ID
/// </summary>
public string TransactionId { get; set; }
/// <summary>
/// Id des Benutzers bei MangoPay der die Transaktion veranlasst hat
/// </summary>
public string AuthorId { get; set; }
/// <summary>
/// Id des Benutzers bei MangoPay der die Transaktion erhalten hat
/// </summary>
public string CreditedUserId { get; set; }
/// <summary>
/// Betrag der dem Benutzer abgezogen wurde, der die Transaktion veranlasst hat
/// </summary>
public long AmmountDebited { get; set; }
/// <summary>
/// Betrag der dem Benutzer gutgeschrieben wurde, der die Transaktion erhalten hat
/// </summary>
public long AmmountCredited { get; set; }
/// <summary>
/// Betrag der als Gebühr abgezogen wurde
/// </summary>
public long Fees { get; set; }
/// <summary>
/// Status der Transaktion
/// </summary>
public PaymentTransactionStatus Status { get; set; }
/// <summary>
/// Ergebniscode der Transaktion
/// </summary>
public string ResultCode { get; set; }
/// <summary>
/// Ergebnismeldung der Transaktion
/// </summary>
public string ResultMessage { get; set; }
/// <summary>
/// Datum der Ausführung
/// </summary>
public DateTimeOffset? ExecutionDate { get; set; }
/// <summary>
/// Transaktions-Typ
/// </summary>
public MangoPayTransactionType TransactionType { get; set; }
/// <summary>
/// Id des Wallets bei Mangopay das die Gutschrift erhalten hat
/// </summary>
public string CreditedWalletId { get; set; }
/// <summary>
/// Id des Wallets bei Mangopay das die Belastung erhalten hat
/// </summary>
public string DebitedWalletId { get; set; }
/// <summary>
/// Typ der Einzahlung
/// </summary>
public PayInType PayInType { get; set; }
}
/// <summary>
/// Repräsentiert ein KYC-Dokument bei MangoPay
/// </summary>
public class MangoPayKycDocument
{
/// <summary>
/// Id des Dokumentes
/// </summary>
public string DocumentId { get; set; }
/// <summary>
/// ID des MangoPay-Users
/// </summary>
public string UserId { get; set; }
/// <summary>
/// ID des Kyc-Dokuments
/// </summary>
public IdentityDocumentType Type { get; set; }
/// <summary>
/// Status des Kyc-Dokuments
/// </summary>
public IdentityDocumentStatus Status { get; set; }
/// <summary>
/// Typ der Ablehnung
/// </summary>
public string RefusedReasonType { get; set; }
/// <summary>
/// Meldung der Ablehnung
/// </summary>
public string RefusedReasonMessage { get; set; }
/// <summary>
/// Liste von Detail-Codes wenn ein Dokument abgelehnt wurde
/// </summary>
public List<string> Flags { get; set; }
/// <summary>
/// Datum der Bearbeitung
/// </summary>
public DateTimeOffset? ProcessedDate { get; set; }
}
/// <summary>
/// Repräsentiert eine Auszahlung bei Mangopay
/// </summary>
public class MangoPayPayout
{
/// <summary>
/// Id der Auszahlung
/// </summary>
public string PayoutId { get; set; }
/// <summary>
/// ID des MangoPay-Users
/// </summary>
public string UserId { get; set; }
/// <summary>
/// Datum der Auführung
/// </summary>
public DateTimeOffset? ExecutionDate { get; set; }
/// <summary>
/// Result code.
/// </summary>
public string ResultCode { get; set; }
/// <summary>
/// The pre-authorization result message explaining the result code.
/// </summary>
public string ResultMessage { get; set; }
/// <summary>
/// Status der Transaktion
/// </summary>
public PaymentTransactionStatus Status { get; set; }
}
/// <summary>
/// Repräsentiert eine Rückzahlung bei Mangopay
/// </summary>
public class MangoPayRefund
{
/// <summary>
/// ID der Rücküberweisung
/// </summary>
public string RefundId { get; set; }
/// <summary>
/// ID der originalen Transaktion (Id der Auszahlungs-Transaktion)
/// </summary>
public string OriginalTransactionId { get; set; }
/// <summary>
/// Typ Grund für Rückzahlung
/// </summary>
[MaxLength(255)]
public string RefundReasonType { get; set; }
/// <summary>
/// Beschreibung Grund Rückzahlung
/// </summary>
[MaxLength(255)]
public string RefundReasonMessage { get; set; }
/// <summary>
/// Status der Transaktion
/// </summary>
public PaymentTransactionStatus Status { get; set; }
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der alle PayPal-Funktionen zur Verfügung stellt
/// </summary>
public interface IPayPalService
{
/// <summary>
/// Prüft ob die PayPal Credentials korrekt sind und eine Verbindung zur Api aufgenommen werden kann
/// </summary>
/// <param name="clientId">PayPal ClientId</param>
/// <param name="secret">PayPal Secret</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> TestCredentialsAsync(string clientId, string secret);
/// <summary>
/// Erstellen einer PayPal-Order
/// </summary>
/// <param name="orderId">Id der simple print Bestellung</param>
/// <param name="clientId">PayPal ClientId</param>
/// <param name="secret">PayPal Secret</param>
/// <param name="successUrl">Redirect-Url für ERFOLG</param>
/// <param name="cancelUrl">Redirect-URL für Fehler oder Abbrechen</param>
/// <returns>REdirect-Url zu PayPal oder leer wenn Fehler</returns>
Task<string> CreateOrderAsync(long orderId, string clientId, string secret, string successUrl, string cancelUrl);
/// <summary>
/// Authorisieren der PayPal Order - Bezahlung durchführen
/// </summary>
/// <param name="payPalOrder">PayPal-Order-Id</param>
/// <param name="clientId">PayPal ClientId</param>
/// <param name="secret">PayPal Secret</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> AuthorizeOrderAsync(string payPalOrder, string clientId, string secret);
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Threading.Tasks;
using gehGassi.Domain.Users;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der die Verwaltung von gespeicherten Authentifizierungs-Ticktes ermöglicht
/// </summary>
public interface IPersistedTicketService : IService<PersistedTicket>
{
/// <summary>
/// Speichern eines Tickets.
/// Wenn es noch nicht existiert, wird es angelegt.
/// Wenn es existiert, wird es aktualisiert
/// </summary>
/// <param name="key">Key des Tickes</param>
/// <param name="userId">Benutzername</param>
/// <param name="ticket">Ticket als Byte-Array</param>
/// <param name="expires">Ablaufdatum des Tickets</param>
/// <returns>Task</returns>
Task SaveAsync(string key, string userId, byte[] ticket, DateTimeOffset? expires);
/// <summary>
/// Löscht alle abgelaufenen Tickets
/// </summary>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> RemoveExpiredAsync();
/// <summary>
/// Löschen eines Tickets aus dem Store
/// </summary>
/// <param name="key">Key des Tickets</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> RemoveAsync(string key);
}
}

View File

@ -0,0 +1,38 @@
using System.Collections.Generic;
using gehGassi.Domain.Placeholders;
using gehGassi.Domain.Users;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der Funktionen rund um die Variablen/Platzhalter bietet
/// </summary>
public interface IPlaceHolderService
{
/// <summary>
/// Gibt die Liste der verfügbaren Platzhalter-Kategorien zurück
/// </summary>
/// <param name="lang">Sprache ISO2</param>
/// <returns>Liste Kategorien</returns>
List<PlaceHolderCategory> GetCategories(string lang);
/// <summary>
/// Gibt die Liste der Platzhalter-Variablen für eine Kategorie zurück
/// </summary>
/// <param name="categoryId">Id der Kategorie</param>
/// <param name="lang">Sprache ISO2</param>
/// <returns>Liste Platzhalter</returns>
List<PlaceHolder> GetPlaceholders(int categoryId, string lang);
/// <summary>
/// Ersetzt in einem Text alle Platzhalter mit den entsprechenden Daten, sofern diese zur Verfügung gestellt wurden.
/// </summary>
/// <param name="content">Text in welchem die Platzhalter ausgefüllt werden sollen</param>
/// <param name="lang">Sprache ISO2</param>
/// <param name="timezoneId">Id der Timezone für Datums-Operationen</param>
/// <param name="useHtml">Soll bei manchen Ersatzoperationen HTML verwendet werden? Z.B. Adress-Block</param>
/// <param name="user">Optional: Benutzer</param>
/// <returns>Anzahl ersetzer Platzhalter. Der Text ist als ref Parameter bearbeitet</returns>
int FillIn(ref string content, string lang, string timezoneId, bool useHtml = false, ApplicationUser user = null);
}
}

View File

@ -0,0 +1,395 @@
using gehGassi.Domain.Pushnotifications;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
using System.Text.Json;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der Push-Notifications senden kann
/// </summary>
public interface IPushNotificationService
{
/// <summary>
/// Registrieren eines Gerätes für Pushnotifications
/// </summary>
/// <param name="deviceInstallation">Informationen für die Installation / Registrierung</param>
/// <returns>True wenn erfolgreich, false sonst</returns>
Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation);
/// <summary>
/// Löschen der Registrierung eines Gerätes für Pushnotifications
/// </summary>
/// <param name="installationId">Id der Installation</param>
/// <returns></returns>
Task<bool> DeleteInstallationByIdAsync(string installationId);
/// <summary>
/// Senden einer Pushnotification Nachricht
/// </summary>
/// <param name="notificationMessage">Nachricht die gesendet werden soll</param>
/// <returns>true wenn gesendet, false sonst</returns>
Task<bool> SendNotificationAsync(NotificationMessage notificationMessage);
/// <summary>
/// Senden einer Pushnotification Nachricht
/// </summary>
/// <param name="notificationMessage">Nachricht die gesendet werden soll</param>
/// <param name="tags">Liste zusätzlicher Tags für die Adressierung der Ziele</param>
/// <returns>true wenn gesendet, false sonst</returns>
Task<bool> SendNotificationAsync(NotificationMessage notificationMessage, List<string> tags);
/// <summary>
/// Auflisten der registrierten Geräte
/// </summary>
/// <returns>Task</returns>
Task ListRegistrationsAsync();
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn eine neue Nachricht für ihn eingetroffen ist
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="conversationId">Id des Konversation</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendNewMessageAsync(string receiverId, string conversationId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn eine neue Text-Nachricht von gehgassi für ihn eingetroffen ist
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="messageId">Id der Nachricht</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendNewTextMessageAsync(string receiverId, string messageId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein neuer Walk für ihn eingetroffen ist
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendNewWalkAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn sich eine öffentliche Anfrage geändert hat
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="publicWalkRequestId">Id des öffentlichen Anfrage</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendPublicWalkRequestChangedAsync(string receiverId, string publicWalkRequestId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn sich eine öffentliche Anfrage in der Nähe verfügbar ist
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="publicWalkRequestId">Id des öffentlichen Anfrage</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendPublicWalkRequestAvailableAsync(string receiverId, string publicWalkRequestId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn eine Antwort auf eine öffentliche Anfrage erstellt wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="publicWalkRequestId">Id des öffentlichen Anfrage</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendPublicWalkResponseAddedAsync(string receiverId, string publicWalkRequestId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn eine Antwort auf eine öffentliche Anfrage sich geändert hat
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="publicWalkRequestId">Id des öffentlichen Anfrage</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendPublicWalkResponseChangedAsync(string receiverId, string publicWalkRequestId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn eine Antwort auf eine öffentliche Anfrage stonriert wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="publicWalkRequestId">Id des öffentlichen Anfrage</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendPublicWalkResponseCancelledAsync(string receiverId, string publicWalkRequestId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn eine Antwort auf eine öffentliche Anfrage abgelehnt wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="publicWalkRequestId">Id des öffentlichen Anfrage</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendPublicWalkResponseDeclinedAsync(string receiverId, string publicWalkRequestId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn eine öffentliche anfrage storniert wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="publicWalkRequestId">Id des öffentlichen Anfrage</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendPublicWalkRequestCancelledAsync(string receiverId, string publicWalkRequestId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk storniert wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="appMode">App-Mode</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkCancelledAsync(string receiverId, string walkId, AppMode appMode);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk gestartet wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkStartedAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk abgeschlossen wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkCompletedAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk abgeschlossen wurde als Erinnerung
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkCompletedReminderAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk angenommen wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkAcceptedAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk abgelehnt wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkDeclinedAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk angefragt wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkRequestedAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk bestätigt wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkConfirmedAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk reklamiert wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkComplainedAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Walk reklamiert wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="appMode">App-Mode</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkComplainedUpdateAsync(string receiverId, string walkId, AppMode appMode);
/// <summary>
/// Sendet eine Pushnotification an einen Hundebesitzer wenn die Bezahlung für einen ein Walk gescheitert ist.
/// Wird verwendet wenn das Payment nicht funktioniert hat und der Hundebesitzer den Walk neu buchen muss.
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendWalkPaymentFailedAsync(string receiverId, string walkId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein KYC-Status (Dokument) geändert wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="documentId">Id des KYC-Dokuments</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendIdentityDocumentUpdateAsync(string receiverId, string documentId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User wenn ein Auszahlungsstatus geändert wurde
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <param name="payoutId">Id der Auszahlung</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendPayoutUpdateAsync(string receiverId, string payoutId);
/// <summary>
/// Sendet eine Pushnotification an einen App-User dass ein Logout durchgeführt werden soll
/// </summary>
/// <param name="receiverId">ID des App-Users</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> SendLogoutAsync(string receiverId);
}
/// <summary>
/// Hilfsklasse zum abspeichern der referenz-Info für Server PushNotifications
/// </summary>
public class ServerPushNotificationInfo
{
/// <summary>
/// Title der Nachricht
/// </summary>
[JsonPropertyName("title")]
public string Title { get; set; }
/// <summary>
/// Inhalt der Nachricht
/// </summary>
[JsonPropertyName("body")]
public string Body { get; set; }
/// <summary>
/// Daten der Nachricht
/// </summary>
[JsonPropertyName("data")]
public string Data { get; set; }
/// <summary>
/// Typ des Appusers damit man bei jenen die beides sind auch unterscheiden kann
/// </summary>
[JsonPropertyName("appMode")]
[JsonConverter(typeof(AppModeConverter))]
public AppMode? AppMode { get; set; }
/// <summary>
/// Id des verknüpften Objektes
/// </summary>
[JsonPropertyName("key")]
public string Key { get; set; }
/// <summary>
/// Typ des Objektes das verbunden wurde
/// </summary>
[JsonPropertyName("table")]
public string Table { get; set; }
/// <summary>
/// Typ der Systemnachricht
/// </summary>
[JsonPropertyName("type")]
[JsonConverter(typeof(SystemMessageTypeConverter))]
public SystemMessageType? Type { get; set; }
/// <summary>
/// Id der Nachricht für Cancel
/// </summary>
[JsonPropertyName("notificationId")]
public int NotificationId { get; set; }
}
public class SystemMessageTypeConverter : JsonConverter<SystemMessageType?>
{
public override SystemMessageType? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
try
{
var value = reader.GetInt32();
return (SystemMessageType)value;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
try
{
var stringVal = reader.GetString();
if (string.IsNullOrEmpty(stringVal))
{
return null;
}
var intVal = int.Parse(stringVal);
return (SystemMessageType)intVal;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return null;
}
public override void Write(Utf8JsonWriter writer, SystemMessageType? value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
return;
}
var intVal = (int)value;
writer.WriteNumberValue(intVal);
}
}
public class AppModeConverter : JsonConverter<AppMode?>
{
public override AppMode? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
try
{
var value = reader.GetInt32();
return (AppMode)value;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
try
{
var stringVal = reader.GetString();
if (string.IsNullOrEmpty(stringVal))
{
return null;
}
var intVal = int.Parse(stringVal);
return (AppMode)intVal;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return null;
}
public override void Write(Utf8JsonWriter writer, AppMode? value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
return;
}
var intVal = (int)value;
writer.WriteNumberValue(intVal);
}
}
}

View File

@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für ein generisches Repository.
/// Das Repository verfügt bewusst über keine Update / Save Funktionen.
/// Das Prinzip legt nahe, dass ein Repository wie eine Collection zu sehen ist.
/// Speichern von Änderungen wird über das UnitOfWork-Pattern durchgeführt.
/// </summary>
/// <typeparam name="T">Typ des Repository</typeparam>
public interface IRepository<T> where T : class
{
#region Get / Abfragen
/// <summary>
/// Gibt eine Entität anhand der eindeutigen Id zurück
/// </summary>
/// <param name="id">Id der Entität</param>
/// <returns>Entität oder null, wenn nicht gefunden</returns>
T Get(object id);
/// <summary>
/// Gibt eine Entität anhand der eindeutigen Id zurück
/// </summary>
/// <param name="id">Id der Entität</param>
/// <returns>Entität oder null, wenn nicht gefunden</returns>
Task<T> GetAsync(object id);
/// <summary>
/// Gibt alle Entitäten zurück
/// </summary>
/// <returns>Liste aller Entitäten</returns>
IEnumerable<T> GetAll();
/// <summary>
/// Gibt alle Entitäten zurück
/// </summary>
/// <returns>Liste aller Entitäten</returns>
Task<IEnumerable<T>> GetAllAsync();
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
Task<IEnumerable<T>> FindNoTrackingAsync(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen.
/// Hier wird zuerst die SQL-Abfrage ausgeführt!
/// </summary>
/// <param name="rawSql">SQL-String</param>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
IEnumerable<T> Find(string rawSql, Expression<Func<T, bool>> predicate = null);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// Hier wird zuerst die SQL-Abfrage ausgeführt!
/// </summary>
/// <param name="rawSql">SQL-String</param>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
Task<IEnumerable<T>> FindAsync(string rawSql, Expression<Func<T, bool>> predicate = null);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// Hier wird zuerst die SQL-Abfrage ausgeführt!
/// </summary>
/// <param name="rawSql">SQL-String</param>
/// <param name="parameters">SQL-Parameter</param>
/// <returns>Liste gefundener Entitäten</returns>
Task<IEnumerable<T>> FindSqlAsync(string rawSql, params object[] parameters);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
IQueryable<T> Query(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen von einem View
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
IQueryable<T> QueryView(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen von einem View
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
Task<IEnumerable<T>> QueryViewListAsync(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen.
/// Hier wird zuerst die SQL-Abfrage ausgeführt!
/// </summary>
/// <param name="rawSql">SQL-String</param>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
IQueryable<T> Query(string rawSql, Expression<Func<T, bool>> predicate = null);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen.
/// Hier wird zuerst die SQL-Abfrage ausgeführt!
/// </summary>
/// <param name="rawSql">SQL-String</param>
/// <param name="predicate">Kriterien</param>
/// <param name="parameters">Parameter für die Stored Procedure</param>
/// <returns>Liste gefundener Entitäten</returns>
IQueryable<T> QueryStoredProcedure(string rawSql, Expression<Func<T, bool>> predicate = null, params object[] parameters);
/// <summary>
/// Gibt eine Entität oder den Default wert zurück.
/// Wenn mehr als 1 gefunden, dann Exception
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
T SingleOrDefault(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Entität oder den Default wert zurück.
/// Wenn mehr als 1 gefunden, dann Exception
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
Task<T> SingleOrDefaultAsync(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Entität oder den Default wert zurück
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
T FirstOrDefault(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Entität oder den Default wert zurück
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate);
#endregion
#region Add / Hinzufügen
/// <summary>
/// Fügt eine Entität zum Repository hinzu
/// </summary>
/// <param name="entity">Entität</param>
void Add(T entity);
/// <summary>
/// Fügt eine Liste von Entitäten zum Repository hinzu
/// </summary>
/// <param name="entities">List von Entitäten</param>
void AddRange(IEnumerable<T> entities);
#endregion
#region remove / Entfernen
/// <summary>
/// Entfernt eine Entität aus dem Repository
/// </summary>
/// <param name="entity">Zu entfernende Entität</param>
void Remove(T entity);
/// <summary>
/// Entfernt eine Liste von Entitäten aus dem Repository
/// </summary>
/// <param name="entities">Liste von Entitäten</param>
void RemoveRange(IEnumerable<T> entities);
#endregion
#region Count
/// <summary>
/// Gibt die Anzahl aller Entitäten zurück
/// </summary>
/// <returns>Anzahl Entitäten</returns>
int Count();
/// <summary>
/// Gibt die Anzahl aller Entitäten zurück
/// </summary>
/// <returns>Anzahl Entitäten</returns>
Task<int> CountAsync();
/// <summary>
/// Gibt die Anzahl aller Entitäten zurück die definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Anzahl Entitäten</returns>
int Count(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt die Anzahl aller Entitäten zurück die definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Anzahl Entitäten</returns>
Task<int> CountAsync(Expression<Func<T, bool>> predicate);
#endregion
#region Custom
/// <summary>
/// Ausführen eines RAW SQL commands
/// </summary>
/// <param name="sql">SQL-String</param>
/// <param name="parameters">Parameter</param>
/// <returns>Anzahl betroffener Datensätze</returns>
Task<int> ExecuteRawSqlAsync(string sql, Dictionary<string, object> parameters);
#endregion
}
}

View File

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service für die Verwaltung von Entitäten
/// </summary>
/// <typeparam name="T">Typ des Service</typeparam>
public interface IService<T> where T : class
{
#region Get
/// <summary>
/// Gibt eine Entität anhand der eindeutigen Id zurück
/// </summary>
/// <param name="id">Id der Entität</param>
/// <returns>Entität oder null, wenn nicht gefunden</returns>
T Get(object id);
/// <summary>
/// Gibt eine Entität anhand der eindeutigen Id zurück
/// </summary>
/// <param name="id">Id der Entität</param>
/// <returns>Entität oder null, wenn nicht gefunden</returns>
Task<T> GetAsync(object id);
/// <summary>
/// Gibt alle Entitäten zurück
/// </summary>
/// <returns>Liste aller Entitäten</returns>
IEnumerable<T> GetAll();
/// <summary>
/// Gibt alle Entitäten zurück
/// </summary>
/// <returns>Liste aller Entitäten</returns>
Task<IEnumerable<T>> GetAllAsync();
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
IQueryable<T> Query(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Entität oder den Default wert zurück
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
T SingleOrDefault(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Entität oder den Default wert zurück.
/// Exception wenn mehr als 1 gefunden
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
Task<T> SingleOrDefaultAsync(Expression<Func<T, bool>> predicate);
/// <summary>
/// Gibt eine Entität oder den Default wert zurück
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate);
#endregion
#region CRUD
/// <summary>
/// Fügt eine Entität zum Repository hinzu
/// </summary>
/// <param name="entity">Entität</param>
void Add(T entity);
/// <summary>
/// Fügt eine Liste von Entitäten zum Repository hinzu
/// </summary>
/// <param name="entities">List von Entitäten</param>
void AddRange(IEnumerable<T> entities);
/// <summary>
/// Entfernt eine Entität aus dem Repository
/// </summary>
/// <param name="entity">Zu entfernende Entität</param>
void Remove(T entity);
/// <summary>
/// Entfernt eine Liste von Entitäten aus dem Repository
/// </summary>
/// <param name="entities">Liste von Entitäten</param>
void RemoveRange(IEnumerable<T> entities);
#endregion
#region Count
/// <summary>
/// Gibt die Anzahl aller Entitäten zurück
/// </summary>
/// <returns>Anzahl</returns>
int Count();
/// <summary>
/// Gibt die Anzahl aller Entitäten zurück
/// </summary>
/// <returns>Anzahl</returns>
Task<int> CountAsync();
#endregion
#region Commit / UnitOfWork
/// <summary>
/// Schließt Daten-Änderungen ab und speichert die selben
/// </summary>
/// <param name="userName">Benutzername</param>
/// <param name="noAudit">Gibt an, dass kein Audit-Eintrag erstellt werden soll, auch wenn Audit aktiviert ist</param>
int Commit(string userName, bool noAudit = false);
/// <summary>
/// Schließt Daten-Änderungen ab und speichert die selben
/// </summary>
/// <param name="userName">Benutzername</param>
/// <param name="noAudit">Gibt an, dass kein Audit-Eintrag erstellt werden soll, auch wenn Audit aktiviert ist</param>
Task<int> CommitAsync(string userName, bool noAudit = false);
#endregion
}
}

View File

@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittestellenbeschreibung für einen Service der die Preise eines Produktes berechnet
/// </summary>
public interface IShopCalculationService
{
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
Task<PriceCalculation> CalculatePriceAsync(int productId, long customerId, int quantity);
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
Task<PriceCalculation> CalculatePriceAsync(int productId, long customerId, string billingCountry, int quantity);
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <param name="budget">Budget für Banner</param>
/// <returns>Preis-Kalkulation</returns>
Task<PriceCalculation> CalculatePriceBannerAsync(int productId, long customerId, string billingCountry, int quantity, decimal budget);
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="priceType">Preistype</param>
/// <param name="priceAliquotType">Typ der aliquoten Berechnung</param>
/// <param name="startDate">Startdatum</param>
/// <param name="endDate">Enddatum</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
Task<PriceCalculation> CalculatePriceAsync(int productId, long customerId, PriceType priceType, PriceAliquotType priceAliquotType, DateTimeOffset startDate, DateTimeOffset endDate, int quantity);
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="priceType">Preistype</param>
/// <param name="priceAliquotType">Typ der aliquoten Berechnung</param>
/// <param name="startDate">Startdatum</param>
/// <param name="endDate">Enddatum</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
Task<PriceCalculation> CalculatePriceAsync(int productId, long customerId, PriceType priceType, PriceAliquotType priceAliquotType, DateTimeOffset startDate, DateTimeOffset endDate, string billingCountry, int quantity);
/// <summary>
/// Aufschlagen von X Prozent auf eine Preis-Kalkulation
/// </summary>
/// <param name="priceCalculation">Preis-Kalkulation</param>
/// <param name="markup">Aufschlag in Prozent</param>
/// <param name="productId">Id des Produktes</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <returns>Preis-Kalkulation</returns>
Task<PriceCalculation> AddMarkupAsync(PriceCalculation priceCalculation, decimal markup, int productId, string billingCountry);
}
/// <summary>
/// Ergebnis einer Preis-Kalkulation
/// </summary>
public class PriceCalculation
{
/// <summary>
/// Preis netto
/// </summary>
public decimal Price { get; set; }
/// <summary>
/// Preis brutto
/// </summary>
public decimal PriceGross { get; set; }
/// <summary>
/// Vorheriger Preis netto
/// </summary>
public decimal OldPrice{ get; set; }
/// <summary>
/// Vorheriger Preis brutto
/// </summary>
public decimal OldPriceGross { get; set; }
/// <summary>
/// Preis2 netto
/// </summary>
public decimal Price2 { get; set; }
/// <summary>
/// Preis2 brutto
/// </summary>
public decimal Price2Gross { get; set; }
/// <summary>
/// Vorheriger Preis2 netto
/// </summary>
public decimal OldPrice2 { get; set; }
/// <summary>
/// Vorheriger Preis2 brutto
/// </summary>
public decimal OldPrice2Gross { get; set; }
/// <summary>
/// Angewandte Steuer - z.B. 10%, 20%
/// </summary>
public decimal TaxRate { get; set; }
/// <summary>
/// Kalkulierte Steuer
/// </summary>
public decimal TaxValue { get; set; }
/// <summary>
/// Gesamtwert Netto (Preis + Preis2)
/// </summary>
public decimal Total { get; set; }
/// <summary>
/// Gesamtwert Brutto (Preis + Preis2)
/// </summary>
public decimal TotalGross { get; set; }
/// <summary>
/// Gesamtwert Vorherig Netto (Preis + Preis2)
/// </summary>
public decimal TotalOld { get; set; }
/// <summary>
/// Gesamtwert vorherig Brutto (Preis + Preis2)
/// </summary>
public decimal TotalOldGross { get; set; }
/// <summary>
/// Stückzahl
/// </summary>
public int Quantity { get; set; }
}
}

View File

@ -0,0 +1,100 @@
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using gehGassi.Domain.Users;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der Token erstellt
/// </summary>
public interface ITokenService
{
/// <summary>
/// Erstellen eines Tokens
/// </summary>
/// <param name="claims">Zu setzende Claims</param>
/// <returns>Token als string</returns>
string GenerateAccessToken(IEnumerable<Claim> claims);
/// <summary>
/// Gibt das ClaimsPricipal basierend auf einem abgelaufenen Token zurück
/// </summary>
/// <param name="token">Abgelaufenes Token</param>
/// <returns>ClaimsPrincipal</returns>
ClaimsPrincipal GetPrincipalFromExpiredToken(string token);
}
/// <summary>
/// Schnittestellenbeschreibung für einen Service der die Verwaltung von Refresh-Tokens realisiert
/// </summary>
public interface IRefreshTokenService : IService<RefreshToken>
{
/// <summary>
/// Erstellten eines Tokens
/// </summary>
/// <returns>RefreshToken</returns>
RefreshToken Create();
/// <summary>
/// Gibt eine Liste aller RefreshTokens eines Benutzers zurück
/// </summary>
/// <param name="userId">Id des Benutzers</param>
/// <returns>Liste Refreshtokens</returns>
Task<List<RefreshToken>> GetAllAsync(string userId);
/// <summary>
/// Gibt ein Refreshtoken zurück
/// </summary>
/// <param name="userId">Id des Benutzers</param>
/// <param name="token">Refreshtoken</param>
/// <returns>RefreshToken</returns>
Task<RefreshToken> GetAsync(string userId, string token);
/// <summary>
/// Gibt ein Refreshtoken anhand des Tokens selbst zurück
/// </summary>
/// <param name="token">Token</param>
/// <returns>RefreshToken</returns>
Task<RefreshToken> GetByTokenAsync(string token);
/// <summary>
/// Gibt zurück ob ein Token gültig ist. Bedeutet, gibt es das Token und ist es noch nicht abgelaufen
/// </summary>
/// <param name="userId">Id des Benutzers</param>
/// <param name="token">Refreshtoken</param>
/// <returns>true wenn gültig, false sonst</returns>
Task<bool> IsValidAsync(string userId, string token);
/// <summary>
/// Löschen eines Refreshtokens
/// </summary>
/// <param name="id">Id des Tokens</param>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn gelöscht, false sonst</returns>
Task<bool> RemoveAsync(long id, string userName);
/// <summary>
/// Löscht alle Refreshtokens eines Benutzers
/// </summary>
/// <param name="userId">Id des Tokens</param>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> RemoveAllAsync(string userId, string userName);
/// <summary>
/// Löscht alle abgelaufenen Refreshtokens eines Benutzers
/// </summary>
/// <param name="userId">Id des Tokens</param>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> RemoveExpiredAsync(string userId, string userName);
/// <summary>
/// Löscht alle abgelaufenen Refreshtokens
/// </summary>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
Task<bool> RemoveExpiredAsync(string userName);
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Threading.Tasks;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Definiert eine Schnittstelle welche Operationen in einem Repository gruppiert und bestätigt (speichert)
/// </summary>
public interface IUnitOfWork : IDisposable
{
/// <summary>
/// Gibt ein Repository im Kontext der UnitOfWork zurück
/// </summary>
/// <typeparam name="T">Typ (Klasse)</typeparam>
/// <returns>IRepository oder null, wenn nicht verfügbar</returns>
IRepository<T> GetRepository<T>() where T : class;
/// <summary>
/// Schließt Daten-Änderungen ab und speichert die selben
/// </summary>
/// <param name="userName">Name des Benutzers</param>
/// <param name="noAudit">Gibt an, dass kein Audit-Eintrag erstellt werden soll, auch wenn Audit aktiviert ist</param>
int Commit(string userName, bool noAudit);
/// <summary>
/// Schließt Daten-Änderungen ab und speichert die selben
/// </summary>
/// <param name="userName">Name des Benutzers</param>
/// <param name="noAudit">Gibt an, dass kein Audit-Eintrag erstellt werden soll, auch wenn Audit aktiviert ist</param>
Task<int> CommitAsync(string userName, bool noAudit);
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der für Benutzer prüfen kann ob diese via SignalR online sind
/// </summary>
public interface IUserOnlineService
{
/// <summary>
/// Gibt zurück ob ein Benutzer via SignalR online ist
/// </summary>
/// <param name="userName">Benutzername</param>
/// <returns>True wenn online, false sonst</returns>
Task<bool> IsUserOnlineAsync(string userName);
/// <summary>
/// Gibt zurück ob ein App-User via SignalR online ist
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>True wenn online, false sonst</returns>
Task<bool> IsAppUserOnlineAsync(string appUserId);
}
}

View File

@ -0,0 +1,222 @@
namespace gehGassi.Core.Interfaces
{
/// <summary>
/// Optionen für die Zugriffsüberprüfung auf die API durch den Webjob
/// </summary>
public class WebJobApiOptions
{
/// <summary>
/// Name des Schlüssels im Header der geprüft werden soll
/// </summary>
public string KeyName { get; set; }
/// <summary>
/// Wert der im Header - Key vorhanden sein muss
/// </summary>
public string KeyValue { get; set; }
/// <summary>
/// Basis-Url der Website
/// </summary>
public string Url { get; set; }
}
/// <summary>
/// Optionen für die Bezahlmethoden
/// </summary>
public class PaymentOptions
{
/// <summary>
/// Soll die PayPal Sandbox verwendet werden?
/// </summary>
public bool PayPalUseSandbox { get; set; }
/// <summary>
/// Basis-Url der PayPal-API
/// </summary>
public string PayPalBaseUrl { get; set; }
/// <summary>
/// Basis-Url der PayPl-Sandbox API
/// </summary>
public string PayPalSandboxUrl { get; set; }
/// <summary>
/// Soll der Klarna Playgrounnd verwendet werden?
/// </summary>
public bool KlarnaUsePlayground { get; set; }
/// <summary>
/// Basis-Url der Klarna API
/// </summary>
public string KlarnaBaseUrl { get; set; }
/// <summary>
/// Basis-Url des Klarna Playground
/// </summary>
public string KlarnaPlaygroundUrl { get; set; }
}
/// <summary>
/// Optionen für Listungen
/// </summary>
public class ListingOptions
{
/// <summary>
/// Wie viele Platin-Listungen je Branche und Zeitraum sind möglich?
/// </summary>
public int Platin { get; set; }
/// <summary>
/// Wie viele Gold-Listungen je Branche und Zeitraum sind möglich?
/// </summary>
public int Gold { get; set; }
/// <summary>
/// Wie viele Silber-Listungen je Branche und Zeitraum sind möglich?
/// </summary>
public int Silver { get; set; }
/// <summary>
/// Wie viele Basis-Listungen je Branche und Zeitraum sind möglich?
/// </summary>
public int Base { get; set; }
}
/// <summary>
/// Einstellungen für den Shop
/// </summary>
public class ShopOptions
{
/// <summary>
/// Quell-Land des Shops. Wichtig für Steuer und Versandkostenberechnung
/// </summary>
public string SourceCountry { get; set; }
/// <summary>
/// Soll die PayPal Sandbox verwendet werden?
/// </summary>
public bool PayPalUseSandbox { get; set; }
/// <summary>
/// PayPal Live-URL
/// </summary>
public string PayPalBaseUrl { get; set; }
/// <summary>
/// PayPal Sandbox-Url
/// </summary>
public string PayPalSandboxUrl { get; set; }
/// <summary>
/// Soll der Klarna Playground verwendet weden?
/// </summary>
public bool KlarnaUsePlayground { get; set; }
/// <summary>
/// Klarna Basis-Url
/// </summary>
public string KlarnaBaseUrl { get; set; }
/// <summary>
/// Klarna Playground URL
/// </summary>
public string KlarnaPlaygroundUrl { get; set; }
}
/// <summary>
/// Einstellungen für den Zugriff auf MangoPay
/// </summary>
public class MangoPayOptions
{
/// <summary>
/// Id des Clients
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// API-Key
/// </summary>
public string ApiKey { get; set; }
/// <summary>
/// URL für die Kommunikation
/// </summary>
public string BaseUrl { get; set; }
}
/// <summary>
/// Einstellungen für den PushNotificationService
/// </summary>
public class PushNotificationOptions
{
/// <summary>
/// Connectionstring für Azure mit Manage,Listen,Send
/// </summary>
public string ConnectionString { get; set; }
/// <summary>
/// Name des Hubs der verwendet werden soll
/// </summary>
public string HubName { get; set; }
}
/// <summary>
/// Einstellungen wie viele Coins man für den Abschluss eines Walks bekommt.
/// Es wird zwischen den unterschiedlichen Service-Typen unterschieden.
/// </summary>
public class CoinsOptions
{
/// <summary>
/// Walks für Dogwalker
/// </summary>
public decimal WalkForDogwalker { get; set; }
/// <summary>
/// Sittings für DogWalker
/// </summary>
public decimal SittingForDowgWalker { get; set; }
/// <summary>
/// Tagesbetreuung für DogWalker
/// </summary>
public decimal DayCareForDowgWalker { get; set; }
/// <summary>
/// Walks für Hundebesitzer
/// </summary>
public decimal WalkForOwner { get; set; }
/// <summary>
/// Sittings für Hundebesitzer
/// </summary>
public decimal SittingForOwner { get; set; }
/// <summary>
/// Tagesbetreuung für Hundebesitzer
/// </summary>
public decimal DayCareForOwner { get; set; }
}
/// <summary>
/// Einstellung wie viele Coins man mindestens erhalten haben muss um an der Topf-Auszahlung teilzunehmen
/// </summary>
public class MinCoinsForPot
{
/// <summary>
/// Mindest-Coins je Monat um am Monats-Topf teilzunehmen - Dogwalker
/// </summary>
public decimal DogWalkerMonthly { get; set; }
/// <summary>
/// Mindest-Coins je Jahr um am Jahres-Topf teilzunehmen - Dogwalker
/// </summary>
public decimal DogWalkerYearly { get; set; }
/// <summary>
/// Mindest-Coins je Monat um am Monats-Topf teilzunehmen - Hundebesitzer
/// </summary>
public decimal OwnerMonthly { get; set; }
/// <summary>
/// Mindest-Coins je Jahr um am Jahres-Topf teilzunehmen - Hundebesitzer
/// </summary>
public decimal OwnerYearly { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Rubriken realisiert
/// </summary>
public class AdvertisementCategoryService : ServiceBase<AdvertisementCategory>, IAdvertisementCategoryService
{
private readonly IRepository<AdvertisementCategoryWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public AdvertisementCategoryService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<AdvertisementCategoryWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Rubriken zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Rubriken</returns>
public IQueryable<AdvertisementCategory> Filter(string filter)
{
return Repository.Query(c => c.Title.Contains(filter));
}
/// <summary>
/// Erstellen einer Rubrik
/// </summary>
/// <returns>Kunde</returns>
public AdvertisementCategory Create()
{
var item = new AdvertisementCategory()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Rubriken zurück. Sucht nur im Titel
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Rubriken</returns>
public async Task<List<AdvertisementCategory>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Title.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Rubriken mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Rubriken</returns>
public IQueryable<AdvertisementCategoryWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_AdvertisementCategoryWithNames({0},{1})", c => c.Title.Contains(filter) || c.Name.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine Liste von Kategorien zurück die Online sind
/// </summary>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste betroffener Rubriken</returns>
public async Task<List<AdvertisementCategoryWithNames>> GetOnlineWithNamesAsync(string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_AdvertisementCategoryWithNames({0},{1})", c => c.Deleted == false && c.OnlineStatus == OnlineStatus.Online, language, fallback);
return await baseQuery.OrderBy(c => c.Name).ToListAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,336 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using Microsoft.EntityFrameworkCore;
using NetTopologySuite.Geometries;
using NetTopologySuite;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service für die Verwaltung von Werbungen
/// </summary>
public class AdvertisementService : ServiceBase<Advertisement>, IAdvertisementService
{
private readonly IRepository<AdvertisementWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public AdvertisementService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<AdvertisementWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Werbungen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Werbungen</returns>
public IQueryable<Advertisement> Filter(string filter)
{
return Repository.Query(c => c.NameLocalized.Contains(filter));
}
/// <summary>
/// Erstellen einer Werbung
/// </summary>
/// <returns>Listung</returns>
public Advertisement Create()
{
var item = new Advertisement()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Werbungen zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Werbungen</returns>
public async Task<List<Advertisement>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.NameLocalized.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Werbungen mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Werbungen</returns>
public IQueryable<AdvertisementWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_AdvertisementsWithNames({0},{1})", c => c.Name.Contains(filter) || c.CustomerName.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != AdvertisementStatus.Draft);
return baseQuery;
}
/// <summary>
/// Gibt eine gefilterte Liste von Werbungen mit Namen für Kunden zurück in einer gewählten Sprache
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Werbungen</returns>
public IQueryable<AdvertisementWithNames> FilterWithNamesCustomer(long customerId, string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_AdvertisementsWithNames({0},{1})", c => c.CustomerId == customerId && (c.Name.Contains(filter) || c.CustomerName.Contains(filter)), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != AdvertisementStatus.Draft);
return baseQuery;
}
/// <summary>
/// Löschen von Werbungen basierend auf einer Rubrik
/// </summary>
/// <param name="categoryId">Id der Rubrik</param>
/// <param name="forceDelete">Wenn true, dann wirklich löschen, auf gelöscht setzen sonst</param>
/// <returns>Task</returns>
public async Task DeleteByCategoryAsync(string categoryId, bool forceDelete)
{
var items = await Repository.FindAsync(c => c.AdvertisementCategoryId == categoryId).ConfigureAwait(false);
foreach (var listing in items)
{
if (!forceDelete)
listing.Deleted = true;
else
Repository.Remove(listing);
}
}
/// <summary>
/// Gibt die Anzahl von Werbungen zurück
/// </summary>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Anzahl Werbungen</returns>
public async Task<int> CountAsync(bool includeDeleted, bool excludeDraft)
{
var query = Repository.Query(c => c.Id != "");
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if (excludeDraft)
query = query.Where(c => c.Status != AdvertisementStatus.Draft);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl von Werbungen in einem Status zurück
/// </summary>
/// <param name="status">Gesuchter Status</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Anzahl Werbungen</returns>
public async Task<int> CountAsync(AdvertisementStatus status, bool includeDeleted)
{
var query = Repository.Query(c => c.Status == status);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Werbung für einen Artikel im Warenkorb zurück
/// </summary>
/// <param name="cartItemId">Id des Artikels im Warenkorb</param>
/// <returns>Werbung oder null, wenn nicht gefunden</returns>
public async Task<Advertisement> GetByCartItemAsync(long cartItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.CartItemId == cartItemId && c.Status == AdvertisementStatus.Draft);
}
/// <summary>
/// Gibt eine Werbung für einen Artikel in einer Bestellung zurück
/// </summary>
/// <param name="orderItemId">Id des Artikels in der Bestellung</param>
/// <returns>Werbung oder null, wenn nicht gefunden</returns>
public async Task<Advertisement> GetByOrderItemAsync(long orderItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.OrderItemId == orderItemId);
}
/// <summary>
/// Setzen des Status von Werbungen nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <returns>Anzahl betroffener Werbungen</returns>
public async Task<int> SetStatusByOrderAsync(long orderId, AdvertisementStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.Status = status;
}
return items.Count();
}
/// <summary>
/// Setzen des Bezahlstatus von Werbungen nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <returns>Anzahl betroffener Werbungen</returns>
public async Task<int> SetPaymentStatusByOrderAsync(long orderId, PaymentStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.PaymentStatus = status;
}
return items.Count();
}
/// <summary>
/// Gibt eine Liste von Werbungen zurück für die Anzeige der Werbungen die bald enden und in der Nähe sind
/// </summary>
/// <param name="country">Land</param>
/// <param name="state">Bundesland</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste passender Listungen</returns>
public async Task<(int total, List<AdvertisementWithNames> list)> GetNearEndForAppAsync(string country, string state, double lat, double lng, string language, string fallback, int skip, int take)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(country))
country = country.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(state))
state = state.ToUpperInvariant();
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_AdvertisementsWithNames({0},{1})", c => c.Deleted == false && c.Status == AdvertisementStatus.Running && c.EndDate > DateTimeOffset.UtcNow &&
(c.GeoMode == GeoMode.None ||
(c.GeoMode == GeoMode.Country && c.Address_CountryCode == country) ||
(c.GeoMode == GeoMode.CountryAndState && c.Address_CountryCode == country && c.Address_State == state) ||
(c.GeoMode == GeoMode.Location && c.Location.IsWithinDistance(location, c.Radius * 1000))), language, fallback);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
//TODO: noch überlegen wie wir eine Lokalisierung machen können
var list = await baseQuery.OrderBy(c => c.EndDate).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
/// <summary>
/// Gibt eine Liste von Werbungen je Kategorie zurück
/// </summary>
/// <param name="categoryId">Id der Kategorie</param>
/// <param name="country">Land</param>
/// <param name="state">Bundesland</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste passender Listungen</returns>
public async Task<(int total, List<AdvertisementWithNames> list)> GetByCategoryForAppAsync(string categoryId, string country, string state, double lat, double lng, string language, string fallback, int skip, int take)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(country))
country = country.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(state))
state = state.ToUpperInvariant();
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_AdvertisementsWithNames({0},{1})", c => c.Deleted == false && c.Status == AdvertisementStatus.Running && c.EndDate > DateTimeOffset.UtcNow && c.AdvertisementCategoryId == categoryId &&
(c.GeoMode == GeoMode.None ||
(c.GeoMode == GeoMode.Country && c.Address_CountryCode == country) ||
(c.GeoMode == GeoMode.CountryAndState && c.Address_CountryCode == country && c.Address_State == state) ||
(c.GeoMode == GeoMode.Location && c.Location.IsWithinDistance(location, c.Radius * 1000))), language, fallback);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
//TODO: noch überlegen wie wir eine Lokalisierung machen können
var list = await baseQuery.OrderBy(c => c.EndDate).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
/// <summary>
/// Gibt eine Werbung in einer gewünschten Sprache zurück
/// </summary>
/// <param name="id">Id der Werbung</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Gefundene Werbung, null sonst</returns>
public async Task<AdvertisementWithNames> GetRunningWithNamesAsync(string id, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_AdvertisementsWithNames({0},{1})", c => c.Id == id && c.Deleted == false && c.Status == AdvertisementStatus.Running && c.EndDate > DateTimeOffset.UtcNow, language, fallback);
var result = await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
return result;
}
/// <summary>
/// Starten von gebuchten Werbungen wenn diese das Start-Datum erreicht haben
/// </summary>
/// <returns>Anzahl gestartete Werbungen</returns>
public async Task<int> StartBookedAsnyc()
{
var items = await Repository.FindAsync(c => c.Status == AdvertisementStatus.Booked && c.Deleted == false && c.StartDate <= DateTimeOffset.UtcNow && c.EndDate.Value >= DateTimeOffset.UtcNow);
foreach (var item in items)
{
item.Status = AdvertisementStatus.Running;
}
return items.Count();
}
/// <summary>
/// Stoppen von abgelaufenen Werbungen wenn diese das Enddatum erreicht haben
/// </summary>
/// <returns>Anzahl gestoppte Werbungen</returns>
public async Task<int> StopRunningAsync()
{
var items = await Repository.FindAsync(c => c.Status == AdvertisementStatus.Running && c.Deleted == false && c.EndDate.Value <= DateTimeOffset.UtcNow);
foreach (var item in items)
{
item.Status = AdvertisementStatus.Ended;
}
return items.Count();
}
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Feedback;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Feedbacks aus der App ermöglicht
/// </summary>
public class AppFeedbackService : ServiceBase<AppFeedback>, IAppFeedbackService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public AppFeedbackService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Erstellen eines Feedback
/// </summary>
/// <returns>AppFeedback</returns>
public AppFeedback Create()
{
var item = new AppFeedback();
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Feedback zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Feedbacks</returns>
public IQueryable<AppFeedback> Filter(string filter)
{
return Repository.Query(c => c.UserName.Contains(filter) || c.Name.Contains(filter));
}
/// <summary>
/// Gibt die Anzahl von Feedbacks in einem bestimmten Status zurück
/// </summary>
/// <param name="status">Gewünschter Status</param>
/// <returns>Anzahl Feedbacks im Status</returns>
public async Task<int> CountAsync(AppFeedbackStatus status)
{
return await Repository.CountAsync(c => c.Status == status).ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,118 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Feedback;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Reporting;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von App-User-Reports ermöglicht
/// </summary>
public class AppUserReportService : ServiceBase<AppUserReport>, IAppUserReportService
{
private readonly IRepository<AppUserReportWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public AppUserReportService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<AppUserReportWithNames>();
}
/// <summary>
/// Erstellen einer Meldung
/// </summary>
/// <returns>AppFeedback</returns>
public AppUserReport Create()
{
var item = new AppUserReport
{
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Meldungen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Feedbacks</returns>
public IQueryable<AppUserReport> Filter(string filter)
{
return Repository.Query(c => c.Email.Contains(filter) || c.UpdatedBy.Contains(filter));
}
/// <summary>
/// Gibt eine gefilterte Liste von Meldungen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Feedbacks</returns>
public IQueryable<AppUserReportWithNames> FilterWithNames(string filter)
{
return _namesRepository.Query(c => c.ReportingAppUserName.Contains(filter) || c.ReportedAppUserName.Contains(filter) || c.ReportingAppUserEmail.Contains(filter) || c.ReportedAppUserEmail.Contains(filter));
}
/// <summary>
/// Gibt die Anzahl von Meldungen in einem bestimmten Status zurück
/// </summary>
/// <param name="status">Gesuchter Status</param>
/// <returns>Anzahl Meldungen im Status</returns>
public async Task<int> CountAsync(AppUserReportStatus status)
{
return await Repository.CountAsync(c => c.Status == status).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Meldung mit Namen aufgelöst zurück
/// </summary>
/// <param name="id">Id der Meldung</param>
/// <returns>AppUserReportWithNames oder null, wenn nicht gefunden</returns>
public async Task<AppUserReportWithNames> GetWithNamesAsync(long id)
{
return await _namesRepository.FirstOrDefaultAsync(c => c.Id == id).ConfigureAwait(false);
}
/// <summary>
/// Gibt zurück wie viele Meldungen ein App-User erstellt hat
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <returns>Anzahl Meldungen</returns>
public async Task<int> CountReportingAsync(string appUserId)
{
return await Repository.CountAsync(c => c.ReportingAppUserId == appUserId).ConfigureAwait(false);
}
/// <summary>
/// Gibt zurück wie viele Meldungen für ein App-User erstellt wurden
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <returns>Anzahl Meldungen erstellt</returns>
public async Task<int> CountReportedAsync(string appUserId)
{
return await Repository.CountAsync(c => c.ReportedAppUserId == appUserId).ConfigureAwait(false);
}
/// <summary>
/// Löscht alle Meldungen and denen ein AppUser beteiligt ist
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <returns>Anzahl Meldungen gelöscht</returns>
public async Task<int> DeleteAllForAppUserAsync(string appUserId)
{
var reports = await Repository.FindAsync(c => c.ReportedAppUserId == appUserId || c.ReportingAppUserId == appUserId).ConfigureAwait(false);
foreach (var report in reports)
{
Repository.Remove(report);
}
return reports.Count();
}
}
}

View File

@ -0,0 +1,660 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.Ratings;
using gehGassi.Domain.Users;
using gehGassi.Dto.DogWalkers;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using NetTopologySuite.Geometries;
using NetTopologySuite;
using gehGassi.Common.Extensions;
using System.Collections;
using gehGassi.Domain.Messages;
using gehGassi.Dto.Common;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von App-Usern realisiert
/// </summary>
public class AppUserService : ServiceBase<AppUser>, IAppUserService
{
private readonly IRepository<DogWalkerProfile> _profileRepository;
private readonly IRepository<Certificate> _certificateRepository;
private readonly IRepository<DogWalker> _walkerRepository;
private readonly IRepository<AppUserBlock> _blockRepository;
private readonly IRepository<AppUserBlockWithNames> _blockWithNamesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public AppUserService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_profileRepository = unitOfWork.GetRepository<DogWalkerProfile>();
_certificateRepository = unitOfWork.GetRepository<Certificate>();
_walkerRepository = unitOfWork.GetRepository<DogWalker>();
_blockRepository = unitOfWork.GetRepository<AppUserBlock>();
_blockWithNamesRepository = unitOfWork.GetRepository<AppUserBlockWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von App-Usern zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>List betroffener App-User</returns>
public IQueryable<AppUser> Filter(string filter, bool includeDeleted)
{
if(includeDeleted)
return Repository.Query(c => c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.Contact.Email.Contains(filter));
else
return Repository.Query(c => c.Deleted == false && (c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.Contact.Email.Contains(filter)));
}
/// <summary>
/// Erstellen eines App-Users
/// </summary>
/// <returns>App-User</returns>
public AppUser Create()
{
var item = new AppUser()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow,
Contact = new ContactMin(),
Address = new Address(),
SocialMedia = new SocialMedia(),
RatingStatistics = new RatingStatistics(),
RatingStatisticsWalker = new RatingStatistics(),
Type = AppUserType.DogOwner
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von App-Usern zurück. Sucht nur im Namen
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>List betroffener App-User</returns>
public async Task<List<AppUser>> SearchAsync(string filter, bool includeDeleted)
{
if(includeDeleted)
return (await Repository.FindAsync(c => c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.Contact.Email.Contains(filter))).ToList();
else
return (await Repository.FindAsync(c => c.Deleted == false && (c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.Contact.Email.Contains(filter)))).ToList();
}
/// <summary>
/// Gibt die nächste freie Nummer für einen App-User zurück
/// </summary>
/// <returns>App-User-Nummer</returns>
public string GetNextNumber()
{
long number = 1;
var total = Repository.Count();
number = total + 1;
return $"AU-{DateTime.UtcNow.Year:0000}{DateTime.UtcNow.Month:00}-{number:0000000000}";
}
/// <summary>
/// Gibt die Anzahl von App-UserN im System zurück
/// </summary>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Anzahl App-User</returns>
public async Task<int> CountAsync(bool includeDeleted)
{
if (includeDeleted)
return await Repository.CountAsync().ConfigureAwait(false);
else
return await Repository.CountAsync(c => c.Deleted == false).ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl der AppUser im System zurück welche einem Typ entsprechen
/// </summary>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="type">Typ der gesucht wird</param>
/// <param name="lastUpdate">Optional: wenn angegeben, dann muss das Datum des letzten Updates größer oder gleich dem lastUpdate Datum sein</param>
/// <returns>Anzahl AppUser</returns>
public async Task<int> CountAsync(bool includeDeleted, AppUserType type, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => true);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if(type == AppUserType.DogWalker)
query = query.Where(c => c.Type == AppUserType.DogWalker);
else if (type == AppUserType.DogOwner)
query = query.Where(c => c.Type == AppUserType.DogOwner);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt >= lastUpdate.Value);
var count = await query.CountAsync().ConfigureAwait(false);
return count;
}
/// <summary>
/// Entsperren von gesperrten App-Usern deren LockedUntil-Datum erreicht wurde.
/// </summary>
/// <returns>Anzahl entsperrte App-User</returns>
public async Task<int> AutoUnlockAsync()
{
var lockedAppUsers = await Repository.FindAsync(c => c.Locked && c.LockedUntil != null && c.LockedUntil <= DateTimeOffset.UtcNow);
var appUsers = lockedAppUsers.ToList();
foreach (var appUser in appUsers)
{
appUser.Locked = false;
appUser.LockedUntil = null;
appUser.LockedReason = string.Empty;
}
return appUsers.Count();
}
/// <summary>
/// Gibt eine Liste von App-Usern zurück, die für die automatische Auszahlung in Frage kommen
/// </summary>
/// <returns>List App-User</returns>
public async Task<List<AppUser>> GetForAutoPayoutAsync()
{
var users = await Repository.FindAsync(c => c.AutoPayout && !string.IsNullOrWhiteSpace(c.PaymentId) && !string.IsNullOrWhiteSpace(c.BankId) && c.TermsAccepted && c.PrivacyAccepted && c.PaymentTermsAccepted && c.KycPassed && !c.Deleted).ConfigureAwait(false);
return users.ToList();
}
/// <summary>
/// Gibt zurück ob ein App-User gesperrt ist
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <returns>true wenn gesperrt, false sonst</returns>
public async Task<bool> IsLockedAsync(string appUserId)
{
var appUser = await Repository.FirstOrDefaultAsync(c => c.Id == appUserId);
if(appUser != null && appUser.Deleted == false)
return appUser.Locked;
return false;
}
/// <summary>
/// Gibt eine List von AppUsern zurück
/// </summary>
/// <param name="skip">skip</param>
/// <param name="take">take</param>
/// <param name="type">Typ der gesucht wird</param>
/// <param name="lastUpdate">Optional: wenn angegeben, dann muss das Datum des letzten Updates größer oder gleich dem lastUpdate Datum sein</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste AppUser</returns>
public async Task<List<AppUser>> GetAllAsync(int skip, int take, AppUserType type, DateTimeOffset? lastUpdate, bool includeDeleted)
{
var query = Repository.Query(c => true);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if (type == AppUserType.DogWalker)
query = query.Where(c => c.Type == AppUserType.DogWalker);
else if (type == AppUserType.DogOwner)
query = query.Where(c => c.Type == AppUserType.DogOwner);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt >= lastUpdate.Value);
query = query.Skip(skip).Take(take);
var list = await query.ToListAsync().ConfigureAwait(false);
return list;
}
#region Profile
/// <summary>
/// Erstellt ein DogWalker Profil wenn es noch nicht existiert
/// </summary>
/// <param name="id">Id des App-Users</param>
/// <returns>DogWalker Propfil</returns>
public async Task<DogWalkerProfile> CreateWalkerProfileIfNotExistsAsync(string id)
{
var profile = await _profileRepository.GetAsync(id);
if (profile == null)
{
profile = new DogWalkerProfile()
{
Id = id,
UpdatedAt = DateTimeOffset.UtcNow,
Deleted = false,
Radius = 10,
About = string.Empty,
AllowedRequests = DogWalkRequestType.Walking,
PuppyAllowed = false,
DifficultAllowed = false,
AggressionLevel = DogAggressionLevel.Low,
AcceptedSize = DogSize.Small,
PriceWalk = 10,
PriceDay = 7,
PriceSitting = 40,
NotAvailable = false,
NotAvailableInfo = string.Empty
};
_profileRepository.Add(profile);
}
return profile;
}
/// <summary>
/// Gibt ein Walker-Profil für einen App-User zurück
/// </summary>
/// <param name="id">Id des App-Users</param>
/// <returns>DogWalker Propfil</returns>
public async Task<DogWalkerProfile> GetWalkerProfileAsync(string id)
{
var profile = await _profileRepository.GetAsync(id);
return profile;
}
/// <summary>
/// Löschen eines Walker-Profils für einen App-User
/// </summary>
/// <param name="id">Id des App-Users</param>
/// <returns>DogWalker Propfil</returns>
public async Task RemoveWalkerProfileAsync(string id)
{
var profile = await _profileRepository.GetAsync(id);
if(profile != null)
_profileRepository.Remove(profile);
}
#endregion
#region DogWalkers
/// <summary>
/// Gibt eine Liste Dogwalkern zurück - sortiert nach Distanz
/// </summary>
/// <param name="ignoreId">Id des App-Users der ignoriert werden soll, falls ein DogOwner auch Walker ist</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="take">Wie viele sollen max gewählt werden?</param>
/// <returns>Liste passender Dogwalker</returns>
public async Task<List<DogWalker>> GetWalkersForAppAsync(string ignoreId, double lat, double lng, int take)
{
//Lokalisierungsmodus --> Andere Abfrage weil wir die Distanz zu den Geokoordinaten brauchen
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var walkersQuery = _walkerRepository.QueryView(c => c.Deleted == false && c.Id != ignoreId);
walkersQuery = walkersQuery.OrderBy(c => c.Location.Distance(location));
var walkers = await walkersQuery.Take(take).ToListAsync().ConfigureAwait(false);
return walkers;
}
/// <summary>
/// Gibt einen Dogwalker zurück
/// </summary>
/// <param name="dogWalkerId">Id des DogWalkers</param>
/// <returns>DogWalker oder null wenn nicht gefunden</returns>
public async Task<DogWalker> GetWalkerAsync(string dogWalkerId)
{
var baseQuery = _walkerRepository.QueryView(c => c.Id == dogWalkerId);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Dogwalkern zurück
/// </summary>
/// <param name="query">Abfrage-Objekt für Dogwalker - Beinhaltet alle Filter und auch Paging</param>
/// <returns>Liste gefudene Walks</returns>
public async Task<(int total, List<DogWalker> list)> GetWalkersAsync(DogWalkerQueryDto query)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(query.Location.Lng.Value, query.Location.Lat.Value));
//var baseQuery = _walkerRepository.QueryView(c => c.Deleted == false);
var baseQuery = _walkerRepository.QueryStoredProcedure("SELECT * FROM tv_DogWalkers({0})", c => c.Deleted == false && c.Locked == false, query.IgnoreId);
if (!string.IsNullOrWhiteSpace(query.IgnoreId))
baseQuery = baseQuery.Where(c => c.Id != query.IgnoreId);
if (!string.IsNullOrWhiteSpace(query.Name))
baseQuery = baseQuery.Where(c => c.FirstName.Contains(query.Name) || c.LastName.Contains(query.Name));
if (!string.IsNullOrWhiteSpace(query.City))
baseQuery = baseQuery.Where(c => c.Address_City.Contains(query.City) || c.Address_Zip.Contains(query.City));
if (!string.IsNullOrWhiteSpace(query.Country))
baseQuery = baseQuery.Where(c => c.Address_CountryCode == query.Country.ToUpperInvariant());
baseQuery = baseQuery.Where(c => c.RatingStatisticsWalker_AverageRating >= query.MinRating);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (query.DynamicSortOrder.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(query.DynamicSortOrder.First().Property, (SortDirection)query.DynamicSortOrder.First().SortDirection); ;
foreach (var sortOrder in query.DynamicSortOrder)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, (SortDirection)sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.Location.Distance(location)).Skip(query.Skip).Take(query.Take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(query.Skip).Take(query.Take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt eine Liste von Dogwalkern zurück welche über eine Adresse verfügen und deren Standort vom angegebenen Standort nicht weiter als der definierte Radius entfernt sind
/// </summary>
/// <param name="ignoreId">Id des Walkers der ignoriert werden soll, falls der hundebesitzer auch Walker ist</param>
/// <param name="location">Location für die Abholung des Hundes - darf nicht weiter als definierter Radius entfernt sein</param>
/// <returns>Liste gefundene Walker</returns>
public async Task<List<DogWalker>> GetWalkersForNotificationAsync(string ignoreId, Point location)
{
var baseQuery = _walkerRepository.QueryView(c => c.Deleted == false && c.Id != ignoreId
&& !string.IsNullOrWhiteSpace(c.Address_AddressLine1)
&& !string.IsNullOrWhiteSpace(c.Address_Zip)
&& !string.IsNullOrWhiteSpace(c.Address_City)
&& c.Location != null);
baseQuery = baseQuery.Where(c => c.Location.IsWithinDistance(location, c.Radius * 1000));
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
#endregion
#region Systemnachrichten
/// <summary>
/// Abfrage des App-Users für eine Systemnachricht
/// </summary>
/// <param name="query">Abfrageparameter</param>
/// <returns>Liste der App-User</returns>
public async Task<List<AppUser>> GetForSystemMessageAsync(SystemMessageAppUserQuery query)
{
var baseQuery = Repository.QueryView(c => c.Deleted == false);
if (query.AppUserType != AppUserType.Both)
{
baseQuery = baseQuery.Where(c => c.Type == query.AppUserType || c.Type == AppUserType.Both);
}
if (!string.IsNullOrWhiteSpace(query.City))
baseQuery = baseQuery.Where(c => c.Address.City.Contains(query.City));
if (!string.IsNullOrWhiteSpace(query.Zip))
baseQuery = baseQuery.Where(c => c.Address.Zip.Contains(query.Zip));
if (!string.IsNullOrWhiteSpace(query.Country))
baseQuery = baseQuery.Where(c => c.Address.CountryCode == query.Country.ToUpperInvariant());
if (!string.IsNullOrWhiteSpace(query.State))
baseQuery = baseQuery.Where(c => c.Address.State == query.State.ToUpperInvariant());
if(query.VerifiedOnly)
baseQuery = baseQuery.Where(c => c.Verified);
if (query.Sex.HasValue && query.Sex.Value != Sex.Undefined)
baseQuery = baseQuery.Where(c => c.Sex == query.Sex.Value);
return await baseQuery.ToListAsync();
}
/// <summary>
/// Gibt zurück wie viele App-User für eine Systemnachricht gefunden werden
/// </summary>
/// <param name="query">Abfrageparameter</param>
/// <returns>Anzahl der App-User</returns>
public async Task<int> CountForSystemMessageAsync(SystemMessageAppUserQuery query)
{
var baseQuery = Repository.QueryView(c => c.Deleted == false);
if (query.AppUserType != AppUserType.Both)
{
baseQuery = baseQuery.Where(c => c.Type == query.AppUserType || c.Type == AppUserType.Both);
}
if (!string.IsNullOrWhiteSpace(query.City))
baseQuery = baseQuery.Where(c => c.Address.City.Contains(query.City));
if (!string.IsNullOrWhiteSpace(query.Zip))
baseQuery = baseQuery.Where(c => c.Address.Zip.Contains(query.Zip));
if (!string.IsNullOrWhiteSpace(query.Country))
baseQuery = baseQuery.Where(c => c.Address.CountryCode == query.Country.ToUpperInvariant());
if (!string.IsNullOrWhiteSpace(query.State))
baseQuery = baseQuery.Where(c => c.Address.State == query.State.ToUpperInvariant());
if (query.VerifiedOnly)
baseQuery = baseQuery.Where(c => c.Verified);
if (query.Sex.HasValue && query.Sex.Value != Sex.Undefined)
baseQuery = baseQuery.Where(c => c.Sex == query.Sex.Value);
return await baseQuery.CountAsync();
}
#endregion
#region Blocks
/// <summary>
/// Erstellen einer Blockierung eines App-Users
/// </summary>
/// <returns>AppUserBlock</returns>
public AppUserBlock CreateBlock()
{
var item = new AppUserBlock
{
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Hinzufügen eines Blocks eine App-Users
/// </summary>
/// <param name="block">AppUserBlock</param>
public void AddBlock(AppUserBlock block)
{
_blockRepository.Add(block);
}
/// <summary>
/// Blockiert einen App-User für einen anderen, wenn noch kein Block existiert
/// </summary>
/// <param name="blockingAppUserId">Id des App-Users der die Blockierung vornimmt</param>
/// <param name="blockedAppUserId">Id des App-Users der blockiert wird</param>
/// <param name="createdBy">Von wem erstellt?</param>
/// <param name="blockUntil">Optional: Bis wann soll der App-User blockiert werden?</param>
/// <param name="reportId">Optional: Verweis auf eine Meldung</param>
/// <returns>Erstellte oder existierende Blockierung</returns>
public async Task<AppUserBlock> BlockAppUserAsync(string blockingAppUserId, string blockedAppUserId, string createdBy, DateTimeOffset? blockUntil = null, long? reportId = null)
{
var block = await _blockRepository.FirstOrDefaultAsync(c => c.BlockingAppUserId == blockingAppUserId && c.BlockedAppUserId == blockedAppUserId).ConfigureAwait(false);
if (block == null)
{
block = CreateBlock();
block.BlockingAppUserId = blockingAppUserId;
block.BlockedAppUserId = blockedAppUserId;
block.CreatedBy = createdBy;
block.AppUserReportId = reportId;
block.BlockedUntil = null;
AddBlock(block);
}
return block;
}
/// <summary>
/// Aufheben der Blockierung eines App-Users durch einen anderen, wenn vorhanden
/// </summary>
/// <param name="blockingAppUserId">Id des App-Users der die Blockierung vornimmt</param>
/// <param name="blockedAppUserId">Id des App-Users der blockiert wird</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> UnblockAppUserAsync(string blockingAppUserId, string blockedAppUserId)
{
var block = await _blockRepository.FirstOrDefaultAsync(c => c.BlockingAppUserId == blockingAppUserId && c.BlockedAppUserId == blockedAppUserId).ConfigureAwait(false);
if (block != null)
{
RemoveBlock(block);
return true;
}
return false;
}
/// <summary>
/// Löschen einer Blockierung
/// </summary>
/// <param name="block">AppUserBlock</param>
public void RemoveBlock(AppUserBlock block)
{
_blockRepository.Remove(block);
}
/// <summary>
/// Löschen einer Blockierung
/// </summary>
/// <param name="id">Id der Blockierung</param>
public async Task RemoveBlockByIdAsync(long id)
{
var block = await _blockRepository.FirstOrDefaultAsync(c => c.Id == id).ConfigureAwait(false);
if (block != null)
_blockRepository.Remove(block);
}
/// <summary>
/// Entfernen von Blocks eines App-Users der blockiert hat
/// </summary>
/// <param name="id">Id des AppUsers der blockiert hat</param>
/// <returns>Task</returns>
public async Task RemoveBlocksByBlockingUserAsync(string id)
{
var blocks = await _blockRepository.FindAsync(c => c.BlockingAppUserId == id).ConfigureAwait(false);
foreach (var appUserBlock in blocks)
{
_blockRepository.Remove(appUserBlock);
}
}
/// <summary>
/// Entfernen von Blocks eines App-Users der blockiert wurde
/// </summary>
/// <param name="id">Id des AppUsers der blockiert wurde</param>
/// <returns>Task</returns>
public async Task RemoveBlocksByBlockedUserAsync(string id)
{
var blocks = await _blockRepository.FindAsync(c => c.BlockedAppUserId == id).ConfigureAwait(false);
foreach (var appUserBlock in blocks)
{
_blockRepository.Remove(appUserBlock);
}
}
/// <summary>
/// Löschen von abgelaufenen Blockierungen, welche ein Ablaufdatum haben
/// </summary>
/// <param name="date">Datum das erreicht werden muss</param>
/// <returns>Anzahl gelösche Blockierungen</returns>
public async Task<int> RemoveExpiredBlocksAsync(DateTimeOffset date)
{
var blocks = (await _blockRepository.FindAsync(c => c.BlockedUntil.HasValue && c.BlockedUntil.Value <= date).ConfigureAwait(false)).ToList();
foreach (var appUserBlock in blocks)
{
_blockRepository.Remove(appUserBlock);
}
return blocks.Count;
}
/// <summary>
/// Gibt zurück ob ein App-User von einem anderen blockiert wurde
/// </summary>
/// <param name="blockingAppUserId">Id des App-Users der blockiert hat</param>
/// <param name="blockedAppUserId">Id des App-Users der blockiert wurde</param>
/// <returns>true wenn blockiert, false sonst</returns>
public async Task<bool> IsBlockedAsync(string blockingAppUserId, string blockedAppUserId)
{
return await _blockRepository.FirstOrDefaultAsync(c => c.BlockingAppUserId == blockingAppUserId && c.BlockedAppUserId == blockedAppUserId).ConfigureAwait(false) != null;
}
/// <summary>
/// Gibt eine Liste von blockierten App-Usern für einen App-User zurück
/// </summary>
/// <param name="query">Abfrageparameter</param>
/// <returns>Liste von gesperrten App-Usern</returns>
public async Task<(int total, List<AppUserBlockWithNames> list)> GetBlockedAppUsersAsync(BlockedQueryDto query)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(query.Location.Lng.Value, query.Location.Lat.Value));
//var baseQuery = _walkerRepository.QueryView(c => c.Deleted == false);
var baseQuery = _blockWithNamesRepository.QueryStoredProcedure("SELECT * FROM tv_AppUserBlocksWithNames({0})", c => c.Deleted == false && c.Locked == false, query.BlockingAppUserId);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (query.DynamicSortOrder.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(query.DynamicSortOrder.First().Property, (SortDirection)query.DynamicSortOrder.First().SortDirection); ;
foreach (var sortOrder in query.DynamicSortOrder)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, (SortDirection)sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.Location.Distance(location)).Skip(query.Skip).Take(query.Take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(query.Skip).Take(query.Take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt eine Liste von AppUsern zurück welche als geöscht markiert sind
/// </summary>
/// <returns>Liste AppUser</returns>
public async Task<List<AppUser>> GetDeletedAsync()
{
return (await Repository.FindAsync(c => c.Deleted).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt eine Liste von AppUsern zurück deren Location nicht valide ist
/// </summary>
/// <returns>Liste AppUser</returns>
public async Task<List<AppUser>> GetWithInvalidLocationAsync()
{
var appUsers = await Repository.FindAsync(c => (c.Location == null || c.Location.X == 0 && c.Location.Y == 0) && c.Deleted == false).ConfigureAwait(false);
return appUsers.ToList();
}
#endregion
}
}

View File

@ -0,0 +1,149 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.Common;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Appversionen realisiert
/// </summary>
public class AppVersionService : ServiceBase<AppVersion>, IAppVersionService
{
private readonly IRepository<AppVersionWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public AppVersionService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<AppVersionWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Appversionen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener AppVersionen</returns>
public IQueryable<AppVersion> Filter(string filter)
{
return Repository.Query(c => c.ReleaseTitleLocalized.Contains(filter) || c.Version.Contains(filter));
}
/// <summary>
/// Erstellen einer Appversion
/// </summary>
/// <returns>Listung</returns>
public AppVersion Create()
{
var item = new AppVersion()
{
Major = 1,
Minor = 0,
Version = "1.0",
IsCurrent = false,
IsMandatory = false,
Platform = Domain.Common.Platform.Android,
Build = null,
Revision = null,
ReleaseDate = DateTimeOffset.UtcNow,
LastUpdate = DateTimeOffset.UtcNow,
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Appversionen zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Appversionen</returns>
public async Task<List<AppVersion>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.ReleaseTitleLocalized.Contains(filter) || c.Version.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Appversionen mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste betroffener Appversionen</returns>
public IQueryable<AppVersionWithNames> FilterWithNames(string filter, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_AppVersionsWithNames({0},{1})", c => c.ReleaseTitle.Contains(filter) || c.Version.Contains(filter), language, fallback);
return baseQuery;
}
/// <summary>
/// Gibt zurück ob eine Versionsnummer für eine Plattform bereits existiert
/// </summary>
/// <param name="platform">Plattform</param>
/// <param name="version">Versionsnummer die geprüft werden soll</param>
/// <param name="ignoreId">ID die ignoriert werden soll</param>
/// <returns>true wenn eine existiert, false sonst</returns>
public async Task<bool> VersionExistsAsync(Platform platform, string version, int ignoreId)
{
var count = await Repository.CountAsync(c => c.Platform == platform && c.Version == version && c.Id != ignoreId).ConfigureAwait(false);
return count > 0;
}
/// <summary>
/// Gibt zurück ob eine Plattform eine aktuelle Version hat
/// </summary>
/// <param name="platform">Plattform</param>
/// <param name="ignoreId">ID die ignoriert werden soll</param>
/// <returns>true wenn eine existiert, false sonst</returns>
public async Task<bool> HasCurrentAsync(Platform platform, int ignoreId)
{
var count = await Repository.CountAsync(c => c.Platform == platform && c.IsCurrent && c.Id != ignoreId).ConfigureAwait(false);
return count > 0;
}
/// <summary>
/// Zurücksetzen der aktuellen Version einer Plattform. Es kann nur einen geben :)
/// </summary>
/// <param name="platform">Plattform</param>
/// <param name="userName">Benutzer der die Änderung vornimmt</param>
/// <returns>Task</returns>
public async Task ResetCurrentAsync(Platform platform, string userName)
{
var current = await Repository.FirstOrDefaultAsync(c => c.Platform == platform && c.IsCurrent).ConfigureAwait(false);
if (current != null)
{
current.IsCurrent = false;
await CommitAsync(userName).ConfigureAwait(false);
}
}
/// <summary>
/// Gibt die aktuelle Version einer Plattform zurück
/// </summary>
/// <param name="platform">Plattform</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Aktuelle Version oder null, wenn keine vorhanden</returns>
public async Task<AppVersionWithNames> GetCurrentwithNamesAsync(Platform platform, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_AppVersionsWithNames({0},{1})", c => c.Platform == platform && c.IsCurrent, language, fallback);
var current = await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
return current;
}
}
}

View File

@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Audits realisiert
/// </summary>
public class AuditService : ServiceBase<Audit>, IAuditService
{
private readonly IRepository<AuditWithNames> _auditWithNamesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public AuditService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_auditWithNamesRepository = unitOfWork.GetRepository<AuditWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von audits zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kunden</returns>
public IQueryable<Audit> Filter(string filter)
{
return Repository.Query(c => c.Table.Contains(filter) || c.UserName.Contains(filter));
}
/// <summary>
/// Gibt eine Liste von Audits zurück
/// </summary>
/// <param name="key">Key des Objektes</param>
/// <param name="table">Typ des Objektes (Table)</param>
/// <returns>Liste von audits</returns>
public async Task<List<Audit>> GetListAsync(string key, string table)
{
return (await Repository.FindAsync(c => c.Key == key && c.Table == table)).ToList();
}
/// <summary>
/// Gibt eine Liste von Audits zurück
/// </summary>
/// <param name="key">Key des Objektes</param>
/// <param name="table">Typ des Objektes (Table)</param>
/// <param name="action">
/// Durchgeführte Aktion</param>
/// <returns>Liste von audits</returns>
public async Task<List<Audit>> GetListAsync(string key, string table, AuditAction action)
{
return (await Repository.FindAsync(c => c.Key == key && c.Table == table && c.Action == action)).ToList();
}
/// <summary>
/// Gibt einen Audit-Eintrag zurück
/// </summary>
/// <param name="key">Key des Objektes</param>
/// <param name="table">Typ des Objektes (Table)</param>
/// <param name="action">
/// Durchgeführte Aktion</param>
/// <returns></returns>
public async Task<Audit> GetAsync(string key, string table, AuditAction action)
{
return await Repository.FirstOrDefaultAsync(c => c.Key == key && c.Table == table && c.Action == action);
}
/// <summary>
/// Gibt eine gefilterte Liste von audits zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kunden</returns>
public IQueryable<AuditWithNames> FilterWithNames(string filter)
{
return _auditWithNamesRepository.Query(c => c.Table.Contains(filter) || c.UserName.Contains(filter));
}
/// <summary>
/// Gibt eine Liste von Audits zurück
/// </summary>
/// <param name="key">Key des Objektes</param>
/// <param name="table">Typ des Objektes (Table)</param>
/// <returns>Liste von audits</returns>
public async Task<List<AuditWithNames>> GetListWithNamesAsync(string key, string table)
{
return (await _auditWithNamesRepository.FindAsync(c => c.Key == key && c.Table == table)).ToList();
}
/// <summary>
/// Gibt eine Liste von Audits zurück
/// </summary>
/// <param name="key">Key des Objektes</param>
/// <param name="table">Typ des Objektes (Table)</param>
/// <param name="action">
/// Durchgeführte Aktion</param>
/// <returns>Liste von audits</returns>
public async Task<List<AuditWithNames>> GetListWithNamesAsync(string key, string table, AuditAction action)
{
return (await _auditWithNamesRepository.FindAsync(c => c.Key == key && c.Table == table)).ToList();
}
/// <summary>
/// Gibt einen Audit-Eintrag zurück
/// </summary>
/// <param name="key">Key des Objektes</param>
/// <param name="table">Typ des Objektes (Table)</param>
/// <param name="action">
/// Durchgeführte Aktion</param>
/// <returns></returns>
public async Task<AuditWithNames> GetWithNamesAsync(string key, string table, AuditAction action)
{
return await _auditWithNamesRepository.FirstOrDefaultAsync(c => c.Key == key && c.Table == table && c.Action == action);
}
/// <summary>
/// Löschen von Audit-Einträgen
/// </summary>
/// <param name="key">Key des Objektes</param>
/// <param name="table">Typ des Objektes (Table)</param>
/// <returns>Anzahl gelöschte Daten</returns>
public async Task<int> DeleteAsync(string key, string table)
{
return await Repository.ExecuteRawSqlAsync("DELETE FROM Audits WHERE [Key] = @key AND [Table] = @table", new Dictionary<string, object> { { "@key", key }, { "@table", table } });
}
/// <summary>
/// Anonymisieren von Audit-Einträgen
/// </summary>
/// <param name="key">Key des Objektes</param>
/// <param name="table">Typ des Objektes (Table)</param>
/// <returns>Anzahl anonymisierte Daten</returns>
public async Task<int> AnonymizeAsync(string key, string table)
{
var anonymized = $"{{\"AnonymizedDate\":\"{DateTimeOffset.UtcNow:O}\"}}";
return await Repository.ExecuteRawSqlAsync("UPDATE Audits SET OldValues = @anonymized, NewValues = @anonymized WHERE [Key] = @key AND [Table] = @table", new Dictionary<string, object> { { "@anonymized", anonymized }, { "@key", key }, { "@table", table } });
}
/// <summary>
/// Loggen des Zugriffs eines Users auf einen Datensatz
/// </summary>
/// <param name="key">Key des Objektes</param>
/// <param name="table">Typ des Objektes (Table)</param>
/// <param name="userName">Benutzer der den Zugriff durchgeführt hat</param>
/// <returns>Task</returns>
public async Task LogAccessAsync(string key, string table, string userName)
{
var audit = new Audit()
{
Key = key,
Table = table,
UserName = userName,
Action = AuditAction.Access,
DateTime = DateTimeOffset.UtcNow,
KeyValues = string.Empty,
OldValues = string.Empty,
NewValues = string.Empty
};
Repository.Add(audit);
await CommitAsync(userName);
}
}
}

View File

@ -0,0 +1,379 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using Microsoft.EntityFrameworkCore;
using NetTopologySuite.Geometries;
using NetTopologySuite;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Bannern realisiert
/// </summary>
public class BannerService : ServiceBase<Banner>, IBannerService
{
private readonly IRepository<BannerWithNames> _namesRepository;
private readonly IRepository<BannerStatistic> _statisticsRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public BannerService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<BannerWithNames>();
_statisticsRepository = unitOfWork.GetRepository<BannerStatistic>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Bannern zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Banner</returns>
public IQueryable<Banner> Filter(string filter)
{
return Repository.Query(c => c.NameLocalized.Contains(filter));
}
/// <summary>
/// Erstellen eines Banners
/// </summary>
/// <returns>Banner</returns>
public Banner Create()
{
var item = new Banner()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Bannern zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Banner</returns>
public async Task<List<Banner>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.NameLocalized.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Bannern mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Banner</returns>
public IQueryable<BannerWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_BannersWithNames({0},{1})", c => c.Name.Contains(filter) || c.CustomerName.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != BannerStatus.Draft);
return baseQuery;
}
/// <summary>
/// Gibt eine gefilterte Liste von Bannern mit Namen für Kunden zurück in einer gewählten Sprache
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Banner</returns>
public IQueryable<BannerWithNames> FilterWithNamesCustomer(long customerId, string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_BannersWithNames({0},{1})", c => c.CustomerId == customerId && (c.Name.Contains(filter) || c.CustomerName.Contains(filter)), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != BannerStatus.Draft);
return baseQuery;
}
/// <summary>
/// Gibt die Anzahl von Bannern zurück
/// </summary>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Anzahl Banner</returns>
public async Task<int> CountAsync(bool includeDeleted, bool excludeDraft)
{
var query = Repository.Query(c => c.Id != "");
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if (excludeDraft)
query = query.Where(c => c.Status != BannerStatus.Draft);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl von Bannern in einem Status zurück
/// </summary>
/// <param name="status">Gesuchter Status</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Anzahl Banner</returns>
public async Task<int> CountAsync(BannerStatus status, bool includeDeleted)
{
var query = Repository.Query(c => c.Status == status);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt einen Banner für einen Artikel im Warenkorb zurück
/// </summary>
/// <param name="cartItemId">Id des Artikels im Warenkorb</param>
/// <returns>Banner oder null, wenn nicht gefunden</returns>
public async Task<Banner> GetByCartItemAsync(long cartItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.CartItemId == cartItemId && c.Status == BannerStatus.Draft);
}
/// <summary>
/// Gibt einen Banner für einen Artikel in einer Bestellung zurück
/// </summary>
/// <param name="orderItemId">Id des Artikels in der Bestellung</param>
/// <returns>Banner oder null, wenn nicht gefunden</returns>
public async Task<Banner> GetByOrderItemAsync(long orderItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.OrderItemId == orderItemId);
}
/// <summary>
/// Setzen des Status von Bannern nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <returns>Anzahl betroffener Banner</returns>
public async Task<int> SetStatusByOrderAsync(long orderId, BannerStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.Status = status;
}
return items.Count();
}
/// <summary>
/// Setzen des Bezahlstatus von Bannern nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <returns>Anzahl betroffener Banner</returns>
public async Task<int> SetPaymentStatusByOrderAsync(long orderId, PaymentStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.PaymentStatus = status;
}
return items.Count();
}
/// <summary>
/// Gibt einen Banner für eine Platzierung zurück
/// </summary>
/// <param name="bannerLocation">Platzierung des Banners</param>
/// <param name="currentBannerId">Optional: Id des Banners der gerade angezeigt wird</param>
/// <param name="country">Land</param>
/// <param name="state">Bundesland</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Zufälliger Banner wenn gefunden, null sonst</returns>
public async Task<BannerWithNames> GetBannerForLocationAsync(BannerLocation bannerLocation, string currentBannerId, string country, string state, double lat, double lng, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(country))
country = country.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(state))
state = state.ToUpperInvariant();
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_BannersWithNames({0},{1})", c => c.Deleted == false && c.Status == BannerStatus.Running && c.BannerLocation == bannerLocation &&
(c.GeoMode == GeoMode.None ||
(c.GeoMode == GeoMode.Country && c.Address_CountryCode == country) ||
(c.GeoMode == GeoMode.CountryAndState && c.Address_CountryCode == country && c.Address_State == state) ||
(c.GeoMode == GeoMode.Location && c.Location.IsWithinDistance(location, c.Radius * 1000))), language, fallback);
var list = await baseQuery.ToListAsync().ConfigureAwait(false);
if (list.Any())
{
if (list.Count > 1 && !string.IsNullOrWhiteSpace(currentBannerId))
{
//Wenn aktuell ein Banner angezeigt wird, dann diesen aus der Liste entfernen.
//Das geht nur wenn mindestens 2 Banner in der Liste sind.
list = list.Where(c => c.Id != currentBannerId).ToList();
}
var random = new Random();
int index = random.Next(list.Count);
return list[index];
}
return null;
}
/// <summary>
/// Fügt eine Anzahl von Views zu einem Banner hinzu und beendet die Laufzeit wenn die Budgetgrenze erreicht wurde
/// </summary>
/// <param name="id">Id des Banners</param>
/// <param name="viewCount">Anzahl Views</param>
/// <param name="appMode">AppMode</param>
/// <param name="language">Sprache</param>
/// <param name="country">Land</param>
/// <param name="state">Bundesland</param>
/// <returns>Anzahl Views gesamt</returns>
public async Task<int> AddViewsAsync(string id, int viewCount, AppMode appMode, string language, string country, string state)
{
var banner = await GetAsync(id);
if (banner != null)
{
AddStatistic(id, appMode, BannerStatisticType.View, viewCount, language, country, state);
banner.CurrentViews += viewCount;
var budgetUsed = (banner.CurrentViews * banner.PriceView) + (banner.CurrentClicks * banner.PriceClick);
banner.BudgetUsed = budgetUsed;
if (banner.BudgetUsed >= banner.Budget)
{
banner.Status = BannerStatus.Ended;
banner.EndDate = DateTimeOffset.UtcNow;
}
return banner.CurrentViews;
}
return 0;
}
/// <summary>
/// Fügt eine Anzahl von Klicks zu einem Banner hinzu und beendet die Laufzeit wenn die Budgetgrenze erreicht wurde
/// </summary>
/// <param name="id">Id des Banners</param>
/// <param name="clickCount">Anzahl Klicks</param>
/// <param name="appMode">AppMode</param>
/// <param name="language">Sprache</param>
/// <param name="country">Land</param>
/// <param name="state">Bundesland</param>
/// <returns>Anzahl Klicks gesamt</returns>
public async Task<int> AddClicksAsync(string id, int clickCount, AppMode appMode, string language, string country, string state)
{
var banner = await GetAsync(id);
if (banner != null)
{
AddStatistic(id, appMode, BannerStatisticType.Click, clickCount, language, country, state);
banner.CurrentClicks += clickCount;
var budgetUsed = (banner.CurrentViews * banner.PriceView) + (banner.CurrentClicks * banner.PriceClick);
banner.BudgetUsed = budgetUsed;
if (banner.BudgetUsed >= banner.Budget)
{
banner.Status = BannerStatus.Ended;
banner.EndDate = DateTimeOffset.UtcNow;
}
return banner.CurrentClicks;
}
return 0;
}
/// <summary>
/// Hinzufügen einer Statstik für einen Banner
/// </summary>
/// <param name="id">Id des Banners</param>
/// <param name="appMode">AppMode</param>
/// <param name="type">Klick oder View</param>
/// <param name="value">Anzahl Klicks oder Views</param>
/// <param name="language">Sprache</param>
/// <param name="country">Land</param>
/// <param name="state">Bundesland</param>
/// <returns>Task</returns>
public void AddStatistic(string id, AppMode appMode, BannerStatisticType type, int value, string language, string country, string state)
{
var statistic = new BannerStatistic
{
BannerId = id,
AppMode = appMode,
StatisticType = type,
Value = value,
Language = language,
CountryCode = country,
StateCode = state,
EventDate = DateTimeOffset.UtcNow
};
statistic.Year = statistic.EventDate.Year;
statistic.Month = statistic.EventDate.Month;
statistic.Day = statistic.EventDate.Day;
statistic.Hour = statistic.EventDate.Hour;
_statisticsRepository.Add(statistic);
}
/// <summary>
/// Starten von gebuchten Bannern wenn diese das Start-Datum erreicht haben
/// </summary>
/// <returns>Anzahl gestartete Banner</returns>
public async Task<int> StartBookedAsnyc()
{
var items = await Repository.FindAsync(c => c.Status == BannerStatus.Booked && c.Deleted == false && c.StartDate <= DateTimeOffset.UtcNow);
foreach (var item in items)
{
item.Status = BannerStatus.Running;
}
return items.Count();
}
/// <summary>
/// Stoppen von abgelaufenen Bannern wenn diese das Enddatum erreicht haben
/// </summary>
/// <returns>Anzahl gestoppte Banner</returns>
public async Task<int> StopRunningAsync()
{
var items = await Repository.FindAsync(c => c.Status == BannerStatus.Running && c.Deleted == false && c.BudgetUsed >= c.Budget);
foreach (var item in items)
{
item.EndDate = DateTimeOffset.UtcNow;
item.Status = BannerStatus.Ended;
}
return items.Count();
}
}
}

View File

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Branchen realisiert
/// </summary>
public class BranchService : ServiceBase<Branch>, IBranchService
{
private readonly IRepository<BranchWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public BranchService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<BranchWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Branchen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Branchen</returns>
public IQueryable<Branch> Filter(string filter)
{
return Repository.Query(c => c.Title.Contains(filter));
}
/// <summary>
/// Erstellen einer Branche
/// </summary>
/// <returns>Branche</returns>
public Branch Create()
{
var item = new Branch()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Branchen zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Branchen</returns>
public async Task<List<Branch>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Title.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Branchen mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Branchen</returns>
public IQueryable<BranchWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_BranchesWithNames({0},{1})", c => c.Title.Contains(filter) || c.Name.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine gefilterte Liste von Branchen mit Namen zurück in einer gewählten Sprache welche für eine Listung aktiviert sind
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Branchen</returns>
public IQueryable<BranchWithNames> FilterWithNamesListings(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_BranchesWithNames({0},{1})", c => c.ListingAllowed && (c.Title.Contains(filter) || c.Name.Contains(filter)), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine Liste von Branchen in einer gewählten Sprache zurück, welche online sind
/// </summary>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste von Branchen</returns>
public async Task<List<BranchWithNames>> GetOnlineAsync(string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_BranchesWithNames({0},{1})", c => c.ListingAllowed && c.OnlineStatus == OnlineStatus.Online && c.Deleted == false, language, fallback);
return await baseQuery.OrderBy(c => c.Order).ToListAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,552 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using gehGassi.Domain.Shop;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Warenkörben realisiert
/// </summary>
public class CartService : ServiceBase<Cart>, ICartService
{
private readonly ITaxRateService _taxRateService;
private readonly IShipmentCostService _shipmentCostService;
private readonly IOptions<ShopOptions> _shopOptions;
private readonly IRepository<CartWithNames> _namesRepository;
private readonly IRepository<CartItem> _itemRepository;
private readonly IRepository<CartItemWithNames> _itemNamesRepository;
private readonly IRepository<Product> _productRepository;
private readonly IRepository<Listing> _listingRepository;
private readonly IRepository<Banner> _bannerRepository;
private readonly IRepository<Advertisement> _advertisementRepository;
private readonly IRepository<Pin> _pinRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
/// <param name="taxRateService">Instanz eines ITaxRateService</param>
/// <param name="shipmentCostService">Instanz eines IShipmentCostService</param>
/// <param name="shopOptions">Instanz von ShopOptions</param>
public CartService(IUnitOfWork unitOfWork, ITaxRateService taxRateService, IShipmentCostService shipmentCostService, IOptions<ShopOptions> shopOptions) : base(unitOfWork)
{
_taxRateService = taxRateService;
_shipmentCostService = shipmentCostService;
_shopOptions = shopOptions;
_namesRepository = unitOfWork.GetRepository<CartWithNames>();
_itemRepository = unitOfWork.GetRepository<CartItem>();
_itemNamesRepository = unitOfWork.GetRepository<CartItemWithNames>();
_productRepository = unitOfWork.GetRepository<Product>();
_listingRepository = unitOfWork.GetRepository<Listing>();
_bannerRepository = unitOfWork.GetRepository<Banner>();
_advertisementRepository = unitOfWork.GetRepository<Advertisement>();
_pinRepository = unitOfWork.GetRepository<Pin>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Warenkörben zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Prache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>List betroffener Warenkörbe</returns>
public IQueryable<CartWithNames> Filter(string filter, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_CartsWithNames({0},{1})", c => c.CustomerName.Contains(filter) || c.AppUserName.Contains(filter), language, fallback);
return baseQuery;
}
/// <summary>
/// Erstellen eines Warenkorbes
/// </summary>
/// <param name="billingCountry">Rechnungsland</param>
/// <param name="deliveryCountry">Lieferland</param>
/// <returns>Warenkorb</returns>
public Cart Create(string billingCountry, string deliveryCountry)
{
var item = new Cart
{
Total = 0,
TotalGross = 0,
Shipment = 0,
ShipmentGross = 0,
Created = DateTimeOffset.UtcNow,
LastUpdate = DateTimeOffset.UtcNow,
BillingCountryCode = billingCountry,
DeliveryCountryCode = deliveryCountry
};
return item;
}
/// <summary>
/// Erstellen eines Warenkorbes für einen Kunden
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="billingCountry">Rechnungsland</param>
/// <param name="deliveryCountry">Lieferland</param>
/// <returns>Warenkorb</returns>
public Cart CreateForCustomer(long customerId, string billingCountry, string deliveryCountry)
{
var item = Create(billingCountry, deliveryCountry);
item.OrderSource = OrderSource.Customer;
item.CustomerId = customerId;
return item;
}
/// <summary>
/// Erstellen eines Warenkorbes für einen App-User
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="orderSource">Quelle</param>
/// <param name="billingCountry">Rechnungsland</param>
/// <param name="deliveryCountry">Lieferland</param>
/// <returns>Warenkorb</returns>
public Cart CreateForAppUser(string appUserId, OrderSource orderSource, string billingCountry, string deliveryCountry)
{
var item = Create(billingCountry, deliveryCountry);
item.OrderSource = orderSource;
item.AppUserId = appUserId;
return item;
}
/// <summary>
/// Gibt einen Warenkorb für einen Kunden zurück oder legt einen an
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="billingCountry">Rechnungsland</param>
/// <param name="deliveryCountry">Lieferland</param>
/// <param name="userName">Benutzer der den Warenkorb anlegt</param>
/// <returns>Warenkorb</returns>
public async Task<Cart> GetOrCreateCustomerAsync(long customerId, string billingCountry, string deliveryCountry, string userName)
{
var cart = await GetByCustomerAsync(customerId);
if (cart == null)
{
cart = CreateForCustomer(customerId, billingCountry, deliveryCountry);
Repository.Add(cart);
await CommitAsync(userName);
}
return cart;
}
/// <summary>
/// Gibt einen Warenkorb für einen App-User zurück oder legt einen an
/// </summary>
/// <param name="appUserId">Id des Hundebesitzers</param>
/// <param name="orderSource">Quelle</param>
/// <param name="billingCountry">Rechnungsland</param>
/// <param name="deliveryCountry">Lieferland</param>
/// <param name="userName">Benutzer der den Warenkorb anlegt</param>
/// <returns>Warenkorb</returns>
public async Task<Cart> GetOrCreateAppUserAsync(string appUserId, OrderSource orderSource, string billingCountry, string deliveryCountry, string userName)
{
var cart = await GetByAppUserAsync(appUserId);
if (cart == null)
{
cart = CreateForAppUser(appUserId, orderSource, billingCountry, deliveryCountry);
Repository.Add(cart);
await CommitAsync(userName);
}
return cart;
}
/// <summary>
/// Gibt einen Warenkorb für einen Kunden zurück
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <returns>Warenkorb oder null, wenn nicht gefunden</returns>
public async Task<Cart> GetByCustomerAsync(long customerId)
{
return await Repository.FirstOrDefaultAsync(c => c.CustomerId == customerId).ConfigureAwait(false);
}
/// <summary>
/// Gibt einen Warenkorb für einen App-Users zurück
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <returns>Warenkorb oder null, wenn nicht gefunden</returns>
public async Task<Cart> GetByAppUserAsync(string appUserId)
{
return await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
}
/// <summary>
/// Gibt einen Warenkorb für einen Kunden zurück
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="language">Gewünschte Prache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Warenkorb oder null, wenn nicht gefunden</returns>
public async Task<CartWithNames> GetByCustomerWithNamesAsync(long customerId, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_CartsWithNames({0},{1})", c => c.CustomerId == customerId, language, fallback);
return await baseQuery.FirstOrDefaultAsync();
}
/// <summary>
/// Gibt einen Warenkorb für einen App-User zurück
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="language">Gewünschte Prache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Warenkorb oder null, wenn nicht gefunden</returns>
public async Task<CartWithNames> GetByAppUserWithNamesAsync(string appUserId, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_CartsWithNames({0},{1})", c => c.AppUserId == appUserId, language, fallback);
return await baseQuery.FirstOrDefaultAsync();
}
/// <summary>
/// Anlegen eines Artikels für den Warenkorb
/// </summary>
/// <returns>Warenkorb Artikel</returns>
public CartItem CreateItem()
{
var item = new CartItem
{
Quantity = 0,
Total = 0,
TotalGross = 0,
Created = DateTimeOffset.UtcNow,
LastUpdate = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Hinzufügen eines Artikels zum Warenkorb
/// </summary>
/// <param name="item">Artikel</param>
public void AddItem(CartItem item)
{
_itemRepository.Add(item);
}
/// <summary>
/// Gibt einen Artikel im Warenkorb zurück
/// </summary>
/// <param name="cartId">Id des Warenkorbs</param>
/// <param name="productId">Id des Artikels</param>
/// <returns>Artikel im Warenkorb oder null, wenn nicht gefunden</returns>
public async Task<CartItem> GetItemAsync(long cartId, int productId)
{
return await _itemRepository.FirstOrDefaultAsync(c => c.CartId == cartId && c.ProductId == productId).ConfigureAwait(false);
}
/// <summary>
/// Gibt einen Artikel im Warenkorb zurück
/// </summary>
/// <param name="cartItemId">Id des Artikels im Warenkorbs</param>
/// <returns>Artikel im Warenkorb oder null, wenn nicht gefunden</returns>
public async Task<CartItem> GetItemAsync(long cartItemId)
{
return await _itemRepository.FirstOrDefaultAsync(c => c.Id == cartItemId).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Artikel im Warenkorb zurück
/// </summary>
/// <param name="cartId">Id des Warenkorbs</param>
/// <returns>Liste der Artikel im Warenkorb</returns>
public async Task<List<CartItem>> GetItemsAsync(long cartId)
{
return (await _itemRepository.FindAsync(c => c.CartId == cartId).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt eine Liste von Artikel im Warenkorb zurück
/// </summary>
/// <param name="cartId">Id des Warenkorbs</param>
/// <param name="language">Gewünschte Prache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste der Artikel im Warenkorb</returns>
public async Task<List<CartItemWithNames>> GetItemsWithNamesAsync(long cartId, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _itemNamesRepository.QueryStoredProcedure("SELECT * FROM tv_CartItemsWithNames({0},{1})", c => c.CartId == cartId, language, fallback);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Aktualisieren oder Anlegen eines Artikels im Warenkorb.
/// Achtung: Es wird keine Berechnung durchgeführt. Dazu den Warenkorb neu berechnen lassen!
/// </summary>
/// <param name="cartId">Id des Warenkorbs</param>
/// <param name="productId">Id des Artikels</param>
/// <param name="quantity">Stückzahl</param>
/// <param name="userName">Benutzer der den Warenkorb anlegt</param>
/// <returns>Warenkorb Artikel oder null, wenn Fehler</returns>
public async Task<CartItem> UpdateOrCreateItemAsync(long cartId, int productId, int quantity, string userName)
{
var cartItem = await GetItemAsync(cartId, productId);
if (cartItem == null)
{
cartItem = CreateItem();
cartItem.CartId = cartId;
cartItem.ProductId = productId;
cartItem.Quantity = quantity;
_itemRepository.Add(cartItem);
}
else
{
cartItem.Quantity += quantity;
cartItem.LastUpdate = DateTime.UtcNow;
}
await CommitAsync(userName);
return cartItem;
}
/// <summary>
/// Berechnen eines Warenkorbes
/// </summary>
/// <param name="cartId">Id des Warenkorbes</param>
/// <param name="orderSource">Quelle dser Bestellung - Typ des Benutzers</param>
/// <param name="targetCountry">Zielland für Versandkostenberechnung</param>
/// <param name="userName">Benutzer der die Kalkulation anstösst</param>
/// <returns>Berechneter Warenkorb</returns>
public async Task<Cart> CalculateAsync(long cartId, OrderSource orderSource, string targetCountry, string userName)
{
var cart = await Repository.GetAsync(cartId);
if (cart != null)
{
var total = 0M;
var totalForShipment = 0M; //Hier extra, da Produkte die Versandkostenfrei sind nicht mitgerechnet werden
var totalGross = 0M;
var quantity = 0;
var totalWeight = 0M; //Versandkostenfrei-Produkte werden hier nicht berücksichtigt
decimal additionalShipmentCosts = 0;
var taxRates = new Dictionary<decimal, decimal>();
var items = await GetItemsAsync(cart.Id);
foreach (var cartItem in items)
{
//Zuerst prüfen ob der Artikel noch interessiert
var product = await _productRepository.GetAsync(cartItem.ProductId);
if (product == null || product.Deleted || !product.AvailableFor(orderSource))
{
_itemRepository.Remove(cartItem);
}
else
{
total += cartItem.Total;
totalGross += cartItem.TotalGross;
quantity += cartItem.Quantity;
if (taxRates.ContainsKey(cartItem.TaxRate))
{
taxRates[cartItem.TaxRate] += cartItem.TaxRateValue;
}
else
{
taxRates.Add(cartItem.TaxRate, cartItem.TaxRateValue);
}
if (!product.IsFreeShipping)
{
totalWeight += (cartItem.Quantity * product.Weight);
totalForShipment += cartItem.Total;
additionalShipmentCosts += (cartItem.Quantity * product.AdditionalShippingCharge);
}
}
}
cart.Total = Math.Round(total, 2, MidpointRounding.AwayFromZero);
cart.TotalGross = Math.Round(totalGross, 2, MidpointRounding.AwayFromZero);
if (quantity > 0)
{
var shipmentCost = await _shipmentCostService.CalculateShipmentAsync(targetCountry, totalWeight, totalForShipment, additionalShipmentCosts);
var taxResult = await _taxRateService.CalculateShipmentGrossAsync(shipmentCost, _shopOptions.Value.SourceCountry, targetCountry);
cart.Shipment = shipmentCost;
cart.ShipmentGross = taxResult.Value;
if (taxRates.ContainsKey(taxResult.TaxRate))
{
taxRates[taxResult.TaxRate] += taxResult.TaxValue;
}
else
{
taxRates.Add(taxResult.TaxRate, taxResult.TaxValue);
}
}
cart.TaxRates = taxRates;
await CommitAsync(userName);
}
return cart;
}
/// <summary>
/// Leeren des Warenkorbs
/// </summary>
/// <param name="cartId">Id des Warenkorbes</param>
/// <param name="removeEntity">Wenn true, werden Entitäten wie Listings, Banner, Werbungen und Pins gelöscht, wenn false werden die Cart- und CartItem Id´s auf null gesetzt</param>
/// <param name="userName">Benutzer der die Aktion anstösst</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> ClearAsync(long cartId, bool removeEntity, string userName)
{
var cart = await Repository.GetAsync(cartId);
if (cart != null)
{
var items = await GetItemsAsync(cart.Id);
foreach (var cartItem in items)
{
if (cartItem.ProductType == ProductType.Listing)
{
var listing = await _listingRepository.GetAsync(cartItem.ItemId);
if (listing != null)
{
if(removeEntity)
_listingRepository.Remove(listing);
else
{
listing.CartId = null;
listing.CartItemId = null;
}
}
}
else if (cartItem.ProductType == ProductType.Banner)
{
var banner = await _bannerRepository.GetAsync(cartItem.ItemId);
if (banner != null)
{
if(removeEntity)
_bannerRepository.Remove(banner);
else
{
banner.CartId = null;
banner.CartItemId = null;
}
}
}
else if (cartItem.ProductType == ProductType.Advertisement)
{
var advertisement = await _advertisementRepository.GetAsync(cartItem.ItemId);
if (advertisement != null)
{
if(removeEntity)
_advertisementRepository.Remove(advertisement);
else
{
advertisement.CartId = null;
advertisement.CartItemId = null;
}
}
}
else if (cartItem.ProductType == ProductType.Pin)
{
var pin = await _pinRepository.GetAsync(cartItem.ItemId);
if (pin != null)
{
if(removeEntity)
_pinRepository.Remove(pin);
else
{
pin.CartId = null;
pin.CartItemId = null;
}
}
}
_itemRepository.Remove(cartItem);
}
cart.Total = 0;
cart.TotalGross = 0;
cart.Shipment = 0;
cart.ShipmentGross = 0;
cart.LastUpdate = DateTime.UtcNow;
await CommitAsync(userName);
}
return true;
}
/// <summary>
/// Löschen eines Artikels aus dem Warenkorb nach ArtikelId
/// </summary>
/// <param name="cartItemId">Id des Artikels</param>
/// <returns>Task</returns>
public async Task DeleteCartItemAsync(long cartItemId)
{
var cartItem = await _itemRepository.FirstOrDefaultAsync(c => c.Id == cartItemId).ConfigureAwait(false);
if (cartItem != null)
{
_itemRepository.Remove(cartItem);
}
}
/// <summary>
/// Löschen eines Artikels aus dem Warenkorb nach ArtikelId
/// </summary>
/// <param name="productId">Id des Artikels</param>
/// <returns>Task</returns>
public async Task DeleteCartItemByProductAsync(int productId)
{
var items = await _itemRepository.FindAsync(c => c.ProductId == productId).ConfigureAwait(false);
if (items.Any())
{
_itemRepository.RemoveRange(items);
}
}
/// <summary>
/// Löschen eines Warenkorbs nach Kunden
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <returns>Task</returns>
public async Task DeleteByCustomerAsync(long customerId)
{
var items = await Repository.FindAsync(c => c.CustomerId == customerId).ConfigureAwait(false);
var enumerable = items.ToList();
if (enumerable.Any())
{
Repository.RemoveRange(enumerable);
}
}
/// <summary>
/// Löschen eines Warenkorbs nach App-User
/// </summary>
/// <param name="appUserId">Id des Hundebesitzers</param>
/// <returns>Task</returns>
public async Task DeleteByAppUserAsync(string appUserId)
{
var items = await Repository.FindAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
var enumerable = items.ToList();
if (enumerable.Any())
{
Repository.RemoveRange(enumerable);
}
}
}
}

View File

@ -0,0 +1,235 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Pages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Coins;
using gehGassi.Domain.Common;
using gehGassi.Domain.Walks;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Coin-Konten, Transaktionen und Monats- und Jahresstatistiken realisiert
/// </summary>
public class CoinsService : ServiceBase<CoinsAccount>, ICoinsService
{
private readonly IRepository<CoinsTransaction> _transactionRepository;
private readonly IRepository<CoinsPerMonth> _perMonthRepository;
private readonly IRepository<CoinsPerMonthWithNames> _perMonthNamesRepository;
private readonly IRepository<CoinsPerYear> _perYearRepository;
private readonly IRepository<CoinsPerYearWithNames> _perYearNamesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public CoinsService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_transactionRepository = unitOfWork.GetRepository<CoinsTransaction>();
_perMonthRepository = unitOfWork.GetRepository<CoinsPerMonth>();
_perMonthNamesRepository = unitOfWork.GetRepository<CoinsPerMonthWithNames>();
_perYearRepository = unitOfWork.GetRepository<CoinsPerYear>();
_perYearNamesRepository = unitOfWork.GetRepository<CoinsPerYearWithNames>();
}
/// <summary>
/// Gibt einen Coin-Account zurück oder erstellt einen neuen wenn noch nicht vorhanden
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <returns>CoinAccount</returns>
public async Task<CoinsAccount> GetOrCreateAsync(string appUserId, AppUserType appUserType)
{
var account = await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.AppUserType == appUserType);
if (account == null)
{
account = new CoinsAccount()
{
Id = Guid.NewGuid().ToString("N"),
AppUserId = appUserId,
AppUserType = appUserType,
Balance = 0,
Created = DateTimeOffset.UtcNow,
UpdatedAt = DateTimeOffset.UtcNow
};
Repository.Add(account);
await CommitAsync("System");
}
return account;
}
/// <summary>
/// Einzahlen einer Coins-Summe auf das passende Konto eines AppUsers
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="ammount">Coins-Betrag der eingezahlt werden soll</param>
/// <param name="walkId">Optional: Id des Walks auf den sich die Transaktion bezieht</param>
/// <returns>Transaktion</returns>
public async Task<CoinsTransaction> DepositAsync(string appUserId, AppUserType appUserType, decimal ammount, string walkId)
{
var account = await GetOrCreateAsync(appUserId, appUserType);
//Transaktion anlegen
var transaction = new CoinsTransaction()
{
TransactionType = CoinsTransactionType.Deposit,
AccountId = account.Id,
Ammount = ammount,
WalkId = walkId,
Created = DateTimeOffset.UtcNow
};
_transactionRepository.Add(transaction);
account.Balance += ammount;
//Punkte für das Monatskonto
var coinsPerMonth = await GetOrCreateCoinsPerMonthAsync(appUserId, appUserType, DateTimeOffset.UtcNow);
coinsPerMonth.Balance += ammount;
coinsPerMonth.LastUpdate = DateTimeOffset.UtcNow;
//Punkte für das Jahreskonto
var coinsPerYear = await GetOrCreateCoinsPerYearAsync(appUserId, appUserType, DateTimeOffset.UtcNow);
coinsPerYear.Balance += ammount;
coinsPerYear.LastUpdate = DateTimeOffset.UtcNow;
await CommitAsync("System");
return transaction;
}
/// <summary>
/// Abbuchen einer Coins-Summe von dem passenden Konto eines AppUsers
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="ammount">Coins-Betrag der eingezahlt werden soll</param>
/// <returns>CoinsTransaction</returns>
public async Task<CoinsTransaction> WithdrawAsync(string appUserId, AppUserType appUserType, decimal ammount)
{
var account = await GetOrCreateAsync(appUserId, appUserType);
if (account.Balance < ammount)
return null;
//Transaktion anlegen
var transaction = new CoinsTransaction()
{
TransactionType = CoinsTransactionType.Withdrawal,
AccountId = account.Id,
Ammount = ammount,
WalkId = string.Empty,
Created = DateTimeOffset.UtcNow
};
_transactionRepository.Add(transaction);
account.Balance -= ammount;
await CommitAsync("System");
return transaction;
}
/// <summary>
/// Gibt den Coins-Stand eines AppUsers für einen Monat zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="date">Datum</param>
/// <returns>CoinsPerMonth</returns>
public async Task<CoinsPerMonth> GetOrCreateCoinsPerMonthAsync(string appUserId, AppUserType appUserType, DateTimeOffset date)
{
var coinsPerMonth = await _perMonthRepository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.AppUserType == appUserType && c.Year == date.Year && c.Month == date.Month);
if (coinsPerMonth == null)
{
coinsPerMonth = new CoinsPerMonth()
{
AppUserId = appUserId,
AppUserType = appUserType,
Year = date.Year,
Month = date.Month,
Balance = 0,
LastUpdate = DateTimeOffset.UtcNow
};
//Keine Wertung rückwirkend erstellen
if (date.Year == DateTimeOffset.UtcNow.Year && date.Month == DateTimeOffset.UtcNow.Month)
{
_perMonthRepository.Add(coinsPerMonth);
await CommitAsync("System");
}
}
return coinsPerMonth;
}
/// <summary>
/// Gibt den Coins-Stand eines AppUsers für ein Jahr zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="date">Datum</param>
/// <returns>CoinsPerYear</returns>
public async Task<CoinsPerYear> GetOrCreateCoinsPerYearAsync(string appUserId, AppUserType appUserType, DateTimeOffset date)
{
var coinsPerYear = await _perYearRepository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.AppUserType == appUserType && c.Year == date.Year);
if (coinsPerYear == null)
{
coinsPerYear = new CoinsPerYear()
{
AppUserId = appUserId,
AppUserType = appUserType,
Year = date.Year,
Balance = 0,
LastUpdate = DateTimeOffset.UtcNow
};
//Keine Wertung rückwirkend erstellen
if (date.Year == DateTimeOffset.UtcNow.Year)
{
_perYearRepository.Add(coinsPerYear);
await CommitAsync("System");
}
}
return coinsPerYear;
}
/// <summary>
/// Gibt die Top X der Coins je Monat für ein Jahr und Monat zurück.
/// Sortiert nach Coins absteigend und bei Gleichstand nach LastUpdate aufsteigend
/// </summary>
/// <param name="count">Anzalh Datensätze</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="year">Jahr</param>
/// <param name="month">Monat</param>
/// <param name="minBalance">Mindest-Wert der erreicht werden musste</param>
/// <returns>Liste CoinsPerMonthWithNames</returns>
public async Task<List<CoinsPerMonthWithNames>> GetMonthlyTopAsync(int count, AppUserType appUserType, int year, int month, decimal minBalance)
{
var coins = await _perMonthNamesRepository.QueryView(c => c.AppUserType == appUserType && c.Year == year && c.Month == month && c.Balance >= minBalance).OrderByDescending(c => c.Balance).ThenBy(c => c.LastUpdate).Take(count).ToListAsync();
return coins;
}
/// <summary>
/// Gibt die Top X der Coins je Jahr zurück.
/// Sortiert nach Coins absteigend und bei Gleichstand nach LastUpdate aufsteigend
/// </summary>
/// <param name="count">Anzalh Datensätze</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="year">Jahr</param>
/// <param name="minBalance">Mindest-Wert der erreicht werden musste</param>
/// <returns>Liste CoinsPerMonthWithNames</returns>
public async Task<List<CoinsPerYearWithNames>> GetYearlyTopAsync(int count, AppUserType appUserType, int year, decimal minBalance)
{
var coins = await _perYearNamesRepository.QueryView(c => c.AppUserType == appUserType && c.Year == year && c.Balance >= minBalance).OrderByDescending(c => c.Balance).ThenBy(c => c.LastUpdate).Take(count).ToListAsync();
return coins;
}
}
}

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Shop;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Gutschriften ermöglicht
/// </summary>
public class CreditNoteService : ServiceBase<CreditNote>, ICreditNoteService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public CreditNoteService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Erstellen einer Gutschrift
/// </summary>
/// <returns>Gutschrift</returns>
public CreditNote Create()
{
var item = new CreditNote()
{
UniqueId = Guid.NewGuid(),
Created = DateTime.UtcNow
};
return item;
}
/// <summary>
/// Erstellen einer Gutschrift
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="invoiceNumber">Rechnungsnummer</param>
/// <returns>Gutschrift</returns>
public async Task<CreditNote> CreateAsync(long orderId, string invoiceNumber)
{
var item = Create();
item.OrderId = orderId;
item.InvoiceNumber = invoiceNumber;
item.Number = await GetNextNumberAsync();
item.Counter = await GetNextCounterAsync();
return item;
}
/// <summary>
/// Gibt eine Gutschrift basierend auf der Bestellung zurück
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <returns>Gutschrift</returns>
public async Task<CreditNote> GetByOrderAsync(long orderId)
{
var item = await Repository.FirstOrDefaultAsync(c => c.OrderId == orderId).ConfigureAwait(false);
return item;
}
/// <summary>
/// Gibt den nächsten freien Gutschrift-spezifischen Zähler zurück
/// </summary>
/// <returns>Nächste freie Nummer</returns>
public async Task<long> GetNextCounterAsync()
{
long number = 1;
var last = await Repository.Query(c => c.Id > 0).OrderByDescending(c => c.Counter).FirstOrDefaultAsync().ConfigureAwait(false);
if (last != null)
number = last.Counter + 1;
return number;
}
/// <summary>
/// Gibt die nächste freie Nummer für eine Gutschrift als Gutschriftennummer zurück
/// </summary>
/// <returns>Gutschrift-Nummer</returns>
public async Task<string> GetNextNumberAsync()
{
long number = 1;
var last = await Repository.Query(c => c.Id > 0).OrderByDescending(c => c.Id).FirstOrDefaultAsync().ConfigureAwait(false);
if (last != null)
number = last.Counter + 1;
return $"{DateTime.UtcNow.Year}{DateTime.UtcNow.Month}-{number:000000}";
}
}
}

View File

@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Customers;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Kunden realisiert
/// </summary>
public class CustomerService : ServiceBase<Customer>, ICustomerService
{
private readonly IRepository<CustomerWithNames> _customerWithNamesRepository;
public CustomerService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_customerWithNamesRepository = unitOfWork.GetRepository<CustomerWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Kunden zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kunden</returns>
public IQueryable<Customer> Filter(string filter)
{
return Repository.Query(c => c.Name.Contains(filter) || c.Number.Contains(filter) || c.Comment.Contains(filter));
}
/// <summary>
/// Erstellen eines Kunden
/// </summary>
/// <returns>Kunde</returns>
public Customer Create()
{
var item = new Customer
{
Contact = new Contact(),
Address = new Address(),
UniqueId = Guid.NewGuid()
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Kunden zurück. Sucht nur im Namen
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kunden</returns>
public async Task<List<Customer>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Name.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Kunden zurück die ein Mangopay Konto haben. Sucht nur im Namen
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kunden</returns>
public async Task<List<Customer>> SearchWithMangopayAccountAsync(string filter)
{
return (await Repository.FindAsync(c => c.Name.Contains(filter) && c.MangopayIdCreated)).ToList();
}
/// <summary>
/// Gibt ein Kunden zurück
/// </summary>
/// <param name="uniqueId">UniqueId des Kunden</param>
/// <returns>Betroffener Kunde</returns>
public async Task<Customer> GetByUniqueIdAsync(Guid uniqueId)
{
return await Repository.FirstOrDefaultAsync(c => c.UniqueId == uniqueId).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine gefilterte Liste von Kunden mit Namen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>List betroffener Kunden</returns>
public IQueryable<CustomerWithNames> FilterWithNames(string filter, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _customerWithNamesRepository.QueryStoredProcedure("SELECT * FROM tv_CustomersWithNames({0},{1})", c => c.Name.Contains(filter) || c.Number.Contains(filter), language, fallback);
return baseQuery;
}
/// <summary>
/// Gibt die nächste freie Nummer für einen Kunden zurück
/// </summary>
/// <returns>Kunden-Nummer</returns>
public string GetNextNumber()
{
long number = 1;
var last = Repository.Query(c => c.Id != -1).OrderByDescending(c => c.Id).FirstOrDefault();
if (last != null)
number = last.Id + 1;
return $"K-{DateTime.UtcNow.Year:0000}{DateTime.UtcNow.Month:00}-{number:00000}";
}
/// <summary>
/// Zurücksetzen des Kunden-Typs auf null
/// </summary>
/// <param name="typeId">Id des Kunden-Typs</param>
/// <returns>Anzahl zurückgesetzte Kunden</returns>
public async Task<int> ResetTypeAsync(long typeId)
{
var items = (await Repository.FindAsync(c => c.TypeId == typeId).ConfigureAwait(false)).ToList();
foreach (var item in items)
{
item.TypeId = null;
}
return items.Count;
}
/// <summary>
/// Zurücksetzen der Branche auf null
/// </summary>
/// <param name="branchId">Id der Branche</param>
/// <returns>Anzahl zurückgesetzte Kunden</returns>
public async Task<int> ResetBranchAsync(string branchId)
{
var items = (await Repository.FindAsync(c => c.BranchId == branchId).ConfigureAwait(false)).ToList();
foreach (var item in items)
{
item.TypeId = null;
}
return items.Count;
}
/// <summary>
/// Zurücksetzen der Branchen-Listungen
/// </summary>
/// <param name="branchId">Id der Branche</param>
/// <returns>Anzahl zurückgesetzte Kunden</returns>
public async Task<int> ResetBranchListingsAsync(string branchId)
{
var items = (await Repository.FindAsync(c => c.BranchesJson.Contains(branchId)).ConfigureAwait(false)).ToList();
foreach (var customer in items)
{
var branches = customer.BranchesJson.Split(',').ToList();
branches.Remove(branchId);
customer.BranchesJson = string.Join(',', branches);
}
return items.Count;
}
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Customers;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Kunden-Typen realisiert
/// </summary>
public class CustomerTypeService : ServiceBase<CustomerType>, ICustomerTypeService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public CustomerTypeService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Gibt eine gefilterte Liste von Kunden-Typen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kunden-Typen</returns>
public IQueryable<CustomerType> Filter(string filter)
{
return Repository.Query(c => c.Name.Contains(filter));
}
/// <summary>
/// Erstellen eines Kunden-Typen
/// </summary>
/// <returns>Kunden-Type</returns>
public CustomerType Create()
{
var item = new CustomerType
{
Created = DateTimeOffset.UtcNow,
UniqueId = Guid.NewGuid()
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Kunden-Typen zurück. Sucht nur im Namen
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kunden-Typen</returns>
public async Task<List<CustomerType>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Name.Contains(filter)).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt ein Kunden-Typen zurück
/// </summary>
/// <param name="uniqueId">UniqueId des Kunden-Typen</param>
/// <returns>Betroffener Kunden-Typ</returns>
public async Task<CustomerType> GetByUniqueIdAsync(Guid uniqueId)
{
return await Repository.FirstOrDefaultAsync(c => c.UniqueId == uniqueId).ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,110 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Favourites;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
using gehGassi.Domain.Devices;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Devices der App-User ermöglicht
/// </summary>
public class DeviceService : ServiceBase<Device>, IDeviceService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public DeviceService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Anlegen oder Aktualisieren eines Devices
/// </summary>
/// <param name="appUserId">ID des AppUsers</param>
/// <param name="installationId">eindeutige InstallationsID</param>
/// <param name="platform">Platform (OS)</param>
/// <param name="notificationPlatform">Platform (PNS)</param>
/// <param name="channel">Token des Users</param>
/// <param name="language">Sprache die am Gerät eingestellt ist</param>
/// <param name="suppresCommit">Soll Commit untertrückt werden</param>
/// <returns>Device</returns>
public async Task<Device> CreateOrUpdateAsync(string appUserId, string installationId, Platform platform, NotificationPlatform notificationPlatform, string channel, string language, bool suppresCommit)
{
var device = await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.InstallationId == installationId);
if (device != null)
{
device.Platform = platform;
device.NotificationPlatform = notificationPlatform;
device.Channel = channel;
device.Language = language;
device.LastUpdate = DateTimeOffset.UtcNow;
}
else
{
device = new Device
{
AppUserId = appUserId,
InstallationId = installationId,
Platform = platform,
NotificationPlatform = notificationPlatform,
Channel = channel,
Language = language,
Created = DateTimeOffset.UtcNow
};
Repository.Add(device);
}
if (!suppresCommit)
await CommitAsync("System");
return device;
}
/// <summary>
/// Löschen eines Devices eines Benutzers mit einer InstallationsId
/// </summary>
/// <param name="appUserId"></param>
/// <param name="installationId"></param>
/// <param name="suppresCommit">Soll Commit untertrückt werden</param>
/// <returns></returns>
public async Task<bool> DeleteAsync(string appUserId, string installationId, bool suppresCommit)
{
var device = await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.InstallationId == installationId);
if (device != null)
{
Remove(device);
if (!suppresCommit)
await CommitAsync("System");
return true;
}
return false;
}
/// <summary>
/// Gibt eine Liste aller Devices eines App-Users zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>Liste von Devices</returns>
public async Task<List<Device>> GetAllAsync(string appUserId)
{
return (await Repository.FindAsync(c => c.AppUserId == appUserId).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt ein Device für einen App-User zurück
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="installationId">InstallationsId des Devices</param>
/// <returns>Device oder null, wenn nicht gefunden</returns>
public async Task<Device> GetAsycn(string appUserId, string installationId)
{
return await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.InstallationId == installationId);
}
}
}

View File

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.News;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Hunderassen realisiert
/// </summary>
public class DogRaceService : ServiceBase<DogRace>, IDogRaceService
{
private readonly IRepository<DogRaceWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public DogRaceService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<DogRaceWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Hunderassen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Hunderassen</returns>
public IQueryable<DogRace> Filter(string filter)
{
return Repository.Query(c => c.NameLocalized.Contains(filter));
}
/// <summary>
/// Erstellen einer Hunderasse
/// </summary>
/// <returns>Hunderasse</returns>
public DogRace Create()
{
var item = new DogRace()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Hunderassen zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Hunderassen</returns>
public async Task<List<DogRace>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.NameLocalized.Contains(filter) && c.Deleted == false)).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Hunderassen zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>List betroffener Hunderassen</returns>
public async Task<List<DogRaceWithNames>> SearchWithNamesAsync(string filter, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_DogRacesWithNames({0},{1})", c => c.Name.Contains(filter) && c.Deleted == false, language, fallback);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine gefilterte Liste von Hunderassen mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Hunderassen</returns>
public IQueryable<DogRaceWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_DogRacesWithNames({0},{1})", c => c.Name.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine Liste von Hunderassen zurück, welche nach dem glieferten Datum geändert wurden.
/// Wird kein Datum angegeben, werden alle zurückgegeben.
/// </summary>
/// <param name="lastUpdate">Letztes Update oder null wenn noch keines</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste von Hunderassen</returns>
public async Task<List<DogRaceWithNames>> GetForSyncAsync(DateTimeOffset? lastUpdate, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
IQueryable<DogRaceWithNames> baseQuery;
if (lastUpdate == null)
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_DogRacesWithNames({0},{1})", null, language, fallback);
else
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_DogRacesWithNames({0},{1})", c => c.UpdatedAt > lastUpdate, language, fallback);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Hunderasse mit Namen aufgelöst zurück
/// </summary>
/// <param name="id">Id der Hunderasse</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Hunderasse oder null, wenn nicht gefunden</returns>
public async Task<DogRaceWithNames> GetWithNamesAsync(string id, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_DogRacesWithNames({0},{1})", c => c.Id == id && c.Deleted == false, language, fallback);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Dogs;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Hunden realisiert
/// </summary>
public class DogService : ServiceBase<Dog>, IDogService
{
private readonly IRepository<DogWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public DogService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<DogWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Hunden zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Hunde</returns>
public IQueryable<Dog> Filter(string filter)
{
return Repository.Query(c => c.Name.Contains(filter) || c.Number.Contains(filter) || c.Comment.Contains(filter));
}
/// <summary>
/// Erstellen eines Hunden
/// </summary>
/// <returns>Kunde</returns>
public Dog Create()
{
var item = new Dog()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Hunden zurück. Sucht nur im Namen
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Hunde</returns>
public async Task<List<Dog>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Name.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Hunden mit Namen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>List betroffener Hunde</returns>
public IQueryable<DogWithNames> FilterWithNames(string filter, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_DogsWithNames({0},{1})", c => c.Deleted == false && (c.Name.Contains(filter) || c.Number.Contains(filter)), language, fallback);
return baseQuery;
}
/// <summary>
/// Gibt die nächste freie Nummer für einen Hund zurück
/// </summary>
/// <returns>Hunde-Nummer</returns>
public async Task<string> GetNextNumberAsync()
{
long number = 1;
var total = await Repository.CountAsync().ConfigureAwait(false);
number = total + 1;
return $"D-{DateTime.UtcNow.Year:00}{DateTime.UtcNow.Month:00}-{number:000000000}";
}
/// <summary>
/// Zurücksetzen der Rasse auf null
/// </summary>
/// <param name="dogRaceId">Id der Hunderasse</param>
/// <returns>Anzahl zurückgesetzte Hunde</returns>
public async Task<int> ResetDogRaceAsync(string dogRaceId)
{
var items = (await Repository.FindAsync(c => c.DogRaceId == dogRaceId).ConfigureAwait(false)).ToList();
foreach (var item in items)
{
item.DogRaceId = null;
}
return items.Count;
}
/// <summary>
/// Gibt eine Liste der Hunde eines Benutzers zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der Hunde</returns>
public async Task<List<Dog>> GetDogsAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.AppUserId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt einen hunde mit Namen aufgelöst zurück
/// </summary>
/// <param name="dogId">Id des Hundes</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte auch abgefragt werden - für historische Korrektheit</param>
/// <returns>Hund oder null, wenn nicht gefunden</returns>
public async Task<DogWithNames> GetWithNamesAsync(string dogId, string language, string fallback,bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
if (includeDeleted == false)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_DogsWithNames({0},{1})", c => c.Deleted == false && c.Id == dogId, language, fallback);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
else
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_DogsWithNames({0},{1})", c => c.Id == dogId, language, fallback);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
}
/// <summary>
/// Gibt alle Hunde mit Namen aufgelöst für einen hundebesitzer zurück
/// </summary>
/// <param name="ownerId">Id des Hundebesitzers</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Hund oder null, wenn nicht gefunden</returns>
public async Task<List<DogWithNames>> GetWithNamesOwnerAsync(string ownerId, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_DogsWithNames({0},{1})", c => c.Deleted == false && c.AppUserId == ownerId, language, fallback);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,103 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Text.Json;
using gehGassi.Core.Interfaces;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
namespace gehGassi.Core.Services
{
/// <summary>
/// Schnittsellenbeschreibung für einen Service der die Validierung von E-Mail Adressen ermöglicht
/// </summary>
public class EmailValidationService : IEmailValidationService
{
private readonly IDistributedCache _memoryCache;
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly IOptions<EmailValidationOptions> _emailValidationOptions;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="memoryCache">Instanz eines IDistributedCache</param>
/// <param name="hostingEnvironment">Instanz eines IWebHostEnvironment</param>
/// <param name="emailValidationOptions">Instanz eines EmailValidationOptions</param>
public EmailValidationService(IDistributedCache memoryCache, IWebHostEnvironment hostingEnvironment, IOptions<EmailValidationOptions> emailValidationOptions)
{
_memoryCache = memoryCache;
_hostingEnvironment = hostingEnvironment;
_emailValidationOptions = emailValidationOptions;
Init();
}
/// <summary>
/// Initialisiert den Service
/// </summary>
private void Init()
{
var path = Path.Combine(_hostingEnvironment.WebRootPath, _emailValidationOptions.Value.File);
if (File.Exists(path))
{
var fakeMailList = new List<string>();
var allDomains = File.ReadAllLines(path, Encoding.Default);
fakeMailList.AddRange(allDomains);
//_memoryCache.Set("FakeMails", fakeMailList);
_memoryCache.SetString("FakeMails", JsonSerializer.Serialize(fakeMailList));
}
}
/// <summary>
/// Gibt zurück ob es sich bei der E-Mail Adresse um eine Wegwerfadresse handelt
/// </summary>
/// <param name="emailAddress">E-Mail Adresse die geprüft werden soll</param>
/// <returns>true wenn Wegwerf-Email, false sonst</returns>
public bool IsDisposable(string emailAddress)
{
if (string.IsNullOrWhiteSpace(emailAddress))
return false;
var fakeMailList = new List<string>();
var json = _memoryCache.GetString("FakeMails");
if (string.IsNullOrWhiteSpace(json))
{
Init();
fakeMailList = JsonSerializer.Deserialize<List<string>>(_memoryCache.GetString("FakeMails"));
}
else
{
fakeMailList = JsonSerializer.Deserialize<List<string>>(json);
}
if (fakeMailList != null && fakeMailList.Any())
{
var mailAddress = new MailAddress(emailAddress);
return fakeMailList?.FirstOrDefault(c => c == mailAddress.Host.ToLower()) != null;
}
return false;
}
}
/// <summary>
/// Einstellungen für den Email-Valiiderungs-Service
/// </summary>
public class EmailValidationOptions
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
public EmailValidationOptions()
{
}
/// <summary>
/// Datei die verwendet werden soll
/// </summary>
public string File { get; set; }
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Linq;
using System.Linq.Expressions;
namespace gehGassi.Core.Services
{
public static class Extensions
{
/// <summary>
/// Linq Erweiterung für Suche in einer Liste. Es muss mind. ein Element gefunden werden
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="predicates"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static IQueryable<T> WhereAny<T>(this IQueryable<T> source, params Expression<Func<T, bool>>[] predicates)
{
if (source == null) throw new ArgumentNullException("source");
if (predicates == null) throw new ArgumentNullException("predicates");
if (predicates.Length == 0) return source.Where(x => false); // no matches!
if (predicates.Length == 1) return source.Where(predicates[0]); // simple
var param = Expression.Parameter(typeof(T), "x");
Expression body = Expression.Invoke(predicates[0], param);
for (int i = 1; i < predicates.Length; i++)
{
body = Expression.OrElse(body, Expression.Invoke(predicates[i], param));
}
var lambda = Expression.Lambda<Func<T, bool>>(body, param);
return source.Where(lambda);
}
/// <summary>
/// Konvertiert ein Objekt in einen Byte-Array mit Hilfe von JsonSerializer
/// </summary>
/// <param name="obj">Objekt das in einen byte-Array konvertiert werden soll</param>
/// <returns>bytearrray oder null, wenn nicht möglich</returns>
public static byte[] ToByteArray(this object obj)
{
if (obj == null)
{
return null;
}
var jsonString = System.Text.Json.JsonSerializer.Serialize(obj);
var byteArray = System.Text.Encoding.UTF8.GetBytes(jsonString);
return byteArray;
}
/// <summary>
/// Konvertiert einen Byte-Array in ein Objekt mit Hilfe von JsonSerializer
/// </summary>
/// <typeparam name="T">Typ</typeparam>
/// <param name="byteArray">Array der in Objekt konvertiert werden soll</param>
/// <returns>Objekt oder null, wenn nicht möglich</returns>
public static T FromByteArray<T>(this byte[] byteArray) where T : class
{
if (byteArray == null)
{
return default(T);
}
var obj = System.Text.Json.JsonSerializer.Deserialize<T>(byteArray);
return obj;
}
}
}

View File

@ -0,0 +1,167 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.News;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
using gehGassi.Domain.Pages;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von FAQs realisiert
/// </summary>
public class FaqService : ServiceBase<Faq>, IFaqService
{
private readonly IRepository<FaqWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public FaqService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<FaqWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von FAQs zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener FAQs</returns>
public IQueryable<Faq> Filter(string filter)
{
return Repository.Query(c => c.Title.Contains(filter));
}
/// <summary>
/// Erstellen einer FAQ
/// </summary>
/// <returns>Faq</returns>
public Faq Create()
{
var item = new Faq()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von FAQs zurück. Sucht nur im Titel
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener FAQs</returns>
public async Task<List<Faq>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Title.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von FAQs mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener FAQs</returns>
public IQueryable<FaqWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_FaqsWithNames({0},{1})", c => c.Title.Contains(filter) || c.Question.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine Liste von FAQs zurück, welche nach dem glieferten Datum geändert wurden.
/// Wird kein Datum angegeben, werden alle zurückgegeben.
/// Es werden alle Kategorien zurückgegeben - egal ob on- oder offline.
/// </summary>
/// <param name="lastUpdate">Letztes Update oder null wenn noch keines</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste von FAQs</returns>
public async Task<List<FaqWithNames>> GetForSyncAsync(DateTimeOffset? lastUpdate, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
IQueryable<FaqWithNames> baseQuery;
if (lastUpdate == null)
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_FaqsWithNames({0},{1})", null, language, fallback);
else
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_FaqsWithNames({0},{1})", c => c.UpdatedAt > lastUpdate, language, fallback);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Erstellt das FAQ-Html für die Integration in die FAQ-Seite.
/// Es werden alle FAQs genommen, welche Online sind und nicht gelöscht wurden
/// </summary>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="type">Typ</param>
/// <returns>string - HTML</returns>
public async Task<string> GenerateHtmlForPageAsync(string language, string fallback, FaqType type)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var faqsQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_FaqsWithNames({0},{1})", c => c.Deleted == false && c.OnlineStatus == OnlineStatus.Online, language, fallback);
if(type != FaqType.Both)
faqsQuery = faqsQuery.Where(c => c.Type == type || c.Type == FaqType.Both);
var faqs = await faqsQuery.OrderBy(c => c.Order).ThenBy(c => c.Question).ToListAsync().ConfigureAwait(false);
if (faqs.Any())
{
var result = $"<div class=\"accordion\" id=\"accordionFaq\">";
foreach (var faq in faqs)
{
result += $"<div class=\"accordion-item\">" +
$"<h2 class=\"accordion-header\" id=\"heading_{faq.Id}\">" +
$"<button class=\"accordion-button collapsed\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#collapse_{faq.Id}\" aria-expanded=\"false\" aria-controls=\"collapse_{faq.Id}\">" +
$"{faq.Question}" +
$"</button>" +
$"</h2>" +
$"<div id=\"collapse_{faq.Id}\" class=\"accordion-collapse collapse\" aria-labelledby=\"heading_{faq.Id}\" data-bs-parent=\"#accordionFaq\">" +
$"<div class=\"accordion-body\">" +
$"{faq.Answer}" +
$"</div>" +
$"</div>" +
$"</div>";
}
result += "</div>";
return result;
}
return string.Empty;
}
/// <summary>
/// Gibt eine Liste aller FAQ mit einem speziellen Typ zurück
/// </summary>
/// <param name="type">Typ</param>
/// <returns>Liste FAQ</returns>
public async Task<List<Faq>> GetAllAsync(FaqType type)
{
var query = Repository.Query(c => c.OnlineStatus == OnlineStatus.Online && c.Deleted == false);
if(type != FaqType.Both)
query = query.Where(c => c.Type == type || c.Type == FaqType.Both);
var list = await query.OrderBy(c => c.Order).ToListAsync().ConfigureAwait(false);
return list;
}
}
}

View File

@ -0,0 +1,112 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Ratings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Favourites;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Favoriten ermöglicht
/// </summary>
public class FavouritesService : ServiceBase<Favourite>, IFavouriteService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public FavouritesService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Erstellen eines Favoriten
/// </summary>
/// <returns>Rating</returns>
public Favourite Create()
{
var favourite = new Favourite()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return favourite;
}
/// <summary>
/// Erstellen eines Favoriten
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="key">Id des verknüpften Objekts</param>
/// <param name="table">Typ des verknüpften Objekts</param>
/// <returns>Rating</returns>
public Favourite Create(string appUserId, string key, string table)
{
var favourite = new Favourite()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow,
AppUserId = appUserId,
Key = key,
Table = table
};
return favourite;
}
/// <summary>
/// Gibt eine Liste der Favoriten eines Benutzers zurück. Kann Owner und Walker sein
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der öffentlichen Anfragen</returns>
public async Task<List<Favourite>> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.AppUserId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste der Favoriten eines Benutzers zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>Liste der Favoriten</returns>
public async Task<List<Favourite>> GetForAppUserAsync(string appUserId)
{
return (await Repository.FindAsync(c => c.AppUserId == appUserId && c.Deleted == false).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt eine Liste der Favoriten eines Benutzers zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="table">Typ des Objektes das gesucht wird</param>
/// <returns>Liste der Favoriten</returns>
public async Task<List<Favourite>> GetForAppUserAsync(string appUserId, string table)
{
return (await Repository.FindAsync(c => c.AppUserId == appUserId && c.Table == table && c.Deleted == false).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Alle Favoriten eines AppUsers löschen
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>Anzahl gelöscht</returns>
public async Task<int> DeleteAllForAppUserAsync(string appUserId)
{
var favourites = await Repository.FindAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
foreach (var favourite in favourites)
{
Repository.Remove(favourite);
}
return favourites.Count();
}
}
}

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Fido2NetLib;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Users;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Fido-Credentials realisiert
/// </summary>
[ExcludeFromCodeCoverage]
public class FidoService : ServiceBase<FidoStoredCredential>, IFidoService
{
private readonly IUserService _userService;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
/// <param name="userService">Instanz eines IUserService</param>
public FidoService(IUnitOfWork unitOfWork, IUserService userService) : base(unitOfWork)
{
_userService = userService;
}
/// <summary>
/// Gibt eine gefilterte Liste von Fido-Credentials zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Fido-Credentials</returns>
public IQueryable<FidoStoredCredential> Filter(string filter)
{
return Repository.Query(c => c.Name.Contains(filter));
}
/// <summary>
/// Gibt eine gefilterte Liste von Fido-Credentials für einen Benutzer zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="username">Benutzername</param>
/// <returns>List betroffener Fido-Credentials</returns>
public IQueryable<FidoStoredCredential> Filter(string filter, string username)
{
return Repository.Query(c => c.Name.Contains(filter) && c.Username == username);
}
/// <summary>
/// Erstellen eines Fido-Credentials
/// </summary>
/// <returns>Fido-Credential</returns>
public FidoStoredCredential Create()
{
var item = new FidoStoredCredential
{
RegDate = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt alle Credentials für einen Benutzer zurück
/// </summary>
/// <param name="username">Benutzername</param>
/// <returns>Liste Credentials</returns>
public async Task<List<FidoStoredCredential>> GetCredentialsAsync(string username)
{
return (await Repository.FindAsync(c => c.Username == username).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt eine Liste aller Benutzer zurück die über eine CredentialId verfügen
/// </summary>
/// <param name="credentialId">CredentialId</param>
/// <returns>Liste Fido2 User</returns>
public async Task<List<Fido2User>> GetUsersByCredentialIdAsync(byte[] credentialId)
{
var credentialIdString = Base64Url.Encode(credentialId);
var credential = await Repository.SingleOrDefaultAsync(c => c.DescriptorJson.Contains(credentialIdString));
if (credential == null)
return new List<Fido2User>();
var users = await _userService.FindAsync(c => Encoding.UTF8.GetBytes(c.UserName).SequenceEqual(credential.UserId));
return users.Select(c => new Fido2User()
{
Name = c.UserName,
Id = Encoding.UTF8.GetBytes(c.UserName)
}).ToList();
}
/// <summary>
/// Gibt zurück ob ein Benutzer über Fido credentials verfügt
/// </summary>
/// <param name="username">Benutzername</param>
/// <returns>true wenn ja, false sonst</returns>
public async Task<bool> HasFidoCredentialsAsync(string username)
{
return (await Repository.CountAsync(c => c.Username == username).ConfigureAwait(false)) > 0;
}
/// <summary>
/// Gibt ein Credential zurück
/// </summary>
/// <param name="id">CredentialId</param>
/// <returns>FidoStoredCredential</returns>
public async Task<FidoStoredCredential> GetByIdAsync(byte[] id)
{
var credentialIdString = Base64Url.Encode(id);
return await Repository.FirstOrDefaultAsync(c => c.DescriptorJson.Contains(credentialIdString)).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Credentials anhand des userHandles zurück
/// </summary>
/// <param name="userHandle">Userhandle</param>
/// <returns>Liste Credentials</returns>
public async Task<List<FidoStoredCredential>> GetCredentialsByUserHandleAsync(byte[] userHandle)
{
return (await Repository.FindAsync(c => c.UserHandle.SequenceEqual(userHandle)).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Aktualisieren des Counters
/// </summary>
/// <param name="credentialId"></param>
/// <param name="counter"></param>
/// <returns>Task</returns>
public async Task UpdateCounterAsync(byte[] credentialId, uint counter)
{
var credentialIdString = Base64Url.Encode(credentialId);
var item = await Repository.FirstOrDefaultAsync(c => c.DescriptorJson.Contains(credentialIdString)).ConfigureAwait(false);
item.SignatureCounter = counter;
await CommitAsync("System");
}
}
}

View File

@ -0,0 +1,524 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Dateien ermöglicht
/// </summary>
public class FileService : IFileService
{
private readonly string _root;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="root">Root-Verzeichnis für alle Datei-Operationen</param>
[ExcludeFromCodeCoverage]
public FileService(string root)
{
_root = root;
if (!Directory.Exists(_root))
{
Directory.CreateDirectory(_root);
}
}
/// <summary>
/// Erstellt einen Container, wenn nicht bereits vorhanden
/// </summary>
/// <param name="name">Name des Containers</param>
private void CreateContainerIfNotExists(string name)
{
var containerRoot = Path.Combine(_root, name);
if (!Directory.Exists(containerRoot))
{
Directory.CreateDirectory(containerRoot);
}
}
/// <summary>
/// Speichern einer Datei in einen Container
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <param name="file">Datei als Byte-Array</param>
/// <param name="overwrite">Überschreiben vorhandener Daten</param>
public async Task<string> StoreAsync(string containerName, string filename, byte[] file, bool overwrite = true)
{
CreateContainerIfNotExists(containerName);
var containerRoot = Path.Combine(_root, containerName);
filename = ToLocalFilename(filename);
var targetFile = Path.Combine(containerRoot, filename);
if (!overwrite)
{
if (File.Exists(targetFile))
throw new IOException("File already exists");
}
await File.WriteAllBytesAsync(targetFile, file);
return ToRelativeFilename(filename);
}
/// <summary>
/// Speichern einer Datei in einen Container
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <param name="fileStream">Datei als Stream</param>
/// <param name="overwrite">Überschreiben vorhandener Daten</param>
public async Task<string> StoreAsync(string containerName, string filename, Stream fileStream, bool overwrite = true)
{
CreateContainerIfNotExists(containerName);
var containerRoot = Path.Combine(_root, containerName);
filename = ToLocalFilename(filename);
var targetFile = Path.Combine(containerRoot, filename);
if (!overwrite)
{
if (File.Exists(targetFile))
throw new IOException("File already exists");
}
await using (var fs = new FileStream(targetFile, FileMode.Create, FileAccess.ReadWrite))
{
await fileStream.CopyToAsync(fs).ConfigureAwait(false);
}
return ToRelativeFilename(filename);
}
/// <summary>
/// Umbenennen einer Datei in einem Container
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <param name="newFilename">Neuer Dateiname</param>
public async Task RenameAsync(string containerName, string filename, string newFilename)
{
CreateContainerIfNotExists(containerName);
var containerRoot = Path.Combine(_root, containerName);
filename = ToLocalFilename(filename);
newFilename = ToLocalFilename(newFilename);
var sourceFileName = Path.Combine(containerRoot, filename);
var targetFileName = Path.Combine(containerRoot, newFilename);
File.Move(sourceFileName, targetFileName);
await Task.CompletedTask;
}
/// <summary>
/// Gibt eine Datei zurück
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <returns></returns>
public async Task<byte[]> GetAsync(string containerName, string filename)
{
CreateContainerIfNotExists(containerName);
var containerRoot = Path.Combine(_root, containerName);
filename = ToLocalFilename(filename);
var targetFile = Path.Combine(containerRoot, filename);
if (File.Exists(targetFile))
{
return await File.ReadAllBytesAsync(targetFile);
}
return null;
}
/// <summary>
/// Gibt eine Datei zurück
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
/// <returns></returns>
public async Task<Stream> GetAsStreamAsync(string containerName, string filename)
{
CreateContainerIfNotExists(containerName);
var containerRoot = Path.Combine(_root, containerName);
filename = ToLocalFilename(filename);
var targetFile = Path.Combine(containerRoot, filename);
if (File.Exists(targetFile))
{
await Task.CompletedTask;
return File.OpenRead(targetFile);
}
return null;
}
/// <summary>
/// Löschen einer Datei in einem Container
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
public async Task DeleteAsync(string containerName, string filename)
{
CreateContainerIfNotExists(containerName);
var containerRoot = Path.Combine(_root, containerName);
filename = ToLocalFilename(filename);
var targetFile = Path.Combine(containerRoot, filename);
if (File.Exists(targetFile))
File.Delete(targetFile);
await Task.CompletedTask;
}
/// <summary>
/// Gibt eine Liste aller Dateien eines Containers zurück die sich in einem Directory befinden
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="directory">Verzeichnis.</param>
/// <param name="fileNamePart">Teil eines Dateinamens für die suche mit Wildcard!!!</param>
/// <returns>Liste von Dateien</returns>
public async Task<List<string>> ListFilesAsync(string containerName, string directory = "", string fileNamePart = "")
{
CreateContainerIfNotExists(containerName);
var resultList = new List<string>();
var containerRoot = Path.Combine(_root, containerName);
directory = ToLocalDirectory(directory);
if (!string.IsNullOrWhiteSpace(fileNamePart))
directory += fileNamePart;
else
directory += "*";
var files = Directory.GetFiles(containerRoot, directory);
foreach (var file in files)
{
resultList.Add(ToRelativeFilename(file.Replace(containerRoot, "").Replace(@"\", "")));
}
await Task.CompletedTask;
return resultList;
}
/// <summary>
/// Gibt eine Liste aller Verzeichnise zurück die sich im Container befindne
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="root">wenn angegeben, wird von dem Verzeihnis als root ausgegangen</param>
/// <returns></returns>
public Task<List<string>> ListDirectoriesAsync(string containerName, string root = "")
{
throw new NotImplementedException();
}
/// <summary>
/// Koperien von Dateinen eines Containers in einen anderen
/// </summary>
/// <param name="sourceContainer">Quellcontainer</param>
/// <param name="sourceDirectory">Quellpfad</param>
/// <param name="targetContainer">ZielContainer</param>
/// <param name="targetDirectory">ZielPfad</param>
public async Task CopyFilesAsync(string sourceContainer, string sourceDirectory, string targetContainer, string targetDirectory)
{
CreateContainerIfNotExists(sourceContainer);
CreateContainerIfNotExists(targetContainer);
var source = Path.Combine(_root, sourceContainer);
sourceDirectory = ToLocalDirectory(sourceDirectory);
var target = Path.Combine(_root, targetContainer);
targetDirectory = ToLocalDirectory(targetDirectory);
var sourceFiles = Directory.GetFiles(source, sourceDirectory + "*");
foreach (var sourceFile in sourceFiles)
{
var fileName = GetFilename(sourceFile);
var targetFile = targetDirectory + fileName;
targetFile = Path.Combine(target, targetFile);
File.Copy(sourceFile, targetFile, true);
}
await Task.CompletedTask;
}
/// <summary>
/// Koperien von Dateien eines Pfades in einen anderen
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="sourceDirectory">Quellpfad</param>
/// <param name="targetDirectory">ZielPfad</param>
public async Task CopyFilesAsync(string containerName, string sourceDirectory, string targetDirectory)
{
CreateContainerIfNotExists(containerName);
var source = Path.Combine(_root, containerName);
sourceDirectory = ToLocalDirectory(sourceDirectory);
var target = Path.Combine(_root, source);
targetDirectory = ToLocalDirectory(targetDirectory);
var sourceFiles = Directory.GetFiles(source, sourceDirectory + "*");
foreach (var sourceFile in sourceFiles)
{
var fileName = GetFilename(sourceFile);
var targetFile = targetDirectory + fileName;
targetFile = Path.Combine(target, targetFile);
File.Copy(sourceFile, targetFile, true);
}
await Task.CompletedTask;
}
/// <summary>
/// Verschieben von Dateien eines Containers in einen anderen
/// </summary>
/// <param name="sourceContainer">Quellcontainer</param>
/// <param name="sourceDirectory">Quellpfad</param>
/// <param name="targetContainer">ZielContainer</param>
/// <param name="targetDirectory">ZielPfad</param>
public async Task MoveFilesAsync(string sourceContainer, string sourceDirectory, string targetContainer, string targetDirectory)
{
CreateContainerIfNotExists(sourceContainer);
CreateContainerIfNotExists(targetContainer);
var source = Path.Combine(_root, sourceContainer);
sourceDirectory = ToLocalDirectory(sourceDirectory);
var target = Path.Combine(_root, targetContainer);
targetDirectory = ToLocalDirectory(targetDirectory);
var sourceFiles = Directory.GetFiles(source, sourceDirectory + "*");
foreach (var sourceFile in sourceFiles)
{
var fileName = GetFilename(sourceFile);
var targetFile = targetDirectory + fileName;
targetFile = Path.Combine(target, targetFile);
if (File.Exists(targetFile))
File.Delete(targetFile);
File.Move(sourceFile, targetFile);
}
await Task.CompletedTask;
}
/// <summary>
/// Verschieben von Dateien eines Pfades in einen anderen
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="sourceDirectory">Quellpfad</param>
/// <param name="targetDirectory">ZielPfad</param>
public async Task MoveFilesAsync(string containerName, string sourceDirectory, string targetDirectory)
{
CreateContainerIfNotExists(containerName);
var source = Path.Combine(_root, containerName);
sourceDirectory = ToLocalDirectory(sourceDirectory);
var target = Path.Combine(_root, source);
targetDirectory = ToLocalDirectory(targetDirectory);
var sourceFiles = Directory.GetFiles(source, sourceDirectory + "*");
foreach (var sourceFile in sourceFiles)
{
var fileName = GetFilename(sourceFile);
var targetFile = targetDirectory + fileName;
targetFile = Path.Combine(target, targetFile);
if (File.Exists(targetFile))
File.Delete(targetFile);
File.Move(sourceFile, targetFile);
}
await Task.CompletedTask;
}
/// <summary>
/// Löschen des Inhalts eines Verzeichnises
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="directory">Verzeichnis</param>
/// <param name="fileNamePart">Optional: Dateinamen Wildcard mit *</param>
public async Task ClearDirectoryAsync(string containerName, string directory, string fileNamePart = "")
{
CreateContainerIfNotExists(containerName);
var containerRoot = Path.Combine(_root, containerName);
directory = ToLocalDirectory(directory);
if (!string.IsNullOrWhiteSpace(fileNamePart))
directory += fileNamePart;
else
directory += "*";
var files = Directory.GetFiles(containerRoot, directory);
foreach (var file in files)
{
File.Delete(file);
}
await Task.CompletedTask;
}
/// <summary>
/// Löscht einen Container und den Inhalt
/// </summary>
/// <param name="containerName">Name des Containers</param>
public async Task DeleteContainerAsync(string containerName)
{
var containerRoot = Path.Combine(_root, containerName);
if (Directory.Exists(containerRoot))
Directory.Delete(containerRoot, true);
await Task.CompletedTask;
}
/// <summary>
/// Löscht Dateien aus einem Ordner auf welche
/// vor dem Datum das letzte mal zugegriffen wurde
/// </summary>
/// <param name="containerName">Name des Containers</param>
/// <param name="directory">Verzeichnis</param>
/// <param name="date">Datum</param>
public async Task DeleteOldFilesAsync(string containerName, string directory, DateTimeOffset date)
{
CreateContainerIfNotExists(containerName);
var containerRoot = Path.Combine(_root, containerName);
directory = ToLocalDirectory(directory);
var files = Directory.GetFiles(containerRoot, directory + "*");
foreach (var file in files)
{
var info = new FileInfo(file);
if (info.LastAccessTimeUtc < date)
File.Delete(file);
}
await Task.CompletedTask;
}
/// <summary>
/// Gibt den reinen Dateinamen eines Files zurück (Ohne Directories usw)
/// </summary>
/// <param name="fileName">Dateiname</param>
/// <returns>Reiner Dateiname</returns>
public string GetFlatFilename(string fileName)
{
var file = ToLocalFilename(fileName);
file = GetFilename(file);
return file;
}
#region private
/// <summary>
/// Konvertiert einen Dateinamen mit Pfadangaben in einen Dateinamen
/// ohne Pfade. Diese sind nun in den Dateinamen integriert
/// </summary>
/// <param name="filename">Dateiname</param>
/// <returns>Dateiname</returns>
private string ToLocalFilename(string filename)
{
filename = filename.Replace(@"[", "");
filename = filename.Replace(@"]", "");
filename = filename.Replace(@"\", "[]");
filename = filename.Replace(@"/", "[]");
return filename;
}
/// <summary>
/// Konvertiert ein Verzeichnis in die lokale Form
/// ohne Pfade. Diese sind nun in den Dateinamen integriert
/// </summary>
/// <param name="directory">Pfad</param>
/// <returns>Pfad</returns>
private string ToLocalDirectory(string directory)
{
if (string.IsNullOrWhiteSpace(directory))
return "";
directory = directory.Replace(@"[", "");
directory = directory.Replace(@"]", "");
directory = directory.Replace(@"\", "[]");
directory = directory.Replace(@"/", "[]");
if (!directory.EndsWith("[]"))
directory += "[]";
return directory;
}
/// <summary>
/// Konvertiert einen lokalen Dateinamen mit Pfadangaben
/// in einen relativen Pfad
/// </summary>
/// <param name="filename">Dateiname</param>
/// <returns>Dateiname</returns>
private string ToRelativeFilename(string filename)
{
filename = filename.Replace(@"[]", "/");
return filename;
}
/// <summary>
/// Konvertiert einen lokalen Dateinamen mit Pfadangaben
/// in einen absoluten Pfad
/// </summary>
/// <param name="filename">Dateiname</param>
/// <returns>Dateiname</returns>
private string ToAbsoluteFilename(string filename)
{
filename = filename.Replace(@"[]", @"\");
return filename;
}
/// <summary>
/// Gibt den Dateinamen einer Datei ohne lokale Pfadangaben zurück
/// </summary>
/// <param name="path">Pfad + Dateinamen</param>
/// <returns>Dateiname ohne Pfadangaben</returns>
private string GetFilename(string path)
{
var fileName = path.Split(new[] { "[]" }, StringSplitOptions.None).Last();
return fileName;
}
#endregion
}
/// <summary>
/// Hilfsklasse für das Finden von Dateien mittels Wildcards
/// </summary>
[ExcludeFromCodeCoverage]
internal class Wildcard : Regex
{
/// <summary>
/// Erstellt eine instanz
/// </summary>
/// <param name="pattern">Wildcard-Ausdruck</param>
public Wildcard(string pattern) : base(ToRegex(pattern))
{
}
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="pattern">Wildcard-Ausdruck</param>
/// <param name="options">Optionen</param>
public Wildcard(string pattern, RegexOptions options) : base(ToRegex(pattern), options)
{
}
/// <summary>
/// Erstellt einen regulären Ausdruck aus dem Wildcard-Ausdruck
/// </summary>
/// <param name="pattern">Wildcard-Ausdruck</param>
/// <returns>Wildcard-Ausdruck in Regex konvertiert</returns>
public static string ToRegex(string pattern)
{
return "^" + Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + "$";
}
}
}

View File

@ -0,0 +1,445 @@
using System;
using System.IO;
using System.Linq;
namespace gehGassi.Core.Services
{
/// <summary>
/// Hilfsklasse für die Benennung von Dateien und Ordnern
/// </summary>
public class FileServiceHelper
{
public const string DocumentContainer = "documents";
public const string TempContainer = "temp";
/// <summary>
/// Pfad zu den Mandanten-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <returns>Pfad</returns>
public static string GetTenantPath(Guid tenantId)
{
return $"Tenants/{tenantId}/";
}
/// <summary>
/// Pfad zu den Dateien eines Benutzers
/// </summary>
/// <param name="id">ID des Benutzers</param>
/// <returns>Pfad</returns>
public static string GetProfilePath(string id)
{
return $"Users/{id}/";
}
/// <summary>
/// Pfad zu den Temp-Bild-Dateien
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="uniqueId">Eindeutige ID</param>
/// <returns>Pfad</returns>
public static string GetImageTempPath(Guid tenantId, Guid uniqueId)
{
return $"Tenants/{tenantId}/{uniqueId}/images/";
}
/// <summary>
/// Pfad zu den Dateien eines Projektes!
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="projectId">Id des Projektes</param>
/// <returns>Pfad</returns>
public static string GetProjectPath(Guid tenantId, long projectId)
{
return $"Tenants/{tenantId}/projects/{projectId}/";
}
/// <summary>
/// Pfad zu den Dateien eines Issues
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="projectId">Id des Projektes</param>
/// <param name="issueId">Id des Issues</param>
/// <returns>Pfad</returns>
public static string GetIssuePath(Guid tenantId, long projectId, long issueId)
{
return $"Tenants/{tenantId}/projects/{projectId}/issues/{issueId}/";
}
/// <summary>
/// Pfad zu den Bild-Dateien eines Issues
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="projectId">Id des Projektes</param>
/// <param name="issueId">Id des Issues</param>
/// <returns>Pfad</returns>
public static string GetIssueImagePath(Guid tenantId, long projectId, long issueId)
{
return $"Tenants/{tenantId}/projects/{projectId}/issues/{issueId}/images/";
}
/// <summary>
/// Pfad zu den Bild-Dateien eines Projektes
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="projectId">Id des Projektes</param>
/// <returns>Pfad</returns>
public static string GetProjectImagePath(Guid tenantId, long projectId)
{
return $"Tenants/{tenantId}/projects/{projectId}/images/";
}
/// <summary>
/// Pfad zu den Bild-Dateien eines Sprints
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="projectId">Id des Projektes</param>
/// <param name="sprintId">Id des Sprints</param>
/// <returns>Pfad</returns>
public static string GetSprintImagePath(Guid tenantId, long projectId, long sprintId)
{
return $"Tenants/{tenantId}/projects/{projectId}/sprints/{sprintId}/images/";
}
/// <summary>
/// Pfad zu den Beleg-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <returns>Pfad</returns>
public static string GetReceiptPath(Guid tenantId)
{
return $"Tenants/{tenantId}/receipts/";
}
/// <summary>
/// Pfad zu den Badge-Daten
/// </summary>
/// <returns>Pfad</returns>
public static string GetBadgePath()
{
return $"Badges/";
}
/// <summary>
/// Pfad zu den Badge-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <returns>Pfad</returns>
public static string GetBadgePath(Guid tenantId)
{
return $"Tenants/{tenantId}/badges/";
}
/// <summary>
/// Pfad zu den Kunden-Daten für Temp
/// </summary>
/// <returns>Pfad</returns>
public static string GetCustomerPath()
{
return $"customers/";
}
/// <summary>
/// Pfad zu den Kunden-Daten
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <returns>Pfad</returns>
public static string GetCustomerPath(long customerId)
{
return $"customers/{customerId}/";
}
/// <summary>
/// Pfad zu den Kunden-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="customerId">Id des Kunden</param>
/// <returns>Pfad</returns>
public static string GetCustomerPath(Guid tenantId, long customerId)
{
return $"Tenants/{tenantId}/customers/{customerId}/";
}
/// <summary>
/// Pfad zu den Trainer-Daten
/// </summary>
/// <returns>Pfad</returns>
public static string GetInstructorPath()
{
return $"instructors/";
}
/// <summary>
/// Pfad zu den Trainer-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <returns>Pfad</returns>
public static string GetInstructorPath(Guid tenantId)
{
return $"Tenants/{tenantId}/instructors/";
}
/// <summary>
/// Pfad zu den Studenten-Daten
/// </summary>
/// <returns>Pfad</returns>
public static string GetStudentPath()
{
return $"students/";
}
/// <summary>
/// Pfad zu den Studenten-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="customerId">Id des Kunden</param>
/// <returns>Pfad</returns>
public static string GetStudentPath(Guid tenantId, long customerId)
{
return $"Tenants/{tenantId}/customers/{customerId}/students/";
}
/// <summary>
/// Pfad zu den Studenten-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="studentId">UniqueId des Studenten</param>
/// <returns>Pfad</returns>
public static string GetStudentPath(Guid tenantId, long customerId, Guid studentId)
{
return $"Tenants/{tenantId}/customers/{customerId}/students/{studentId}/";
}
/// <summary>
/// Pfad zu den Kurs-Daten
/// </summary>
/// <returns>Pfad</returns>
public static string GetCoursePath()
{
return $"courses/";
}
/// <summary>
/// Pfad zu den Kurs-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <returns>Pfad</returns>
public static string GetCoursePath(Guid tenantId)
{
return $"Tenants/{tenantId}/courses/";
}
/// <summary>
/// Pfad zu den Kurs-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <param name="courseId">Id des Kurses</param>
/// <param name="version">Versionsnummer</param>
/// <returns>Pfad</returns>
public static string GetCoursePath(Guid tenantId, long courseId, int version)
{
return $"Tenants/{tenantId}/courses/{courseId}/{version}/";
}
/// <summary>
/// Pfad zu den Zertifikat-Daten
/// </summary>
/// <returns>Pfad</returns>
public static string GetCertificatePath()
{
return $"certificates/";
}
/// <summary>
/// Pfad zu den Zertifikat-Daten
/// </summary>
/// <param name="tenantId">Id des Mandanten</param>
/// <returns>Pfad</returns>
public static string GetCertificatePath(Guid tenantId)
{
return $"Tenants/{tenantId}/certificates/";
}
/// <summary>
/// Pfad zu den Werbungs-Daten Kategorien / Rubriken
/// </summary>
/// <param name="id">Id der Rubrik</param>
/// <returns>Pfad</returns>
public static string GetAdvertisementCategoryPath(string id)
{
return $"AdsCat/{id}/";
}
/// <summary>
/// Pfad zu den Werbungs-Daten
/// </summary>
/// <param name="id">Id der Werbung</param>
/// <returns>Pfad</returns>
public static string GetAdvertisementPath(string id)
{
return $"Ads/{id}/";
}
/// <summary>
/// Pfad zu den Banner-Daten
/// </summary>
/// <param name="id">Id des Banners</param>
/// <returns>Pfad</returns>
public static string GetBannerPath(string id)
{
return $"Banner/{id}/";
}
/// <summary>
/// Pfad zu den Gutschein-Kampagnen-Daten
/// </summary>
/// <param name="id">Id der Gutschein-Kampagne</param>
/// <returns>Pfad</returns>
public static string GetVoucherCampaignPath(string id)
{
return $"VoucherCampaigns/{id}/";
}
/// <summary>
/// Pfad zu den Branchen-Daten
/// </summary>
/// <param name="id">Id der Rubrik</param>
/// <returns>Pfad</returns>
public static string GetBranchPath(string id)
{
return $"Branch/{id}/";
}
/// <summary>
/// Pfad zu den Hunderassen-Daten
/// </summary>
/// <param name="id">Id der Rubrik</param>
/// <returns>Pfad</returns>
public static string GetDogRacePath(string id)
{
return $"DogRace/{id}/";
}
/// <summary>
/// Pfad zu den Produktkategorie-Daten
/// </summary>
/// <param name="id">Id der Rubrik</param>
/// <returns>Pfad</returns>
public static string GetProductCategoryPath(int id)
{
return $"ProdsCat/{id}/";
}
/// <summary>
/// Pfad zu den Werbungs-Daten
/// </summary>
/// <param name="id">Id der Rubrik</param>
/// <returns>Pfad</returns>
public static string GetProductPath(int id)
{
return $"Products/{id}/";
}
/// <summary>
/// Pfad zu den App-User-Daten
/// </summary>
/// <param name="id">Id des App-Users</param>
/// <returns>Pfad</returns>
public static string GetAppUserPath(string id)
{
return $"AppUsers/{id}/";
}
/// <summary>
/// Pfad zu den Hundeausführer-Daten
/// </summary>
/// <param name="id">Id des Hundeausführers</param>
/// <returns>Pfad</returns>
public static string GetDogWalkerPath(string id)
{
return $"DogWalkers/{id}/";
}
/// <summary>
/// Pfad zu den Hunde-Daten
/// </summary>
/// <param name="id">Id des Hundes</param>
/// <returns>Pfad</returns>
public static string GetDogPath(string id)
{
return $"Dogs/{id}/";
}
/// <summary>
/// Pfad zu den Listungs-Daten
/// </summary>
/// <param name="id">Id der Rubrik</param>
/// <returns>Pfad</returns>
public static string GetListingPath(string id)
{
return $"Listing/{id}/";
}
/// <summary>
/// Pfad zu den Pin-Daten
/// </summary>
/// <param name="id">Id des Pins</param>
/// <returns>Pfad</returns>
public static string GetPinPath(string id)
{
return $"Pin/{id}/";
}
/// <summary>
/// Pfad zu den temp-Upload Daten
/// </summary>
/// <returns>Pfad</returns>
public static string GetUploadTempPath()
{
return $"uploadtemp/";
}
/// <summary>
/// Pfad zu den News-Daten Kategorien
/// </summary>
/// <param name="id">Id der Rubrik</param>
/// <returns>Pfad</returns>
public static string GetNewsCategoryPath(string id)
{
return $"NewsCat/{id}/";
}
/// <summary>
/// Pfad zu den News-Daten
/// </summary>
/// <param name="id">Id der News</param>
/// <returns>Pfad</returns>
public static string GetNewsPath(long id)
{
return $"News/{id}/";
}
#region Helper
/// <summary>
/// Ersetzt ungültige Zeichen in einem Dateinamen
/// </summary>
/// <param name="filename">Dateiname</param>
/// <returns>Bereinigter Dateiname</returns>
public static string SanitizeFileName(string filename)
{
if (string.IsNullOrWhiteSpace(filename))
return string.Empty;
var invalidCharacters = Path.GetInvalidFileNameChars().ToList();
invalidCharacters.Remove('/');
invalidCharacters.Add('&');
invalidCharacters.Add('%');
var sanitizedFileName = string.Join("_", filename.Split(invalidCharacters.ToArray()));
sanitizedFileName = sanitizedFileName.Replace(" ", "");
return sanitizedFileName;
}
#endregion
}
}

View File

@ -0,0 +1,61 @@
using System.Collections.Generic;
using gehGassi.Domain.Common;
namespace gehGassi.Core.Services
{
/// <summary>
/// Schnittstellenbeschreibung für einen Service der den Umgang mit Ländern vereinfacht
/// </summary>
public interface ICountryService
{
/// <summary>
/// Gibt eine Liste aller Länder zurück
/// </summary>
/// <returns>Liste</returns>
List<Country> GetCountries();
/// <summary>
/// Gibt ein Land anhand des Codes zurück
/// </summary>
/// <param name="countryCode">ISO2</param>
/// <returns>Land oder null, wenn nicht gefunden</returns>
Country GetCountry(string countryCode);
/// <summary>
/// Gibt eine Liste von Bundesländern zurück
/// </summary>
/// <returns>Liste von Bundesländern</returns>
List<State> GetStates();
/// <summary>
/// Gibt eine Liste von Bundesländern / Staaten eines Landes zurück
/// </summary>
/// <param name="countryCode">ISO2 des Landes</param>
/// <returns>Liste von Bundesländern</returns>
List<State> GetStates(string countryCode);
/// <summary>
/// Gibt ein Bundesland / Staat eines Landes zurück
/// </summary>
/// <param name="countryCode">ISO2 des Landes</param>
/// <param name="stateCode">Code des Bundeslandes</param>
/// <returns>Bundesland oder null, wenn nicht gefunden</returns>
State GetState(string countryCode, string stateCode);
/// <summary>
/// Gibt eine Liste der Ländercodes zurück die zu einem Filter passen
/// </summary>
/// <param name="filter">Filter</param>
/// <param name="operatorValue">Operator der angewendet werden soll</param>
/// <returns>Liste gefundener Ländercodes</returns>
List<string> FindCountryCodes(string filter, string operatorValue);
/// <summary>
/// Gibt eine Liste der Bundesland-Codes zurück die zu einem Filter passen
/// </summary>
/// <param name="filter">Filter</param>
/// <param name="operatorValue">Operator der angewendet werden soll</param>
/// <returns>Liste gefundener Bundeslandcodes</returns>
List<string> FindStateCodes(string filter, string operatorValue);
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Favourites;
using gehGassi.Domain.Payment;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von KYC-Dokumenten ermöglicht
/// </summary>
public class IdentityDocumentService : ServiceBase<IdentityDocument>, IIdentityDocumentService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public IdentityDocumentService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Erstellen eines KYC-Dokuments
/// </summary>
/// <returns>IdentityDocument</returns>
public IdentityDocument Create()
{
var item = new IdentityDocument()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Erstellen eines KYC-Dokuments
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="mangoPayId">Id des KYC-Dokumentes bei MangoPay</param>
/// <param name="mangoPayUserId">Id des Users bei MangoPay</param>
/// <param name="documentType">Typ des Dokumentes</param>
/// <param name="source">Quelle / Dokumentenart</param>
/// <param name="status">Status der Validierung des Dokumentes</param>
/// <param name="pageCount">Anzahl der Seiten</param>
/// <returns>IdentityDocument</returns>
public IdentityDocument Create(string appUserId, string mangoPayId, string mangoPayUserId, IdentityDocumentType documentType, IdentityDocumentSource source, IdentityDocumentStatus status, int pageCount)
{
var item = Create();
item.AppUserId = appUserId;
item.MangoPayId = mangoPayId;
item.MangoPayUserId = mangoPayUserId;
item.Type = documentType;
item.Source = source;
item.Status = status;
item.PageCount = pageCount;
return item;
}
/// <summary>
/// Gibt ein KYC-Dokument anhand der MangoPayId zurück
/// </summary>
/// <param name="mangoPayId">Id des Dokumentes bei MangoPay</param>
/// <returns>IdentityDocument oder null, wenn nicht gefunden</returns>
public async Task<IdentityDocument> GetByMangoPayIdAsync(string mangoPayId)
{
var item = await Repository.FirstOrDefaultAsync(c => c.MangoPayId == mangoPayId).ConfigureAwait(false);
return item;
}
/// <summary>
/// Gibt ein KYC-Dokument zurück, bei welchem die Validierung noch läuft
/// </summary>
/// <param name="appUserId">Id des App-USers</param>
/// <returns>IdentityDocument oder null, wenn keines gefunden</returns>
public async Task<IdentityDocument> GetValidationInProgressAsync(string appUserId)
{
var item = await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.Status == IdentityDocumentStatus.VALIDATION_ASKED).ConfigureAwait(false);
return item;
}
/// <summary>
/// Gibt das zuletzt erstellte KYC-Dokument für einen App-User zurück
/// </summary>
/// <param name="appUserId">Id des App-USers</param>
/// <returns>IdentityDocument oder null, wenn keines gefunden</returns>
public async Task<IdentityDocument> GetLatestAsync(string appUserId)
{
var item = await Repository.Query(c => c.AppUserId == appUserId).OrderByDescending(c => c.Created).FirstOrDefaultAsync().ConfigureAwait(false);
return item;
}
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Shop;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Rechnungen ermöglicht
/// </summary>
public class InvoiceService : ServiceBase<Invoice>, IInvoiceService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public InvoiceService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Erstellen einer Rechnung
/// </summary>
/// <returns>Bestellung</returns>
public Invoice Create()
{
var item = new Invoice()
{
UniqueId = Guid.NewGuid(),
Created = DateTime.UtcNow
};
return item;
}
/// <summary>
/// Erstellen einer Rechnung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <returns>Bestellung</returns>
public async Task<Invoice> CreateAsync(long orderId)
{
var item = Create();
item.OrderId = orderId;
item.Number = await GetNextNumberAsync();
item.Counter = await GetNextCounterAsync();
return item;
}
/// <summary>
/// Gibt eine Rechnung basierend auf der Bestellung zurück
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <returns></returns>
public async Task<Invoice> GetByOrderAsync(long orderId)
{
var item = await Repository.FirstOrDefaultAsync(c => c.OrderId == orderId).ConfigureAwait(false);
return item;
}
/// <summary>
/// Gibt den nächsten freien Rechnungs-spezifischen Zähler zurück
/// </summary>
/// <returns>Nächste freie Nummer</returns>
public async Task<long> GetNextCounterAsync()
{
long number = 1;
var last = await Repository.Query(c => c.Id > 0).OrderByDescending(c => c.Counter).FirstOrDefaultAsync().ConfigureAwait(false);
if (last != null)
number = last.Counter + 1;
return number;
}
/// <summary>
/// Gibt die nächste freie Nummer für eine Rechnung als Rechnungsnummer zurück
/// </summary>
/// <returns>EndKunden-Nummer</returns>
public async Task<string> GetNextNumberAsync()
{
long number = 1;
var last = await Repository.Query(c => c.Id > 0).OrderByDescending(c => c.Id).FirstOrDefaultAsync().ConfigureAwait(false);
if (last != null)
number = last.Counter + 1;
return $"{DateTime.UtcNow.Year}{DateTime.UtcNow.Month}-{number:000000}";
}
}
}

View File

@ -0,0 +1,61 @@
using System;
using gehGassi.Core.Interfaces;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der eine Kombination aus private und public Key erstellen kann
/// </summary>
public class KeyGeneratorService : IKeyGeneratorService
{
/// <summary>
/// Erstellt einen public und einen Private key mit Standard 1024 bit Verschlüsselung
/// </summary>
/// <returns>PrivateKey und Public Key</returns>
public Tuple<string, string> GenerateKeys()
{
return GenerateKeys(1024);
}
/// <summary>
/// Erstellt einen public und einen Private key
/// </summary>
/// <param name="keySize">Schlüssel Größe</param>
/// <returns>PrivateKey und Public Key</returns>
public Tuple<string, string> GenerateKeys(int keySize)
{
var secureRandom = new SecureRandom();
return GenerateKeys(keySize, secureRandom.GenerateSeed(32));
}
/// <summary>
/// Erstellt einen public und einen Private key
/// </summary>
/// <param name="keySize">Schlüssel Größe</param>
/// <param name="seed">Seed</param>
/// <returns>PrivateKey und Public Key</returns>
public Tuple<string, string> GenerateKeys(int keySize, byte[] seed)
{
var generator = new RsaKeyPairGenerator();
var secureRandom = new SecureRandom();
secureRandom.SetSeed(seed);
generator.Init(new KeyGenerationParameters(secureRandom, keySize));
var pair = generator.GenerateKeyPair();
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private);
var serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetDerEncoded();
var serializedPrivate = Convert.ToBase64String(serializedPrivateBytes);
var publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pair.Public);
var serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
var serializedPublic = Convert.ToBase64String(serializedPublicBytes);
return new Tuple<string, string>(serializedPrivate, serializedPublic);
}
}
}

View File

@ -0,0 +1,475 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using gehGassi.Domain.News;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using NetTopologySuite.Geometries;
using NetTopologySuite;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service für die Verwaltung von Listungen
/// </summary>
public class ListingService : ServiceBase<Listing>, IListingService
{
private readonly IOptions<ListingOptions> _listingOptions;
private readonly IRepository<ListingWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
/// <param name="listingOptions">Instanz von ListingOptions</param>
public ListingService(IUnitOfWork unitOfWork, IOptions<ListingOptions> listingOptions) : base(unitOfWork)
{
_listingOptions = listingOptions;
_namesRepository = unitOfWork.GetRepository<ListingWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Listings zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Listings</returns>
public IQueryable<Listing> Filter(string filter)
{
return Repository.Query(c => c.NameLocalized.Contains(filter));
}
/// <summary>
/// Erstellen einer Listung
/// </summary>
/// <returns>Listung</returns>
public Listing Create()
{
var item = new Listing()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Listings zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Listings</returns>
public async Task<List<Listing>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.NameLocalized.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Listings mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Listings</returns>
public IQueryable<ListingWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ListingsWithNames({0},{1})", c => c.Name.Contains(filter) || c.CustomerName.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != ListingStatus.Draft);
return baseQuery;
}
/// <summary>
/// Gibt eine gefilterte Liste von Listings mit Namen für Kunden zurück in einer gewählten Sprache
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Listings</returns>
public IQueryable<ListingWithNames> FilterWithNamesCustomer(long customerId, string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ListingsWithNames({0},{1})", c => c.CustomerId == customerId && (c.Name.Contains(filter) || c.CustomerName.Contains(filter)), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != ListingStatus.Draft);
return baseQuery;
}
/// <summary>
/// Löschen von Listungen basierend auf einer Branche
/// </summary>
/// <param name="branchId">Id der Branche</param>
/// <param name="forceDelete">Wenn true, dann wirklich löschen, auf gelöscht setzen sonst</param>
/// <returns>Task</returns>
public async Task DeleteByBranchAsync(string branchId, bool forceDelete)
{
var items = await Repository.FindAsync(c => c.BranchId == branchId).ConfigureAwait(false);
foreach (var listing in items)
{
if(!forceDelete)
listing.Deleted = true;
else
Repository.Remove(listing);
}
}
/// <summary>
/// Prüfen der Verfügbarkeit einer Listung für eine Buchung
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="branchId">Id der Branche</param>
/// <param name="listingType">Listungs-Typ</param>
/// <param name="startDate">Startdatum</param>
/// <param name="endDate">Enddatum</param>
/// <param name="countryIso">ISO2 des Landes</param>
/// <param name="ignoreId">Id die ignoriert werden soll</param>
/// <returns>ListingValidationResult</returns>
public async Task<ListingValidationResult> IsAvailableAsync(long customerId, string branchId, ListingType listingType, DateTimeOffset startDate, DateTimeOffset endDate, string countryIso, string ignoreId)
{
var result = new ListingValidationResult()
{
Valid = true,
Booked = 0,
Reserved = 0,
ErrorCode = ListingValidationError.None
};
//anpassung --> alle Listungstypen berücksichtigen. Es darf nur eine Listung je Kunde je Land und Branche geben. Der Listungstyp ist dabei egal.
//var customerCount = await Repository.CountAsync(c => c.Id != ignoreId && c.Deleted == false && c.BranchId == branchId && c.ListingType == listingType && c.CustomerId == customerId && c.Address.CountryCode == countryIso && (c.Status == ListingStatus.ToPay || c.Status == ListingStatus.Booked || c.Status == ListingStatus.Running) && ((startDate >= c.StartDate && startDate <= c.EndDate) || (endDate >= c.StartDate && endDate <= c.EndDate)));
var customerCount = await Repository.CountAsync(c => c.Id != ignoreId && c.Deleted == false && c.BranchId == branchId && c.CustomerId == customerId && c.Address.CountryCode == countryIso && (c.Status == ListingStatus.ToPay || c.Status == ListingStatus.Booked || c.Status == ListingStatus.Running) && ((startDate >= c.StartDate && startDate <= c.EndDate) || (endDate >= c.StartDate && endDate <= c.EndDate)));
var bookedCount = await Repository.CountAsync(c => c.Id != ignoreId && c.Deleted == false && c.BranchId == branchId && c.ListingType == listingType && c.CustomerId != customerId && c.Address.CountryCode == countryIso && (c.Status == ListingStatus.ToPay || c.Status == ListingStatus.Booked || c.Status == ListingStatus.Running) && ((startDate >= c.StartDate && startDate <= c.EndDate) || (endDate >= c.StartDate && endDate <= c.EndDate)));
var reservedCount = await Repository.CountAsync(c => c.Id != ignoreId && c.Deleted == false && c.BranchId == branchId && c.ListingType == listingType && c.CustomerId != customerId && c.Address.CountryCode == countryIso && c.ReservedUntil != null);
result.Booked = bookedCount;
result.Reserved = reservedCount;
var total = bookedCount + reservedCount;
if (customerCount > 0)
{
result.ErrorCode = ListingValidationError.AlreadyBooked;
result.Valid = false;
}
else
{
switch (listingType)
{
case ListingType.Base:
if (total >= _listingOptions.Value.Base)
{
result.ErrorCode = ListingValidationError.NoSlotsAvailable;
result.Valid = false;
}
break;
case ListingType.Silver:
if (total >= _listingOptions.Value.Silver)
{
result.ErrorCode = ListingValidationError.NoSlotsAvailable;
result.Valid = false;
}
break;
case ListingType.Gold:
if (total >= _listingOptions.Value.Gold)
{
result.ErrorCode = ListingValidationError.NoSlotsAvailable;
result.Valid = false;
}
break;
case ListingType.Platin:
if (total >= _listingOptions.Value.Platin)
{
result.ErrorCode = ListingValidationError.NoSlotsAvailable;
result.Valid = false;
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(listingType), listingType, null);
}
}
return result;
}
/// <summary>
/// Gibt die Anzahl von Listungen zurück
/// </summary>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Anzahl Listungen</returns>
public async Task<int> CountAsync(bool includeDeleted, bool excludeDraft)
{
var query = Repository.Query(c => c.Id != "");
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if(excludeDraft)
query = query.Where(c => c.Status != ListingStatus.Draft);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl von Listungen in einem Status zurück
/// </summary>
/// <param name="status">Gesuchter Status</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Anzahl Listungen</returns>
public async Task<int> CountAsync(ListingStatus status, bool includeDeleted)
{
var query = Repository.Query(c => c.Status == status);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Prüft ob sich eine Listung bereits im Warenkorb befindet.
/// Listungen mit bestimmten Parametern dürfen nur einmal vorkommen
/// </summary>
/// <param name="cartId">Id des Warenkorb</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="branchId">Id der Branche</param>
/// <param name="listingType">Listungs-Typ</param>
/// <param name="startDate">Startdatum</param>
/// <param name="endDate">Enddatum</param>
/// <param name="countryIso">ISO2 des Landes</param>
/// <param name="ignoreId">Id die ignoriert werden soll</param>
/// <returns>True wenn die Listung gefunden wurde, false sonst</returns>
public async Task<bool> CheckListingInCartAsync(long cartId, long customerId, string branchId, ListingType listingType, DateTimeOffset startDate, DateTimeOffset endDate, string countryIso, string ignoreId)
{
//anpassung --> alle Listungstypen berücksichtigen. Es darf nur eine Listung je Kunde je Land und Branche geben. Der Listungstyp ist dabei egal.
//var count = await Repository.CountAsync(c => c.Id != ignoreId && c.Deleted == false && c.CartId == cartId && c.BranchId == branchId && c.ListingType == listingType && c.CustomerId == customerId && c.Address.CountryCode == countryIso && c.Status == ListingStatus.Draft && ((startDate >= c.StartDate && startDate <= c.EndDate) || (endDate >= c.StartDate && endDate <= c.EndDate)));
var count = await Repository.CountAsync(c => c.Id != ignoreId && c.Deleted == false && c.CartId == cartId && c.BranchId == branchId && c.CustomerId == customerId && c.Address.CountryCode == countryIso && c.Status == ListingStatus.Draft && ((startDate >= c.StartDate && startDate <= c.EndDate) || (endDate >= c.StartDate && endDate <= c.EndDate)));
return count != 0;
}
/// <summary>
/// Gibt eine Listung für einen Artikel im Warenkorb zurück
/// </summary>
/// <param name="cartItemId">Id des Artikels im Warenkorb</param>
/// <returns>Listing oder null, wenn nicht gefunden</returns>
public async Task<Listing> GetByCartItemAsync(long cartItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.CartItemId == cartItemId && c.Status == ListingStatus.Draft);
}
/// <summary>
/// Gibt eine Listung für einen Artikel in einer Bestellung zurück
/// </summary>
/// <param name="orderItemId">Id des Artikels in der Bestellung</param>
/// <returns>Listing oder null, wenn nicht gefunden</returns>
public async Task<Listing> GetByOrderItemAsync(long orderItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.OrderItemId == orderItemId);
}
/// <summary>
/// Setzen des Status von Listungen nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <param name="reservedDate">Datum das für die Reservierung gesetzt werden soll</param>
/// <returns>Anzahl betroffener Listungen</returns>
public async Task<int> SetStatusByOrderAsync(long orderId, ListingStatus status, DateTimeOffset? reservedDate)
{
var listings = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var listing in listings)
{
listing.Status = status;
listing.ReservedUntil = reservedDate;
}
return listings.Count();
}
/// <summary>
/// Setzen des Bezahlstatus von Listingen nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <returns>Anzahl betroffener Listungen</returns>
public async Task<int> SetPaymentStatusByOrderAsync(long orderId, PaymentStatus status)
{
var listings = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var listing in listings)
{
listing.PaymentStatus = status;
}
return listings.Count();
}
/// <summary>
/// Zurücksetzen von Reservierungen wenn eine Zeit erreicht ist
/// </summary>
/// <param name="reservedDate">Obere Grenze der Reservierung</param>
/// <returns>Anzahl betroffener Listungen</returns>
public async Task<int> ResetReservedAsync(DateTimeOffset reservedDate)
{
var listings = await Repository.FindAsync(c => c.Status == ListingStatus.Reserverd && ((c.ReservedUntil.HasValue && c.ReservedUntil < reservedDate) || c.ReservedUntil == null));
foreach (var listing in listings)
{
listing.ReservedUntil = null;
listing.Status = ListingStatus.Draft;
}
return listings.Count();
}
/// <summary>
/// Gibt eine Liste von Listungen zurück für die Anzeige der Partner in der Nähe
/// </summary>
/// <param name="country">Land</param>
/// <param name="state">Bundesland</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste passender Listungen und gesamt verfügbar</returns>
public async Task<(int total, List<ListingWithNames> list)> GetNearForAppAsync(string country, string state, double lat, double lng, string language, string fallback, int skip, int take)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(country))
country = country.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(state))
state = state.ToUpperInvariant();
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ListingsWithNames({0},{1})", c => c.Deleted == false && c.Status == ListingStatus.Running && c.BranchOnlineStatus == OnlineStatus.Online &&
(c.GeoMode == GeoMode.None ||
(c.GeoMode == GeoMode.Country && c.Address_CountryCode == country) ||
(c.GeoMode == GeoMode.CountryAndState && c.Address_CountryCode == country && c.Address_State == state) ||
(c.GeoMode == GeoMode.Location && c.Location.IsWithinDistance(location, c.Radius * 1000))), language, fallback);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
//TODO: noch überlegen wie wir eine Lokalisierung machen können
baseQuery = baseQuery.OrderBy(c => c.ListingLocation.Distance(location));
baseQuery = baseQuery.Skip(skip).Take(take);
var list = await baseQuery.ToListAsync().ConfigureAwait(false);
return (total, list);
}
/// <summary>
/// Gibt eine Liste von Listungen zurück für eine Branche zurück.
/// Die Sortierung erfolgt nach Listungstyp und dann nach Distanz
/// </summary>
/// <param name="branchId">Id der Branche</param>
/// <param name="filter">Optional: Filter für Name und/oder Ort</param>
/// <param name="listingCountry">Optional: Listings-Land</param>
/// <param name="country">Land</param>
/// <param name="state">Bundesland</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste passender Listungen</returns>
public async Task<(int total, List<ListingWithNames> list)> GetByBranchForAppAsync(string branchId, string filter, string listingCountry, string country, string state, double lat, double lng, string language, string fallback, int skip, int take)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(country))
country = country.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(state))
state = state.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(listingCountry))
listingCountry = listingCountry.ToUpperInvariant();
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ListingsWithNames({0},{1})", c => c.BranchId == branchId && c.Deleted == false && c.Status == ListingStatus.Running && c.BranchOnlineStatus == OnlineStatus.Online &&
(c.GeoMode == GeoMode.None ||
(c.GeoMode == GeoMode.Country && c.Address_CountryCode == country) ||
(c.GeoMode == GeoMode.CountryAndState && c.Address_CountryCode == country && c.Address_State == state) ||
(c.GeoMode == GeoMode.Location && c.Location.IsWithinDistance(location, c.Radius * 1000))), language, fallback);
if (!string.IsNullOrWhiteSpace(filter))
{
baseQuery = baseQuery.Where(c => c.Name.Contains(filter) || c.ListingAddress_City.Contains(filter));
}
if (!string.IsNullOrWhiteSpace(listingCountry))
{
baseQuery = baseQuery.Where(c => c.ListingAddress_CountryCode == listingCountry);
}
var total = await baseQuery.CountAsync().ConfigureAwait(false);
var list = await baseQuery.OrderByDescending(c => c.ListingType).ThenBy(c => c.ListingLocation.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
/// <summary>
/// Gibt eine Listung in einer gewünschten Sprache zurück
/// </summary>
/// <param name="id">Id der Listung</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Gefundene Werbung, null sonst</returns>
public async Task<ListingWithNames> GetRunningWithNamesAsync(string id, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ListingsWithNames({0},{1})", c => c.Id == id && c.Deleted == false && c.Status == ListingStatus.Running && c.EndDate > DateTimeOffset.UtcNow, language, fallback);
var result = await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
return result;
}
/// <summary>
/// Starten von gebuchten Listungen wenn diese das Start-Datum erreicht haben
/// </summary>
/// <returns>Anzahl gestartete Listungen</returns>
public async Task<int> StartBookedAsnyc()
{
var listings = await Repository.FindAsync(c => c.Status == ListingStatus.Booked && c.Deleted == false && c.StartDate <= DateTimeOffset.UtcNow && c.EndDate.Value >= DateTimeOffset.UtcNow);
foreach (var listing in listings)
{
listing.Status = ListingStatus.Running;
}
return listings.Count();
}
/// <summary>
/// Stoppen von abgelaufenen Listungen wenn diese das Enddatum erreicht haben
/// </summary>
/// <returns>Anzahl gestoppte Listungen</returns>
public async Task<int> StopRunningAsync()
{
var listings = await Repository.FindAsync(c => c.Status == ListingStatus.Running && c.Deleted == false && c.EndDate.Value <= DateTimeOffset.UtcNow);
foreach (var listing in listings)
{
listing.Status = ListingStatus.Ended;
}
return listings.Count();
}
}
}

View File

@ -0,0 +1,667 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using gehGassi.Core.Interfaces;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Dateien als share ermöglicht.
/// Alle Ordner und Dateinamen sind relativ anzugeben.
/// z.B. test/Ordner1/order2
/// z.B. test/Ordner1/test.png
/// </summary>
public class LocalFileShareService : IFileShareService
{
private readonly string _root;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="root">Root-Folder für Shares</param>
[ExcludeFromCodeCoverage]
public LocalFileShareService(string root)
{
_root = root;
if (!Directory.Exists(_root))
{
Directory.CreateDirectory(_root);
}
}
/// <summary>
/// Auflisten der Ordner in einem Ordner
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Liste der Ordner im Share</returns>
public List<FolderResult> ListFolders(string shareName, string folder)
{
var result = new List<FolderResult>();
if (!string.IsNullOrWhiteSpace(shareName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var directory = sharePath;
if (!string.IsNullOrWhiteSpace(folder))
directory = Path.Combine(sharePath, folder);
var folders = Directory.GetDirectories(directory);
foreach (var folderInfo in folders)
{
var info = new DirectoryInfo(folderInfo);
var item = new FolderResult
{
Name = folderInfo.Replace(directory, "").TrimStart('\\'),
LastModified = info.LastWriteTimeUtc,
Created = info.CreationTimeUtc,
HasSubfolders = info.GetDirectories().Length > 0
};
result.Add(item);
}
}
return result;
}
/// <summary>
/// Gibt Informationen über einen Ordner zurück
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Ordner oder null</returns>
public FolderResult GetFolderInfo(string shareName, string folder)
{
if (!string.IsNullOrWhiteSpace(shareName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var folderInfo = sharePath;
if (!string.IsNullOrWhiteSpace(folder))
folderInfo = Path.Combine(sharePath, folder);
var info = new DirectoryInfo(folderInfo);
var item = new FolderResult
{
Name = folder,
LastModified = info.LastWriteTimeUtc,
Created = info.CreationTimeUtc,
HasSubfolders = info.GetDirectories().Length > 0
};
return item;
}
return null;
}
/// <summary>
/// Auflisten der Dateien in einem Ordner
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Liste der Dateien im Ordner</returns>
public List<FileResult> ListFiles(string shareName, string folder)
{
var result = new List<FileResult>();
if (!string.IsNullOrWhiteSpace(shareName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var directory = sharePath;
if (!string.IsNullOrWhiteSpace(folder))
directory = Path.Combine(sharePath, folder);
var files = Directory.GetFiles(directory);
foreach (var file in files)
{
var info = new FileInfo(file);
var item = new FileResult
{
Name = file.Replace(directory, "").TrimStart('\\'),
LastModified = info.LastWriteTimeUtc,
Created = info.CreationTimeUtc,
Size = info.Length
};
result.Add(item);
}
}
return result;
}
/// <summary>
/// Gibt Informationen über eine Datei zurück
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname relativ</param>
/// <returns>FileInfo oder null</returns>
public FileResult GetFileInfo(string shareName, string fileName)
{
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(fileName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var filePath = sharePath;
if (!string.IsNullOrWhiteSpace(fileName))
filePath = Path.Combine(sharePath, fileName);
if (File.Exists(filePath))
{
var info = new FileInfo(filePath);
var item = new FileResult
{
Name = fileName,
LastModified = info.LastWriteTimeUtc,
Created = info.CreationTimeUtc,
Size = info.Length
};
return item;
}
}
return null;
}
/// <summary>
/// Auflisten der Dateien und Ordner in einem Ordner
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Liste Dateien und Liste Ordner</returns>
public (List<FileResult>, List<FolderResult>) ListFilesAndFolders(string shareName, string folder)
{
throw new NotImplementedException();
}
/// <summary>
/// Anlegen eines Ordners.
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="newfolderName">Neuer Ordner</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public bool CreateFolder(string shareName, string newfolderName)
{
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(newfolderName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var folder = sharePath;
if (!string.IsNullOrWhiteSpace(folder))
folder = Path.Combine(sharePath, newfolderName);
try
{
Directory.CreateDirectory(folder);
return true;
}
catch
{
// ignored
}
}
return false;
}
/// <summary>
/// Speichern einer Datei
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <param name="file">Datei</param>
/// <param name="overwrite">true wenn überschrieben werden soll, false sonst</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public bool Store(string shareName, string fileName, byte[] file, bool overwrite = false)
{
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(fileName) && file != null && file.Length > 0)
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var filePath = sharePath;
if (!string.IsNullOrWhiteSpace(fileName))
filePath = Path.Combine(sharePath, fileName);
try
{
File.WriteAllBytes(filePath, file);
return true;
}
catch
{
// ignored
}
}
return false;
}
/// <summary>
/// Holt eine Datei
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <returns>Datei oder null, wenn nicht gefunden</returns>
public byte[] Get(string shareName, string fileName)
{
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(fileName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var filePath = sharePath;
if (!string.IsNullOrWhiteSpace(fileName))
filePath = Path.Combine(sharePath, fileName);
if (File.Exists(filePath))
return File.ReadAllBytes(filePath);
}
return null;
}
/// <summary>
/// Holt eine Datei als Stream
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <returns>Stream oder null, wenn nicht gefunden</returns>
public Stream GetStream(string shareName, string fileName)
{
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(fileName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var filePath = sharePath;
if (!string.IsNullOrWhiteSpace(fileName))
filePath = Path.Combine(sharePath, fileName);
if (File.Exists(filePath))
return File.OpenRead(filePath);
}
return null;
}
/// <summary>
/// Holt eine Datei als Stream
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <param name="start">Wo soll gestartet werden</param>
/// <param name="end">WoEnde</param>
/// <returns>Stream oder null, wenn nicht gefunden</returns>
public Stream GetStream(string shareName, string fileName, long start, long end)
{
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(fileName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var filePath = sharePath;
if (!string.IsNullOrWhiteSpace(fileName))
filePath = Path.Combine(sharePath, fileName);
if (File.Exists(filePath))
return File.OpenRead(filePath);
}
return null;
}
/// <summary>
/// Umbenennen einer Datei
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Aktueller Dateiname</param>
/// <param name="newFileName">Neuer Dateiname</param>
/// <param name="copy">Soll eine Kopie gemacht werden? Wenn nicht wird die Quelldatei gelöscht</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public bool RenameFile(string shareName, string fileName, string newFileName, bool copy)
{
var success = false;
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(fileName) && !string.IsNullOrWhiteSpace(newFileName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var filePath = sharePath;
if (!string.IsNullOrWhiteSpace(fileName))
filePath = Path.Combine(sharePath, fileName);
var newFilePath = sharePath;
if (!string.IsNullOrWhiteSpace(newFileName))
newFilePath = Path.Combine(sharePath, newFileName);
if (File.Exists(filePath) && !File.Exists(newFilePath))
{
try
{
if(!copy)
File.Move(filePath, newFilePath);
else
File.Copy(filePath, newFilePath);
success = true;
}
catch
{
success = false;
}
}
}
return success;
}
/// <summary>
/// Umbenennen eines Ordners
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Aktueller Ordnername</param>
/// <param name="newFolder">Neuer Ordnername</param>
/// <param name="copy">Soll eine Kopie gemacht werden? Wenn nicht wird der QuellOrdner gelöscht</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public bool RenameFolder(string shareName, string folder, string newFolder, bool copy)
{
var success = false;
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(folder) && !string.IsNullOrWhiteSpace(newFolder))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var folderPath = sharePath;
if (!string.IsNullOrWhiteSpace(folder))
folderPath = Path.Combine(sharePath, folder);
var newFolderPath = sharePath;
if (!string.IsNullOrWhiteSpace(newFolder))
newFolderPath = Path.Combine(sharePath, newFolder);
if (Directory.Exists(folderPath))
{
try
{
if (!copy && !Directory.Exists(newFolderPath))
Directory.Move(folderPath, newFolderPath);
else
DirectoryCopy(folderPath, newFolderPath, true);
success = true;
}
catch
{
success = false;
}
}
}
return success;
}
/// <summary>
/// Umbenennen eines Ordners - Ohne TransferManager
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Aktueller Ordnername</param>
/// <param name="newFolder">Neuer Ordnername</param>
/// <param name="copy">Soll eine Kopie gemacht werden? Wenn nicht wird der QuellOrdner gelöscht</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public bool RenameFolderEx(string shareName, string folder, string newFolder, bool copy)
{
var success = false;
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(folder) && !string.IsNullOrWhiteSpace(newFolder))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var folderPath = sharePath;
if (!string.IsNullOrWhiteSpace(folder))
folderPath = Path.Combine(sharePath, folder);
var newFolderPath = sharePath;
if (!string.IsNullOrWhiteSpace(newFolder))
newFolderPath = Path.Combine(sharePath, newFolder);
if (Directory.Exists(folderPath))
{
try
{
if (!copy && !Directory.Exists(newFolderPath))
Directory.Move(folderPath, newFolderPath);
else
DirectoryCopy(folderPath, newFolderPath, true);
success = true;
}
catch
{
success = false;
}
}
}
return success;
}
/// <summary>
/// Löschen einer Datei
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="fileName">Dateiname mit Ordnern relativ</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public bool DeleteFile(string shareName, string fileName)
{
var success = false;
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(fileName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var filePath = sharePath;
if (!string.IsNullOrWhiteSpace(fileName))
filePath = Path.Combine(sharePath, fileName);
if (File.Exists(filePath))
{
try
{
File.Delete(filePath);
success = true;
}
catch
{
success = false;
}
}
}
return success;
}
/// <summary>
/// Löschen eines Ordners
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public bool DeleteFolder(string shareName, string folder)
{
var success = false;
if (!string.IsNullOrWhiteSpace(shareName) && !string.IsNullOrWhiteSpace(folder))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var folderPath = sharePath;
if (!string.IsNullOrWhiteSpace(folder))
folderPath = Path.Combine(sharePath, folder);
if (Directory.Exists(folderPath))
{
try
{
Directory.Delete(folderPath,true);
success = true;
}
catch
{
success = false;
}
}
}
return success;
}
/// <summary>
/// Löschen eines Shares
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public bool DeleteShare(string shareName)
{
var success = false;
if (!string.IsNullOrWhiteSpace(shareName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
if (Directory.Exists(sharePath))
{
try
{
Directory.Delete(sharePath, true);
success = true;
}
catch
{
success = false;
}
}
}
return success;
}
/// <summary>
/// Gibt die größe des Ordners (Dateien) als byte zurück
/// </summary>
/// <param name="shareName">Name des Shares</param>
/// <param name="folder">Ordner</param>
/// <returns>Liste der Ordner im Share</returns>
public long GetFolderSize(string shareName, string folder)
{
var size = 0L;
if (!string.IsNullOrWhiteSpace(shareName))
{
var sharePath = Path.Combine(_root, shareName);
if (!Directory.Exists(sharePath))
Directory.CreateDirectory(sharePath);
var directory = sharePath;
if (!string.IsNullOrWhiteSpace(folder))
directory = Path.Combine(sharePath, folder);
var directories = Directory.GetDirectories(directory);
foreach (var dir in directories)
{
InternalGetFolderSize(dir, ref size);
}
var files = Directory.GetFiles(directory);
foreach (var file in files)
{
var info = new FileInfo(file);
size += info.Length;
}
}
return size;
}
/// <summary>
/// Deep-Copy eines Ordners
/// </summary>
/// <param name="sourceDirName">Quelle</param>
/// <param name="destDirName">Ziel</param>
/// <param name="copySubDirs">Sollen Unterordner und Daten auch kopiert werden</param>
[ExcludeFromCodeCoverage]
private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
{
// Get the subdirectories for the specified directory.
var dir = new DirectoryInfo(sourceDirName);
if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}
var dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
Directory.CreateDirectory(destDirName);
// Get the files in the directory and copy them to the new location.
var files = dir.GetFiles();
foreach (var file in files)
{
var tempPath = Path.Combine(destDirName, file.Name);
file.CopyTo(tempPath, false);
}
// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (var subdir in dirs)
{
string tempPath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, tempPath, true);
}
}
}
/// <summary>
/// Hilfsmethode - rukrsiv Ordnergrößen anhand der Dateien berechnen
/// </summary>
/// <param name="directory">Ordner</param>
/// <param name="size">Größe in bytes</param>
[ExcludeFromCodeCoverage]
private void InternalGetFolderSize(string directory, ref long size)
{
var directories = Directory.GetDirectories(directory);
foreach (var dir in directories)
{
InternalGetFolderSize(dir, ref size);
}
var files = Directory.GetFiles(directory);
foreach (var file in files)
{
var info = new FileInfo(file);
size += info.Length;
}
}
}
}

View File

@ -0,0 +1,393 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Messages;
using gehGassi.Domain.News;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Konversationen und Nachrichten realisiert
/// </summary>
public class MessageService : ServiceBase<Message>, IMessageService
{
private readonly IRepository<Conversation> _conversationRepository;
private readonly IRepository<ConversationWithNames> _conversationNamesRepository;
private readonly IRepository<MessageCopy> _copyRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public MessageService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_conversationRepository = unitOfWork.GetRepository<Conversation>();
_conversationNamesRepository = unitOfWork.GetRepository<ConversationWithNames>();
_copyRepository = unitOfWork.GetRepository<MessageCopy>();
}
/// <summary>
/// Gibt eine Liste der Konversationen eines Benutzers zurück
/// </summary>
/// <param name="senderId">Id des Senders</param>
/// /// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der Konversationen</returns>
public async Task<List<Conversation>> GetConversationsAsync(string senderId, DateTimeOffset? lastUpdate)
{
var query = _conversationRepository.Query(c => c.SenderId == senderId || c.ReceiverId == senderId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste der Konversationen mit Namen aufgelöst eines Benutzers zurück
/// </summary>
/// <param name="senderId">Id des Senders</param>
/// <returns>Liste der Konversationen</returns>
public async Task<List<ConversationWithNames>> GetConversationsWithNamesAsync(string senderId)
{
var query = _conversationNamesRepository.Query(c => c.SenderId == senderId || c.ReceiverId == senderId);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste der Konversationen eines Benutzers zurück
/// </summary>
/// <param name="senderId">Id des Senders</param>
/// <param name="filter">Filter der in Namen gesucht werden soll</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der Konversationen</returns>
public async Task<List<Conversation>> GetConversationsAsync(string senderId, string filter, DateTimeOffset? lastUpdate)
{
var query = _conversationRepository.Query(c => c.SenderId == senderId || c.ReceiverId == senderId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Konversation anhand der Teilnehmer zurück
/// </summary>
/// <param name="senderId">Id des Senders</param>
/// <param name="receiverId">Id des Empfängers</param>
/// <returns>Konversation oder null wenn nicht gefunden</returns>
public async Task<Conversation> GetConversationAsync(string senderId, string receiverId)
{
var query = _conversationRepository.Query(c => (c.SenderId == senderId && c.ReceiverId == receiverId) || (c.SenderId == receiverId && c.ReceiverId == senderId));
var found = await query.FirstOrDefaultAsync().ConfigureAwait(false);
return found;
}
/// <summary>
/// Gibt eine gefilterte Liste von Konversationen mit Namen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Konversationen</returns>
public IQueryable<ConversationWithNames> FilterConversationsWithNames(string filter)
{
return _conversationNamesRepository.QueryView(c => c.SenderName.Contains(filter) || c.ReceiverName.Contains(filter));
}
/// <summary>
/// Hinzufügen einer Konversation
/// </summary>
/// <param name="senderId">Id des Senders</param>
/// <param name="receiverId">Id des Empfängers</param>
/// <param name="userName">Benutzer der die Änderung vornimmt</param>
/// <returns>Konversation</returns>
public async Task<Conversation> AddConversationAsync(string senderId, string receiverId, string userName)
{
var conversation = new Conversation()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow,
UpdatedAt = DateTimeOffset.UtcNow,
SenderId = senderId,
ReceiverId = receiverId,
LastMessage = null,
MessageCount = 0
};
_conversationRepository.Add(conversation);
await CommitAsync(userName).ConfigureAwait(false);
return conversation;
}
/// <summary>
/// Hinzufügen einer Konversation
/// </summary>
/// <param name="id">Id des Konversation</param>
/// <param name="senderId">Id des Senders</param>
/// <param name="receiverId">Id des Empfängers</param>
/// <param name="userName">Benutzer der die Änderung vornimmt</param>
/// <returns>Konversation</returns>
public async Task<Conversation> AddConversationAsync(string id, string senderId, string receiverId, string userName)
{
var conversation = new Conversation()
{
Id = id,
Created = DateTimeOffset.UtcNow,
UpdatedAt = DateTimeOffset.UtcNow,
SenderId = senderId,
ReceiverId = receiverId,
LastMessage = null,
MessageCount = 0
};
_conversationRepository.Add(conversation);
await CommitAsync(userName).ConfigureAwait(false);
return conversation;
}
/// <summary>
/// Entfernen einer Konversation
/// </summary>
/// <param name="id">Id der Konversation</param>
/// <param name="userName">Benutzer der die Änderung vornimmt</param>
public async Task<bool> RemoveConversationAsync(string id, string userName)
{
var conversation = await _conversationRepository.FirstOrDefaultAsync(c => c.Id == id).ConfigureAwait(false);
if (conversation != null)
{
_conversationRepository.Remove(conversation);
await CommitAsync(userName).ConfigureAwait(false);
return true;
}
return false;
}
/// <summary>
/// Entfernen einer Konversation nach AppUser
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>Anzahl Konversationen</returns>
public async Task<int> RemoveConversationByAppUserAsync(string appUserId)
{
var conversations = await Repository.FindAsync(c => c.ReceiverId == appUserId || c.SenderId == appUserId).ConfigureAwait(false);
foreach (var conversation in conversations)
{
Repository.Remove(conversation);
}
return conversations.Count();
}
/// <summary>
/// Gibt eine Liste noch nicht zugestellter Nachrichten für einen Benutzer zurück.
/// </summary>
/// <param name="senderId">Id des Users - wird als Receiver gesucht</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste nicht erhaltener Nachrichten</returns>
public async Task<List<Message>> GetMessagesAsync(string senderId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.ReceiverId == senderId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste noch nicht zugestellter Nachrichten in einer Konversation für einen Benutzer zurück.
/// </summary>
/// <param name="senderId">Id des Users - wird als Receiver gesucht</param>
/// <param name="conversationId">Id der Konversation</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste nicht erhaltener Nachrichten</returns>
public async Task<List<Message>> GetMessagesAsync(string senderId, string conversationId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.ReceiverId == senderId && c.ConversationId == conversationId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Berstätigen des Erhaltes von Nachrichten für einen Benutzer. Alle die älter als das UpdateDatum sind werden gelöscht
/// </summary>
/// <param name="senderId">Id des Users - wird als Receiver gesucht</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <param name="userName">Benutzer der die Änderung vornimmt</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> ConfirmMessagesAsync(string senderId, DateTimeOffset lastUpdate, string userName)
{
var query = Repository.Query(c => c.ReceiverId == senderId && c.UpdatedAt <= lastUpdate);
var list = await query.ToListAsync().ConfigureAwait(false);
if (list.Any())
{
await SetMessageCopiesRead(list, DateTimeOffset.UtcNow).ConfigureAwait(false);
Repository.RemoveRange(list);
await CommitAsync(userName).ConfigureAwait(false);
}
return true;
}
/// <summary>
/// Berstätigen des Erhaltes von Nachrichten für eine Konversation für einen Benutzer. Alle die älter als das UpdateDatum sind werden gelöscht
/// </summary>
/// <param name="senderId">Id des Users - wird als Receiver gesucht</param>
/// <param name="conversationId">Id der Konversation</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <param name="userName">Benutzer der die Änderung vornimmt</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> ConfirmMessagesAsync(string senderId, string conversationId, DateTimeOffset lastUpdate, string userName)
{
var query = Repository.Query(c => c.ReceiverId == senderId && c.ConversationId == conversationId && c.UpdatedAt <= lastUpdate);
var list = await query.ToListAsync().ConfigureAwait(false);
if (list.Any())
{
await SetMessageCopiesRead(list, DateTimeOffset.UtcNow).ConfigureAwait(false);
Repository.RemoveRange(list);
await CommitAsync(userName).ConfigureAwait(false);
}
return true;
}
/// <summary>
/// Hinzufügen einer Nachricht
/// </summary>
/// <param name="message">Nachricht</param>
/// <param name="userName">Benutzer der die Änderung vornimmt</param>
/// <returns>Angelegte Nachricht</returns>
public async Task<Message> AddMessageAsync(Message message, string userName)
{
Repository.Add(message);
var copy = new MessageCopy()
{
Id = message.Id,
Version = message.Version,
UpdatedAt = message.UpdatedAt,
Deleted = message.Deleted,
ConversationId = message.ConversationId,
SenderId = message.SenderId,
ReceiverId = message.ReceiverId,
Content = message.Content,
Read = false,
ReadDate = null
};
_copyRepository.Add(copy);
await CommitAsync(userName).ConfigureAwait(false);
return message;
}
/// <summary>
/// Neusetzen des UpdatedAt Datums falls sich das foto eines Benutzers geändert hat
/// </summary>
/// <param name="appUserId"></param>
/// <param name="updatedAt"></param>
/// <param name="userName"></param>
/// <returns></returns>
public async Task<int> SetUpdatedAtAsync(string appUserId, DateTimeOffset updatedAt, string userName)
{
var conversations = await _conversationRepository.FindAsync(c => (c.ReceiverId == appUserId || c.SenderId == appUserId )&& c.Deleted == false).ConfigureAwait(false);
foreach (var conversation in conversations)
{
conversation.UpdatedAt = updatedAt;
}
await CommitAsync(userName).ConfigureAwait(false);
return conversations.Count();
}
/// <summary>
/// Gibt die Anzahl der noch nicht gelesenen Nachrichten für einen Benutzer zurück
/// </summary>
/// <param name="receiverId">Id des Empfängers</param>
/// <param name="conversationId">Id der Konversation</param>
/// <returns>Anzahl ungelesene Nachrichten</returns>
public async Task<int> CountOpenMessagesAsync(string receiverId, string conversationId)
{
var messageCount = await Repository.Query(m => m.ReceiverId == receiverId && m.ConversationId == conversationId).CountAsync().ConfigureAwait(false);
return messageCount;
}
/// <summary>
/// Entfernt Nachrichten die in einem bestimmten Zeitraum nicht gelesen wurden
/// </summary>
/// <param name="olderThanDate">Nachricht muss älter als dieses Datum sein</param>
/// <returns>Anzahl gelöschte Nachrichten</returns>
public async Task<int> RemoveUnreadMessagesAsync(DateTimeOffset olderThanDate)
{
var messages = await Repository.FindAsync(c => c.UpdatedAt < olderThanDate);
foreach (var message in messages)
{
Repository.Remove(message);
}
return messages.Count();
}
/// <summary>
/// Entfernt Nachrichten-Kopien die alter als ein Datum sind
/// </summary>
/// <param name="olderThanDate">Nachricht muss älter als dieses Datum sein</param>
/// <returns>Anzahl gelöschte Nachrichten</returns>
public async Task<int> RemoveMessageCopiesAsync(DateTimeOffset olderThanDate)
{
var messages = await _copyRepository.FindAsync(c => c.UpdatedAt < olderThanDate);
foreach (var message in messages)
{
_copyRepository.Remove(message);
}
return messages.Count();
}
/// <summary>
/// Gibt die Nachrichten-Kopien einer Konversation zurück
/// </summary>
/// <param name="conversationId">Id der Konversation</param>
/// <returns>Liste nicht erhaltener Nachrichten</returns>
public async Task<List<MessageCopy>> GetMessageCopiesAsync(string conversationId)
{
var messages = (await _copyRepository.FindAsync(c => c.ConversationId == conversationId).ConfigureAwait(false)).OrderBy(c => c.UpdatedAt).ToList();
return messages;
}
#region private
/// <summary>
/// Setzt die Kopiene einer Liste von Nachrichten auf gelesen
/// </summary>
/// <param name="messages">Liste der Nachrichten</param>
/// <param name="readDate">Datum gelesen</param>
/// <returns>Task</returns>
private async Task SetMessageCopiesRead(List<Message> messages, DateTimeOffset readDate)
{
foreach (var message in messages)
{
var copy = await _copyRepository.GetAsync(message.Id);
if (copy != null)
{
copy.Read = true;
copy.ReadDate = readDate;
}
}
}
#endregion
}
}

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.News;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Kategorien für News realisiert
/// </summary>
public class NewsCategoryService : ServiceBase<NewsCategory>, INewsCategoryService
{
private readonly IRepository<NewsCategoryWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public NewsCategoryService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<NewsCategoryWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Kategorien zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kategorien</returns>
public IQueryable<NewsCategory> Filter(string filter)
{
return Repository.Query(c => c.Title.Contains(filter));
}
/// <summary>
/// Erstellen einer Kategorie
/// </summary>
/// <returns>Kategorie</returns>
public NewsCategory Create()
{
var item = new NewsCategory()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Kategorien zurück. Sucht nur im Titel
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Kategorien</returns>
public async Task<List<NewsCategory>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Title.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Kategorien mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Kategorien</returns>
public IQueryable<NewsCategoryWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsCategoryWithNames({0},{1})", c => c.Title.Contains(filter) || c.Name.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine Liste von Kategorien zurück, welche nach dem glieferten Datum geändert wurden.
/// Wird kein Datum angegeben, werden alle zurückgegeben.
/// Es werden alle Kategorien zurückgegeben - egal ob on- oder offline.
/// </summary>
/// <param name="lastUpdate">Letztes Update oder null wenn noch keines</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste von Kategorien</returns>
public async Task<List<NewsCategoryWithNames>> GetForSyncAsync(DateTimeOffset? lastUpdate, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
IQueryable<NewsCategoryWithNames> baseQuery;
if (lastUpdate == null)
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsCategoryWithNames({0},{1})", null, language, fallback);
else
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsCategoryWithNames({0},{1})", c => c.UpdatedAt > lastUpdate, language, fallback);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,248 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Common.Data;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using gehGassi.Domain.News;
using Microsoft.EntityFrameworkCore;
using NetTopologySuite;
using NetTopologySuite.Geometries;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von News realisiert
/// </summary>
public class NewsService : ServiceBase<News>, INewsService
{
private readonly IRepository<NewsWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public NewsService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<NewsWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von News zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener News</returns>
public IQueryable<News> Filter(string filter)
{
return Repository.Query(c => c.Headline.Contains(filter));
}
/// <summary>
/// Erstellen einer News
/// </summary>
/// <returns>News</returns>
public News Create()
{
var item = new News()
{
Status = NewsStatus.Draft,
Visibility = NewsVisibility.None,
GeoMode = GeoMode.None,
Created = DateTimeOffset.UtcNow,
Location = new Point(0,0){SRID = 4326}
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von News zurück. Sucht nur im Titel
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener News</returns>
public async Task<List<News>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Headline.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von News mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener News</returns>
public IQueryable<NewsWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsWithNames({0},{1})", c => c.Headline.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine gefilterte Liste von News mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="states">Liste nach Bundesländern nach Filter</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="countries">Liste von Ländern nach Filter</param>
/// <returns>Liste betroffener News</returns>
public IQueryable<NewsWithNames> FilterWithNames(string filter, List<string> countries, List<string> states, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsWithNames({0},{1})", c => c.Headline.Contains(filter), language, fallback);
if (countries.Any())
baseQuery = baseQuery.Where(c => c.CountryCode != null && c.CountryCode != "" && countries.Contains(c.CountryCode));
if (states.Any())
baseQuery = baseQuery.Where(c => c.State != null && c.State != "" && states.Contains(c.State));
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine Liste von News-Meldungen für die App zurück. Es werden nur laufende News berücksichtigt
/// </summary>
/// <param name="visibility">Sichtbar für wen? Wird adaptiert!</param>
/// <param name="categoryId">Gewünschte Kategorie oder leer wenn alle</param>
/// <param name="country">Land</param>
/// <param name="state">Bundesland</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste passender News</returns>
public async Task<(int total, List<NewsWithNames> list)> GetForAppAsync(NewsVisibility visibility, string categoryId, string country, string state, double? lat, double? lng, string language, string fallback, int skip, int take)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
if(!string.IsNullOrWhiteSpace(country))
country = country.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(state))
state = state.ToUpperInvariant();
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng.Value, lat.Value));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsWithNames({0},{1})", c => c.CategoryId == categoryId && c.Deleted == false && c.Status == NewsStatus.Running && c.Language == language && (c.Visibility == visibility || c.Visibility == NewsVisibility.DogWalkerAndOwners) &&
(c.GeoMode == GeoMode.None ||
(c.GeoMode == GeoMode.Country && c.CountryCode == country) ||
(c.GeoMode == GeoMode.CountryAndState && c.CountryCode == country && c.State == state) ||
(c.GeoMode == GeoMode.Location && c.Location.IsWithinDistance(location, c.Radius * 1000))), language, fallback);
//if (geoMode == GeoMode.None)
//{
// var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsWithNames({0},{1})", c => c.Deleted == false && c.Status == NewsStatus.Running && c.Language == language && c.GeoMode == GeoMode.None && (c.Visibility == visibility || c.Visibility == NewsVisibility.DogWalkerAndOwners), language, fallback);
// if(!string.IsNullOrEmpty(categoryId))
// baseQuery = baseQuery.Where(c => c.CategoryId == categoryId);
// return await baseQuery.ToListAsync().ConfigureAwait(false);
//}
//else if (geoMode == GeoMode.Country && !string.IsNullOrWhiteSpace(country))
//{
// var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsWithNames({0},{1})", c => c.Deleted == false && c.Status == NewsStatus.Running && c.Language == language && c.GeoMode == GeoMode.Country && c.CountryCode == country && (c.Visibility == visibility || c.Visibility == NewsVisibility.DogWalkerAndOwners), language, fallback);
// if (!string.IsNullOrEmpty(categoryId))
// baseQuery = baseQuery.Where(c => c.CategoryId == categoryId);
// return await baseQuery.ToListAsync().ConfigureAwait(false);
//}
//else if (geoMode == GeoMode.CountryAndState && !string.IsNullOrWhiteSpace(country) && !string.IsNullOrWhiteSpace(state))
//{
// var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsWithNames({0},{1})", c => c.Deleted == false && c.Status == NewsStatus.Running && c.Language == language && c.GeoMode == GeoMode.CountryAndState && c.CountryCode == country && c.State == state && (c.Visibility == visibility || c.Visibility == NewsVisibility.DogWalkerAndOwners), language, fallback);
// if (!string.IsNullOrEmpty(categoryId))
// baseQuery = baseQuery.Where(c => c.CategoryId == categoryId);
// return await baseQuery.ToListAsync().ConfigureAwait(false);
//}
//else if(geoMode == GeoMode.Location && lat.HasValue && lng.HasValue)
//{
// //Lokalisierungsmodus --> Andere Abfrage weil wir die Distanz zu den Geokoordinaten brauchen
// var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
// var location = geometryFactory.CreatePoint(new Coordinate(lng.Value, lat.Value));
// var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsWithNames({0},{1})", c => c.Deleted == false && c.Status == NewsStatus.Running && c.Language == language && c.GeoMode == GeoMode.Location && (c.Visibility == visibility || c.Visibility == NewsVisibility.DogWalkerAndOwners), language, fallback);
// baseQuery = baseQuery.Where(c => c.Location.IsWithinDistance(location, c.Radius * 1000));
// if (!string.IsNullOrEmpty(categoryId))
// baseQuery = baseQuery.Where(c => c.CategoryId == categoryId);
// return await baseQuery.ToListAsync().ConfigureAwait(false);
// //var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsWithNamesDistance({0},{1},{2},{3})", c => c.Deleted == false && c.Status == NewsStatus.Running && c.Language == language && c.GeoMode == GeoMode.Location && (c.Visibility == visibility || c.Visibility == NewsVisibility.DogWalkerAndOwners), language, fallback, lat.Value, lng.Value);
// //if (!string.IsNullOrEmpty(categoryId))
// // baseQuery = baseQuery.Where(c => c.CategoryId == categoryId);
// //return await baseQuery.ToListAsync().ConfigureAwait(false);
//}
var total = await baseQuery.CountAsync().ConfigureAwait(false);
var list = await baseQuery.OrderByDescending(c => c.Start).ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
/// <summary>
/// Gibt eine News mit Sprachen aufgelöst zurück
/// </summary>
/// <param name="newsId">Id der News</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>News mit Namen aufgelöst</returns>
public async Task<NewsWithNames> GetWithNamesAsync(long newsId, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_NewsWithNames({0},{1})", c => c.Deleted == false && c.Id == newsId, language, fallback);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
/// <summary>
/// Starten von freigegebenen News wenn diese das Start-Datum erreicht haben
/// </summary>
/// <returns>Anzahl gestartete News</returns>
public async Task<int> StartApprovedAsnyc()
{
var items = await Repository.FindAsync(c => c.Status == NewsStatus.Approved && c.Deleted == false && c.Start <= DateTimeOffset.UtcNow && c.End.Value >= DateTimeOffset.UtcNow);
foreach (var item in items)
{
item.Status = NewsStatus.Running;
}
return items.Count();
}
/// <summary>
/// Stoppen von abgelaufenen News wenn diese das Enddatum erreicht haben
/// </summary>
/// <returns>Anzahl gestoppte News</returns>
public async Task<int> StopRunningAsync()
{
var items = await Repository.FindAsync(c => c.Status == NewsStatus.Running && c.Deleted == false && c.End.Value <= DateTimeOffset.UtcNow);
foreach (var item in items)
{
item.Status = NewsStatus.Ended;
}
return items.Count();
}
}
}

View File

@ -0,0 +1,756 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using gehGassi.Domain.Customers;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.Shop;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Bestellungen realisiert
/// </summary>
public class OrderService : ServiceBase<Order>, IOrderService
{
private readonly IRepository<OrderItem> _itemRepository;
private readonly IRepository<Customer> _customerRepository;
private readonly IRepository<AppUser> _appUserRepository;
private readonly IRepository<Product> _productRepository;
private readonly IRepository<ProductWithNames> _productNamesRepository;
private readonly IRepository<Listing> _listingRepository;
private readonly IRepository<Banner> _bannerRepository;
private readonly IRepository<Advertisement> _advertisementRepository;
private readonly IRepository<Pin> _pinRepository;
private readonly IRepository<OrderPhysical> _physicalRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public OrderService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_itemRepository = unitOfWork.GetRepository<OrderItem>();
_customerRepository = unitOfWork.GetRepository<Customer>();
_appUserRepository = unitOfWork.GetRepository<AppUser>();
_productRepository = unitOfWork.GetRepository<Product>();
_productNamesRepository = unitOfWork.GetRepository<ProductWithNames>();
_listingRepository = unitOfWork.GetRepository<Listing>();
_bannerRepository = unitOfWork.GetRepository<Banner>();
_advertisementRepository = unitOfWork.GetRepository<Advertisement>();
_pinRepository = unitOfWork.GetRepository<Pin>();
_physicalRepository = unitOfWork.GetRepository<OrderPhysical>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Bestellungen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="includeDeleted">Gelöschte Daten inkludieren</param>
/// <returns>List betroffener Bestellungen</returns>
public IQueryable<Order> Filter(string filter, bool includeDeleted)
{
if (includeDeleted)
return Repository.Query(c => c.CustomerName.Contains(filter) || c.AppUserName.Contains(filter));
else
return Repository.Query(c => c.Deleted == false && (c.CustomerName.Contains(filter) || c.AppUserName.Contains(filter)));
}
/// <summary>
/// Gibt eine gefilterte Liste von Bestellungen für einen Kunden zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="includeDeleted">Gelöschte Daten inkludieren</param>
/// <returns>List betroffener Bestellungen</returns>
public IQueryable<Order> FilterByCustomer(string filter, long customerId, bool includeDeleted)
{
if (includeDeleted)
return Repository.Query(c => c.CustomerId == customerId && (c.CustomerName.Contains(filter)));
else
return Repository.Query(c => c.CustomerId == customerId && c.Deleted == false && (c.CustomerName.Contains(filter)));
}
/// <summary>
/// Gibt eine gefilterte Liste von Bestellungen für einen App-User zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="appUserId">Id des Hundebesitzers</param>
/// <param name="includeDeleted">Gelöschte Daten inkludieren</param>
/// <returns>List betroffener Bestellungen</returns>
public IQueryable<Order> FilterByAppUser(string filter, string appUserId, bool includeDeleted)
{
if (includeDeleted)
return Repository.Query(c => c.AppUserId == appUserId && (c.AppUserName.Contains(filter) || c.AppUserCompany.Contains(filter)));
else
return Repository.Query(c => c.AppUserId == appUserId && c.Deleted == false && (c.AppUserName.Contains(filter) || c.AppUserCompany.Contains(filter)));
}
/// <summary>
/// Erstellen einer Bestellung
/// </summary>
/// <returns>Bestellung</returns>
public Order Create()
{
var item = new Order
{
UniqueId = Guid.NewGuid(),
Total = 0,
TotalGross = 0,
Shipment = 0,
ShipmentGross = 0,
Created = DateTime.UtcNow,
LastUpdate = DateTime.UtcNow
};
return item;
}
/// <summary>
/// Erstellen einer Bestellung
/// </summary>
/// <param name="cart">Warenkorb</param>
/// <param name="cartItems">Artikel im Warenkorb</param>
/// <param name="paymentType">Zahlungsart</param>
/// <param name="paymentStatus">Status der Zahlung</param>
/// <param name="shipmentType">Art der Zustellung</param>
/// <param name="shipmentStatus">Versandstatus</param>
/// <param name="deliveryAddress">Lieferadresse</param>
/// <param name="billingAddress">Rechnungsadresse</param>
/// <param name="deliveryAddressIsBilling">Lieferadresse ist auch Rechnungsadresse</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="userName">Benutzer der die Bestellung anlegt</param>
/// <returns>Bestellung</returns>
public async Task<Order> CreateAsync(Cart cart, List<CartItem> cartItems, PaymentType paymentType, PaymentStatus paymentStatus, ShipmentType shipmentType, ShipmentStatus shipmentStatus,
OrderAddress deliveryAddress, OrderAddress billingAddress, bool deliveryAddressIsBilling, string language, string fallback, string userName)
{
var order = Create();
order.OrderSource = cart.OrderSource;
order.Number = await GetNextNumberAsync();
order.Counter = await GetNextCounterAsync();
order.Subtotal = cart.Total;
order.SubtotalGross = cart.TotalGross;
order.Shipment = cart.Shipment;
order.ShipmentGross = cart.ShipmentGross;
order.Total = order.Subtotal + order.Shipment;
order.TotalGross = order.SubtotalGross + order.ShipmentGross;
order.OrderStatus = OrderStatus.Pending;
order.PaymentType = paymentType;
order.PaymentStatus = paymentStatus;
order.ShipmentType = shipmentType;
order.ShipmentStatus = shipmentStatus;
order.DeliveryAddress = deliveryAddress;
order.BillingAddress = billingAddress;
order.Comment = string.Empty;
order.TaxRatesJson = cart.TaxRatesJson;
order.DeliveryAddressIsBilling = deliveryAddressIsBilling;
order.Created = DateTime.UtcNow;
order.LastUpdate = DateTime.UtcNow;
order.AcceptTermsAndConditionsDate = DateTime.UtcNow;
order.AcceptGdprDate = DateTime.UtcNow;
order.CustomerId = null;
order.CustomerName = string.Empty;
order.AppUserId = string.Empty;
order.AppUserName = string.Empty;
order.AppUserCompany = string.Empty;
switch (order.OrderSource)
{
case OrderSource.Customer:
var customer = await _customerRepository.GetAsync(cart.CustomerId);
order.CustomerId = customer.Id;
order.CustomerName = customer.Name;
break;
case OrderSource.AppUserDogOwner:
case OrderSource.AppUserDogWalker:
case OrderSource.AppUserBoth:
var dogOwner = await _appUserRepository.GetAsync(cart.AppUserId);
order.AppUserId = dogOwner.Id;
order.AppUserName = $"{dogOwner.FirstName} {dogOwner.LastName}";
order.AppUserCompany = string.Empty;
break;
default:
throw new ArgumentOutOfRangeException();
}
Repository.Add(order);
await CommitAsync(userName);
//Items
foreach (var cartItem in cartItems)
{
var product = await _productRepository.FirstOrDefaultAsync(c => c.Id == cartItem.ProductId);
var orderItem = CreateItem();
orderItem.OrderId = order.Id;
orderItem.ProductId = cartItem.ProductId;
orderItem.ProductCategoryId = product.ProductCategoryId;
orderItem.Name = product.Name;
orderItem.NameDefault = product.Get("Name", fallback);
orderItem.Sku = product.Sku;
orderItem.ProductType = cartItem.ProductType;
orderItem.ItemId = cartItem.ItemId;
orderItem.TaxRateId = product.TaxRateId;
orderItem.IsTaxExempt = product.IsTaxExempt;
orderItem.IsShipEnabled = product.IsShipEnabled;
orderItem.IsFreeShipping = product.IsFreeShipping;
orderItem.AdditionalShippingCharge = product.AdditionalShippingCharge;
orderItem.Quantity = cartItem.Quantity;
orderItem.Price = cartItem.Price;
orderItem.PriceGross = cartItem.PriceGross;
orderItem.Price2 = cartItem.Price2;
orderItem.Price2Gross = cartItem.Price2Gross;
orderItem.Total = cartItem.Total;
orderItem.TotalGross = cartItem.TotalGross;
orderItem.Weight = product.Weight;
orderItem.Length = product.Length;
orderItem.Width = product.Width;
orderItem.Height = product.Height;
orderItem.Created = DateTimeOffset.UtcNow;
orderItem.LastUpdate = DateTimeOffset.UtcNow;
_itemRepository.Add(orderItem);
}
await CommitAsync(userName);
await SetOrderAndPaymentAsync(paymentType, paymentStatus, null, userName, order);
return order;
}
/// <summary>
/// Aktualisieren einer Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung die aktualisiert werden soll</param>
/// <param name="cart">Warenkorb</param>
/// <param name="cartItems">Artikel im Warenkorb</param>
/// <param name="paymentType">Zahlungsart</param>
/// <param name="paymentStatus">Status der Zahlung</param>
/// <param name="shipmentType">Art der Zustellung</param>
/// <param name="shipmentStatus">Versandstatus</param>
/// <param name="deliveryAddress">Lieferadresse</param>
/// <param name="billingAddress">Rechnungsadresse</param>
/// <param name="deliveryAddressIsBilling">Lieferadresse ist auch Rechnungsadresse</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="userName">Benutzer der die Bestellung anlegt</param>
/// <returns>Bestellung</returns>
public async Task<Order> UpdateAsync(long orderId, Cart cart, List<CartItem> cartItems, PaymentType paymentType, PaymentStatus paymentStatus, ShipmentType shipmentType, ShipmentStatus shipmentStatus,
OrderAddress deliveryAddress, OrderAddress billingAddress, bool deliveryAddressIsBilling, string language, string fallback, string userName)
{
var order = await Repository.GetAsync(orderId);
order.Subtotal = cart.Total;
order.SubtotalGross = cart.TotalGross;
order.Shipment = cart.Shipment;
order.ShipmentGross = cart.ShipmentGross;
order.Total = order.Subtotal + order.Shipment;
order.TotalGross = order.SubtotalGross + order.ShipmentGross;
order.OrderStatus = OrderStatus.Pending;
order.PaymentType = paymentType;
order.PaymentStatus = paymentStatus;
order.ShipmentType = shipmentType;
order.ShipmentStatus = shipmentStatus;
order.BillingAddress.Title = billingAddress.Title;
order.BillingAddress.FirstName = billingAddress.FirstName;
order.BillingAddress.LastName = billingAddress.LastName;
order.BillingAddress.Company = billingAddress.Company;
order.BillingAddress.Vat = billingAddress.Vat;
order.BillingAddress.AddressLine1 = billingAddress.AddressLine1;
order.BillingAddress.AddressLine2 = billingAddress.AddressLine2;
order.BillingAddress.Zip = billingAddress.Zip;
order.BillingAddress.City = billingAddress.City;
order.BillingAddress.State = billingAddress.State;
order.BillingAddress.CountryCode = billingAddress.CountryCode;
order.DeliveryAddress.Title = deliveryAddress.Title;
order.DeliveryAddress.FirstName = deliveryAddress.FirstName;
order.DeliveryAddress.LastName = deliveryAddress.LastName;
order.DeliveryAddress.Company = deliveryAddress.Company;
order.DeliveryAddress.Vat = deliveryAddress.Vat;
order.DeliveryAddress.AddressLine1 = deliveryAddress.AddressLine1;
order.DeliveryAddress.AddressLine2 = deliveryAddress.AddressLine2;
order.DeliveryAddress.Zip = deliveryAddress.Zip;
order.DeliveryAddress.City = deliveryAddress.City;
order.DeliveryAddress.State = deliveryAddress.State;
order.DeliveryAddress.CountryCode = deliveryAddress.CountryCode;
order.Comment = string.Empty;
order.TaxRatesJson = cart.TaxRatesJson;
order.DeliveryAddressIsBilling = deliveryAddressIsBilling;
order.LastUpdate = DateTime.UtcNow;
order.AcceptTermsAndConditionsDate = DateTime.UtcNow;
order.AcceptGdprDate = DateTime.UtcNow;
await CommitAsync(userName);
//Zurücksetzen der Relationen weil neue OrderItems angelegt werden
await ResetOrderAsync(userName, order);
//Items
var oldItems = await _itemRepository.FindAsync(c => c.OrderId == orderId);
foreach (var orderItem in oldItems)
{
_itemRepository.Remove(orderItem);
}
await CommitAsync(userName);
//Items
foreach (var cartItem in cartItems)
{
var product = await _productRepository.FirstOrDefaultAsync(c => c.Id == cartItem.ProductId);
var orderItem = CreateItem();
orderItem.OrderId = order.Id;
orderItem.ProductId = cartItem.ProductId;
orderItem.ProductCategoryId = product.ProductCategoryId;
orderItem.Name = product.Name;
orderItem.NameDefault = product.Get("Name", fallback);
orderItem.Sku = product.Sku;
orderItem.ProductType = cartItem.ProductType;
orderItem.ItemId = cartItem.ItemId;
orderItem.TaxRateId = product.TaxRateId;
orderItem.IsTaxExempt = product.IsTaxExempt;
orderItem.IsShipEnabled = product.IsShipEnabled;
orderItem.IsFreeShipping = product.IsFreeShipping;
orderItem.AdditionalShippingCharge = product.AdditionalShippingCharge;
orderItem.Quantity = cartItem.Quantity;
orderItem.Price = cartItem.Price;
orderItem.PriceGross = cartItem.PriceGross;
orderItem.Price2 = cartItem.Price2;
orderItem.Price2Gross = cartItem.Price2Gross;
orderItem.Total = cartItem.Total;
orderItem.TotalGross = cartItem.TotalGross;
orderItem.Weight = product.Weight;
orderItem.Length = product.Length;
orderItem.Width = product.Width;
orderItem.Height = product.Height;
orderItem.Created = DateTimeOffset.UtcNow;
orderItem.LastUpdate = DateTimeOffset.UtcNow;
_itemRepository.Add(orderItem);
}
await CommitAsync(userName);
await SetOrderAndPaymentAsync(paymentType, paymentStatus, null, userName, order);
return order;
}
/// <summary>
/// Anlegen eines Artikels für eine Bestellung
/// </summary>
/// <returns>Bestellung Artikel</returns>
public OrderItem CreateItem()
{
var item = new OrderItem
{
Quantity = 0,
Total = 0,
TotalGross = 0,
Created = DateTime.UtcNow,
LastUpdate = DateTime.UtcNow
};
return item;
}
/// <summary>
/// Gibt einen Artikel in einer Bestellung zurück
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="productId">Id des Artikels</param>
/// <returns>Artikel in der Bestellung oder null, wenn nicht gefunden</returns>
public async Task<OrderItem> GetItemAsync(long orderId, int productId)
{
return await _itemRepository.FirstOrDefaultAsync(c => c.OrderId == orderId && c.ProductId == productId).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Artikel in der Bestellung zurück
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <returns>Liste der Artikel in der Bestellung</returns>
public async Task<List<OrderItem>> GetItemsAsync(long orderId)
{
return (await _itemRepository.FindAsync(c => c.OrderId == orderId).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt den nächsten freien Bestell-spezifischen Zähler zurück
/// </summary>
/// <returns>Nächste freie Nummer</returns>
public async Task<long> GetNextCounterAsync()
{
long number = 1;
var last = await Repository.Query(c => c.Id > 0).OrderByDescending(c => c.Counter).FirstOrDefaultAsync();
if (last != null)
number = last.Counter + 1;
return number;
}
/// <summary>
/// Gibt die nächste freie Nummer für eine Bestellung als Bestellnummer zurück
/// </summary>
/// <returns>EndKunden-Nummer</returns>
public async Task<string> GetNextNumberAsync()
{
long number = 1;
var last = await Repository.Query(c => c.Id > 0).OrderByDescending(c => c.Id).FirstOrDefaultAsync();
if (last != null)
number = last.Counter + 1;
return $"{DateTime.UtcNow.Year}{DateTime.UtcNow.Month}-{number:000000}";
}
/// <summary>
/// Löschen einer Bestellung
/// </summary>
/// <param name="id">Id der Bestellung</param>
/// <param name="softDelete">Softdelete anwenden - Daten werden auf gelöscht gesetzt</param>
/// <returns>Task</returns>
public async Task DeleteAsync(long id, bool softDelete)
{
var order = await GetAsync(id);
if (order != null)
{
if (softDelete)
order.Deleted = true;
else
Repository.Remove(order);
}
}
/// <summary>
/// Löschen aller Bestellungen die einem Kunden zugeordnet sind
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="softDelete">Softdelete anwenden - Daten werden auf gelöscht gesetzt</param>
/// <returns>Task</returns>
public async Task DeleteByCustomerAsync(long customerId, bool softDelete)
{
var orders = await Repository.FindAsync(c => c.CustomerId == customerId && c.Deleted == false).ConfigureAwait(false);
if (orders.Any())
{
if (softDelete)
{
foreach (var order in orders)
{
order.Deleted = true;
}
}
else
Repository.RemoveRange(orders);
}
}
/// <summary>
/// Löschen aller Bestellungen die einem App-User zugeordnet sind
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="softDelete">Softdelete anwenden - Daten werden auf gelöscht gesetzt</param>
/// <returns>Task</returns>
public async Task DeleteByAppUserAsync(string appUserId, bool softDelete)
{
var orders = await Repository.FindAsync(c => c.AppUserId == appUserId && c.Deleted == false).ConfigureAwait(false);
if (orders.Any())
{
if (softDelete)
{
foreach (var order in orders)
{
order.Deleted = true;
}
}
else
Repository.RemoveRange(orders);
}
}
/// <summary>
/// Gibt die Anzahl aller Bestellungen im System zurück
/// </summary>
/// <param name="includeDeleted">Gelöschte Daten inkludieren</param>
/// <returns>Anzahl Bestellungen</returns>
public async Task<int> CountOrdersAsync(bool includeDeleted)
{
if (includeDeleted)
return await Repository.CountAsync().ConfigureAwait(false);
else
return await Repository.CountAsync(c => c.Deleted == false).ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl der Bestellungen in einem bestimmten Status zurück
/// </summary>
/// <param name="status">Gesuchter Status</param>
/// <param name="includeDeleted">Gelöschte Daten inkludieren</param>
/// <returns>Anzahl Bestellungen</returns>
public async Task<int> CountOrdersAsync(OrderStatus status, bool includeDeleted)
{
if (includeDeleted)
return await Repository.CountAsync(c => c.OrderStatus == status).ConfigureAwait(false);
else
return await Repository.CountAsync(c => c.OrderStatus == status && c.Deleted == false).ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl der Bestellungen in einem bestimmten Status für Kunden zurück
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="status">Gesuchter Status</param>
/// <param name="includeDeleted">Gelöschte Daten inkludieren</param>
/// <returns>Anzahl Bestellungen</returns>
public async Task<int> CountOrdersByCustomerAsync(long customerId, OrderStatus status, bool includeDeleted)
{
if (includeDeleted)
return await Repository.CountAsync(c => c.CustomerId == customerId && c.OrderStatus == status).ConfigureAwait(false);
else
return await Repository.CountAsync(c => c.CustomerId == customerId && c.OrderStatus == status && c.Deleted == false).ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl der Bestellungen in einem bestimmten Status für App-User zurück
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="status">Gesuchter Status</param>
/// <param name="includeDeleted">Gelöschte Daten inkludieren</param>
/// <returns>Anzahl Bestellungen</returns>
public async Task<int> CountOrdersByAppUserAsync(string appUserId, OrderStatus status, bool includeDeleted)
{
if (includeDeleted)
return await Repository.CountAsync(c => c.AppUserId == appUserId && c.OrderStatus == status).ConfigureAwait(false);
else
return await Repository.CountAsync(c => c.AppUserId == appUserId && c.OrderStatus == status && c.Deleted == false).ConfigureAwait(false);
}
/// <summary>
/// Setzt die ItemId für einen Artikel einer Bestellung auf null, wenn der Artikel gelöscht wird
/// </summary>
/// <param name="productId">Id des Artikels</param>
/// <returns>Task</returns>
public async Task ResetProductForOrderItemAsync(int productId)
{
var orderItems = await _itemRepository.FindAsync(c => c.ProductId == productId);
foreach (var orderItem in orderItems)
{
orderItem.ItemId = null;
}
}
/// <summary>
/// Gibt eine Bestellung anhand der PayPal-ID zurück
/// </summary>
/// <param name="payPalId">PayPal Id der Bestellung</param>
/// <returns>Order oder null, wenn nicht gefunden</returns>
public async Task<Order> GetByPayPalIdAsync(string payPalId)
{
return await Repository.FirstOrDefaultAsync(c => c.PayPalOrderId == payPalId).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Bestellung anhand der Unique-ID zurück
/// </summary>
/// <param name="uniqueId">Unique Id der Bestellung</param>
/// <returns>Order oder null, wenn nicht gefunden</returns>
public async Task<Order> GetByUniqueIdAsync(Guid uniqueId)
{
return await Repository.FirstOrDefaultAsync(c => c.UniqueId == uniqueId).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Bestellung anhand Bestellnummer zurück
/// </summary>
/// <param name="orderNumber">Bestellnummer</param>
/// <returns>Order oder null, wenn nicht gefunden</returns>
public async Task<Order> GetByOrderNumberAsync(string orderNumber)
{
return await Repository.FirstOrDefaultAsync(c => c.Number == orderNumber).ConfigureAwait(false);
}
/// <summary>
/// Setzt den "Versendet" Status für Bestellungen die via Klarna bezahlt wurden und welche keine physischen Produkte beinhalten!
/// </summary>
/// <returns>Task</returns>
public async Task SetShipmentForKlarnaDigitalOnlyAsync()
{
var ordersPhysical = await _physicalRepository.FindAsync(c => c.Deleted == false &&
c.PaymentType == PaymentType.Klarna &&
c.ShipmentStatus == ShipmentStatus.NotYetShipped &&
c.OrderStatus != OrderStatus.Cancelled &&
c.PaymentStatus == PaymentStatus.Paid && c.KlarnaShipmentError == false &&
c.PhysicalCount == 0).ConfigureAwait(false);
if (ordersPhysical.Any())
{
foreach (var orderPhysical in ordersPhysical)
{
var order = await Repository.GetAsync(orderPhysical.Id);
order.ShipmentStatus = ShipmentStatus.Shipped;
}
}
}
/// <summary>
/// Gibt eine Liste aller Klarna-Bestellungen zurück, für welche an Klarna der "versendet" Status gesendet werden soll
/// </summary>
/// <returns>Liste Bestellungen</returns>
public async Task<List<Order>> GetOrdersKlarnaToShipAsync()
{
var items = (await Repository.FindAsync(c => c.PaymentType == PaymentType.Klarna &&
c.ShipmentStatus == ShipmentStatus.Shipped &&
c.KlarnaShipmentSentDate == null &&
c.OrderStatus != OrderStatus.Cancelled &&
c.PaymentStatus == PaymentStatus.Paid &&
c.KlarnaShipmentError == false).ConfigureAwait(false)).ToList();
return items;
}
/// <summary>
/// Gibt zurück ob eine Bestellung nur digitale Produkte beinhaltet
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <returns>true wenn nur digital, false sonst</returns>
public async Task<bool> IsDigitaglOnlyAsync(long orderId)
{
var physicalOrder = await _physicalRepository.FirstOrDefaultAsync(c => c.Id == orderId && c.Deleted == false);
if (physicalOrder != null)
return physicalOrder.PhysicalCount == 0;
return false;
}
#region Private
/// <summary>
/// Setzen der Relation zu einer Bestellung und des Zahlungsstatus für spezielle Entitäten in einer Bestellung.
/// Das sind Listungen, Banner, Werbungen und Pins
/// </summary>
/// <param name="paymentType">Zahlungsart</param>
/// <param name="paymentStatus">Zahlungsstatus</param>
/// <param name="payedDate">Zahlunsdatum</param>
/// <param name="userName">Benutzer</param>
/// <param name="order">Bestellung</param>
/// <returns></returns>
private async Task SetOrderAndPaymentAsync(PaymentType paymentType, PaymentStatus paymentStatus, DateTimeOffset? payedDate, string userName, Order order)
{
var orderItems = await GetItemsAsync(order.Id);
foreach (var orderItem in orderItems)
{
if (orderItem.ProductType == ProductType.Listing)
{
var listing = await _listingRepository.GetAsync(orderItem.ItemId);
if (listing != null)
{
listing.OrderId = order.Id;
listing.OrderItemId = orderItem.Id;
listing.PaymentType = paymentType;
listing.PaymentStatus = paymentStatus;
listing.PayedDate = payedDate;
}
}
else if (orderItem.ProductType == ProductType.Advertisement)
{
var advertisement = await _advertisementRepository.GetAsync(orderItem.ItemId);
if (advertisement != null)
{
advertisement.OrderId = order.Id;
advertisement.OrderItemId = orderItem.Id;
advertisement.PaymentType = paymentType;
advertisement.PaymentStatus = paymentStatus;
advertisement.PayedDate = payedDate;
}
}
else if (orderItem.ProductType == ProductType.Banner)
{
var banner = await _bannerRepository.GetAsync(orderItem.ItemId);
if (banner != null)
{
banner.OrderId = order.Id;
banner.OrderItemId = orderItem.Id;
banner.PaymentType = paymentType;
banner.PaymentStatus = paymentStatus;
banner.PayedDate = payedDate;
}
}
else if (orderItem.ProductType == ProductType.Pin)
{
var pin = await _pinRepository.GetAsync(orderItem.ItemId);
if (pin != null)
{
pin.OrderId = order.Id;
pin.OrderItemId = orderItem.Id;
pin.PaymentType = paymentType;
pin.PaymentStatus = paymentStatus;
pin.PayedDate = payedDate;
}
}
}
await CommitAsync(userName);
}
/// <summary>
/// Zurücksetzen der Relation zwischen einer Bestellung und speziellen Entitäten.
/// Das sind Listungen, Banner, Werbungen und Pins
/// </summary>
/// <param name="userName">Benutzer</param>
/// <param name="order">Bestellung</param>
/// <returns></returns>
private async Task ResetOrderAsync(string userName, Order order)
{
var orderItems = await GetItemsAsync(order.Id);
foreach (var orderItem in orderItems)
{
if (orderItem.ProductType == ProductType.Listing)
{
var listing = await _listingRepository.GetAsync(orderItem.ItemId);
if (listing != null)
{
listing.OrderId = null;
listing.OrderItemId = null;
}
}
else if (orderItem.ProductType == ProductType.Advertisement)
{
var advertisement = await _advertisementRepository.GetAsync(orderItem.ItemId);
if (advertisement != null)
{
advertisement.OrderId = null;
advertisement.OrderItemId = null;
}
}
else if (orderItem.ProductType == ProductType.Banner)
{
var banner = await _bannerRepository.GetAsync(orderItem.ItemId);
if (banner != null)
{
banner.OrderId = null;
banner.OrderItemId = null;
}
}
else if (orderItem.ProductType == ProductType.Pin)
{
var pin = await _pinRepository.GetAsync(orderItem.ItemId);
if (pin != null)
{
pin.OrderId = null;
pin.OrderItemId = null;
}
}
}
await CommitAsync(userName);
}
#endregion
}
}

View File

@ -0,0 +1,89 @@
using gehGassi.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Pages;
using gehGassi.Domain.Shop;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Inhalts-Seiten ermöglicht
/// </summary>
public class PageService : ServiceBase<Page>, IPageService
{
private readonly IRepository<PageWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public PageService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<PageWithNames>();
}
/// <summary>
/// Erstellen einer Seite
/// </summary>
/// <returns></returns>
public Page Create()
{
var page = new Page();
return page;
}
/// <summary>
/// Gibt eine gefilterte Liste von Seiten zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste betroffener Seiten</returns>
public IQueryable<PageWithNames> FilterWithNames(string filter, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PagesWithNames({0},{1})", c => c.Title.Contains(filter), language, fallback);
return baseQuery;
}
/// <summary>
/// Prüfen ob ein Code noch verfügbar ist
/// </summary>
/// <param name="code">Zu prüfender Code</param>
/// <param name="id">Id die ausgenommen werden soll</param>
/// <returns>true wenn verfügbar, false sonst</returns>
public async Task<bool> IsCodeAvailableAsync(string code, int id)
{
var existingCode = await Repository.FirstOrDefaultAsync(c => c.Code == code).ConfigureAwait(false);
if (existingCode != null && id > 0)
existingCode = await Repository.FirstOrDefaultAsync(c => c.Code == code && c.Id != id).ConfigureAwait(false);
return existingCode == null;
}
/// <summary>
/// Gibt eine Text-Seite in gewünschter Sprache zurück
/// </summary>
/// <param name="code">code der Seite</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Seite oder null, wenn nicht gefunden</returns>
public async Task<PageWithNames> GetwithNamesAsync(string code, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
code = code.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PagesWithNames({0},{1})", c => c.Code == code, language, fallback);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Payment;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Zahlungstransaktionen ermöglicht
/// </summary>
public class PaymentTransactionService : ServiceBase<PaymentTransaction>, IPaymentTransactionService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public PaymentTransactionService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Erstellen einer Zahlungs-Transaktion
/// </summary>
/// <returns>Zahlungs-Transaktion</returns>
public PaymentTransaction Create()
{
var item = new PaymentTransaction
{
Created = DateTimeOffset.UtcNow,
ErrorMessage = string.Empty,
ErrorType = string.Empty,
ErrorMessages = string.Empty
};
return item;
}
/// <summary>
/// Gibt eine Transaktion anhand der MangoPay TransaktionsId zurück.
/// Diese muss den entsprechenden TransaktionsTyp und Status haben
/// </summary>
/// <param name="transactionId">Id der Transaktion bei MangoPay</param>
/// <param name="type">Typ der Transaktion</param>
/// <param name="status">Status der Transaktion</param>
/// <returns>Transaktion oder null, wenn nicht gefunden</returns>
public async Task<PaymentTransaction> GetAsync(string transactionId, PaymentTransactionType type, PaymentTransactionStatus status)
{
return await Repository.FirstOrDefaultAsync(c => c.TransactionId == transactionId && c.Type == type && c.Status == status).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Transaktion anhand der MangoPay TransaktionsId zurück. Hier wird die zuletzt erstellte Transaktion zurückgegeben.
/// Diese muss den entsprechenden TransaktionsTyp und Status haben
/// </summary>
/// <param name="transactionId">Id der Transaktion bei MangoPay</param>
/// <param name="type">Typ der Transaktion</param>
/// <returns>Transaktion oder null, wenn nicht gefunden</returns>
public async Task<PaymentTransaction> GetAsync(string transactionId, PaymentTransactionType type)
{
var transactions = await Repository.FindAsync(c => c.TransactionId == transactionId && c.Type == type).ConfigureAwait(false);
var transaction = transactions.MaxBy(c => c.Created);
return transaction;
}
/// <summary>
/// Erstellen einer Zahlungs-Transaktion
/// </summary>
/// <param name="transactionId">Id der Transaktion bei MangoPay</param>
/// <param name="type">Typ der Transaktion</param>
/// <param name="status">Status der Transaktion</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="targetAppUserId">Id des AppUsers der die Zahlung erhält</param>
/// <param name="targetWalletId">Id des Wallets, auf das die Transaktion angewendet wird</param>
/// <param name="sourceAppUserId">Id des AppUsers der die Zahlung veranlasst</param>
/// <param name="sourceWalletId">Id des Wallets, von dem die Transaktion ausgeht</param>
/// <param name="ammount">Betrag in der kleinsten Einheit der Währung</param>
/// <param name="fromCredit">Zusätzlich vom Guthabenkonto in der kleinsten Einheit der Währung</param>
/// <param name="currency">Währung ISO 4217 Code</param>
/// <param name="payInType">Art der Zahlung</param>
/// <returns>Zahlungs-Transaktion</returns>
public PaymentTransaction Create(string transactionId, PaymentTransactionType type, PaymentTransactionStatus status, string walkId, string targetAppUserId, string targetWalletId, string sourceAppUserId,
string sourceWalletId, long ammount, long fromCredit, string currency, PayInType? payInType)
{
var item = new PaymentTransaction()
{
TransactionId = transactionId,
Type = type,
Status = status,
WalkId = walkId,
TargetAppUserId = targetAppUserId,
TargetWalletId = targetWalletId,
SourceAppUserId = sourceAppUserId,
SourceWalletId = sourceWalletId,
Ammount = ammount,
FromCredit = fromCredit,
Currency = currency,
ErrorMessage = string.Empty,
ErrorType = string.Empty,
ErrorMessages = string.Empty,
Created = DateTimeOffset.UtcNow,
PayInType = payInType
};
return item;
}
/// <summary>
/// Erstellen einer Zahlungs-Transaktion mit Fehlern nach Mangopay
/// </summary>
/// <param name="transactionId">Id der Transaktion bei MangoPay</param>
/// <param name="type">Typ der Transaktion</param>
/// <param name="status">Status der Transaktion</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="targetAppUserId">Id des AppUsers der die Zahlung erhält</param>
/// <param name="targetWalletId">Id des Wallets, auf das die Transaktion angewendet wird</param>
/// <param name="sourceAppUserId">Id des AppUsers der die Zahlung veranlasst</param>
/// <param name="sourceWalletId">Id des Wallets, von dem die Transaktion ausgeht</param>
/// <param name="ammount">Betrag in der kleinsten Einheit der Währung</param>
/// <param name="fromCredit">Zusätzlich vom Guthabenkonto in der kleinsten Einheit der Währung</param>
/// <param name="currency">Währung ISO 4217 Code</param>
/// <param name="errorMessage">Fehlermeldung Mangopay</param>
/// <param name="errorType">Fehlertyp nach Mangopay</param>
/// <param name="errorMessages">Fehlermledungen nach Mangopay - Zusatzinfo</param>
/// <param name="payInType">Art der Zahlung</param>
/// <returns>Zahlungs-Transaktion</returns>
public PaymentTransaction Create(string transactionId, PaymentTransactionType type, PaymentTransactionStatus status, string walkId, string targetAppUserId, string targetWalletId, string sourceAppUserId,
string sourceWalletId, long ammount, long fromCredit, string currency, string errorMessage, string errorType, Dictionary<string, string> errorMessages, PayInType? payInType)
{
var item = new PaymentTransaction()
{
TransactionId = transactionId,
Type = type,
Status = status,
WalkId = walkId,
TargetAppUserId = targetAppUserId,
TargetWalletId = targetWalletId,
SourceAppUserId = sourceAppUserId,
SourceWalletId = sourceWalletId,
Ammount = ammount,
FromCredit = fromCredit,
Currency = currency,
ErrorMessage = string.Empty,
ErrorType = string.Empty,
ErrorMessages = string.Empty,
Created = DateTimeOffset.UtcNow,
PayInType = payInType
};
if (!string.IsNullOrEmpty(errorMessage))
{
item.ErrorMessage = errorMessage.Length <= 500 ? errorMessage : errorMessage[..500];
}
if (!string.IsNullOrEmpty(errorType))
{
item.ErrorType = errorType.Length <= 255 ? errorType : errorType[..255];
}
if (errorMessages != null && errorMessages.Any())
{
item.ErrorMessages = JsonSerializer.Serialize(errorMessages);
if (item.ErrorMessages.Length > 1000)
{
item.ErrorMessages = item.ErrorMessages[..1000];
}
}
return item;
}
}
}

View File

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Common.Extensions;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Payment;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Auszahlungen ermöglicht
/// </summary>
public class PayoutService : ServiceBase<Payout>, IPayoutService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eine IUnitOfWork</param>
public PayoutService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Erstellen einer Auszahlung
/// </summary>
/// <returns>Payout</returns>
public Payout Create()
{
var item = new Payout()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Erstellen einer Auszahlung
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="mangoPayUserId">Id des Users bei MangoPay</param>
/// <param name="mangoPayId">Id der Auszahlung bei MangoPay</param>
/// <param name="walletId">Id des Wallets</param>
/// <param name="mangoPayWalletId">Id des Wallets bei MangoPay</param>
/// <param name="mangoPayBankId">Id der Bankverbindung bei mangoPay</param>
/// <param name="iban">Iban letzte 4 Stellen</param>
/// <param name="bic">Bic letzte 4 Stellen oder leer</param>
/// <param name="ammount">Summe die ausgezahlt werden soll in der kleinsten Einheit</param>
/// <param name="currency">Währung ISO 4217 Code</param>
/// <returns>Payout</returns>
public Payout Create(string appUserId, string mangoPayUserId, string mangoPayId, string walletId, string mangoPayWalletId, string mangoPayBankId, string iban, string bic, long ammount, string currency)
{
var item = Create();
item.AppUserId = appUserId;
item.MangoPayUserId = mangoPayUserId;
item.MangoPayId = mangoPayId;
item.WalletId = walletId;
item.MangoPayWalletId = mangoPayWalletId;
item.MangoPayBankId = mangoPayBankId;
item.Iban = iban;
item.Bic = bic;
item.Ammount = ammount;
item.Currency = currency;
return item;
}
/// <summary>
/// Gibt eine Liste der Auszahlungen eines Benutzers zurück.
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der öffentlichen Anfragen</returns>
public async Task<List<Payout>> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.AppUserId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Auszahlung anhand der MangoPayId zurück
/// </summary>
/// <param name="mangoPayId">Id der Auszahlung bei MangoPay</param>
/// <returns>Payout oder null, wenn nicht gefunden</returns>
public async Task<Payout> GetByMangoPayIdAsync(string mangoPayId)
{
var item = await Repository.FirstOrDefaultAsync(c => c.MangoPayId == mangoPayId).ConfigureAwait(false);
return item;
}
/// <summary>
/// Gibt eine Liste von Auszahlungen zurück
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="sortList">Optional: Liste der sortierung</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste gefudene Walks</returns>
public async Task<(int total, List<Payout> list)> GetAsync(string appUserId, List<DynamicSortOrder> sortList, int skip, int take)
{
var baseQuery = Repository.Query(c => c.AppUserId == appUserId);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderByDescending(c => c.Created).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt die letzte Auszahlung eines Benutzers zurück
/// </summary>
/// <param name="appUserId">Id des Benutzers</param>
/// <returns>Auszahlung oder null, wenn nicht gefunden</returns>
public async Task<Payout> GetLastPayoutAsync(string appUserId)
{
var lastPayout = await Repository.Query(c => c.AppUserId == appUserId).OrderByDescending(c => c.Created).FirstOrDefaultAsync();
return lastPayout;
}
/// <summary>
/// Gibt die Anzahl der Auszahlungen eines Benutzers in einem Zeitrahmen zurück
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <param name="startDate">Startdatum</param>
/// <param name="endDate">Enddatum</param>
/// <returns>Anzahl Auszahlungen</returns>
public async Task<int> CountPayoutsInTimeRangeAsync(string appUserId, DateTimeOffset startDate, DateTimeOffset endDate)
{
return await Repository.Query(c => c.AppUserId == appUserId && c.Created >= startDate && c.Created <= endDate).CountAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Users;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von gespeicherten Authentifizierungs-Ticktes ermöglicht
/// </summary>
public class PersistedTicketService : ServiceBase<PersistedTicket>, IPersistedTicketService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
/// <param name="configuration">Instanz eines IConfiguration</param>
public PersistedTicketService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Löscht alle abgelaufenen Tickets
/// </summary>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> RemoveExpiredAsync()
{
var items = await Repository.FindAsync(c => c.Expires < DateTimeOffset.UtcNow).ConfigureAwait(false);
if (items.Any())
{
Repository.RemoveRange(items);
return true;
}
return false;
}
/// <summary>
/// Speichern eines Tickets.
/// Wenn es noch nicht existiert, wird es angelegt.
/// Wenn es existiert, wird es aktualisiert
/// </summary>
/// <param name="key">Key des Tickes</param>
/// <param name="userId">Benutzername</param>
/// <param name="ticket">Ticket als Byte-Array</param>
/// <param name="expires">Ablaufdatum des Tickets</param>
/// <returns>Task</returns>
public async Task SaveAsync(string key, string userId, byte[] ticket, DateTimeOffset? expires)
{
var item = await Repository.FirstOrDefaultAsync(c => c.Key == key).ConfigureAwait(false);
if (item != null)
{
item.Ticket = ticket;
item.Expires = expires;
}
else
{
var persistedTicket = new PersistedTicket()
{
Key = key,
UserId = userId,
Expires = expires,
Ticket = ticket
};
Repository.Add(persistedTicket);
}
}
/// <summary>
/// Löschen eines Tickets aus dem Store
/// </summary>
/// <param name="key">Key des Tickets</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> RemoveAsync(string key)
{
var item = await Repository.FirstOrDefaultAsync(c => c.Key == key).ConfigureAwait(false);
if (item != null)
{
Repository.Remove(item);
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,260 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Pins realisiert
/// </summary>
public class PinService : ServiceBase<Pin>, IPinService
{
private readonly IRepository<PinWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public PinService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<PinWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Pins zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Pins</returns>
public IQueryable<Pin> Filter(string filter)
{
return Repository.Query(c => c.NameLocalized.Contains(filter));
}
/// <summary>
/// Erstellen eines Pins
/// </summary>
/// <returns>Pin</returns>
public Pin Create()
{
var item = new Pin()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Pins zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Pins</returns>
public async Task<List<Pin>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.NameLocalized.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Pins mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Pins</returns>
public IQueryable<PinWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PinsWithNames({0},{1})", c => c.Name.Contains(filter) || c.CustomerName.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != PinStatus.Draft);
return baseQuery;
}
/// <summary>
/// Gibt eine gefilterte Liste von Pins mit Namen für Kunden zurück in einer gewählten Sprache
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Pins</returns>
public IQueryable<PinWithNames> FilterWithNamesCustomer(long customerId, string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PinsWithNames({0},{1})", c => c.CustomerId == customerId && (c.Name.Contains(filter) || c.CustomerName.Contains(filter)), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != PinStatus.Draft);
return baseQuery;
}
/// <summary>
/// Zurücksetzen der Zuordnung zu einer Listung
/// </summary>
/// <param name="listingId">Id der Listung</param>
/// <returns>Task</returns>
public async Task<int> ResetListingAsync(string listingId)
{
var items = (await Repository.FindAsync(c => c.ListingId == listingId).ConfigureAwait(false)).ToList();
foreach (var item in items)
{
item.ListingId = string.Empty;
}
return items.Count;
}
/// <summary>
/// Stornieren eines Pins basierend auf einer Listung
/// </summary>
/// <param name="listingId">Id der Listung</param>
/// <returns>Task</returns>
public async Task<int> CancelByListingAsync(string listingId)
{
var items = (await Repository.FindAsync(c => c.ListingId == listingId).ConfigureAwait(false)).ToList();
foreach (var item in items)
{
item.Status = PinStatus.Cancelled;
}
return items.Count;
}
/// <summary>
/// Gibt die Anzahl von Pins zurück
/// </summary>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Anzahl Pins</returns>
public async Task<int> CountAsync(bool includeDeleted, bool excludeDraft)
{
var query = Repository.Query(c => c.Id != "");
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if (excludeDraft)
query = query.Where(c => c.Status != PinStatus.Draft);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl von Pins in einem Status zurück
/// </summary>
/// <param name="status">Gesuchter Status</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Anzahl Pins</returns>
public async Task<int> CountAsync(PinStatus status, bool includeDeleted)
{
var query = Repository.Query(c => c.Status == status);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt einen Pin für einen Artikel im Warenkorb zurück
/// </summary>
/// <param name="cartItemId">Id des Artikels im Warenkorb</param>
/// <returns>Pin oder null, wenn nicht gefunden</returns>
public async Task<Pin> GetByCartItemAsync(long cartItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.CartItemId == cartItemId && c.Status == PinStatus.Draft);
}
/// <summary>
/// Gibt einen Pin für einen Artikel in einer Bestellung zurück
/// </summary>
/// <param name="orderItemId">Id des Artikels in der Bestellung</param>
/// <returns>Pin oder null, wenn nicht gefunden</returns>
public async Task<Pin> GetByOrderItemAsync(long orderItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.OrderItemId == orderItemId);
}
/// <summary>
/// Setzen des Status von Pins nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <returns>Anzahl betroffener Pins</returns>
public async Task<int> SetStatusByOrderAsync(long orderId, PinStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.Status = status;
}
return items.Count();
}
/// <summary>
/// Setzen des Bezahlstatus von Pins nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <returns>Anzahl betroffener Pins</returns>
public async Task<int> SetPaymentStatusByOrderAsync(long orderId, PaymentStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.PaymentStatus = status;
}
return items.Count();
}
/// <summary>
/// Starten von gebuchten Pins wenn diese das Start-Datum erreicht haben
/// </summary>
/// <returns>Anzahl gestartete Pins</returns>
public async Task<int> StartBookedAsnyc()
{
var items = await Repository.FindAsync(c => c.Status == PinStatus.Booked && c.Deleted == false && c.StartDate <= DateTimeOffset.UtcNow && c.EndDate.Value >= DateTimeOffset.UtcNow);
foreach (var item in items)
{
item.Status = PinStatus.Running;
}
return items.Count();
}
/// <summary>
/// Stoppen von abgelaufenen Pins wenn diese das Enddatum erreicht haben
/// </summary>
/// <returns>Anzahl gestoppte Pins</returns>
public async Task<int> StopRunningAsync()
{
var items = await Repository.FindAsync(c => c.Status == PinStatus.Running && c.Deleted == false && c.EndDate.Value <= DateTimeOffset.UtcNow);
foreach (var item in items)
{
item.Status = PinStatus.Ended;
}
return items.Count();
}
}
}

View File

@ -0,0 +1,110 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Placeholders;
using gehGassi.Domain.Users;
using Newtonsoft.Json;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der Funktionen rund um die Variablen/Platzhalter bietet
/// </summary>
public class PlaceHolderService : IPlaceHolderService
{
private readonly string _directory;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="directory">Pfad in welchem die Platzhalter-Dateien liegen</param>
public PlaceHolderService(string directory)
{
_directory = directory;
}
/// <summary>
/// Gibt die Liste der verfügbaren Platzhalter-Kategorien zurück
/// </summary>
/// <param name="lang">Sprache ISO2</param>
/// <returns>Liste Kategorien</returns>
public List<PlaceHolderCategory> GetCategories(string lang)
{
var list = Load(lang);
return list;
}
/// <summary>
/// Gibt die Liste der Platzhalter-Variablen für eine Kategorie zurück
/// </summary>
/// <param name="categoryId">Id der Kategorie</param>
/// <param name="lang">Sprache ISO2</param>
/// <returns>Liste Platzhalter</returns>
public List<PlaceHolder> GetPlaceholders(int categoryId, string lang)
{
var list = new List<PlaceHolder>();
var categories = Load(lang);
if (categories == null || !categories.Any()) return list;
var category = categories.FirstOrDefault(c => c.Id == categoryId);
if (category != null)
list = category.PlaceHolders.ToList();
return list;
}
/// <summary>
/// Ersetzt in einem Text alle Platzhalter mit den entsprechenden Daten, sofern diese zur Verfügung gestellt wurden.
/// </summary>
/// <param name="content">Text in welchem die Platzhalter ausgefüllt werden sollen</param>
/// <param name="lang">Sprache ISO2</param>
/// <param name="timezoneId">Id der Timezone für Datums-Operationen</param>
/// <param name="useHtml">Soll bei manchen Ersatzoperationen HTML verwendet werden? Z.B. Adress-Block</param>
/// <param name="tenant">Optional: Mandant</param>
/// <param name="user">Optional: Benutzer</param>
/// <param name="course">Optional: Kurs</param>
/// <param name="student">Optional: Student</param>
/// <returns>Anzahl ersetzer Platzhalter. Der Text ist als ref Parameter bearbeitet</returns>
public int FillIn(ref string content, string lang, string timezoneId, bool useHtml = false, ApplicationUser user = null)
{
var replaceCount = 0;
return replaceCount;
}
#region Private
/// <summary>
/// Laden der Datei mit den Paltzhaltern
/// </summary>
/// <param name="lang"></param>
/// <returns></returns>
private List<PlaceHolderCategory> Load(string lang)
{
var list = new List<PlaceHolderCategory>();
//Prüfen ob das Verzeichnis existiert
if (!Directory.Exists(_directory))
Directory.CreateDirectory(_directory);
try
{
var filename = Path.Combine(_directory, $"placeholders_{lang}.json");
if (File.Exists(filename))
{
list = JsonConvert.DeserializeObject<PlaceHolderCategory[]>(File.ReadAllText(filename)).ToList();
}
}
catch
{
// ignored
}
return list;
}
#endregion
}
}

View File

@ -0,0 +1,296 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Shop;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Produktkategorien realisiert
/// </summary>
public class ProductCategoryService : ServiceBase<ProductCategory>, IProductCategoryService
{
private readonly IRepository<ProductCategoryWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public ProductCategoryService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<ProductCategoryWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Produktkategorien zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Produktkategorien</returns>
public IQueryable<ProductCategory> Filter(string filter)
{
return Repository.Query(c => c.Title.Contains(filter));
}
/// <summary>
/// Erstellen einer Produktkategorie
/// </summary>
/// <returns>Produktkategorie</returns>
public ProductCategory Create()
{
var item = new ProductCategory()
{
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Produktkategorien mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Produktkategorien</returns>
public IQueryable<ProductCategoryWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ProductCategoriesWithNames({0},{1})", c => c.Title.Contains(filter) || c.Name.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine gefilterte Liste von Produktkategorien mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="parentId">Id des PArents oder null wenn root</param>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Produktkategorien</returns>
public IQueryable<ProductCategoryWithNames> FilterWithNames(int? parentId, string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ProductCategoriesWithNames({0},{1})", c => c.ParentId == parentId && (c.Title.Contains(filter) || c.Name.Contains(filter)), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Erstellen eines Kategorie-Baums
/// </summary>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="excludeId">ID die ausgeschlossen werden soll, wenn es um die Auswahl eines Parents geht</param>
/// <param name="includeDeleted">Sollen gelöschte Kategorien inkludiert werden?</param>
/// <returns>Kategorie-Baum</returns>
public async Task<List<ProductCategoryWithNamesTree>> GetTreeAsync(string language, string fallback, int excludeId = -1, bool includeDeleted = false)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ProductCategoriesWithNames({0},{1})", c => c.ParentId == null && c.Id != excludeId, language, fallback);
if(!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
var resultList = new List<ProductCategoryWithNamesTree>();
var rootItems = await baseQuery.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Title).ToListAsync().ConfigureAwait(false);
foreach (var rootItem in rootItems)
{
var rootItemTree = new ProductCategoryWithNamesTree(rootItem);
var children = await GetTreeChildrenAsync(rootItem.Id, language, fallback, excludeId, includeDeleted);
rootItemTree.Children.AddRange(children);
resultList.Add(rootItemTree);
}
return resultList;
}
/// <summary>
/// Erstellen eines Kategorie-Baums für Kunden
/// </summary>
/// <param name="orderSource">Quelle - für wen holen?</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="excludeId">ID die ausgeschlossen werden soll, wenn es um die Auswahl eines Parents geht</param>
/// <param name="includeDeleted">Sollen gelöschte Kategorien inkludiert werden?</param>
/// <returns>Kategorie-Baum</returns>
public async Task<List<ProductCategoryWithNamesTree>> GetTreeAsync(OrderSource orderSource, string language, string fallback, int excludeId = -1, bool includeDeleted = false)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ProductCategoriesWithNames({0},{1})", c => c.ParentId == null && c.Id != excludeId, language, fallback);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
var resultList = new List<ProductCategoryWithNamesTree>();
var rootItems = await baseQuery.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Title).ToListAsync().ConfigureAwait(false);
foreach (var rootItem in rootItems)
{
var rootItemTree = new ProductCategoryWithNamesTree(rootItem);
var children = await GetTreeChildrenAsync(orderSource, rootItem.Id, language, fallback, excludeId, includeDeleted);
rootItemTree.Children.AddRange(children);
resultList.Add(rootItemTree);
}
return resultList;
}
/// <summary>
/// Setzen einer neuen ParentId für Kateorien
/// </summary>
/// <param name="oldId">Alte Id welche ersetzt werden soll</param>
/// <param name="newId">Neue Id die gesetzt werden soll. Kann auch NULL sein, was bedeutet, dass root gesetzt werden soll</param>
/// <returns></returns>
public async Task<int> ResetParentAsync(int oldId, int? newId)
{
var items = (await Repository.FindAsync(c => c.ParentId == oldId).ConfigureAwait(false)).ToList();
foreach (var item in items)
{
item.ParentId = newId;
}
return items.Count;
}
/// <summary>
/// Gibt zurück ob eine Kategorie Kinder hat
/// </summary>
/// <param name="id">Id der Kategorie</param>
/// <returns>true wenn ja, false sonst</returns>
public async Task<bool> HasChildrenAsync(int id)
{
return await Repository.CountAsync(c => c.ParentId == id && c.Deleted == false).ConfigureAwait(false) > 0;
}
#region Helper
/// <summary>
/// Suchen der Kinder eines Parents bis auf die unterste Ebene
/// </summary>
/// <param name="parentId">Id des Parents</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="excludeId">ID die ausgeschlossen werden soll, wenn es um die Auswahl eines Parents geht</param>
/// <param name="includeDeleted">Sollen gelöschte Kategorien inkludiert werden?</param>
/// <returns>Baum-Kinder eines Parents</returns>
private async Task<List<ProductCategoryWithNamesTree>> GetTreeChildrenAsync(int parentId, string language, string fallback, int excludeId = -1, bool includeDeleted = false)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ProductCategoriesWithNames({0},{1})", c => c.ParentId == parentId && c.Id != excludeId, language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
var resultList = new List<ProductCategoryWithNamesTree>();
var rootItems = await baseQuery.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Title).ToListAsync().ConfigureAwait(false);
foreach (var rootItem in rootItems)
{
var rootItemTree = new ProductCategoryWithNamesTree(rootItem);
var children = await GetTreeChildrenAsync(rootItem.Id, language, fallback, excludeId, includeDeleted);
rootItemTree.Children.AddRange(children);
resultList.Add(rootItemTree);
}
return resultList;
}
/// <summary>
/// Suchen der Kinder eines Parents bis auf die unterste Ebene
/// </summary>
/// <param name="orderSource">Quelle - für Wen?</param>
/// <param name="parentId">Id des Parents</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="excludeId">ID die ausgeschlossen werden soll, wenn es um die Auswahl eines Parents geht</param>
/// <param name="includeDeleted">Sollen gelöschte Kategorien inkludiert werden?</param>
/// <returns>Baum-Kinder eines Parents</returns>
private async Task<List<ProductCategoryWithNamesTree>> GetTreeChildrenAsync(OrderSource orderSource, int parentId, string language, string fallback, int excludeId = -1, bool includeDeleted = false)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ProductCategoriesWithNames({0},{1})", c => c.ParentId == parentId && c.Id != excludeId, language, fallback);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
var resultList = new List<ProductCategoryWithNamesTree>();
var rootItems = await baseQuery.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Title).ToListAsync().ConfigureAwait(false);
foreach (var rootItem in rootItems)
{
var rootItemTree = new ProductCategoryWithNamesTree(rootItem);
var children = await GetTreeChildrenAsync(rootItem.Id, language, fallback, excludeId, includeDeleted);
rootItemTree.Children.AddRange(children);
resultList.Add(rootItemTree);
}
return resultList;
}
/// <summary>
/// Sucht alle Kinder einer Produktkategorie
/// </summary>
/// <param name="startId">Id des Parent</param>
/// <param name="childIds">Liste der gefundenen Kinder</param>
private void FindChildren(int startId, ref List<int> childIds)
{
var children = Repository.Find(c => c.ParentId == startId);
var productCategories = children.ToList();
if (!productCategories.Any()) return;
foreach (var child in productCategories)
{
childIds.Add(child.Id);
FindChildren(child.Id, ref childIds);
}
}
#endregion
}
}

View File

@ -0,0 +1,652 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Shop;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Artikeln / Produkten ermöglicht
/// </summary>
public class ProductService : ServiceBase<Product>, IProductService
{
private readonly IRepository<ProductWithNames> _namesRepository;
private readonly IRepository<ProductCountry> _countriesRepository;
private readonly IRepository<ProductWithNamesCountry> _namesCountryRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public ProductService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<ProductWithNames>();
_countriesRepository = unitOfWork.GetRepository<ProductCountry>();
_namesCountryRepository = unitOfWork.GetRepository<ProductWithNamesCountry>();
}
/// <summary>
/// Erstellen eines Produktes
/// </summary>
/// <returns>Produkt</returns>
public Product Create()
{
var item = new Product()
{
ProductType = ProductType.Physical,
ListingType = ListingType.Base,
Visibility = ShopVisibility.None,
AdditionalShippingCharge = 0m,
TaxRateId = -1,
Price = 0m,
OldPrice = 0m,
Weight = 0m,
Width = 0m,
Height = 0m,
Length = 0m,
DisplayOrder = 100,
Deleted = false,
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Produkten mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Produkte</returns>
public IQueryable<ProductWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNames({0},{1})", c => c.Name.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return baseQuery;
}
/// <summary>
/// Prüft ob für einen Listungstypen und eine Branche schon ein Produkt existiert
/// </summary>
/// <param name="listingType">Listungstyp</param>
/// <param name="branchId">Branche</param>
/// <returns>true wenn existiert, false sonst</returns>
public async Task<bool> CheckListingExistsAsync(ListingType listingType, string branchId)
{
var itemFound = await Repository.FirstOrDefaultAsync(c => c.ProductType == ProductType.Listing && c.ListingType == listingType && c.BranchId == branchId && c.Deleted == false).ConfigureAwait(false);
return itemFound != null;
}
/// <summary>
/// Prüft ob für einen Listungstypen und eine Branche schon ein Produkt existiert
/// </summary>
/// <param name="listingType">Listungstyp</param>
/// <param name="branchId">Branche</param>
/// <param name="priceType">Preistyp</param>
/// <returns>true wenn existiert, false sonst</returns>
public async Task<bool> CheckListingExistsAsync(ListingType listingType, string branchId, PriceType priceType)
{
var itemFound = await Repository.FirstOrDefaultAsync(c => c.ProductType == ProductType.Listing && c.ListingType == listingType && c.BranchId == branchId && c.PriceType == priceType && c.Deleted == false).ConfigureAwait(false);
return itemFound != null;
}
/// <summary>
/// Prüft ob für eine Rubrik schon ein Produkt für Werbung existiert
/// </summary>
/// <param name="advertisementCategoryId">Rubrik</param>
/// <returns>true wenn existiert, false sonst</returns>
public async Task<bool> CheckAdvertisementExistsAsync(string advertisementCategoryId)
{
var itemFound = await Repository.FirstOrDefaultAsync(c => c.ProductType == ProductType.Advertisement && c.AdvertisementCategoryId == advertisementCategoryId && c.Deleted == false).ConfigureAwait(false);
return itemFound != null;
}
/// <summary>
/// Gibt zurück wie viele nicht gelöschte Produkte eine Rubrik verwenden
/// </summary>
/// <param name="advertisementCategoryId">Id der Rubrik</param>
/// <returns>Anzahl der Produkte die eine Rubrik verwenden</returns>
public async Task<int> CountByAdvertisementCategoryAsync(string advertisementCategoryId)
{
return await Repository.CountAsync(c => c.AdvertisementCategoryId == advertisementCategoryId && !c.Deleted).ConfigureAwait(false);
}
/// <summary>
/// Setzt für Produkte die Rubrik zurück
/// </summary>
/// <param name="advertisementCategoryId">Id der Rubrik</param>
/// <param name="deletedOnly">true wenn nur gelöschte Produkte zurückgesetzt werden sollen</param>
/// <returns>Anzahl Produkte zurückgesetzt</returns>
public async Task<int> ResetAdvertisementCategoryAsync(string advertisementCategoryId, bool deletedOnly)
{
var itemsQuery = Repository.Query(c => c.AdvertisementCategoryId == advertisementCategoryId);
if (deletedOnly)
itemsQuery = itemsQuery.Where(c => c.Deleted);
var items = await itemsQuery.ToListAsync();
foreach (var product in items)
{
product.AdvertisementCategoryId = null;
}
return items.Count;
}
/// <summary>
/// Setzt für Produkte die Produktkategorie zurück
/// </summary>
/// <param name="categoryId">Id der Produktkategorie</param>
/// <param name="deletedOnly">true wenn nur gelöschte Produkte zurückgesetzt werden sollen</param>
/// <returns>Anzahl Produkte zurückgesetzt</returns>
public async Task<int> ResetProductCategoryAsync(int categoryId, bool deletedOnly)
{
var itemsQuery = Repository.Query(c => c.ProductCategoryId == categoryId);
if (deletedOnly)
itemsQuery = itemsQuery.Where(c => c.Deleted);
var items = await itemsQuery.ToListAsync();
foreach (var product in items)
{
product.ProductCategoryId = null;
}
return items.Count;
}
/// <summary>
/// Gibt zurück wie viele Produkte eine Steuergruppe verwenden
/// </summary>
/// <param name="taxRateId">Id der Steuergruppe</param>
/// <returns>Anzahl der Produkte die eine Steuergruppe verwenden</returns>
public async Task<int> CountByTaxRateAsync(int taxRateId)
{
return await Repository.CountAsync(c => c.TaxRateId == taxRateId).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine gefilterte Liste von Produkteinstellungen für Länder zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="productId"></param>
/// <returns>Liste betroffener Produkteinstellungen</returns>
public IQueryable<ProductCountry> FilterCountry(string filter, int productId)
{
return _countriesRepository.Query(c => c.ProductId == productId && c.CountryIso.Contains(filter));
}
/// <summary>
/// Gibt eine Produkteinstellung für ein Land zurück
/// </summary>
/// <param name="id">Id der Einstellung</param>
/// <returns>Produkteinstellung oder null, wenn nicht gefunden</returns>
public async Task<ProductCountry> GetCountryAsync(int id)
{
return await _countriesRepository.FirstOrDefaultAsync(c => c.Id == id).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Produkteinstellung für ein Land zurück
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="countryIso">Land ISO2</param>
/// <returns>Produkteinstellung oder null, wenn nicht gefunden</returns>
public async Task<ProductCountry> GetCountryAsync(int productId, string countryIso)
{
return await _countriesRepository.FirstOrDefaultAsync(c => c.ProductId == productId && c.CountryIso == countryIso).ConfigureAwait(false);
}
/// <summary>
/// Erstellen einer Produkteinstellung für ein Land
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="countryIso">ISO2 Land</param>
/// <returns>Produkteinstellung</returns>
public ProductCountry CreateCountry(int productId, string countryIso)
{
var item = new ProductCountry()
{
ProductId = productId,
CountryIso = countryIso
};
return item;
}
/// <summary>
/// Hinzufügen einer Produkteinstellung für ein Land
/// </summary>
/// <param name="productCountry">Produkteinstellung</param>
public void AddCountry(ProductCountry productCountry)
{
_countriesRepository.Add(productCountry);
}
/// <summary>
/// Löschen einer Produkteinstellung für ein Land
/// </summary>
/// <param name="productCountry">Produkteinstellung</param>
public void RemoveCountry(ProductCountry productCountry)
{
_countriesRepository.Remove(productCountry);
}
/// <summary>
/// Gibt eine Liste von Produkten vom Typ Listung zurück
/// </summary>
/// <param name="orderSource">Für welchen Typen? Kunde, Hundebesitzer, Hundeausführer</param>
/// <param name="countryIso">Land</param>
/// <param name="allowedBranches">Branchen des Kunden</param>
/// <param name="language">Sprache</param>
/// <param name="fallback">Facllback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste von Produkten</returns>
public async Task<List<ProductWithNamesCountry>> GetListingProductsAsync(OrderSource orderSource, string countryIso, string allowedBranches, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
countryIso = countryIso.ToUpperInvariant();
if (string.IsNullOrWhiteSpace(allowedBranches))
allowedBranches = string.Empty;
var baseQuery = _namesCountryRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNamesCountry({0},{1},{2})", c =>c.ProductType == ProductType.Listing && allowedBranches.Contains(c.BranchId) && c.NotAvailable == false, language, fallback, countryIso);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Produkten vom Typ Listung zurück
/// </summary>
/// <param name="orderSource">Für welchen Typen? Kunde, Hundebesitzer, Hundeausführer</param>
/// <param name="countryIso">Land</param>
/// <param name="allowedBranches">Branchen des Kunden</param>
/// <param name="filter">Filtertext für Name</param>
/// <param name="language">Sprache</param>
/// <param name="fallback">Facllback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="categories">Gewählte Produktkategorien</param>
/// <returns>Liste von Produkten</returns>
public async Task<List<ProductWithNamesCountry>> GetListingProductsAsync(OrderSource orderSource, string countryIso, string allowedBranches, List<int> categories, string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
countryIso = countryIso.ToUpperInvariant();
if (string.IsNullOrWhiteSpace(allowedBranches))
allowedBranches = string.Empty;
var baseQuery = _namesCountryRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNamesCountry({0},{1},{2})", c => c.ProductType == ProductType.Listing && allowedBranches.Contains(c.BranchId) && c.NotAvailable == false, language, fallback, countryIso);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (categories.Any())
{
baseQuery = baseQuery.Where(c => c.ProductCategoryId != null && categories.Contains(c.ProductCategoryId.Value));
}
if (!string.IsNullOrWhiteSpace(filter))
{
baseQuery = baseQuery.Where(c => c.Name.Contains(filter));
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Produkten vom Typ Werbung zurück
/// </summary>
/// <param name="orderSource">Für welchen Typen? Kunde, Hundebesitzer, Hundeausführer</param>
/// <param name="countryIso">Land</param>
/// <param name="language">Sprache</param>
/// <param name="fallback">Facllback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste von Produkte</returns>
public async Task<List<ProductWithNamesCountry>> GetAdvertisementProductsAsync(OrderSource orderSource, string countryIso, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
countryIso = countryIso.ToUpperInvariant();
var baseQuery = _namesCountryRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNamesCountry({0},{1},{2})", c => c.ProductType == ProductType.Advertisement && c.NotAvailable == false, language, fallback, countryIso);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Produkten vom Typ Werbung zurück
/// </summary>
/// <param name="orderSource">Für welchen Typen? Kunde, Hundebesitzer, Hundeausführer</param>
/// <param name="countryIso">Land</param>
/// <param name="categories">Gewählte Produktkategorien</param>
/// <param name="filter">Filtertext für Name</param>
/// <param name="language">Sprache</param>
/// <param name="fallback">Facllback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste von Produkte</returns>
public async Task<List<ProductWithNamesCountry>> GetAdvertisementProductsAsync(OrderSource orderSource, string countryIso, List<int> categories, string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
countryIso = countryIso.ToUpperInvariant();
var baseQuery = _namesCountryRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNamesCountry({0},{1},{2})", c => c.ProductType == ProductType.Advertisement && c.NotAvailable == false, language, fallback, countryIso);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (categories.Any())
{
baseQuery = baseQuery.Where(c => c.ProductCategoryId != null && categories.Contains(c.ProductCategoryId.Value));
}
if (!string.IsNullOrWhiteSpace(filter))
{
baseQuery = baseQuery.Where(c => c.Name.Contains(filter));
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Produkten vom Typ Banner zurück
/// </summary>
/// <param name="orderSource">Für welchen Typen? Kunde, Hundebesitzer, Hundeausführer</param>
/// <param name="countryIso">Land</param>
/// <param name="language">Sprache</param>
/// <param name="fallback">Facllback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste von Produkte</returns>
public async Task<List<ProductWithNamesCountry>> GetBannerProductsAsync(OrderSource orderSource, string countryIso, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
countryIso = countryIso.ToUpperInvariant();
var baseQuery = _namesCountryRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNamesCountry({0},{1},{2})", c => c.ProductType == ProductType.Banner && c.NotAvailable == false, language, fallback, countryIso);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Produkten vom Typ Banner zurück
/// </summary>
/// <param name="orderSource">Für welchen Typen? Kunde, Hundebesitzer, Hundeausführer</param>
/// <param name="countryIso">Land</param>
/// <param name="categories">Gewählte Produktkategorien</param>
/// <param name="filter">Filtertext für Name</param>
/// <param name="language">Sprache</param>
/// <param name="fallback">Facllback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste von Produkte</returns>
public async Task<List<ProductWithNamesCountry>> GetBannerProductsAsync(OrderSource orderSource, string countryIso, List<int> categories, string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
countryIso = countryIso.ToUpperInvariant();
var baseQuery = _namesCountryRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNamesCountry({0},{1},{2})", c => c.ProductType == ProductType.Banner && c.NotAvailable == false, language, fallback, countryIso);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (categories.Any())
{
baseQuery = baseQuery.Where(c => c.ProductCategoryId != null && categories.Contains(c.ProductCategoryId.Value));
}
if (!string.IsNullOrWhiteSpace(filter))
{
baseQuery = baseQuery.Where(c => c.Name.Contains(filter));
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Produkten vom Typ Pin zurück
/// </summary>
/// <param name="orderSource">Für welchen Typen? Kunde, Hundebesitzer, Hundeausführer</param>
/// <param name="countryIso">Land</param>
/// <param name="language">Sprache</param>
/// <param name="fallback">Facllback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste von Produkte</returns>
public async Task<List<ProductWithNamesCountry>> GetPinProductsAsync(OrderSource orderSource, string countryIso, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
countryIso = countryIso.ToUpperInvariant();
var baseQuery = _namesCountryRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNamesCountry({0},{1},{2})", c => c.ProductType == ProductType.Pin && c.NotAvailable == false, language, fallback, countryIso);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Produkten vom Typ Pin zurück
/// </summary>
/// <param name="orderSource">Für welchen Typen? Kunde, Hundebesitzer, Hundeausführer</param>
/// <param name="countryIso">Land</param>
/// <param name="categories">Gewählte Produktkategorien</param>
/// <param name="filter">Filtertext für Name</param>
/// <param name="language">Sprache</param>
/// <param name="fallback">Facllback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste von Produkte</returns>
public async Task<List<ProductWithNamesCountry>> GetPinProductsAsync(OrderSource orderSource, string countryIso, List<int> categories, string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
countryIso = countryIso.ToUpperInvariant();
var baseQuery = _namesCountryRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNamesCountry({0},{1},{2})", c => c.ProductType == ProductType.Pin && c.NotAvailable == false, language, fallback, countryIso);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (categories.Any())
{
baseQuery = baseQuery.Where(c => c.ProductCategoryId != null && categories.Contains(c.ProductCategoryId.Value));
}
if (!string.IsNullOrWhiteSpace(filter))
{
baseQuery = baseQuery.Where(c => c.Name.Contains(filter));
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt ein Produkt mit Daten für Land und Sprache erweitert zurück.
/// Es wird geprüft ob der kunde auch Zugriff auf das Produkt hat.
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="orderSource">Für welchen Typen? Kunde, Hundebesitzer, Hundeausführer</param>
/// <param name="countryIso">Land</param>
/// <param name="language">Sprache</param>
/// <param name="fallback">Facllback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Produkt</returns>
public async Task<ProductWithNamesCountry> GetWithNamesCountryAsync(int productId, OrderSource orderSource, string countryIso, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
countryIso = countryIso.ToUpperInvariant();
var baseQuery = _namesCountryRepository.QueryStoredProcedure("SELECT * FROM tv_ProductsWithNamesCountry({0},{1},{2})", c => c.Id == productId, language, fallback, countryIso);
switch (orderSource)
{
case OrderSource.Customer:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.Customers);
break;
case OrderSource.AppUserDogOwner:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserDogWalker:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
case OrderSource.AppUserBoth:
baseQuery = baseQuery.Where(c => c.Visibility == ShopVisibility.All || c.Visibility == ShopVisibility.AppUserDogOwner || c.Visibility == ShopVisibility.AppUserDogWalker || c.Visibility == ShopVisibility.AppUserBoth);
break;
default:
throw new ArgumentOutOfRangeException(nameof(orderSource), orderSource, null);
}
if (!includeDeleted)
baseQuery = baseQuery.Where(c => !c.Deleted);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,515 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using gehGassi.Domain.Walks;
using Microsoft.EntityFrameworkCore;
using NetTopologySuite.Geometries;
using NetTopologySuite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Common.Extensions;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von öffentlichen Anfragen ermöglicht
/// </summary>
public class PublicWalkRequestService : ServiceBase<PublicWalkRequest>, IPublicWalkRequestService
{
private readonly IRepository<PublicWalkRequestWithNames> _namesRepository;
private readonly IRepository<PublicWalkRequestWithNamesAndResponseStatus> _namesWithStatusRepository;
private readonly IRepository<PublicWalkResponse> _responsesRepository;
private readonly IRepository<PublicWalkRequestNotification> _notificationsRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public PublicWalkRequestService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<PublicWalkRequestWithNames>();
_namesWithStatusRepository = unitOfWork.GetRepository<PublicWalkRequestWithNamesAndResponseStatus>();
_responsesRepository = unitOfWork.GetRepository<PublicWalkResponse>();
_notificationsRepository = unitOfWork.GetRepository<PublicWalkRequestNotification>();
}
/// <summary>
/// Erstellen einer öffentlichen Anfrage
/// </summary>
/// <returns></returns>
public PublicWalkRequest Create()
{
var request = new PublicWalkRequest
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return request;
}
/// <summary>
/// Gibt eine Liste der öffentlichen Anfragen eines Benutzers zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der öffentlichen Anfragen</returns>
public async Task<List<PublicWalkRequest>> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.DogOwnerId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt die anzahl öffentlicher Anfragen eines AppUsers zurück
/// </summary>
/// <param name="appUserId">AppUserId</param>
/// <returns>Anzahl öffentliche Anfragen</returns>
public async Task<int> CountAsync(string appUserId)
{
var items = await Repository.CountAsync(c => c.DogOwnerId == appUserId);
return items;
}
/// <summary>
/// Abfrage der öffentlichen Anfragen eines AppUsers "Latest"
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="take">Wie viele Datensätze</param>
/// <returns>Liste der öffentlichen Anfragen</returns>
public async Task<List<PublicWalkRequestWithNames>> GetLatestAsync(string appUserId, int take)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.DogOwnerId == appUserId && c.Deleted == false);
var requests = await baseQuery.OrderByDescending(c => c.UpdatedAt).Take(take).ToListAsync();
return requests;
}
/// <summary>
/// Gibt eine Liste von eigenen öffentlichen Anfragen für die App zurück.
/// </summary>
/// <param name="ignoreId">App-User Id die ignoriert werden soll (dogOwnerId)</param>
/// <param name="dogOwnerId">Optional: Id des AppUsers der die Anfragen erstellt hat</param>
/// <param name="dogWalkerId">Optional: Id des DogWalkers</param>
/// <param name="status">Gesuchert Status. Alle wenn NULL</param>
/// <param name="sortList">Optional: Liste der sortierung</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste passender Anfragen</returns>
public async Task<(int total, List<PublicWalkRequestWithNames> list)> GetAsync(string ignoreId, string dogOwnerId, string dogWalkerId, PublicWalkRequestStatus? status, List<DynamicSortOrder> sortList, double lat, double lng, int skip, int take)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.Deleted == false);
if (status.HasValue)
baseQuery = baseQuery.Where(c => c.Status == status);
if (!string.IsNullOrEmpty(ignoreId))
baseQuery = baseQuery.Where(c => c.DogOwnerId != ignoreId);
if (!string.IsNullOrEmpty(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if (!string.IsNullOrEmpty(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt eine Liste von eigenen öffentlichen Anfragen für die App zurück.
/// </summary>
/// <param name="ignoreId">App-User Id die ignoriert werden soll (dogOwnerId)</param>
/// <param name="dogOwnerId">Optional: Id des AppUsers der die Anfragen erstellt hat</param>
/// <param name="dogWalkerId">Optional: Id des DogWalkers</param>
/// <param name="status">Gesuchert Status. Alle wenn NULL</param>
/// <param name="minPrice">Mindest-Preis</param>
/// <param name="sortList">Optional: Liste der sortierung</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <param name="maxRadius">Maximaler Radius</param>
/// <param name="type">Typ der öffentlichen Anfrage, Alle wenn NULL</param>
/// <returns>Liste passender Anfragen</returns>
public async Task<(int total, List<PublicWalkRequestWithNames> list)> GetAsync(string ignoreId, string dogOwnerId, string dogWalkerId, PublicWalkRequestStatus? status, double maxRadius, PublicWalkRequestType? type, decimal minPrice, List<DynamicSortOrder> sortList, double lat, double lng, int skip, int take)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.Deleted == false);
if (status.HasValue)
baseQuery = baseQuery.Where(c => c.Status == status);
if (!string.IsNullOrEmpty(ignoreId))
baseQuery = baseQuery.Where(c => c.DogOwnerId != ignoreId);
if (!string.IsNullOrEmpty(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if (!string.IsNullOrEmpty(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
if(type.HasValue)
baseQuery = baseQuery.Where(c => c.RequestType == type);
if(minPrice > 0)
baseQuery = baseQuery.Where(c => c.Price >= minPrice);
baseQuery = baseQuery.Where(c => c.Location.IsWithinDistance(location, maxRadius * 1000));
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt eine Liste von eigenen öffentlichen Anfragen für die App zurück. Mit dem Antwort-Status für einen DogWalker
/// </summary>
/// <param name="ignoreId">App-User Id die ignoriert werden soll (dogOwnerId)</param>
/// <param name="dogOwnerId">Optional: Id des AppUsers der die Anfragen erstellt hat</param>
/// <param name="dogWalkerId">Optional: Id des DogWalkers</param>
/// <param name="status">Gesuchert Status. Alle wenn NULL</param>
/// <param name="mustHaveResponse">es muss eine Antwort vorhanden sein</param>
/// <param name="sortList">Optional: Liste der sortierung</param>
/// <param name="includeBlockedAndLocked">Sollten blockierte unnd/oder gesperrte Benutzer in die Liste eingefügt werden?</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <param name="targetDogWalkerId">Id des Dogwalkers für welchen die Liste aufbereitet werden soll</param>
/// <param name="responseStatus">Gesuchter Status oder aller wenn null</param>
/// <returns>Liste passender Anfragen</returns>
public async Task<(int total, List<PublicWalkRequestWithNamesAndResponseStatus> list)> GetWithResponseStatusAsync(string ignoreId, string dogOwnerId, string dogWalkerId, PublicWalkRequestStatus? status, string targetDogWalkerId, PublicWalkResponseStatus? responseStatus, bool mustHaveResponse, List<DynamicSortOrder> sortList, double lat, double lng, bool includeBlockedAndLocked, int skip, int take)
{
var baseQuery = _namesWithStatusRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNamesAndResponseStatus({0})", c => c.Deleted == false, targetDogWalkerId);
if (!includeBlockedAndLocked)
baseQuery = baseQuery.Where(c => c.DogOwnerBlocked == false && c.DogOwnerLocked == false);
if (status.HasValue)
baseQuery = baseQuery.Where(c => c.Status == status);
if (!string.IsNullOrEmpty(ignoreId))
baseQuery = baseQuery.Where(c => c.DogOwnerId != ignoreId);
if (!string.IsNullOrEmpty(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if (!string.IsNullOrEmpty(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
if (responseStatus.HasValue)
baseQuery = baseQuery.Where(c => c.ResponseStatus == responseStatus || c.ResponseStatus == null);
if (mustHaveResponse)
baseQuery = baseQuery.Where(c => c.ResponseStatus != null);
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt eine Liste von eigenen öffentlichen Anfragen für die App zurück. Mit dem Antwort-Status für einen DogWalker
/// </summary>
/// <param name="ignoreId">App-User Id die ignoriert werden soll (dogOwnerId)</param>
/// <param name="dogOwnerId">Optional: Id des AppUsers der die Anfragen erstellt hat</param>
/// <param name="dogWalkerId">Optional: Id des DogWalkers</param>
/// <param name="status">Gesuchert Status. Alle wenn null</param>
/// <param name="searchText">Suchtext für Suche in Hundename, OwnerName oder Stadt</param>
/// <param name="mustHaveResponse">es muss eine Antwort vorhanden sein</param>
/// <param name="sortList">Optional: Liste der sortierung</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="includeBlockedAndLocked">Sollen blockierte unnd/oder gesperrte Benutzer in die Liste eingefügt werden?</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <param name="targetDogWalkerId">Id des Dogwalkers für welchen die Liste aufbereitet werden soll</param>
/// <param name="responseStatus">Gesuchter Status oder alle wenn null</param>
/// <param name="maxRadius">Maximaler Radius</param>
/// <param name="type">Typ der öffentlichen Anfrage, oder alle wenn NULL</param>
/// <param name="minPrice">Mindest-Preis</param>
/// <returns>Liste passender Anfragen</returns>
public async Task<(int total, List<PublicWalkRequestWithNamesAndResponseStatus> list)> GetWithResponseStatusAsync(string ignoreId, string dogOwnerId, string dogWalkerId, PublicWalkRequestStatus? status, string targetDogWalkerId, PublicWalkResponseStatus? responseStatus, double maxRadius, PublicWalkRequestType? type, decimal minPrice, bool mustHaveResponse, string searchText, List<DynamicSortOrder> sortList, double lat, double lng, bool includeBlockedAndLocked, int skip, int take)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesWithStatusRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNamesAndResponseStatus({0})", c => c.Deleted == false, targetDogWalkerId);
if (!includeBlockedAndLocked)
baseQuery = baseQuery.Where(c => c.DogOwnerBlocked == false && c.DogOwnerLocked == false);
if (status.HasValue)
baseQuery = baseQuery.Where(c => c.Status == status);
if (!string.IsNullOrEmpty(ignoreId))
baseQuery = baseQuery.Where(c => c.DogOwnerId != ignoreId);
if (!string.IsNullOrEmpty(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if (!string.IsNullOrEmpty(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
if (type.HasValue)
baseQuery = baseQuery.Where(c => c.RequestType == type);
if (minPrice > 0)
baseQuery = baseQuery.Where(c => c.Price >= minPrice);
if (responseStatus.HasValue)
baseQuery = baseQuery.Where(c => c.ResponseStatus == responseStatus || c.ResponseStatus == null);
if (mustHaveResponse)
baseQuery = baseQuery.Where(c => c.ResponseStatus != null);
if (!string.IsNullOrWhiteSpace(searchText))
baseQuery = baseQuery.Where(c => c.DogName.Contains(searchText) || c.DogsJson.Contains(searchText) || c.DogOwnerName.Contains(searchText) || c.PickupAddress_City.Contains(searchText));
baseQuery = baseQuery.Where(c => c.Location.IsWithinDistance(location, maxRadius * 1000));
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.StartDate).ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt eine öffentliche Ausschreibung mit Namen aufgelöst zurück
/// </summary>
/// <param name="id">ID der Ausschreibung</param>
/// <returns>öffentliche Ausschreibung oder null, wenn nicht gefunden</returns>
public async Task<PublicWalkRequestWithNames> GetWithNamesAsync(string id)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.Id == id);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
/// <summary>
/// Setzt öffntliche Anfragen auf abgelaufen, wenn diese nicht angenommen oder storniert wurden
/// </summary>
/// <param name="date">Datum für Prüfung</param>
/// <returns>Anzahl timed out</returns>
public async Task<int> SetTimedOutAsync(DateTimeOffset date)
{
var requests = await Repository.FindAsync(c => c.ExpirationDate <= date && c.Deleted == false && c.Status == PublicWalkRequestStatus.Open).ConfigureAwait(false);
foreach (var request in requests)
{
request.Status = PublicWalkRequestStatus.TimedOut;
request.UpdatedAt = DateTimeOffset.UtcNow;
var responses = await _responsesRepository.FindAsync(c => c.PublicWalkRequestId == request.Id).ConfigureAwait(false);
foreach (var response in responses)
{
response.Status = PublicWalkResponseStatus.Closed;
response.UpdatedAt = DateTimeOffset.UtcNow;
}
}
return requests.Count();
}
/// <summary>
/// Zurücksetzen einer öffentlichen Anfrage
/// </summary>
/// <param name="id">Id der öffentlichen Anfrage</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> ResetAsync(string id)
{
var request = await Repository.FirstOrDefaultAsync(c => c.Id == id);
if (request != null && request.Deleted == false && request.Status == PublicWalkRequestStatus.Placed)
{
request.Status = PublicWalkRequestStatus.Open;
request.PlacedDate = null;
request.PublicWalkResponseId = string.Empty;
request.DogWalkerId = string.Empty;
request.UpdatedAt = DateTimeOffset.UtcNow;
return true;
}
return false;
}
/// <summary>
/// Gibt eine Liste der offenen Anfragen für einen Hundebesitzer in einem gewählten Status zurück
/// </summary>
/// <param name="dogOwnerId">Id des Hundebesitzers</param>
/// <param name="status">Gesuchter Status</param>
/// <returns></returns>
public async Task<List<PublicWalkRequest>> GetByStatusAsync(string dogOwnerId, PublicWalkRequestStatus status)
{
var requests = await Repository.FindAsync(c => c.DogOwnerId == dogOwnerId && c.Status == status && c.Deleted == false).ConfigureAwait(false);
return requests.ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von öffentlichen Anfragen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener öffentlicher Anfragen</returns>
public IQueryable<PublicWalkRequestWithNames> FilterPublicWalkRequestsWithNames(string filter, bool includeDeleted)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.DogOwnerName.Contains(filter) || c.PickupAddress_City.Contains(filter));
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
#region Notifications
/// <summary>
/// Erstellt einen Eintrag für die Benachrichtigung einer neuen öffentlichen Anfrage
/// </summary>
/// <param name="publicWalkRequestId">Id der öffentlichen Anfrage</param>
/// <param name="dogOwnerId">Id des Hundebesitzers - AppUserId</param>
/// <param name="pickupAddress">Abholadresse</param>
/// <param name="location">GEO-Koordinaten</param>
/// <returns>PublicWalkRequestNotification</returns>
public PublicWalkRequestNotification CreateNotification(string publicWalkRequestId, string dogOwnerId, Address pickupAddress, Point location)
{
var item = new PublicWalkRequestNotification
{
PublicWalkRequestId = publicWalkRequestId,
DogOwnerId = dogOwnerId,
SentDate = null,
SentCount = 0,
HasBeenSent = false,
Created = DateTimeOffset.UtcNow,
PickupAddress =
{
AddressLine1 = pickupAddress.AddressLine1,
AddressLine2 = pickupAddress.AddressLine2,
Zip = pickupAddress.Zip,
City = pickupAddress.City,
CountryCode = pickupAddress.CountryCode,
State = pickupAddress.State
},
Location = location
};
return item;
}
/// <summary>
/// Hinzufügen einer Benachrichtigung
/// </summary>
/// <param name="publicWalkRequestNotification">Benachrichtigungs-Objekt</param>
public void AddNotification(PublicWalkRequestNotification publicWalkRequestNotification)
{
_notificationsRepository.Add(publicWalkRequestNotification);
}
/// <summary>
/// Gibt eine Liste von zu sendenden Benachrichtigungen zurück
/// </summary>
/// <returns>Liste zu sendende Benachrichtigungen</returns>
public async Task<List<PublicWalkRequestNotification>> GetNotificationsToSendAsync()
{
var notifications = await _notificationsRepository.FindAsync(c => c.HasBeenSent == false && c.SentDate == null).ConfigureAwait(false);
return notifications.ToList();
}
/// <summary>
/// Zählt wie viele öffenltiche Anfragen in einem bestimmten Zeitraum durch einen Benutzer erstellt wurden
/// </summary>
/// <param name="startDate">Start-Datum</param>
/// <param name="endDate">End-Datum</param>
/// <param name="dogOwnerId">Id des Hundebesitzers</param>
/// <param name="maxStatus">Max Status der Ausschreibung</param>
/// <returns>Anzahl öffentliche Anfragen</returns>
public async Task<int> CountByCreationDateAsync(DateTimeOffset startDate, DateTimeOffset endDate, string dogOwnerId, PublicWalkRequestStatus maxStatus)
{
var count = await Repository.CountAsync(c => c.DogOwnerId == dogOwnerId && c.Created >= startDate && c.Created <= endDate && c.Status <= maxStatus).ConfigureAwait(false);
return count;
}
#endregion
}
}

View File

@ -0,0 +1,178 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.Walks;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Antworten auf öffentlichen Anfragen ermöglicht
/// </summary>
public class PublicWalkResponseService : ServiceBase<PublicWalkResponse>, IPublicWalkResponseService
{
private readonly IRepository<PublicWalkResponseWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public PublicWalkResponseService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<PublicWalkResponseWithNames>();
}
/// <summary>
/// Erstellen einer Antwort auf eine öffentlichen Anfrage
/// </summary>
/// <returns></returns>
public PublicWalkResponse Create(string publicWalkRequestId)
{
var item = new PublicWalkResponse()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow,
PublicWalkRequestId = publicWalkRequestId
};
return item;
}
/// <summary>
/// Gibt eine Liste der Antworten auf öffentlichen Anfragen eines Benutzers zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der Antworten auf öffentlichen Anfragen</returns>
public async Task<List<PublicWalkResponse>> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.DogWalkerId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Antwort eines Walkers auf eine öffentliche Anfrage zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers dem die öffentliche Anfrage gehört</param>
/// <param name="publicWalkResponseId">Id der Antwort zur öffentlichen Anfrage</param>
/// <returns>PublicWalkResponse oder null, wenn keine gefunden</returns>
public async Task<PublicWalkResponseWithNames> GetWithNamesAsync(string appUserId, string publicWalkResponseId)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkResponseWithNames({0})", c => c.Id == publicWalkResponseId && c.Deleted == false, appUserId);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Antwort eines Walkers auf eine öffentliche Anfrage zurück
/// </summary>
/// <param name="dogWalkerId">Id des Walkers</param>
/// <param name="publicWalkRequestId">Id der öffntlichen Anfrage</param>
/// <returns>PublicWalkResponse oder null, wenn keine gefunden</returns>
public async Task<PublicWalkResponse> GetForWalkerAsync(string dogWalkerId, string publicWalkRequestId)
{
return await Repository.FirstOrDefaultAsync(c => c.DogWalkerId == dogWalkerId && c.PublicWalkRequestId == publicWalkRequestId).ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl der Antworten auf eine öffentliche Anfrage zurück
/// </summary>
/// <param name="publicWalkRequestId">Id der öffentlichen Anfrage</param>
/// <returns>Anzahl Antworten</returns>
public async Task<int> CountForPublicWalkRequestAsync(string publicWalkRequestId)
{
return await Repository.CountAsync(c => c.PublicWalkRequestId == publicWalkRequestId && c.Deleted == false).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Antworten zu einer öffentlichen Anfragen für die App zurück.
/// </summary>
/// <param name="appUserId">Id des AppUsers dem die öffentliche Anfrage gehört</param>
/// <param name="publicRequestId">Id der öffentlichen Anfrage</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste passender antworten</returns>
public async Task<(int total, List<PublicWalkResponseWithNames> list)> GetResponsesAsync(string appUserId, string publicRequestId, int skip, int take)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkResponseWithNames({0})", c => c.PublicWalkRequestId == publicRequestId && c.Deleted == false, appUserId);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
var list = await baseQuery.OrderByDescending(c => c.UpdatedAt).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
/// <summary>
/// Gibt eine Liste aller Antworten zu einer öffentlichen Anfragen zurück.
/// </summary>
/// <param name="appUserId">Id des AppUsers dem die öffentliche Anfrage gehört</param>
/// <param name="publicRequestId">Id der öffentlichen Anfrage</param>
/// <returns>Liste passender antworten</returns>
public async Task<List<PublicWalkResponseWithNames>> GetResponsesAsync(string appUserId, string publicRequestId)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkResponseWithNames({0})", c => c.PublicWalkRequestId == publicRequestId && c.Deleted == false, appUserId);
return await baseQuery.ToListAsync();
}
/// <summary>
/// Setzen des Closed Status für Angebote die im Warten-Status sind.
/// Wird verwendet wenn ein Angebot angenommen wurde und man nun den anderen Bewerbern mitteilen muss, dass wer anderes genommen wurde
/// </summary>
/// <param name="publicWalkRequestId">Id der öffentlichen Anfrage</param>
/// <returns>Anzahl geännderte Angebote</returns>
public async Task<int> SetCompletedForWaitingAsync(string publicWalkRequestId)
{
var responses = await Repository.FindAsync(c => c.PublicWalkRequestId == publicWalkRequestId && c.Deleted == false && c.Status == PublicWalkResponseStatus.Awaiting).ConfigureAwait(false);
foreach (var response in responses)
{
response.Status = PublicWalkResponseStatus.Closed;
response.UpdatedAt = DateTimeOffset.UtcNow;
}
return responses.Count();
}
/// <summary>
/// Setzen des Open Status für Angebote die im Closed-Status sind.
/// Wird verwendet wenn ein angenommenes Angebot nicht bezahlt wurde und ein Reset der öffentlichen Anfrage durchgeführt wird
/// </summary>
/// <param name="publicWalkRequestId">Id der öffentlichen Anfrage</param>
/// <returns>Anzahl geännderte Angebote</returns>
public async Task<int> SetOpenForClosedOrAcceptedAsync(string publicWalkRequestId)
{
var responses = await Repository.FindAsync(c => c.PublicWalkRequestId == publicWalkRequestId && c.Deleted == false && (c.Status == PublicWalkResponseStatus.Closed || c.Status == PublicWalkResponseStatus.Accepted)).ConfigureAwait(false);
foreach (var response in responses)
{
response.Status = PublicWalkResponseStatus.Awaiting;
response.UpdatedAt = DateTimeOffset.UtcNow;
}
return responses.Count();
}
/// <summary>
/// Stornieren von Angeobten für offene öffentliche Anfragen wenn ein Benutzer gesperrt wurde
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <returns>Anzahl stornierte Angebote</returns>
public async Task<int> CancelForLockedAsync(string appUserId)
{
var responses = await Repository.FindAsync(c => c.DogWalkerId == appUserId && c.Deleted == false && c.Status == PublicWalkResponseStatus.Awaiting).ConfigureAwait(false);
foreach (var response in responses)
{
response.Status = PublicWalkResponseStatus.Cancelled;
response.UpdatedAt = DateTimeOffset.UtcNow;
response.CancellationReason = "User locked";
}
await CommitAsync("System");
return responses.Count();
}
}
}

View File

@ -0,0 +1,372 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Walks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.Ratings;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore;
using gehGassi.Common.Extensions;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Ratings ermöglicht
/// </summary>
public class RatingService : ServiceBase<Rating>, IRatingService
{
private readonly IRepository<RatingWithNames> _namesRepository;
private readonly IRepository<RatingWithNamesEx> _namesExRepository;
private readonly IRepository<Dog> _dogsRepository;
private readonly IRepository<AppUser> _appUserRepository;
private readonly IRepository<Walk> _walksRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public RatingService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<RatingWithNames>();
_namesExRepository = unitOfWork.GetRepository<RatingWithNamesEx>();
_dogsRepository = unitOfWork.GetRepository<Dog>();
_appUserRepository = unitOfWork.GetRepository<AppUser>();
_walksRepository = unitOfWork.GetRepository<Walk>();
}
/// <summary>
/// Erstellen eines Ratings
/// </summary>
/// <returns>Rating</returns>
public Rating Create()
{
var rating = new Rating()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return rating;
}
/// <summary>
/// Erstellen eines Ratings
/// </summary>
/// <param name="fromId">Id der Rating Quelle</param>
/// <param name="fromType">Typ der Rating Quelle</param>
/// <param name="targetId">Id des Ziels des Ratings</param>
/// <param name="targetType">Typ des Rating Ziels</param>
/// <param name="points">Vergebene Punkte</param>
/// <param name="info">Zusatzinfo zum Rating</param>
/// <returns>Rating</returns>
public Rating Create(string fromId, AppUserType fromType, string targetId, RatingTarget targetType, decimal points, string info)
{
var rating = new Rating()
{
Id = Guid.NewGuid().ToString("N"),
FromId = fromId,
FromType = fromType,
TargetId = targetId,
TargetType = targetType,
Points = points,
Info = info,
Created = DateTimeOffset.UtcNow
};
return rating;
}
/// <summary>
/// Gibt eine Liste der Ratings eines Benutzers zurück. Kann Owner und Walker sein
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der öffentlichen Anfragen</returns>
public async Task<List<Rating>> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.FromId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt ein Rating basierend auf Abfragekriterien zurück
/// </summary>
/// <param name="fromId">Id der Rating Quelle</param>
/// <param name="fromType">Typ der Rating Quelle</param>
/// <param name="targetId">Id des Ziels des Ratings</param>
/// <param name="targetType">Typ des Rating Ziels</param>
/// <returns>Rating wenn gefunden, null sonst</returns>
public async Task<Rating> GetRatingExAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType)
{
var rating = await Repository.FirstOrDefaultAsync(c => c.FromId == fromId && c.FromType == fromType && c.TargetId == targetId && c.TargetType == targetType).ConfigureAwait(false);
return rating;
}
/// <summary>
/// Neu berechnen der Ratings-Statistics für ein Ziel
/// </summary>
/// <param name="targetId">Id des Ziels des Ratings</param>
/// <param name="targetType">Typ des Rating Ziels</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> UpdateRatingStatisticsAsync(string targetId, RatingTarget targetType)
{
if (targetType == RatingTarget.Dog || targetType == RatingTarget.Owner || targetType == RatingTarget.Walker)
{
var totalRatings = await Repository.CountAsync(c => c.TargetId == targetId && c.Deleted == false).ConfigureAwait(false);
var totalPoints = (await Repository.FindAsync(c => c.TargetId == targetId && c.Deleted == false).ConfigureAwait(false)).Sum(c => c.Points);
var averageRating = 0m;
if (totalRatings > 0)
averageRating = totalPoints / totalRatings;
if (targetType == RatingTarget.Dog)
{
var dog = await _dogsRepository.FirstOrDefaultAsync(c => c.Id == targetId);
if (dog != null)
{
dog.RatingStatistics.TotalRatings = totalRatings;
dog.RatingStatistics.TotalPoints = totalPoints;
dog.RatingStatistics.AverageRating = averageRating;
dog.UpdatedAt = DateTimeOffset.UtcNow;
return true;
}
}
if (targetType == RatingTarget.Owner)
{
var appUser = await _appUserRepository.FirstOrDefaultAsync(c => c.Id == targetId);
if (appUser != null)
{
appUser.RatingStatistics.TotalRatings = totalRatings;
appUser.RatingStatistics.TotalPoints = totalPoints;
appUser.RatingStatistics.AverageRating = averageRating;
appUser.UpdatedAt = DateTimeOffset.UtcNow;
return true;
}
}
if (targetType == RatingTarget.Walker)
{
var appUser = await _appUserRepository.FirstOrDefaultAsync(c => c.Id == targetId);
if (appUser != null)
{
appUser.RatingStatisticsWalker.TotalRatings = totalRatings;
appUser.RatingStatisticsWalker.TotalPoints = totalPoints;
appUser.RatingStatisticsWalker.AverageRating = averageRating;
appUser.UpdatedAt = DateTimeOffset.UtcNow;
return true;
}
}
}
return false;
}
/// <summary>
/// Gibt an ob ein Bewerter ein Ziel bereits bewertet hat
/// </summary>
/// <param name="fromId">Id der Rating Quelle</param>
/// <param name="fromType">Typ der Rating Quelle</param>
/// <param name="targetId">Id des Ziels des Ratings</param>
/// <param name="targetType">Typ des Rating Ziels</param>
/// <returns>true wenn bereits bewertet, false sonst</returns>
public async Task<bool> HasRatedAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType)
{
var rating = await Repository.FirstOrDefaultAsync(c => c.FromId == fromId && c.FromType == fromType && c.TargetId == targetId && c.TargetType == targetType).ConfigureAwait(false);
return rating != null;
}
/// <summary>
/// Gibt zurück ob ein Bewerter ein Ziel bewerten darf.
/// z.B. ein Walker darf einen hund nur bewerten, wenn mindestens ein Walk mit dem hund stattgefunden hat.
/// Ein Owner darf einen Walker nur bewerten, wenn dieser mindestens einen Walk für den Owen gemacht hat usw.
/// </summary>
/// <param name="fromId">Id der Rating Quelle</param>
/// <param name="fromType">Typ der Rating Quelle</param>
/// <param name="targetId">Id des Ziels des Ratings</param>
/// <param name="targetType">Typ des Rating Ziels</param>
/// <returns>true wenn eine Bewertung stattfinden darf, false sonst</returns>
public async Task<bool> CanRateAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType)
{
if (targetType == RatingTarget.Dog || targetType == RatingTarget.Owner || targetType == RatingTarget.Walker)
{
//1. Darf ein Walker einen Hund bewerten?
if ((fromType == AppUserType.DogWalker || fromType == AppUserType.Both) && targetType == RatingTarget.Dog)
{
var existingWalk = await _walksRepository.FirstOrDefaultAsync(c => c.DogsJson.Contains(targetId) && c.DogWalkerId == fromId && c.Completed.HasValue).ConfigureAwait(false);
return existingWalk != null;
}
//2. Darf ein Owner einen Walker bewerten?
if ((fromType == AppUserType.DogOwner || fromType == AppUserType.Both) && targetType == RatingTarget.Walker)
{
var existingWalk = await _walksRepository.FirstOrDefaultAsync(c => c.DogOwnerId == fromId && c.DogWalkerId == targetId && c.Completed.HasValue).ConfigureAwait(false);
return existingWalk != null;
}
//2. Darf ein Walker einen Owner bewerten?
if ((fromType == AppUserType.DogWalker || fromType == AppUserType.Both) && targetType == RatingTarget.Owner)
{
var existingWalk = await _walksRepository.FirstOrDefaultAsync(c => c.DogOwnerId == targetId && c.DogWalkerId == fromId && c.Completed.HasValue).ConfigureAwait(false);
return existingWalk != null;
}
}
return false;
}
/// <summary>
/// Alle Ratings eines AppUsers und für einen AppUser löschen
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>Anzahl gelöscht</returns>
public async Task<List<Tuple<string, RatingTarget>>> DeleteAllForAppUserAsync(string appUserId)
{
var targetsToUpdate = new List<Tuple<string, RatingTarget>>();
//Zuerst werden alle Ratings gelöscht, die an den AppUser gestellt wurden
var ratings = await Repository.FindAsync(c => c.TargetId == appUserId).ConfigureAwait(false);
foreach (var rating in ratings)
{
Repository.Remove(rating);
}
//Nun alle suchen die von dem Benutzer kommen und diese löschen.
//Achutung: bei den Zielbenutzern muss dann die Statistik aktualisiert werden!
var ratingsFromUser = await Repository.FindAsync(c => c.FromId == appUserId).ConfigureAwait(false);
foreach (var rating in ratingsFromUser)
{
if (rating.TargetType == RatingTarget.Dog || rating.TargetType == RatingTarget.Owner || rating.TargetType == RatingTarget.Walker)
{
targetsToUpdate.Add(new Tuple<string, RatingTarget>(rating.TargetId, rating.TargetType));
}
Repository.Remove(rating);
}
return targetsToUpdate;
}
/// <summary>
/// Gibt eine Liste von Ratings mit Namen aufgelöst zurück
/// </summary>
/// <param name="fromId">Id der Rating Quelle</param>
/// <param name="fromType">Typ der Rating Quelle</param>
/// <param name="targetId">Id des Ziels des Ratings</param>
/// <param name="targetType">Typ des Rating Ziels</param>
/// <param name="sortList">Optional: Liste der sortierung</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste gefudene Walks</returns>
public async Task<(int total, List<RatingWithNames> list)> GetWithNamesAsync(string fromId, AppUserType? fromType, string targetId, RatingTarget? targetType, List<DynamicSortOrder> sortList, int skip, int take)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM vw_RatingsWithNames", c => c.Deleted == false);
if (!string.IsNullOrWhiteSpace(fromId))
baseQuery = baseQuery.Where(c => c.FromId == fromId);
if (fromType.HasValue)
baseQuery = baseQuery.Where(c => c.FromType == fromType);
if (!string.IsNullOrWhiteSpace(targetId))
baseQuery = baseQuery.Where(c => c.TargetId == targetId);
if (targetType.HasValue)
baseQuery = baseQuery.Where(c => c.TargetType == targetType);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderByDescending(c => c.UpdatedAt).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt eine Liste von Ratings mit Namen aufgelöst und mit lock und block Info zurück
/// </summary>
/// <param name="blockingAppUserId">Id des Benutzers der abfrägt und für den Blocking geprüft wird</param>
/// <param name="fromId">Id der Rating Quelle</param>
/// <param name="fromType">Typ der Rating Quelle</param>
/// <param name="targetId">Id des Ziels des Ratings</param>
/// <param name="targetType">Typ des Rating Ziels</param>
/// <param name="sortList">Optional: Liste der sortierung</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste gefudene Walks</returns>
public async Task<(int total, List<RatingWithNamesEx> list)> GetWithNamesExAsync(string blockingAppUserId, string fromId, AppUserType? fromType, string targetId, RatingTarget? targetType, List<DynamicSortOrder> sortList, int skip, int take)
{
var baseQuery = _namesExRepository.QueryStoredProcedure("SELECT * FROM tv_RatingsWithNamesEx({0})", c => c.Deleted == false, blockingAppUserId);
if (!string.IsNullOrWhiteSpace(fromId))
baseQuery = baseQuery.Where(c => c.FromId == fromId);
if (fromType.HasValue)
baseQuery = baseQuery.Where(c => c.FromType == fromType);
if (!string.IsNullOrWhiteSpace(targetId))
baseQuery = baseQuery.Where(c => c.TargetId == targetId);
if (targetType.HasValue)
baseQuery = baseQuery.Where(c => c.TargetType == targetType);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderByDescending(c => c.UpdatedAt).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt ein Rating mit Namen aufgelöst zurück
/// </summary>
/// <param name="ratingId">Id des Ratings</param>
/// <returns>Rating oder null, wenn nicht gefunden</returns>
public async Task<RatingWithNames> GetWithNamesAsync(string ratingId)
{
return await _namesRepository.FirstOrDefaultAsync(c => c.Id == ratingId).ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Users;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der Token erstellt
/// </summary>
public class TokenService : ITokenService
{
private readonly IConfiguration _configuration;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="configuration">Instanz einer IConfiguration</param>
public TokenService(IConfiguration configuration)
{
_configuration = configuration;
}
/// <summary>
/// Erstellen eines Tokens
/// </summary>
/// <param name="claims">Zu setzende Claims</param>
/// <returns>Token als string</returns>
public string GenerateAccessToken(IEnumerable<Claim> claims)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
var jwtToken = new JwtSecurityToken(
issuer: _configuration["JWT:ValidIssuer"],
audience: _configuration["JWT:ValidAudience"],
claims: claims,
notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddMinutes(int.Parse(_configuration["JWT:TokenMinutes"])),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(jwtToken);
}
/// <summary>
/// Gibt das ClaimsPricipal basierend auf einem abgelaufenen Token zurück
/// </summary>
/// <param name="token">Abgelaufenes Token</param>
/// <returns>ClaimsPrincipal</returns>
public ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
{
try
{
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true, //you might want to validate the audience and issuer depending on your use case
ValidAudience = _configuration["JWT:ValidAudience"],
ValidateIssuer = true,
ValidIssuer = _configuration["JWT:ValidIssuer"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"])),
ValidateLifetime = false //here we are saying that we don't care about the token's expiration date
};
var tokenHandler = new JwtSecurityTokenHandler();
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var securityToken);
if (securityToken != null)
{
var jwtSecurityToken = securityToken as JwtSecurityToken;
if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
return null;
return principal;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return null;
}
}
/// <summary>
/// Service der die Verwaltung von Refresh-Tokens realisiert
/// </summary>
public class RefreshTokenService : ServiceBase<RefreshToken>, IRefreshTokenService
{
private readonly IConfiguration _configuration;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
/// <param name="configuration">Instanz eines IConfiguration</param>
public RefreshTokenService(IUnitOfWork unitOfWork, IConfiguration configuration) : base(unitOfWork)
{
_configuration = configuration;
}
/// <summary>
/// Erstellten eines Tokens
/// </summary>
/// <returns>RefreshToken</returns>
public RefreshToken Create()
{
var item = new RefreshToken();
var randomNumber = new byte[32];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomNumber);
item.Token = Convert.ToBase64String(randomNumber);
item.Expires = DateTimeOffset.UtcNow.AddMinutes(int.Parse(_configuration["JWT:RefreshTokenMinutes"]));
return item;
}
/// <summary>
/// Gibt eine Liste aller RefreshTokens eines Benutzers zurück
/// </summary>
/// <param name="userId">Id des Benutzers</param>
/// <returns>Liste Refreshtokens</returns>
public async Task<List<RefreshToken>> GetAllAsync(string userId)
{
return (await Repository.FindAsync(c => c.UserId == userId).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt ein Refreshtoken zurück
/// </summary>
/// <param name="userId">Id des Benutzers</param>
/// <param name="token">Refreshtoken</param>
/// <returns>RefreshToken</returns>
public async Task<RefreshToken> GetAsync(string userId, string token)
{
return await Repository.SingleOrDefaultAsync(c => c.UserId == userId && c.Token == token);
}
/// <summary>
/// Gibt ein Refreshtoken anhand des Tokens selbst zurück
/// </summary>
/// <param name="token">Token</param>
/// <returns>RefreshToken</returns>
public async Task<RefreshToken> GetByTokenAsync(string token)
{
return await Repository.SingleOrDefaultAsync(c => c.Token == token);
}
/// <summary>
/// Gibt zurück ob ein Token gültig ist. Bedeutet, gibt es das Token und ist es noch nicht abgelaufen
/// </summary>
/// <param name="userId">Id des Benutzers</param>
/// <param name="token">Refreshtoken</param>
/// <returns>true wenn gültig, false sonst</returns>
public async Task<bool> IsValidAsync(string userId, string token)
{
var item = await GetAsync(userId, token);
if (item != null)
{
return item.Expires > DateTimeOffset.UtcNow;
}
return false;
}
/// <summary>
/// Löschen eines Refreshtokens
/// </summary>
/// <param name="id">Id des Tokens</param>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn gelöscht, false sonst</returns>
public async Task<bool> RemoveAsync(long id, string userName)
{
var item = await GetAsync(id);
if (item != null)
{
Repository.Remove(item);
await CommitAsync(userName);
return true;
}
return false;
}
/// <summary>
/// Löscht alle Refreshtokens eines Benutzers
/// </summary>
/// <param name="userId">Id des Tokens</param>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> RemoveAllAsync(string userId, string userName)
{
var items = await Repository.FindAsync(c => c.UserId == userId);
if (items.Any())
{
Repository.RemoveRange(items);
return true;
}
return false;
}
/// <summary>
/// Löscht alle abgelaufenen Refreshtokens eines Benutzers
/// </summary>
/// <param name="userId">Id des Tokens</param>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> RemoveExpiredAsync(string userId, string userName)
{
var items = await Repository.FindAsync(c => c.UserId == userId && c.Expires < DateTimeOffset.UtcNow);
if (items.Any())
{
Repository.RemoveRange(items);
return true;
}
return false;
}
/// <summary>
/// Löscht alle abgelaufenen Refreshtokens
/// </summary>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> RemoveExpiredAsync(string userName)
{
var items = await Repository.FindAsync(c => c.Expires < DateTimeOffset.UtcNow);
if (items.Any())
{
Repository.RemoveRange(items);
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Dogs;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service die Verwaltung von Relationen zwischen Entitäten ermöglicht
/// </summary>
public class AppUserRelationService : ServiceBase<AppUserRelation>, IAppUserRelationService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public AppUserRelationService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Fügt eine Relation hinzu wenn diese nicht bereits existiert.
/// Dabei werden beide Richtungen geprüft.
/// </summary>
/// <param name="leftId">Id der ersten Entität</param>
/// <param name="rightId">Id der zweiten Entität</param>
/// <param name="userName">Benutzer der die Änderung vorgenommen hat</param>
/// <returns>Neue Relation oder gefundene Relation</returns>
public async Task<AppUserRelation> AddIfNotExistsAsync(string leftId, string rightId, string userName)
{
var query = Repository.Query(c => (c.LeftId == leftId && c.RightId == rightId) || (c.LeftId == rightId && c.RightId == leftId));
var found = await query.FirstOrDefaultAsync().ConfigureAwait(false);
if (found == null)
{
found = new AppUserRelation
{
LeftId = leftId,
RightId = rightId,
Created = DateTime.Now
};
Repository.Add(found);
await CommitAsync(userName);
}
return found;
}
/// <summary>
/// Setzen einer Relation auf gelöscht - einseitig.
/// Für den Initiator wird "gelöscht" gesetzt.
/// Der Initiator wird dabei aber in Left und Right gesucht.
/// </summary>
/// <param name="initiatorId">Id des Initiators</param>
/// <param name="otherId">Id der anderen Entität</param>
/// <param name="userName">Benutzer der die Änderung vorgenommen hat</param>
/// <returns>true wenn auf gelöscht gesetzt, false sonst</returns>
public async Task<bool> DeleteAsync(string initiatorId, string otherId, string userName)
{
var query = Repository.Query(c => (c.LeftId == initiatorId && c.RightId == otherId) || (c.LeftId == otherId && c.RightId == initiatorId));
var found = await query.FirstOrDefaultAsync().ConfigureAwait(false);
if (found != null)
{
if (found.LeftId == initiatorId)
found.LeftDeleted = true;
else
found.RightDeleted = true;
}
return false;
}
/// <summary>
/// Gibt eine Liste von Relationszielen zurück, zu welcher der Initiator eine Beziehung hat
/// </summary>
/// <param name="initiatorId">Id des Initiators</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste von Relationszielen</returns>
public async Task<List<AppUserRelationTarget>> GetRelations(string initiatorId, bool includeDeleted = false)
{
var resultList = new List<AppUserRelationTarget>();
var query = Repository.Query(c => c.LeftId == initiatorId || c.RightId == initiatorId );
if (includeDeleted == false)
query = query.Where(c => c.RightDeleted == false && c.LeftDeleted == false);
var items = await query.ToListAsync().ConfigureAwait(false);
foreach (var item in items)
{
if(item.LeftId == initiatorId)
resultList.Add(new AppUserRelationTarget(){Id = item.Id, TargetId = item.RightId});
else
resultList.Add(new AppUserRelationTarget() { Id = item.Id, TargetId = item.LeftId});
}
return resultList;
}
}
}

View File

@ -0,0 +1,270 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
namespace gehGassi.Core.Services
{
/// <summary>
/// Abstrakte Implementation des Service Interface als Basis für spezialisierte Klassen
/// </summary>
/// <typeparam name="T">Typ der Klasse</typeparam>
public abstract class ServiceBase<T> : IDisposable, IService<T> where T : class
{
private readonly IUnitOfWork _unitOfWork;
private bool _disposed = false;
internal readonly IRepository<T> Repository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
protected ServiceBase(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
Repository = _unitOfWork.GetRepository<T>();
}
#region Get
/// <summary>
/// Gibt eine Entität anhand der eindeutigen Id zurück
/// </summary>
/// <param name="id">Id der Entität</param>
/// <returns>Entität oder null, wenn nicht gefunden</returns>
public virtual T Get(object id)
{
return id == null ? null : Repository.Get(id);
}
/// <summary>
/// Gibt eine Entität anhand der eindeutigen Id zurück
/// </summary>
/// <param name="id">Id der Entität</param>
/// <returns>Entität oder null, wenn nicht gefunden</returns>
public virtual async Task<T> GetAsync(object id)
{
return id == null ? null : await Repository.GetAsync(id);
}
/// <summary>
/// Gibt alle Entitäten zurück
/// </summary>
/// <returns>Liste aller Entitäten</returns>
public virtual IEnumerable<T> GetAll()
{
return Repository.GetAll();
}
/// <summary>
/// Gibt alle Entitäten zurück
/// </summary>
/// <returns>Liste aller Entitäten</returns>
public virtual async Task<IEnumerable<T>> GetAllAsync()
{
return await Repository.GetAllAsync();
}
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
public virtual IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
{
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
return Repository.Find(predicate);
}
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
public virtual async Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate)
{
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
return await Repository.FindAsync(predicate);
}
/// <summary>
/// Gibt eine Liste aller Entitäten zurück welche definierten Kriterien entsprechen
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Liste gefundener Entitäten</returns>
public IQueryable<T> Query(Expression<Func<T, bool>> predicate)
{
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
return Repository.Query(predicate);
}
/// <summary>
/// Gibt eine Entität oder den Default wert zurück
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
public virtual T SingleOrDefault(Expression<Func<T, bool>> predicate)
{
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
return Repository.SingleOrDefault(predicate);
}
/// <summary>
/// Gibt eine Entität oder den Default wert zurück
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
public virtual async Task<T> SingleOrDefaultAsync(Expression<Func<T, bool>> predicate)
{
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
return await Repository.SingleOrDefaultAsync(predicate);
}
/// <summary>
/// Gibt eine Entität oder den Default wert zurück
/// </summary>
/// <param name="predicate">Kriterien</param>
/// <returns>Gefundene entität oder Default-Wert</returns>
public async Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate)
{
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
return await Repository.FirstOrDefaultAsync(predicate);
}
#endregion
#region CRUD
/// <summary>
/// Fügt eine Entität zum Repository hinzu
/// </summary>
/// <param name="entity">Entität</param>
public virtual void Add(T entity)
{
if (entity == null)
throw new ArgumentNullException(nameof(entity));
Repository.Add(entity);
}
/// <summary>
/// Fügt eine Liste von Entitäten zum Repository hinzu
/// </summary>
/// <param name="entities">List von Entitäten</param>
public virtual void AddRange(IEnumerable<T> entities)
{
if (entities == null)
throw new ArgumentNullException(nameof(entities));
Repository.AddRange(entities);
}
/// <summary>
/// Entfernt eine Entität aus dem Repository
/// </summary>
/// <param name="entity">Zu entfernende Entität</param>
public virtual void Remove(T entity)
{
if (entity == null)
throw new ArgumentNullException(nameof(entity));
Repository.Remove(entity);
}
/// <summary>
/// Entfernt eine Liste von Entitäten aus dem Repository
/// </summary>
/// <param name="entities">Liste von Entitäten</param>
public virtual void RemoveRange(IEnumerable<T> entities)
{
if (entities == null)
throw new ArgumentNullException(nameof(entities));
Repository.RemoveRange(entities);
}
#endregion
#region Count
/// <summary>
/// Gibt die Anzahl aller Entitäten zurück
/// </summary>
/// <returns>Anzahl</returns>
public virtual int Count()
{
return Repository.Count();
}
/// <summary>
/// Gibt die Anzahl aller Entitäten zurück
/// </summary>
/// <returns>Anzahl</returns>
public virtual async Task<int> CountAsync()
{
return await Repository.CountAsync();
}
#endregion
#region Commit / UnitOfWork
/// <summary>
/// Schließt Daten-Änderungen ab und speichert die selben
/// </summary>
/// <param name="userName">Benutzername</param>
/// <param name="noAudit">Gibt an, dass kein Audit-Eintrag erstellt werden soll, auch wenn Audit aktiviert ist</param>
[ExcludeFromCodeCoverage]
public int Commit(string userName, bool noAudit = false)
{
return _unitOfWork.Commit(userName, noAudit);
}
/// <summary>
/// Schließt Daten-Änderungen ab und speichert die selben
/// </summary>
/// <param name="userName">Benutzername</param>
/// <param name="noAudit">Gibt an, dass kein Audit-Eintrag erstellt werden soll, auch wenn Audit aktiviert ist</param>
[ExcludeFromCodeCoverage]
public async Task<int> CommitAsync(string userName, bool noAudit = false)
{
return await _unitOfWork.CommitAsync(userName, noAudit);
}
#endregion
#region IDisposable
/// <summary>
/// Dispose
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
_unitOfWork.Dispose();
}
}
this._disposed = true;
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}

View File

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Shop;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Versandkosten ermöglicht
/// </summary>
public class ShipmentCostService : ServiceBase<ShipmentCost>, IShipmentCostService
{
private readonly IShopSettingsService _shopSettingsService;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
/// <param name="shopSettingsService">Instanz eines IShopSettingsService</param>
public ShipmentCostService(IUnitOfWork unitOfWork, IShopSettingsService shopSettingsService) : base(unitOfWork)
{
_shopSettingsService = shopSettingsService;
}
/// <summary>
/// Gibt eine gefilterte Liste von Versandkosten zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Versandkosten</returns>
public IQueryable<ShipmentCost> Filter(string filter)
{
return Repository.Query(c => c.TargetCountryIso.Contains(filter));
}
/// <summary>
/// Erstellen von Versandkosten
/// </summary>
/// <returns>Versandkosten</returns>
public ShipmentCost Create()
{
var item = new ShipmentCost
{
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Versandkosten zurück. Sucht nur im Land
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Versandkosten</returns>
public async Task<List<ShipmentCost>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.TargetCountryIso.Contains(filter)).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Berechnen der Versandkosten
/// </summary>
/// <param name="targetCountry">Zielland</param>
/// <param name="weight">Gewicht</param>
/// <param name="subtotal">Zwischensumme netto</param>
/// <param name="additionalShipmentCosts">Summe der zusätzlichen Versandkosten je Produkt aber in Summe</param>
/// <returns>Versandkosten netto</returns>
public async Task<decimal> CalculateShipmentAsync(string targetCountry, decimal weight, decimal subtotal, decimal additionalShipmentCosts)
{
targetCountry = targetCountry.ToUpper();
var result = 0M;
var shopSettings = await _shopSettingsService.GetAsync();
var rulesQuery = Repository.Query(c => (c.TargetCountryIso == "--" || c.TargetCountryIso == targetCountry));
if (shopSettings.ShipmentCalculationType == ShipmentCalculationType.Weight)
{
rulesQuery = rulesQuery.Where(c => c.WeightFrom <= weight && c.WeightTo >= weight);
}
else
{
rulesQuery = rulesQuery.Where(c => c.SubtotalFrom <= subtotal && c.SubtotalTo >= subtotal);
}
var rules = await rulesQuery.ToListAsync();
if (rules.Any())
{
//Wenn es mehrere Regeln gibt, dann jene für das Zielland wählen
var ruleToAppy = rules.FirstOrDefault(c => c.TargetCountryIso == targetCountry) ?? rules.FirstOrDefault();
if (ruleToAppy != null)
{
if (shopSettings.ShipmentCalculationType == ShipmentCalculationType.Weight)
{
result = ruleToAppy.FixedPrice;
}
else
{
result = (subtotal / 100) * ruleToAppy.SubtotalPercent;
}
}
}
result += additionalShipmentCosts;
return result;
}
}
}

View File

@ -0,0 +1,455 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Common;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using Microsoft.Extensions.Options;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Preise eines Produktes berechnet
/// </summary>
public class ShopCalculationService : IShopCalculationService
{
private readonly IProductService _productService;
private readonly ICustomerService _customerService;
private readonly IOptions<ShopOptions> _shopOptions;
private readonly IOptions<VatValidationSettings> _vatValidationSettings;
private readonly ITaxRateService _taxRateService;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="productService">Instanz eines IProductService</param>
/// <param name="customerService">Instanz eines ICustomerService</param>
/// <param name="shopOptions">Instanz von ShopOptions</param>
/// <param name="vatValidationSettings">Instanz von VatValidationSettings</param>
/// <param name="taxRateService">Instanz eines ITaxRateService</param>
public ShopCalculationService(IProductService productService, ICustomerService customerService, IOptions<ShopOptions> shopOptions, IOptions<VatValidationSettings> vatValidationSettings, ITaxRateService taxRateService)
{
_productService = productService;
_customerService = customerService;
_shopOptions = shopOptions;
_vatValidationSettings = vatValidationSettings;
_taxRateService = taxRateService;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> CalculatePriceAsync(int productId, long customerId, int quantity)
{
var customer = await _customerService.GetAsync(customerId);
var result = await CalculatePriceAsync(productId, customerId, customer.Address.CountryCode, quantity);
return result;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> CalculatePriceAsync(int productId, long customerId, string billingCountry, int quantity)
{
var result = new PriceCalculation
{
TaxRate = 0,
TaxValue = 0
};
var product = await _productService.GetAsync(productId);
var customer = await _customerService.GetAsync(customerId);
var productCountry = await _productService.GetCountryAsync(productId, billingCountry);
result.Price = product.Price;
result.Price2 = product.Price2;
result.OldPrice = product.OldPrice;
result.OldPrice2 = product.OldPrice2;
if (productCountry != null)
{
result.Price = productCountry.Price;
result.Price2 = productCountry.Price2;
result.OldPrice = productCountry.OldPrice;
result.OldPrice2 = productCountry.OldPrice2;
}
decimal markup = 0m;
switch (product.ProductType)
{
case ProductType.Physical:
break;
case ProductType.Download:
break;
case ProductType.Listing:
markup = customer.MarkupListing;
break;
case ProductType.Advertisement:
markup = customer.MarkupAdvertisement;
break;
case ProductType.Pin:
markup = customer.MarkupPin;
break;
case ProductType.Banner:
markup = customer.MarkupBanner;
break;
default:
throw new ArgumentOutOfRangeException();
}
if (markup != 0)
{
result.Price += ((result.Price * markup) / 100);
result.OldPrice += ((result.OldPrice * markup) / 100);
result.Price2 += ((result.Price2 * markup) / 100);
result.OldPrice2 += ((result.OldPrice2 * markup) / 100);
}
result.Quantity = quantity;
result.Total = (result.Price + result.Price2) * quantity;
result.TotalOld = (result.OldPrice + result.OldPrice2) * quantity;
result.PriceGross = result.Price;
result.OldPriceGross = result.OldPrice;
result.Price2Gross = result.Price2;
result.OldPrice2Gross = result.OldPrice2;
result.TotalGross = result.Total;
result.TotalOldGross = result.TotalOld;
if (!product.IsTaxExempt)
{
//EU-Ausnahme. Gilt wenn Land in der EU und ungleich Quellland und Kunde ist Firma
var isEuCountry = _vatValidationSettings.Value.Countries.Contains(billingCountry);
var isNoTaxCountry = _vatValidationSettings.Value.NoTaxCountries.Contains(billingCountry);
var isSameAsSource = _shopOptions.Value.SourceCountry == billingCountry;
if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource)
{
var taxRate = await _taxRateService.GetAsync(product.TaxRateId);
result.PriceGross = result.Price + ((result.Price * taxRate.Value) / 100);
result.OldPriceGross = result.OldPrice + ((result.OldPrice * taxRate.Value) / 100);
result.Price2Gross = result.Price2 + ((result.Price2 * taxRate.Value) / 100);
result.OldPrice2Gross = result.OldPrice2 + ((result.OldPrice2 * taxRate.Value) / 100);
result.PriceGross = Math.Round(result.PriceGross, 2, MidpointRounding.AwayFromZero);
result.OldPriceGross = Math.Round(result.OldPriceGross, 2, MidpointRounding.AwayFromZero);
result.Price2Gross = Math.Round(result.Price2Gross, 2, MidpointRounding.AwayFromZero);
result.OldPrice2Gross = Math.Round(result.OldPrice2Gross, 2, MidpointRounding.AwayFromZero);
result.TotalGross = (result.PriceGross + result.Price2Gross) * quantity;
result.TotalOldGross = (result.OldPriceGross + result.OldPrice2Gross) * quantity;
result.TaxRate = taxRate.Value;
}
}
result.TaxValue = result.TotalGross - result.Total;
return result;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <param name="budget">Budget für Banner</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> CalculatePriceBannerAsync(int productId, long customerId, string billingCountry, int quantity, decimal budget)
{
var result = new PriceCalculation
{
TaxRate = 0,
TaxValue = 0
};
var product = await _productService.GetAsync(productId);
var customer = await _customerService.GetAsync(customerId);
var productCountry = await _productService.GetCountryAsync(productId, billingCountry);
result.Price = budget;
result.Price2 = 0m;
result.OldPrice = 0m;
result.OldPrice2 = 0m;
result.Quantity = quantity;
result.Total = (result.Price + result.Price2) * quantity;
result.TotalOld = (result.OldPrice + result.OldPrice2) * quantity;
result.PriceGross = result.Price;
result.OldPriceGross = result.OldPrice;
result.Price2Gross = result.Price2;
result.OldPrice2Gross = result.OldPrice2;
result.TotalGross = result.Total;
result.TotalOldGross = result.TotalOld;
if (!product.IsTaxExempt)
{
//EU-Ausnahme. Gilt wenn Land in der EU und ungleich Quellland und Kunde ist Firma
var isEuCountry = _vatValidationSettings.Value.Countries.Contains(billingCountry);
var isNoTaxCountry = _vatValidationSettings.Value.NoTaxCountries.Contains(billingCountry);
var isSameAsSource = _shopOptions.Value.SourceCountry == billingCountry;
if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource)
{
var taxRate = await _taxRateService.GetAsync(product.TaxRateId);
var grossFactor = 1 + (taxRate.Value / 100);
result.Price = decimal.Round(result.PriceGross / grossFactor, 4, MidpointRounding.AwayFromZero);
result.Total = (result.Price + result.Price2) * quantity;
result.TaxRate = taxRate.Value;
}
}
result.TaxValue = result.TotalGross - result.Total;
return result;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="priceType">Preistype</param>
/// <param name="priceAliquotType">Typ der aliquoten Berechnung</param>
/// <param name="startDate">Startdatum</param>
/// <param name="endDate">Enddatum</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> CalculatePriceAsync(int productId, long customerId, PriceType priceType, PriceAliquotType priceAliquotType, DateTimeOffset startDate, DateTimeOffset endDate, int quantity)
{
var customer = await _customerService.GetAsync(customerId);
var result = await CalculatePriceAsync(productId, customerId, priceType, priceAliquotType, startDate, endDate, customer.Address.CountryCode, quantity);
return result;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="priceType">Preistype</param>
/// <param name="priceAliquotType">Typ der aliquoten Berechnung</param>
/// <param name="startDate">Startdatum</param>
/// <param name="endDate">Enddatum</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> CalculatePriceAsync(int productId, long customerId, PriceType priceType, PriceAliquotType priceAliquotType, DateTimeOffset startDate, DateTimeOffset endDate, string billingCountry, int quantity)
{
var result = new PriceCalculation
{
TaxRate = 0,
TaxValue = 0
};
var product = await _productService.GetAsync(productId);
var customer = await _customerService.GetAsync(customerId);
var productCountry = await _productService.GetCountryAsync(productId, billingCountry);
result.Price = product.Price;
result.Price2 = product.Price2;
result.OldPrice = product.OldPrice;
result.OldPrice2 = product.OldPrice2;
if (productCountry != null)
{
result.Price = productCountry.Price;
result.Price2 = productCountry.Price2;
result.OldPrice = productCountry.OldPrice;
result.OldPrice2 = productCountry.OldPrice2;
}
decimal markup = 0m;
switch (product.ProductType)
{
case ProductType.Physical:
break;
case ProductType.Download:
break;
case ProductType.Listing:
markup = customer.MarkupListing;
break;
case ProductType.Advertisement:
markup = customer.MarkupAdvertisement;
break;
case ProductType.Pin:
markup = customer.MarkupPin;
break;
case ProductType.Banner:
markup = customer.MarkupBanner;
break;
default:
throw new ArgumentOutOfRangeException();
}
if (markup != 0)
{
result.Price += ((result.Price * markup) / 100);
result.OldPrice += ((result.OldPrice * markup) / 100);
result.Price2 += ((result.Price2 * markup) / 100);
result.OldPrice2 += ((result.OldPrice2 * markup) / 100);
}
if (priceAliquotType == PriceAliquotType.Month && priceType == PriceType.Year)
{
result.Price /= 12;
result.Price2 /= 12;
result.OldPrice /= 12;
result.OldPrice2 /= 12;
var months = (((endDate.Year - startDate.Year) * 12) + endDate.Month - startDate.Month) + 1;
result.Price *= months;
result.Price2 *= months;
result.OldPrice *= months;
result.OldPrice2 *= months;
}
if (priceAliquotType == PriceAliquotType.Day && priceType == PriceType.Month)
{
var totalDays = DateTime.DaysInMonth(endDate.Year, endDate.Month);
result.Price /= totalDays;
result.Price2 /= totalDays;
result.OldPrice /= totalDays;
result.OldPrice2 /= totalDays;
var days = (int)(endDate - startDate).TotalDays + 1;
result.Price *= days;
result.Price2 *= days;
result.OldPrice *= days;
result.OldPrice2 *= days;
}
if (priceType == PriceType.Day)
{
var days = (int)(endDate - startDate).TotalDays;
result.Price *= days;
result.Price2 *= days;
result.OldPrice *= days;
result.OldPrice2 *= days;
}
result.Quantity = quantity;
result.Total = (result.Price + result.Price2) * quantity;
result.TotalOld = (result.OldPrice + result.OldPrice2) * quantity;
result.PriceGross = result.Price;
result.OldPriceGross = result.OldPrice;
result.Price2Gross = result.Price2;
result.OldPrice2Gross = result.OldPrice2;
result.TotalGross = result.Total;
result.TotalOldGross = result.TotalOld;
if (!product.IsTaxExempt)
{
//EU-Ausnahme. Gilt wenn Land in der EU und ungleich Quellland und Kunde ist Firma
var isEuCountry = _vatValidationSettings.Value.Countries.Contains(billingCountry);
var isNoTaxCountry = _vatValidationSettings.Value.NoTaxCountries.Contains(billingCountry);
var isSameAsSource = _shopOptions.Value.SourceCountry == billingCountry;
if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource)
{
var taxRate = await _taxRateService.GetAsync(product.TaxRateId);
result.PriceGross = result.Price + ((result.Price * taxRate.Value) / 100);
result.OldPriceGross = result.OldPrice + ((result.OldPrice * taxRate.Value) / 100);
result.Price2Gross = result.Price2 + ((result.Price2 * taxRate.Value) / 100);
result.OldPrice2Gross = result.OldPrice2 + ((result.OldPrice2 * taxRate.Value) / 100);
result.PriceGross = Math.Round(result.PriceGross, 2, MidpointRounding.AwayFromZero);
result.OldPriceGross = Math.Round(result.OldPriceGross, 2, MidpointRounding.AwayFromZero);
result.Price2Gross = Math.Round(result.Price2Gross, 2, MidpointRounding.AwayFromZero);
result.OldPrice2Gross = Math.Round(result.OldPrice2Gross, 2, MidpointRounding.AwayFromZero);
result.TotalGross = (result.PriceGross + result.Price2Gross) * quantity;
result.TotalOldGross = (result.OldPriceGross + result.OldPrice2Gross) * quantity;
result.TaxRate = taxRate.Value;
}
}
result.TaxValue = result.TotalGross - result.Total;
return result;
}
/// <summary>
/// Aufschlagen von X Prozent auf eine Preis-Kalkulation
/// </summary>
/// <param name="priceCalculation">Preis-Kalkulation</param>
/// <param name="markup">Aufschlag in Prozent</param>
/// <param name="productId">Id des Produktes</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> AddMarkupAsync(PriceCalculation priceCalculation, decimal markup, int productId, string billingCountry)
{
var product = await _productService.GetAsync(productId);
var productCountry = await _productService.GetCountryAsync(productId, billingCountry);
priceCalculation.Price += ((priceCalculation.Price * markup) / 100);
priceCalculation.OldPrice += ((priceCalculation.OldPrice * markup) / 100);
priceCalculation.Price2 += ((priceCalculation.Price2 * markup) / 100);
priceCalculation.OldPrice2 += ((priceCalculation.OldPrice2 * markup) / 100);
priceCalculation.Total = (priceCalculation.Price + priceCalculation.Price2) * priceCalculation.Quantity;
priceCalculation.TotalOld = (priceCalculation.OldPrice + priceCalculation.OldPrice2) * priceCalculation.Quantity;
priceCalculation.PriceGross = priceCalculation.Price;
priceCalculation.OldPriceGross = priceCalculation.OldPrice;
priceCalculation.Price2Gross = priceCalculation.Price2;
priceCalculation.OldPrice2Gross = priceCalculation.OldPrice2;
priceCalculation.TotalGross = priceCalculation.Total;
priceCalculation.TotalOldGross = priceCalculation.TotalOld;
if (!product.IsTaxExempt)
{
//EU-Ausnahme. Gilt wenn Land in der EU und ungleich Quellland und Kunde ist Firma
var isEuCountry = _vatValidationSettings.Value.Countries.Contains(billingCountry);
var isNoTaxCountry = _vatValidationSettings.Value.NoTaxCountries.Contains(billingCountry);
var isSameAsSource = _shopOptions.Value.SourceCountry == billingCountry;
if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource)
{
var taxRate = await _taxRateService.GetAsync(product.TaxRateId);
priceCalculation.PriceGross = priceCalculation.Price + ((priceCalculation.Price * taxRate.Value) / 100);
priceCalculation.OldPriceGross = priceCalculation.OldPrice + ((priceCalculation.OldPrice * taxRate.Value) / 100);
priceCalculation.Price2Gross = priceCalculation.Price2 + ((priceCalculation.Price2 * taxRate.Value) / 100);
priceCalculation.OldPrice2Gross = priceCalculation.OldPrice2 + ((priceCalculation.OldPrice2 * taxRate.Value) / 100);
priceCalculation.PriceGross = Math.Round(priceCalculation.PriceGross, 2, MidpointRounding.AwayFromZero);
priceCalculation.OldPriceGross = Math.Round(priceCalculation.OldPriceGross, 2, MidpointRounding.AwayFromZero);
priceCalculation.Price2Gross = Math.Round(priceCalculation.Price2Gross, 2, MidpointRounding.AwayFromZero);
priceCalculation.OldPrice2Gross = Math.Round(priceCalculation.OldPrice2Gross, 2, MidpointRounding.AwayFromZero);
priceCalculation.TotalGross = (priceCalculation.PriceGross + priceCalculation.Price2Gross) * priceCalculation.Quantity;
priceCalculation.TotalOldGross = (priceCalculation.OldPriceGross + priceCalculation.OldPrice2Gross) * priceCalculation.Quantity;
priceCalculation.TaxRate = taxRate.Value;
}
}
priceCalculation.TaxValue = priceCalculation.TotalGross - priceCalculation.Total;
return priceCalculation;
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Shop;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung Shop-Einstellungen realisiert
/// </summary>
public class ShopSettingsService : ServiceBase<ShopSettings>, IShopSettingsService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public ShopSettingsService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Gibt die aktuellen Shop-Einstellungen zurück
/// </summary>
/// <returns>ShopSettings</returns>
public async Task<ShopSettings> GetAsync()
{
return await Repository.FirstOrDefaultAsync( c => c.Id > 0).ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Walks;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der Statistiken zur Verfügung stellt
/// </summary>
public class StatistcService : IStatisticService
{
private readonly IUnitOfWork _unitOfWork;
private readonly IRepository<Walk> _walksRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz einer IUnitOfWork</param>
public StatistcService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_walksRepository = _unitOfWork.GetRepository<Walk>();
}
/// <summary>
/// Gibt zurück wie viele gültige Walks insgesamt vorhanden sind.
/// Das sind alle Walks die nicht folgenden Status haben:
/// - Requested
/// - Declined
/// - Cancelled
/// </summary>
/// <param name="minPaymentStatus">Minimaler Zahlungsstatus, null wenn alle</param>
/// <param name="maxPaymentStatus">Maximaler Zahlungsstatus, null wenn alle</param>
/// <param name="countryIso">Land oder leer wenn alle</param>
/// <returns>Anzahl Walks</returns>
public async Task<int> CountWalksTotalAsync(PaymentStatus? minPaymentStatus, PaymentStatus? maxPaymentStatus, string countryIso)
{
var baseQuery = _walksRepository.Query(c => c.Status != WalkStatus.Requested && c.Status != WalkStatus.Declined && c.Status != WalkStatus.Cancelled && !c.Deleted);
if (!string.IsNullOrEmpty(countryIso))
{
baseQuery = baseQuery.Where(c => c.PickupAddress.CountryCode == countryIso);
}
if (minPaymentStatus.HasValue)
{
baseQuery = baseQuery.Where(c => c.PaymentStatus >= minPaymentStatus.Value);
}
if (maxPaymentStatus.HasValue)
{
baseQuery = baseQuery.Where(c => c.PaymentStatus <= maxPaymentStatus.Value);
}
var count = await baseQuery.CountAsync();
return count;
}
/// <summary>
/// Gibt zurück wie viele gültige Walks in einem Zeitraum vorhanden sind.
/// Das sind alle Walks die nicht folgenden Status haben:
/// - Requested
/// - Declined
/// - Cancelled
/// </summary>
/// <param name="minPaymentStatus">Minimaler Zahlungsstatus, null wenn alle</param>
/// <param name="maxPaymentStatus">Maximaler Zahlungsstatus, null wenn alle</param>
/// <param name="start">Startdatum</param>
/// <param name="end">Enddatum</param>
/// <param name="countryIso">Land oder leer wenn alle</param>
/// <returns>Anzahl Walks</returns>
public async Task<int> CountWalksAsync(PaymentStatus? minPaymentStatus, PaymentStatus? maxPaymentStatus, DateTimeOffset start, DateTimeOffset end, string countryIso)
{
var baseQuery = _walksRepository.Query(c => c.Status != WalkStatus.Requested && c.Status != WalkStatus.Declined && c.Status != WalkStatus.Cancelled && !c.Deleted);
if (!string.IsNullOrEmpty(countryIso))
{
baseQuery = baseQuery.Where(c => c.PickupAddress.CountryCode == countryIso);
}
if (minPaymentStatus.HasValue)
{
baseQuery = baseQuery.Where(c => c.PaymentStatus >= minPaymentStatus.Value);
}
if (maxPaymentStatus.HasValue)
{
baseQuery = baseQuery.Where(c => c.PaymentStatus <= maxPaymentStatus.Value);
}
//Start- oder Enddatum des Walks liegen im Bereich zwischen start und end
baseQuery = baseQuery.Where(c => (c.Start >= start && c.Start <= end) || (c.End >= start && c.End <= end));
var count = await baseQuery.CountAsync();
return count;
}
/// <summary>
/// Gibt die Anzahl von Walks in einem bestimmten Status in einem Zeitraum zurück
/// </summary>
/// <param name="status">Status der gesucht wird, null wenn alle</param>
/// <param name="minPaymentStatus">Minimaler Zahlungsstatus, null wenn alle</param>
/// <param name="maxPaymentStatus">Maximaler Zahlungsstatus, null wenn alle</param>
/// <param name="start">Startdatum</param>
/// <param name="end">Enddatum</param>
/// <param name="countryIso">Land oder leer wenn alle</param>
/// <returns>Anzahl Walks</returns>
public async Task<int> CountWalkByStatusAsync(WalkStatus? status, PaymentStatus? minPaymentStatus, PaymentStatus? maxPaymentStatus, DateTimeOffset start, DateTimeOffset end, string countryIso)
{
var baseQuery = _walksRepository.Query(c => !c.Deleted);
if (status.HasValue)
{
baseQuery = baseQuery.Where(c => c.Status == status.Value);
}
if (!string.IsNullOrEmpty(countryIso))
{
baseQuery = baseQuery.Where(c => c.PickupAddress.CountryCode == countryIso);
}
if (minPaymentStatus.HasValue)
{
baseQuery = baseQuery.Where(c => c.PaymentStatus >= minPaymentStatus.Value);
}
if (maxPaymentStatus.HasValue)
{
baseQuery = baseQuery.Where(c => c.PaymentStatus <= maxPaymentStatus.Value);
}
//Start- oder Enddatum des Walks liegen im Bereich zwischen start und end
baseQuery = baseQuery.Where(c => (c.Start >= start && c.Start <= end) || (c.End >= start && c.End <= end));
var count = await baseQuery.CountAsync();
return count;
}
}
}

View File

@ -0,0 +1,249 @@
using gehGassi.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.Subscriptions;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service für Abonnements
/// </summary>
public class SubscriptionService : ServiceBase<Subscription>, ISubscriptionService
{
private readonly IRepository<SubscriptionWithNames> _namesRepository;
private readonly IRepository<AppUserSubscription> _appUserSubscriptionRepository;
private readonly IRepository<AppUserSubscriptionWithNames> _appUserSubscriptionNamesRepository;
/// <summary>
/// Erstellt eine neue Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public SubscriptionService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<SubscriptionWithNames>();
_appUserSubscriptionRepository = unitOfWork.GetRepository<AppUserSubscription>();
_appUserSubscriptionNamesRepository = unitOfWork.GetRepository<AppUserSubscriptionWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Abonnements zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Abonnements</returns>
public IQueryable<Subscription> Filter(string filter)
{
return Repository.Query(c => c.Name.Contains(filter) || c.NameLocalized.Contains(filter));
}
/// <summary>
/// Erstellen eines Abonnements
/// </summary>
/// <returns>Abonnement</returns>
public Subscription Create()
{
var item = new Subscription()
{
Id = Guid.NewGuid().ToString("N"),
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Abonnements zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Abonnements</returns>
public async Task<List<Subscription>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.NameLocalized.Contains(filter) && c.Deleted == false)).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Abonnements mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Abonnements</returns>
public IQueryable<SubscriptionWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_SubscriptionsWithNames({0},{1})", c => c.Name.Contains(filter) || c.Description.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine Liste von Abonnements zurück, welche dem gesuchten AppMode entsprechend und aktiv sind
/// </summary>
/// <param name="appMode">Gesuchter App mode</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste Abonnements</returns>
public async Task<List<SubscriptionWithNames>> GetAllAsync(AppMode appMode, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_SubscriptionsWithNames({0},{1})", c => c.AppMode == appMode && c.Enabled && c.Deleted == false, language, fallback);
var list = await baseQuery.ToListAsync();
return list;
}
/// <summary>
/// Gibt eine gefilterte Liste von Abonnements mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste betroffener Abonnements</returns>
public IQueryable<AppUserSubscriptionWithNames> FilterAppUserSubscritionsWithNames(string filter, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _appUserSubscriptionNamesRepository.QueryStoredProcedure("SELECT * FROM tv_AppUserSubscriptionsWithNames({0},{1})", c => c.AppUserName.Contains(filter), language, fallback);
return baseQuery;
}
/// <summary>
/// Gibt eine Liste von Abonnements zurück, welche ein App-User abonniert hat
/// </summary>
/// <param name="appUserId">App user id</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste betroffener Abonnement-Buchungen</returns>
public async Task<List<AppUserSubscriptionWithNames>> GetAllAppUserSubscriptionsAsync(string appUserId, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _appUserSubscriptionNamesRepository.QueryStoredProcedure("SELECT * FROM tv_AppUserSubscriptionsWithNames({0},{1})", c => c.AppUserId == appUserId, language, fallback);
return await baseQuery.ToListAsync();
}
/// <summary>
/// Gibt eine Liste von Abonnements zurück, welche ein App-User abonniert hat die gültig und aktiv sind
/// </summary>
/// <param name="appUserId">App user id</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <returns>Liste betroffener Abonnement-Buchungen</returns>
public async Task<List<AppUserSubscriptionWithNames>> GetAllAppUserSubscriptionsValidAsync(string appUserId, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _appUserSubscriptionNamesRepository.QueryStoredProcedure("SELECT * FROM tv_AppUserSubscriptionsWithNames({0},{1})", c => c.AppUserId == appUserId && c.IsValid, language, fallback);
return await baseQuery.ToListAsync();
}
/// <summary>
/// Gibt eine Abonnementbuchung mit Namen zurück
/// </summary>
/// <param name="id">Id der Buchung</param>
/// <param name="language"></param>
/// <param name="fallback"></param>
/// <returns>AppUserSubscriptionWithNames</returns>
public async Task<AppUserSubscriptionWithNames> GetAppUserSubscriptionAsync(long id, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _appUserSubscriptionNamesRepository.QueryStoredProcedure("SELECT * FROM tv_AppUserSubscriptionsWithNames({0},{1})", c => c.Id == id, language, fallback);
return await baseQuery.FirstOrDefaultAsync();
}
/// <summary>
/// Erstellen eines Abonnements für einen App-User
/// </summary>
/// <returns>Abonnement</returns>
public AppUserSubscription CreateAppUserSubscription()
{
var item = new AppUserSubscription()
{
};
return item;
}
/// <summary>
/// Gibt ein App-User-Abonnement zurück
/// </summary>
/// <param name="id">AppUserSubcription id</param>
/// <returns>AppUserSubscription</returns>
public async Task<AppUserSubscription> GetAppUserSubscriptionAsync(long id)
{
return await _appUserSubscriptionRepository.GetAsync(id);
}
/// <summary>
/// Gibt ein App-User-Abonnement zurück
/// </summary>
/// <param name="appUserId">App user id</param>
/// <param name="subscriptionId">Abo-Id</param>
/// <returns>AppUserSubscription oder null, wenn nicht gefunden</returns>
public async Task<AppUserSubscription> GetAppUserSubscriptionAsync(string appUserId, string subscriptionId)
{
return await _appUserSubscriptionRepository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.SubscriptionId == subscriptionId);
}
/// <summary>
/// Fügt eine Entität zum Repository hinzu
/// </summary>
/// <param name="entity">Entität</param>
public void AddAppUserSubscription(AppUserSubscription entity)
{
_appUserSubscriptionRepository.Add(entity);
}
/// <summary>
/// Entfernt eine Entität aus dem Repository
/// </summary>
/// <param name="entity">Zu entfernende Entität</param>
public void RemoveAppUserSubscription(AppUserSubscription entity)
{
_appUserSubscriptionRepository.Remove(entity);
}
/// <summary>
/// Prüft ob ein App-User ein Abonnement hat für einen AppMode das noch gültig ist
/// </summary>
/// <param name="appUserId">App user id</param>
/// <param name="appMode">AppMode</param>
/// <returns>true wenn ja, false else</returns>
public async Task<bool> AppUserHasActiveBookingAsync(string appUserId, AppMode appMode)
{
var baseQuery = _appUserSubscriptionNamesRepository.QueryStoredProcedure("SELECT * FROM tv_AppUserSubscriptionsWithNames({0},{1})", c => c.AppUserId == appUserId && c.SubscriptionAppMode == appMode && c.IsValid, "DE", "DE");
var items = await baseQuery.CountAsync();
return items != 0;
}
/// <summary>
/// Setzt Abo-Buchungen die abgelaufen sind auf ungültig
/// </summary>
/// <returns></returns>
public async Task<int> InvalidateExipredAppUserSubscriptionsAsync(DateTimeOffset date)
{
var items = await _appUserSubscriptionRepository.FindAsync(c => c.IsValid && c.ExpirationDateWithGrace <= date);
foreach (var item in items)
{
item.IsValid = false;
}
return items.Count();
}
}
}

View File

@ -0,0 +1,279 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Favourites;
using gehGassi.Domain.Messages;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Dto.Messages;
using gehGassi.Domain.Dogs;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Systemnachrichten ermöglicht
/// </summary>
public class SystemMessageService : ServiceBase<SystemMessage>, ISystemMessageService
{
private readonly IRepository<SystemMessageToSend> _toSendRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public SystemMessageService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_toSendRepository = unitOfWork.GetRepository<SystemMessageToSend>();
}
/// <summary>
/// Erstellen einer Systemnachrichten
/// </summary>
/// <returns>Systemnachricht</returns>
public SystemMessage Create()
{
var item = new SystemMessage()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow,
Read = false,
ReadDate = null
};
return item;
}
/// <summary>
/// Erstellen einer Systemnachrichten
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des App-Users</param>
/// <param name="key">Id des verknüpften Objekts</param>
/// <param name="table">Typ des verknüpften Objekts</param>
/// <param name="type">Typ der Systemnachricht</param>
/// <returns>Systemnachricht</returns>
public SystemMessage Create(string appUserId, AppUserType appUserType, string key, string table, SystemMessageType type)
{
var item = new SystemMessage()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow,
AppUserId = appUserId,
AppUserType = appUserType,
Key = key,
Table = table,
Type = type,
Read = false,
ReadDate = null
};
return item;
}
/// <summary>
/// Gibt eine Liste der Systemnachrichten eines Benutzers zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der Systemnachricht</returns>
public async Task<List<SystemMessage>> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.AppUserId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Berstätigen des Erhaltes von SystemNachrichten für einen Benutzer. Alle die älter als das UpdateDatum sind werden gelöscht
/// </summary>
/// <param name="senderId">Id des Users - wird als Receiver gesucht</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <param name="userName">Benutzer der die Änderung vornimmt</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> ConfirmAsync(string senderId, DateTimeOffset lastUpdate, string userName)
{
var query = Repository.Query(c => c.AppUserId == senderId && c.UpdatedAt <= lastUpdate);
var list = await query.ToListAsync().ConfigureAwait(false);
if (list.Any())
{
Repository.RemoveRange(list);
await CommitAsync(userName).ConfigureAwait(false);
}
return true;
}
/// <summary>
/// Entfernen von Systemnachrichten für einen Benutzer wenn diese in der App schon gesehen wurden
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des App-Users</param>
/// <param name="key">Id des verknüpften Objekts</param>
/// <param name="table">Typ des verknüpften Objekts</param>
/// <param name="created">Datum - das created Datum der Systemnachricht muss kleiner gleich sein</param>
/// <param name="userName">Benutzer der die Änderung vornimmt</param>
/// <returns>Systemnachricht</returns>
public async Task<bool> RemoveAsync(string appUserId, AppUserType appUserType, string key, string table, DateTimeOffset? created, string userName)
{
var query = Repository.Query(c => c.AppUserId == appUserId && c.AppUserType == appUserType && c.Key == key && c.Table == table && c.Created <= created.Value);
var list = await query.ToListAsync().ConfigureAwait(false);
if (list.Any())
{
Repository.RemoveRange(list);
await CommitAsync(userName).ConfigureAwait(false);
}
return true;
}
/// <summary>
/// Hinzufügen eines Systemnachricht
/// </summary>
/// <param name="appUserId">Id des Ziels</param>
/// <param name="appUserType">Typ des App-Users</param>
/// <param name="key">Id des Objekts</param>
/// <param name="table">Objekttyp</param>
/// <param name="type">Nachrichten-Typ</param>
/// <param name="expires">Ablaufdatum</param>
/// <returns>Task</returns>
public void Add(string appUserId, AppUserType appUserType, string key, string table, SystemMessageType type, DateTimeOffset expires)
{
var systemMessage = Create(appUserId, appUserType, key, table, type);
systemMessage.Expires = expires;
Repository.Add(systemMessage);
}
/// <summary>
/// Hinzufügen einer Systemnachricht als Text. Setzt den Typen automatisch
/// </summary>
/// <param name="appUserId">Id des Ziels</param>
/// <param name="appUserType">Typ des App-Users</param>
/// <param name="key">Id des Objekts</param>
/// <param name="expires">Ablaufdatum</param>
/// <param name="message">Nachricht</param>
/// <returns>Task</returns>
public void Add(string appUserId, AppUserType appUserType, string key, DateTimeOffset expires, string message)
{
var systemMessage = Create();
systemMessage.AppUserId = appUserId;
systemMessage.AppUserType = appUserType;
systemMessage.Key = key;
systemMessage.Table = SystemMessageTables.Message;
systemMessage.Expires = expires;
systemMessage.Type = SystemMessageType.Text;
systemMessage.Message = message;
Repository.Add(systemMessage);
}
/// <summary>
/// Löschen abgelaufener Nachrichten
/// </summary>
/// <param name="date">Grenzdatum für Expired</param>
/// <returns>Anzahl gelöschte Nachrichten</returns>
public async Task<int> RemoveExpiredAsync(DateTimeOffset date)
{
var expiredMessages = await Repository.FindAsync(c => c.Expires < date).ConfigureAwait(false);
foreach (var expiredMessage in expiredMessages)
{
Repository.Remove(expiredMessage);
}
return expiredMessages.Count();
}
/// <summary>
/// Alle Systemnachrichten eines AppUsers löschen
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>Anzahl gelöscht</returns>
public async Task<int> DeleteAllForAppUserAsync(string appUserId)
{
var messages = await Repository.FindAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
foreach (var message in messages)
{
Repository.Remove(message);
}
return messages.Count();
}
/// <summary>
/// Erstellen einer Systemnachrichten die gesendet werden sollen
/// </summary>
/// <returns>Systemnachricht die gesendet werden soll</returns>
public SystemMessageToSend CreateToSend()
{
var item = new SystemMessageToSend()
{
Created = DateTimeOffset.UtcNow,
AppUserCount = 0,
SentCount = 0,
HasBeenSent = false,
SentDate = null,
SentBy = string.Empty
};
return item;
}
#region SystemMessagesToSend
/// <summary>
/// Gibt eine gefilterte Liste von Systemnachrichten die gesendet werden sollen
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Systemnachrichten die gesendet werden sollen</returns>
public IQueryable<SystemMessageToSend> FilterSystemMessagesToSend(string filter)
{
return _toSendRepository.Query(c => c.Title.Contains(filter));
}
/// <summary>
/// Hinzufügen einer Systemnachricht die gesendet werden soll
/// </summary>
/// <param name="item">SystemMessageToSend</param>
public void AddToSend(SystemMessageToSend item)
{
_toSendRepository.Add(item);
}
/// <summary>
/// Löschen einer Systemnachricht die gesendet werden soll
/// </summary>
/// <param name="item">SystemMessageToSend</param>
public void RemoveToSend(SystemMessageToSend item)
{
_toSendRepository.Remove(item);
}
/// <summary>
/// Gibt eine Systemnachricht die gesendet werden soll zurück
/// </summary>
/// <param name="id">Id der Systemnachricht</param>
/// <returns>SystemMessageToSend oder null, wenn nicht gefunden</returns>
public async Task<SystemMessageToSend> GetToSendAsync(long id)
{
return await _toSendRepository.GetAsync(id).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste von Systemnachrichten die gesendet werden sollen zurück.
/// Bereits gesendete oder Nachrichten die kein Versanddatum haben werden nicht zurückgegeben
/// </summary>
/// <param name="date">Datum das größer als das Versanddatum sein muss</param>
/// <returns>Liste Nachrichten zu senden</returns>
public async Task<List<SystemMessageToSend>> GetToSendAsync(DateTimeOffset date)
{
var list = await _toSendRepository.FindAsync(c => c.SentDate != null && c.HasBeenSent == false && c.ToSendDate <= date).ConfigureAwait(false);
return list.ToList();
}
#endregion
}
}

View File

@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Common;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Shop;
using Microsoft.Extensions.Options;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Steuern realisiert
/// </summary>
public class TaxRateService : ServiceBase<TaxRate>, ITaxRateService
{
private readonly IOptions<VatValidationSettings> _vatValidationOptions;
private readonly IRepository<TaxRateWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
/// <param name="vatValidationOptions">Instanz von VatValidationSettings</param>
/// <param name="shopOptions">Instanz von ShopSettings</param>
public TaxRateService(IUnitOfWork unitOfWork, IOptions<VatValidationSettings> vatValidationOptions) : base(unitOfWork)
{
_vatValidationOptions = vatValidationOptions;
_namesRepository = unitOfWork.GetRepository<TaxRateWithNames>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Steuern zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Steuern</returns>
public IQueryable<TaxRate> Filter(string filter)
{
return Repository.Query(c => c.Title.Contains(filter));
}
/// <summary>
/// Erstellen einer Steuer
/// </summary>
/// <returns>Steuer</returns>
public TaxRate Create()
{
var item = new TaxRate
{
Value = 0
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Steuern zurück. Sucht nur im Titel
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Hundeausführern</returns>
public async Task<List<TaxRate>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Title.Contains(filter)).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Steuern mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache falls keine Übersetzung vorhanden</param>
/// <returns>Liste betroffener Steuern</returns>
public IQueryable<TaxRateWithNames> FilterWithNames(string filter, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_TaxRatesWithNames({0},{1})", c => c.Title.Contains(filter) || c.Name.Contains(filter), language, fallback);
return baseQuery;
}
/// <summary>
/// Prüft ob für ein Land schon eine Steuer mit einem Wert existiert
/// </summary>
/// <param name="countryIso">Land ISO2</param>
/// <param name="value">Steuer-Wert</param>
/// <returns>true wenn existiert, false sonst</returns>
public async Task<bool> ExistsAsync(string countryIso, decimal value)
{
var existing = await Repository.FirstOrDefaultAsync(c => c.CountryIso == countryIso && c.Value == value);
return existing != null;
}
/// <summary>
/// Zurücksetzen wird für Versandkosten verwendet
/// </summary>
/// <param name="excludeId">Id die ausgenommen werden soll</param>
/// <returns>Task</returns>
public async Task ResetShipmentAsync(int excludeId)
{
var items = await Repository.FindAsync(c => c.Id != excludeId && c.UseForShipment).ConfigureAwait(false);
foreach (var taxRate in items)
{
taxRate.UseForShipment = false;
}
}
/// <summary>
/// Gibt den Steuersatz für Versandkosten zurück
/// </summary>
/// <returns>Versandkosten-Steuersatz in Prozent</returns>
public async Task<decimal> GetForShipmentAsync()
{
var result = 0m;
var item = await Repository.FirstOrDefaultAsync(c => c.UseForShipment).ConfigureAwait(false);
if(item != null)
result = item.Value;
return result;
}
/// <summary>
/// Berechnen eines Brutto-Wertes für einen Preis
/// </summary>
/// <param name="value">Preis netto</param>
/// <param name="taxRateId">Id der Steuerstufe</param>
/// <param name="sourceCountry">QuellLand des Shops</param>
/// <param name="targetCountry">Zielland</param>
/// <returns>Brutto-Wert</returns>
public async Task<TaxGrossResult> CalculateGrossAsync(decimal value, int taxRateId, string sourceCountry, string targetCountry)
{
var result = new TaxGrossResult()
{
Value = 0,
TaxRate = 0,
TaxValue = 0
};
var taxRate = await Repository.FirstOrDefaultAsync(c => c.Id == taxRateId);
if (taxRate != null)
{
var isEuCountry = _vatValidationOptions.Value.Countries.Contains(targetCountry);
var isNoTaxCountry = _vatValidationOptions.Value.NoTaxCountries.Contains(targetCountry);
var isSameAsSource = sourceCountry == targetCountry;
if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource)
{
result.TaxRate = taxRate.Value;
result.TaxValue = Math.Round(((value * taxRate.Value) / 100),2,MidpointRounding.AwayFromZero);
result.Value = Math.Round(value + ((value * taxRate.Value) / 100),2,MidpointRounding.AwayFromZero);
}
}
return result;
}
/// <summary>
/// Berechnen eines Brutto-Wertes für Versandkosten
/// </summary>
/// <param name="value">Preis netto</param>
/// <param name="sourceCountry">QuellLand des Shops</param>
/// <param name="targetCountry">Zielland</param>
/// <returns>Brutto-Wert</returns>
public async Task<TaxGrossResult> CalculateShipmentGrossAsync(decimal value, string sourceCountry, string targetCountry)
{
var result = new TaxGrossResult()
{
Value = 0,
TaxRate = 0,
TaxValue = 0
};
var taxRate = await Repository.FirstOrDefaultAsync(c => c.UseForShipment);
if (taxRate != null)
{
var isEuCountry = _vatValidationOptions.Value.Countries.Contains(targetCountry);
var isNoTaxCountry = _vatValidationOptions.Value.NoTaxCountries.Contains(targetCountry);
var isSameAsSource = sourceCountry == targetCountry;
if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource)
{
result.TaxRate = taxRate.Value;
result.TaxValue = Math.Round(((value * taxRate.Value) / 100), 2, MidpointRounding.AwayFromZero);
result.Value = Math.Round(value + ((value * taxRate.Value) / 100), 2, MidpointRounding.AwayFromZero);
//var result = value + ((value * taxRate.Value) / 100);
//return Math.Round(result, 2, MidpointRounding.AwayFromZero);
}
}
return result;
}
}
}

View File

@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Payment;
using gehGassi.Domain.Shop;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Transaktionsgebühren ermöglichts
/// </summary>
public class TransactionFeeService : ServiceBase<TransactionFee>, ITransactionFeeService
{
private readonly IRepository<GehGassiFee> _gehGassiRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public TransactionFeeService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_gehGassiRepository = unitOfWork.GetRepository<GehGassiFee>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Transaktionsgebühren zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Transaktionsgebühren</returns>
public IQueryable<TransactionFee> Filter(string filter)
{
return Repository.Query(c => c.PayInType != null && c.Deleted == false);
}
/// <summary>
/// Erstellen einer Tranksaktionsgebühr
/// </summary>
/// <returns>Tranksaktionsgebühr</returns>
public TransactionFee Create()
{
var item = new TransactionFee()
{
Id = Guid.NewGuid().ToString("N")
};
return item;
}
/// <summary>
/// Prüft ob schon eine Einstellung für einen Typ vorhanden ist
/// </summary>
/// <param name="payInType">Zahlungsmethode</param>
/// <returns>true wenn bereits vorhanden, false sonst</returns>
public async Task<bool> ExistsAsync(PayInType payInType)
{
var item = await Repository.FirstOrDefaultAsync(c => c.PayInType == payInType && c.Deleted == false).ConfigureAwait(false);
return item != null;
}
/// <summary>
/// Gibt eine Liste von Transaktionsgebühren zurück, die für die Synchronisation benötigt werden
/// </summary>
/// <param name="lastUpdate">Letztes Update oder null wenn noch keines</param>
/// <returns>Liste von Transaktionsgebühren</returns>
public async Task<List<TransactionFee>> GetForSyncAsync(DateTimeOffset? lastUpdate)
{
if (lastUpdate.HasValue)
{
return (await Repository.FindAsync(c => c.UpdatedAt > lastUpdate.Value).ConfigureAwait(false)).ToList();
}
else
{
return (await Repository.GetAllAsync().ConfigureAwait(false)).ToList();
}
}
/// <summary>
/// Gibt eine gefilterte Liste von GehGassi-Gebühren zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener GehGassi-Gebühren</returns>
public IQueryable<GehGassiFee> FilterGehGassiFees(string filter)
{
return _gehGassiRepository.Query(c => c.Id != "" && c.Deleted == false);
}
/// <summary>
/// Erstellen einer GehGassi-Gebühr
/// </summary>
/// <returns>GehGassi-Gebühr</returns>
public GehGassiFee CreateGehGassiFee()
{
var item = new GehGassiFee()
{
Id = Guid.NewGuid().ToString("N")
};
return item;
}
/// <summary>
/// Gibt eine GehGassi-Gebühr zurück
/// </summary>
/// <param name="id">Id der Gebühr</param>
/// <returns>Gebühr oder null, wenn nicht gefunden</returns>
public async Task<GehGassiFee> GetGehGassiFeeAsync(string id)
{
return await _gehGassiRepository.FirstOrDefaultAsync(c => c.Id == id).ConfigureAwait(false);
}
/// <summary>
/// Prüft ob schon eine Einstellung für einen Startbetrag vorhanden ist
/// </summary>
/// <param name="startAmmount">Startbetrag</param>
/// <param name="ignoreId">ID die ignoriert werden soll</param>
/// <returns>true wenn bereits vorhanden, false sonst</returns>
public async Task<bool> ExistsGehGassiFeeAsync(decimal startAmmount, string ignoreId)
{
var item = await _gehGassiRepository.FirstOrDefaultAsync(c => c.StartAmmount == startAmmount && c.Deleted == false && c.Id != ignoreId).ConfigureAwait(false);
return item != null;
}
/// <summary>
/// Hinzufügen einer GehGassi-Gebühr
/// </summary>
/// <param name="gehGassiFee">GehGassi-Gebühr</param>
public void AddGehGassiFee(GehGassiFee gehGassiFee)
{
_gehGassiRepository.Add(gehGassiFee);
}
/// <summary>
/// Löschen einer GehGassi-Gebühr
/// </summary>
/// <param name="gehGassiFee">GehGassi-Gebühr</param>
public void RemoveGehGassiFee(GehGassiFee gehGassiFee)
{
_gehGassiRepository.Remove(gehGassiFee);
}
/// <summary>
/// Gibt eine GehGassi-Gebühr zurück die für den Startbetrag gilt
/// </summary>
/// <param name="startAmmount">Start-Betrag</param>
/// <returns>Gebühr oder null, wenn nicht gefunden</returns>
public async Task<GehGassiFee> GetMinGehGassiFeeAsync(decimal startAmmount)
{
var fee = await (_gehGassiRepository.Query(c => c.StartAmmount <= startAmmount && c.Deleted == false).OrderByDescending(c => c.StartAmmount)).FirstOrDefaultAsync().ConfigureAwait(false);
return fee;
}
}
}

View File

@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Users;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Benutzern realisiert
/// </summary>
public class UserService : ServiceBase<ApplicationUser>, IUserService
{
private readonly IRepository<ApplicationUserWithNames> _applicationUserWithNamesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public UserService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_applicationUserWithNamesRepository = unitOfWork.GetRepository<ApplicationUserWithNames>();
}
/// <summary>
/// Prüft ob ein Benutzername noch verfügbar ist.
/// </summary>
/// <param name="userName">Zu prüfender Benutzername</param>
/// <param name="id">Id die ignoriert werden soll</param>
/// <returns>true wenn verfügbar, false sonst</returns>
public async Task<bool> IsUsernameAvailableAsync(string userName, string id = "")
{
if (string.IsNullOrWhiteSpace(userName))
throw new ArgumentException("Username may not be null or empty");
var existingUser = await Repository.FirstOrDefaultAsync(c => c.UserName == userName).ConfigureAwait(false);
if (existingUser != null && !string.IsNullOrWhiteSpace(id))
existingUser = await Repository.FirstOrDefaultAsync(c => c.UserName == userName && c.Id != id).ConfigureAwait(false);
return existingUser == null;
}
/// <summary>
/// Gibt einen Benutzer anhand des Benutzernamens zurück
/// </summary>
/// <param name="userName">Benutzername</param>
/// <returns>ApplicationUser oder null, wenn nicht gefunden</returns>
public async Task<ApplicationUser> GetByUsernameAsync(string userName)
{
var existingUser = await Repository.FirstOrDefaultAsync(c => c.UserName == userName).ConfigureAwait(false);
return existingUser;
}
/// <summary>
/// Gibt eine gefilterte Liste von Benutzer zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Benutzer</returns>
public IQueryable<ApplicationUser> Filter(string filter)
{
return Repository.Query(c => c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.UserName.Contains(filter));
}
/// <summary>
/// Erstellen eines Benutzers
/// </summary>
/// <returns>Benutzer</returns>
public ApplicationUser Create()
{
var user = new ApplicationUser { RegistrationDate = DateTimeOffset.UtcNow };
return user;
}
/// <summary>
/// Gibt eine gefilterte Liste von Benutzern mit Namen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Benutzer</returns>
public IQueryable<ApplicationUserWithNames> FilterWithNames(string filter)
{
return _applicationUserWithNamesRepository.QueryView(c => c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.UserName.Contains(filter));
}
/// <summary>
/// Gibt eine gefilterte Liste von Benutzern mit Namen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="customerId">Id des Kunden</param>
/// <returns>List betroffener Benutzer</returns>
public IQueryable<ApplicationUserWithNames> FilterWithNamesCustomer(string filter, long customerId)
{
return _applicationUserWithNamesRepository.QueryView(c => c.CustomerId == customerId && (c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.UserName.Contains(filter)));
}
/// <summary>
/// Zurücksetzen des Kunden auf null
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <returns>Task</returns>
public async Task ResetCustomerAsync(long customerId)
{
var items = (await Repository.FindAsync(c => c.CustomerId == customerId).ConfigureAwait(false)).ToList();
foreach (var item in items)
{
item.CustomerId = null;
}
}
/// <summary>
/// Zurücksetzen des App-Users auf null
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <returns>Task</returns>
public async Task ResetAppUserAsync(string appUserId)
{
var items = (await Repository.FindAsync(c => c.AppUserId == appUserId).ConfigureAwait(false)).ToList();
foreach (var item in items)
{
item.AppUserId = null;
}
}
/// <summary>
/// Gibt einen Benutzer anhand der App-UserId zurück
/// </summary>
/// <param name="appUserId">App-UserId</param>
/// <returns>ApplicationUser oder null, wenn nicht gefunden</returns>
public async Task<ApplicationUser> GetByAppUserAsync(string appUserId)
{
return await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,498 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Vouchers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
using Microsoft.EntityFrameworkCore;
using System.Security.Cryptography;
using NetTopologySuite.Algorithm;
using static Org.BouncyCastle.Asn1.Cmp.Challenge;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Gutscheinkampagnen übernimmt
/// </summary>
public class VoucherCampaignService : ServiceBase<VoucherCampaign>, IVoucherCampaignService
{
private readonly IRepository<VoucherCampaignWithNames> _namesRepository;
private readonly IRepository<Voucher> _vouchersRepository;
private readonly IRepository<VoucherUsage> _voucherUsagesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public VoucherCampaignService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<VoucherCampaignWithNames>();
_vouchersRepository = unitOfWork.GetRepository<Voucher>();
_voucherUsagesRepository = unitOfWork.GetRepository<VoucherUsage>();
}
/// <summary>
/// Gibt eine gefilterte Liste von Gutschein-Kampagnen zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Gutschein-Kampagnen</returns>
public IQueryable<VoucherCampaign> Filter(string filter)
{
return Repository.Query(c => c.Title.Contains(filter) || c.NameLocalized.Contains(filter));
}
/// <summary>
/// Erstellen einer Gutschein-Kampagne
/// </summary>
/// <returns>Gutschein-Kampagne</returns>
public VoucherCampaign Create()
{
var item = new VoucherCampaign()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Gutschein-Kampagnen zurück.
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <returns>List betroffener Gutschein-Kampagnen</returns>
public async Task<List<VoucherCampaign>> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.Title.Contains(filter) || c.NameLocalized.Contains(filter))).ToList();
}
/// <summary>
/// Gibt eine gefilterte Liste von Gutschein-Kampagnen mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Gutschein-Kampagnen</returns>
public IQueryable<VoucherCampaignWithNames> FilterWithNames(string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_VoucherCampignsWithNames({0},{1})", c => c.Title.Contains(filter) || c.Name.Contains(filter) || c.CustomerName.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != VoucherCampaignStatus.Draft);
return baseQuery;
}
/// <summary>
/// Gibt eine gefilterte Liste von Gutschein-Kampagnen mit Namen für Kunden zurück in einer gewählten Sprache
/// </summary>
/// <param name="customerId">Id des Kunden</param>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="fallback">Fallback Sprache</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Liste betroffener Gutschein-Kampagnen</returns>
public IQueryable<VoucherCampaignWithNames> FilterWithNamesCustomer(long customerId, string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_VoucherCampignsWithNames({0},{1})", c => c.CustomerId == customerId && (c.Title.Contains(filter) || c.Name.Contains(filter) || c.CustomerName.Contains(filter)), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != VoucherCampaignStatus.Draft);
return baseQuery;
}
/// <summary>
/// Gibt die Anzahl von Gutschein-Kampagnen zurück
/// </summary>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <param name="excludeDraft">Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?</param>
/// <returns>Anzahl Gutschein-Kampagnen</returns>
public async Task<int> CountAsync(bool includeDeleted, bool excludeDraft)
{
var query = Repository.Query(c => c.Id != "");
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if (excludeDraft)
query = query.Where(c => c.Status != VoucherCampaignStatus.Draft);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl von Gutschein-Kampagnen in einem Status zurück
/// </summary>
/// <param name="status">Gesuchter Status</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Anzahl Gutschein-Kampagnen</returns>
public async Task<int> CountAsync(VoucherCampaignStatus status, bool includeDeleted)
{
var query = Repository.Query(c => c.Status == status);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
return await query.CountAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Gutschein-Kampagne für einen Artikel im Warenkorb zurück
/// </summary>
/// <param name="cartItemId">Id des Artikels im Warenkorb</param>
/// <returns>Gutschein-Kampagne oder null, wenn nicht gefunden</returns>
public async Task<VoucherCampaign> GetByCartItemAsync(long cartItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.CartItemId == cartItemId && c.Status == VoucherCampaignStatus.Draft);
}
/// <summary>
/// Gibt einen Gutschein-Kampagne für einen Artikel in einer Bestellung zurück
/// </summary>
/// <param name="orderItemId">Id des Artikels in der Bestellung</param>
/// <returns>Gutschein-Kampagne oder null, wenn nicht gefunden</returns>
public async Task<VoucherCampaign> GetByOrderItemAsync(long orderItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.OrderItemId == orderItemId);
}
/// <summary>
/// Setzen des Status von Gutschein-Kampagnen nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <returns>Anzahl betroffener Gutschein-Kampagnen</returns>
public async Task<int> SetStatusByOrderAsync(long orderId, VoucherCampaignStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.Status = status;
}
return items.Count();
}
/// <summary>
/// Setzen des Bezahlstatus von Gutschein-Kampagnen nach Bestellung
/// </summary>
/// <param name="orderId">Id der Bestellung</param>
/// <param name="status">Zu setzender Status</param>
/// <returns>Anzahl betroffener Gutschein-Kampagnen</returns>
public async Task<int> SetPaymentStatusByOrderAsync(long orderId, PaymentStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.PaymentStatus = status;
}
return items.Count();
}
/// <summary>
/// Starten von gebuchten Gutschein-Kampagnen wenn diese das Start-Datum erreicht haben
/// </summary>
/// <returns>Anzahl gestartete Gutschein-Kampagnen</returns>
public async Task<int> StartBookedAsnyc()
{
var items = await Repository.FindAsync(c => c.Status == VoucherCampaignStatus.Booked && c.Deleted == false && c.StartDate <= DateTimeOffset.UtcNow);
foreach (var item in items)
{
item.Status = VoucherCampaignStatus.Running;
}
return items.Count();
}
/// <summary>
/// Stoppen von abgelaufenen Gutschein-Kampagnen wenn diese das Enddatum erreicht haben
/// </summary>
/// <returns>Anzahl gestoppte Gutschein-Kampagnen</returns>
public async Task<int> StopRunningAsync()
{
var items = await Repository.FindAsync(c => (c.Status == VoucherCampaignStatus.Running && c.Deleted == false && c.BudgetUsed >= c.Budget) || (c.Status == VoucherCampaignStatus.Running && c.Deleted == false && c.EndDate.Value <= DateTimeOffset.UtcNow));
foreach (var item in items)
{
item.EndDate = DateTimeOffset.UtcNow;
item.Status = VoucherCampaignStatus.Ended;
}
return items.Count();
}
/// <summary>
/// Erstellen von Gutscheinen für eine Gutschein-Kampagne
/// </summary>
/// <param name="voucherCampaignId">Id der Kampagne</param>
/// <param name="userName">Der Benutzer der die Gutscheine angelegt hat</param>
/// <returns>Anzahl der erstellten Gutscheine</returns>
public async Task<int> CreateVouchersForCampaign(string voucherCampaignId, string userName)
{
var campaign = await Repository.FirstOrDefaultAsync(c => c.Id == voucherCampaignId);
if (campaign != null)
{
int sufficientBufferSizeInBytes = (8 * 6 + 7) / 8;
for (var i = 0; i < campaign.NumberOfVouchers; i++)
{
var buffer = new byte[sufficientBufferSizeInBytes];
buffer = RandomNumberGenerator.GetBytes(8);
string code = Convert.ToBase64String(buffer).Substring(0, 8);
var voucher = new Voucher()
{
Id = Guid.NewGuid().ToString("N"),
VoucherCampaignId = campaign.Id,
Code = $"{campaign.Prefix}-" + GenerateManualCode(8),
QrCode = Guid.NewGuid().ToString("N").ToUpperInvariant(),
UsedCount = 0,
VoucherAmmount = campaign.VoucherAmmount,
IsValid = true
};
_vouchersRepository.Add(voucher);
if (i % 100 == 0)
{
await CommitAsync(userName, noAudit: true);
}
}
await CommitAsync(userName, noAudit: true);
return campaign.NumberOfVouchers;
}
return 0;
}
/// <summary>
/// Prüft ob ein Präfix für eine Gutschein-Kampagne verfügbar ist
/// </summary>
/// <param name="prefix">Präfix der geprüft werden soll</param>
/// <param name="voucherCampaignId">Id der Kampagne die ignoriert werden soll</param>
/// <returns>true wenn verfügbar, false sonst</returns>
public async Task<bool> IsPrefixAvailableAsync(string prefix, string voucherCampaignId)
{
var itemFound = await Repository.FirstOrDefaultAsync(c => c.Id != voucherCampaignId && c.Prefix == prefix).ConfigureAwait(false);
return itemFound == null;
}
/// <summary>
/// Aktualisiert den Gutschein-Betrag für alle noch nicht eingelösten oder deaktivierten Gutscheine einer Kampagne
/// </summary>
/// <param name="voucherCampaignId">Id der Kampagne</param>
/// <param name="voucherAmmount">Wert der gesetzt werden soll</param>
/// <returns>Anzahl Gutscheine die betroffen sind</returns>
public async Task<int> UpdateVoucherAmmountAsync(string voucherCampaignId, decimal voucherAmmount)
{
var vouchers = await _vouchersRepository.FindAsync(c => c.VoucherCampaignId == voucherCampaignId && c.UsedCount == 0 && c.IsValid == true);
foreach (var voucher in vouchers)
{
voucher.VoucherAmmount = voucherAmmount;
}
return vouchers.Count();
}
/// <summary>
/// Löschen der Gutscheine und deren Verwendung für eine Kampagne
/// </summary>
/// <param name="voucherCampaignId">Id der Kampagne</param>
/// <returns>Anzahl gelöscht</returns>
public async Task<int> RemoveVouchersAsync(string voucherCampaignId)
{
var vouchers = await _vouchersRepository.FindAsync(c => c.VoucherCampaignId == voucherCampaignId);
foreach (var voucher in vouchers)
{
var usages = await _voucherUsagesRepository.FindAsync(c => c.VoucherId == voucher.Id);
foreach (var usage in usages)
{
_voucherUsagesRepository.Remove(usage);
}
_vouchersRepository.Remove(voucher);
}
return vouchers.Count();
}
/// <summary>
/// Gibt eine Liste von Gutscheinen für eine Kampagne zurück
/// </summary>
/// <param name="voucherCampaignId">Id der Kampagne</param>
/// <returns>Liste Gutscheine</returns>
public async Task<List<Voucher>> GetVouchersForCampaignAsync(string voucherCampaignId)
{
var vouchers = await _vouchersRepository.FindAsync(c => c.VoucherCampaignId == voucherCampaignId).ConfigureAwait(false);
return vouchers.ToList();
}
/// <summary>
/// Prüft ob ein manueller Gutscheincode für einen Benutzer gültig ist und eingesetzt werden kann
/// </summary>
/// <param name="code">Code</param>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>Validierungs-Ergebnis</returns>
public async Task<VoucherValidationResult> ValidateVoucherCodeAsync(string code, string appUserId)
{
var voucher = await _vouchersRepository.FirstOrDefaultAsync(c => c.Code == code).ConfigureAwait(false);
if (voucher != null)
{
var voucherCampaign = await Repository.FirstOrDefaultAsync(c => c.Id == voucher.VoucherCampaignId).ConfigureAwait(false);
if (voucherCampaign != null)
{
//Läuft die Kampange noch nicht?
if(voucherCampaign.Status < VoucherCampaignStatus.Running)
return VoucherValidationResult.CampaignNotStarted;
//Ist die Kampagne schon beendet?
if (voucherCampaign.Status > VoucherCampaignStatus.Running)
return VoucherValidationResult.CampaignEnded;
//Wurde das Budget der Kampagne schon aufgebraucht?
if (voucherCampaign.BudgetUsed >= voucherCampaign.Budget)
return VoucherValidationResult.CampaignBudgetUsedUp;
//Ist der Gutschein noch gültig?
if(voucher.IsValid == false)
return VoucherValidationResult.VoucherAlreadyUsed;
//Wurde der Gutschein schon zu oft verwendet?
var usedByUserCount = await _voucherUsagesRepository.CountAsync(c => c.VoucherId == voucher.Id && c.AppUserId == appUserId).ConfigureAwait(false);
if(usedByUserCount >= voucherCampaign.MaxUsagePerUser)
return VoucherValidationResult.MaxUsageReached;
return VoucherValidationResult.Valid;
}
}
return VoucherValidationResult.Invalid;
}
/// <summary>
/// Gibt einen Gutschein anhand des Codes zurück
/// </summary>
/// <param name="code">Code des Gutscheins</param>
/// <returns>Gutschein oder null, wenn nicht gefunden</returns>
public async Task<Voucher> GetVoucherByCodeAsync(string code)
{
var voucher = await _vouchersRepository.FirstOrDefaultAsync(c => c.Code == code).ConfigureAwait(false);
return voucher;
}
/// <summary>
/// Gibt einen Gutschein anhand des QR-Codes zurück
/// </summary>
/// <param name="qrCode">QR-Code des Gutscheins</param>
/// <returns>Gutschein oder null, wenn nicht gefunden</returns>
public async Task<Voucher> GetVoucherByQrCodeAsync(string qrCode)
{
var voucher = await _vouchersRepository.FirstOrDefaultAsync(c => c.QrCode == qrCode).ConfigureAwait(false);
return voucher;
}
/// <summary>
/// Reserviert einen Gutschein für einen Benutzer und einen Walk
/// </summary>
/// <param name="voucherId">Id des Vouchers</param>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="walkId">Id des Walks</param>
/// <param name="voucherAmmount">Wert des Gutscheins</param>
/// <param name="voucherAmmountUsed">Wert der verbraucht wurde. Kann kleiner Gutscheinwert sein</param>
/// <returns>VoucherUsage</returns>
public async Task<VoucherUsage> ReserveVoucherAsync(string voucherId, string appUserId, string walkId, decimal voucherAmmount, decimal voucherAmmountUsed)
{
var voucherUsage = new VoucherUsage()
{
VoucherId = voucherId,
AppUserId = appUserId,
WalkId = walkId,
VoucherAmmount = voucherAmmount,
VoucherAmmountUsed = voucherAmmountUsed,
UsageStatus = VoucherUsageStatus.Reserved,
Created = DateTimeOffset.UtcNow
};
_voucherUsagesRepository.Add(voucherUsage);
return voucherUsage;
}
/// <summary>
/// Löschen der Verwendung eines Gutscheins
/// </summary>
/// <param name="voucherUsage">VoucherUsage</param>
public void RemoveUsage(VoucherUsage voucherUsage)
{
_voucherUsagesRepository.Remove(voucherUsage);
}
/// <summary>
/// Gibt die Verwendung eines Gutscheins zurück
/// </summary>
/// <param name="voucherId">Id des Gutscheins</param>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="walkId">Id des Walks</param>
/// <returns>VoucherUsage oder null, wenn nicht gefunden</returns>
public async Task<VoucherUsage> GetUsageAsync(string voucherId, string appUserId, string walkId)
{
var voucherUsage = await _voucherUsagesRepository.FirstOrDefaultAsync(c => c.VoucherId == voucherId && c.AppUserId == appUserId && c.WalkId == walkId).ConfigureAwait(false);
return voucherUsage;
}
/// <summary>
/// Gibt einen Gutschein zurück
/// </summary>
/// <param name="voucherId">Id des Gutscheins</param>
/// <returns>Voucher oder null, wenn nicht gefunden</returns>
public async Task<Voucher> GetVoucherAsync(string voucherId)
{
var voucher = await _vouchersRepository.FirstOrDefaultAsync(c => c.Id == voucherId).ConfigureAwait(false);
return voucher;
}
/// <summary>
/// Löschen von Voucher-Usages für einen AppUser
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <returns>Anzahl gelöscht</returns>
public async Task<int> RemoveVoucherUsageForAppUserAsync(string appUserId)
{
var usages = await _voucherUsagesRepository.FindAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
foreach (var voucherUsage in usages)
{
_voucherUsagesRepository.Remove(voucherUsage);
}
return usages.Count();
}
#region Helper
/// <summary>
/// Erstellt einen zufälligen Code in einer bestimmten Länge
/// </summary>
/// <param name="length">Länge des Codes</param>
/// <returns>Code</returns>
private string GenerateManualCode(int length)
{
var sufficientBufferSizeInBytes = (length * 6 + 7) / 8;
var buffer = RandomNumberGenerator.GetBytes(sufficientBufferSizeInBytes);
var code = Convert.ToBase64String(buffer)[..length];
code = code.ToUpperInvariant();
var finalCode = $"{code.Substring(0,length / 2)}-{code.Substring(length/2, length/2)}";
if (finalCode.Contains("+") || finalCode.Contains("/") || finalCode.Contains("="))
{
return GenerateManualCode(length);
}
return finalCode;
}
#endregion
}
}

View File

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Walks;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Walk-Beschwerden ermöglicht
/// </summary>
public class WalkComplaintService : ServiceBase<WalkComplaint>, IWalkComplaintService
{
private readonly IRepository<WalkComplaintWithNames> _namesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public WalkComplaintService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository<WalkComplaintWithNames>();
}
/// <summary>
/// Erstellen einer Walk-Beschwerde
/// </summary>
/// <returns>Walk</returns>
public WalkComplaint Create()
{
var item = new WalkComplaint()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
/// <summary>
/// Gibt eine gefilterte Liste von Walk-Beschwerden mit Namen zurück in einer gewählten Sprache
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener Walk-Beschwerden</returns>
public IQueryable<WalkComplaintWithNames> FilterWithNames(string filter, bool includeDeleted)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalkComplaintsWithNames()", c => c.DogOwnerName.Contains(filter) || c.DogWalkerName.Contains(filter) || c.UserName.Contains(filter));
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine Walk-Beschwerden mit Namen aufgelöst zurück
/// </summary>
/// <param name="id">Id der Beschwerde</param>
/// <returns>Walk-Beschwerden mit Namen aufgelöst</returns>
public async Task<WalkComplaintWithNames> GetWithNamesAsync(string id)
{
return await _namesRepository.FirstOrDefaultAsync(c => c.Id == id).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Walk-Beschwerden mit Namen aufgelöst zurück
/// </summary>
/// <param name="walkId">Id des Walks</param>
/// <returns>Walk-Beschwerden mit Namen aufgelöst</returns>
public async Task<WalkComplaintWithNames> GetWithNamesByWalkAsync(string walkId)
{
return await _namesRepository.FirstOrDefaultAsync(c => c.WalkId == walkId).ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Walk-Beschwerden zurück
/// </summary>
/// <param name="walkId">Id des Walks</param>
/// <returns>Walk-Beschwerden</returns>
public async Task<WalkComplaint> GetByWalkAsync(string walkId)
{
return await Repository.FirstOrDefaultAsync(c => c.WalkId == walkId).ConfigureAwait(false);
}
/// <summary>
/// Gibt die Anzahl der Walk-Beschwerden mit einem bestimmten Status zurück
/// </summary>
/// <param name="status">Gewünschter Status</param>
/// <returns>Anzahl Beschwerden im Status</returns>
public async Task<int> CountAsync(WalkComplaintStatus status)
{
return await Repository.CountAsync(c => c.Status == status).ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,871 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Walks;
using Microsoft.EntityFrameworkCore;
using NetTopologySuite.Geometries;
using NetTopologySuite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Common.Extensions;
using gehGassi.Domain.Common;
using Microsoft.Data.SqlClient;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.Payment;
using Azure;
using gehGassi.Dto.Messages;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Walks ermöglicht
/// </summary>
public class WalkService : ServiceBase<Walk>, IWalkService
{
private readonly IServiceProvider _serviceProvider;
private readonly IRepository<WalkWithNames> _namesRepository;
private readonly IRepository<PublicWalkRequest> _publicWalkRequestRepository;
private readonly IRepository<PublicWalkResponse> _publicWalkResponseRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
/// <param name="serviceProvider">Instanz eines IServiceProvider</param>
public WalkService(IUnitOfWork unitOfWork, IServiceProvider serviceProvider) : base(unitOfWork)
{
_serviceProvider = serviceProvider;
_namesRepository = unitOfWork.GetRepository<WalkWithNames>();
_publicWalkRequestRepository = unitOfWork.GetRepository<PublicWalkRequest>();
_publicWalkResponseRepository = unitOfWork.GetRepository<PublicWalkResponse>();
}
/// <summary>
/// Erstellen eines Walks
/// </summary>
/// <returns>Walk</returns>
public Walk Create()
{
var walk = new Walk()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return walk;
}
/// <summary>
/// Gibt eine Liste der Walks eines Benutzers zurück. Kann Owner und Walker sein
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der öffentlichen Anfragen</returns>
public async Task<List<Walk>> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.DogOwnerId == appUserId || c.DogWalkerId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine gefilterte Liste von Walks zurück
/// </summary>
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
/// <param name="includeDeleted">Sollen gelöschte inkludiert werden?</param>
/// <returns>Liste betroffener öffentlicher Anfragen</returns>
public IQueryable<WalkWithNames> FilterWithNames(string filter, bool includeDeleted)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogOwnerName.Contains(filter) || c.DogWalkerName.Contains(filter) || c.PickupAddress_City.Contains(filter));
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
/// <summary>
/// Gibt eine Liste von Walks mit Namen aufgelöst zurück
/// </summary>
/// <param name="dogWalkerId">Id des Dogwalkers oder leer wenn nicht für DogWalker</param>
/// <param name="dogOwnerId">Id des DogOwners oder leer wenn nicht für DogOwner</param>
/// <param name="date">Datum ab dem gesucht werden soll oder NULL wenn alle</param>
/// <param name="status">Status des Walks, NULL wenn alle</param>
/// <param name="serviceType">Service Type, NULL wenn alle</param>
/// <param name="paymentStatus">Zahlungsstatus, NULL wenn alle</param>
/// <param name="dogFilter">Optinal: nach HundeID filtern</param>
/// <param name="walkerFilter">Optional: nach Walkername filtern</param>
/// <param name="ownerFilter">Optional: nach Ownername filtern</param>
/// <param name="ignoreCancelled">Stornierte Ignorieren</param>
/// <param name="forNextWalk">Sollen nur Walks für den nächsten Walk selektiert werden? Dann nur Angenommene oder Laufende</param>
/// <param name="ignoreRequested">Sollen Walks die noch eine Anfrage sind, ohne Akzeptiert oder Agelehnt ignorniert werden?</param>
/// <param name="ignoreDeclined">Sollen abgelehnte Walks ignoriert werden?</param>
/// <param name="sortList">Optional: Liste der sortierung</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste gefudene Walks</returns>
public async Task<(int total, List<WalkWithNames> list)> GetWithNamesAsync(string dogWalkerId, string dogOwnerId, DateTimeOffset? date, WalkStatus? status, WalkServiceType? serviceType, PaymentStatus? paymentStatus, string dogFilter, string walkerFilter, string ownerFilter, bool ignoreCancelled, bool forNextWalk, bool ignoreRequested, bool ignoreDeclined, List<DynamicSortOrder> sortList, double lat, double lng, int skip, int take)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.Deleted == false);
if(!string.IsNullOrWhiteSpace(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
if (!string.IsNullOrWhiteSpace(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if(date.HasValue)
baseQuery = baseQuery.Where(c => c.Start >= date);
if (status.HasValue)
{
baseQuery = baseQuery.Where(c => c.Status == status);
}
else
{
if(ignoreCancelled)
baseQuery = baseQuery.Where(c => c.Status != WalkStatus.Cancelled);
if (forNextWalk)
{
baseQuery = baseQuery.Where(c => c.Status == WalkStatus.Accepted || c.Status == WalkStatus.Started);
}
if (ignoreRequested)
baseQuery = baseQuery.Where(c => c.Status != WalkStatus.Requested);
if (ignoreDeclined)
baseQuery = baseQuery.Where(c => c.Status != WalkStatus.Declined);
}
if (serviceType.HasValue)
baseQuery = baseQuery.Where(c => c.ServiceType == serviceType);
if (paymentStatus.HasValue)
baseQuery = baseQuery.Where(c => c.PaymentStatus == paymentStatus);
if (!string.IsNullOrWhiteSpace(dogFilter))
baseQuery = baseQuery.Where(c => c.DogsJson.Contains(dogFilter));
if (!string.IsNullOrWhiteSpace(walkerFilter))
baseQuery = baseQuery.Where(c => c.DogWalkerName.Contains(walkerFilter));
if (!string.IsNullOrWhiteSpace(ownerFilter))
baseQuery = baseQuery.Where(c => c.DogOwnerName.Contains(ownerFilter));
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.PickupLocation.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Start).ThenBy(c => c.PickupLocation.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt eine Liste von Walks mit Namen aufgelöst zurück für Walker
/// Sonderfall hier ist, dass keine Walks mit einem PaymentStatus unter Authorized zurückgibt
/// </summary>
/// <param name="dogWalkerId">Id des Dogwalkers oder leer wenn nicht für DogWalker</param>
/// <param name="dogOwnerId">Id des DogOwners oder leer wenn nicht für DogOwner</param>
/// <param name="date">Datum ab dem gesucht werden soll oder NULL wenn alle</param>
/// <param name="status">Status des Walks, NULL wenn alle</param>
/// <param name="serviceType">Service Type, NULL wenn alle</param>
/// <param name="paymentStatus">Zahlungsstatus, NULL wenn alle</param>
/// <param name="dogFilter">Optinal: nach HundeID filtern</param>
/// <param name="walkerFilter">Optional: nach Walkername filtern</param>
/// <param name="ownerFilter">Optional: nach Ownername filtern</param>
/// <param name="ignoreCancelled">Stornierte Ignorieren</param>
/// <param name="forNextWalk">Sollen nur Walks für den nächsten Walk selektiert werden? Dann nur Angenommene oder Laufende</param>
/// <param name="ignoreRequested">Sollen Walks die noch eine Anfrage sind, ohne Akzeptiert oder Agelehnt ignorniert werden?</param>
/// <param name="ignoreDeclined">Sollen abgelehnte Walks ignoriert werden?</param>
/// <param name="sortList">Optional: Liste der sortierung</param>
/// <param name="lat">Optional: Breitengrad</param>
/// <param name="lng">Optional: Längengrad</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste gefudene Walks</returns>
public async Task<(int total, List<WalkWithNames> list)> GetWithNamesForWalkerAsync(string dogWalkerId, string dogOwnerId, DateTimeOffset? date, WalkStatus? status, WalkServiceType? serviceType, PaymentStatus? paymentStatus, string dogFilter, string walkerFilter, string ownerFilter, bool ignoreCancelled, bool forNextWalk, bool ignoreRequested, bool ignoreDeclined, List<DynamicSortOrder> sortList, double lat, double lng, int skip, int take)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.Deleted == false);
if (!string.IsNullOrWhiteSpace(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
if (!string.IsNullOrWhiteSpace(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if (date.HasValue)
baseQuery = baseQuery.Where(c => c.Start >= date);
if (status.HasValue)
{
baseQuery = baseQuery.Where(c => c.Status == status);
}
else
{
if (ignoreCancelled)
baseQuery = baseQuery.Where(c => c.Status != WalkStatus.Cancelled);
if (forNextWalk)
{
baseQuery = baseQuery.Where(c => c.Status == WalkStatus.Accepted || c.Status == WalkStatus.Started);
}
if (ignoreRequested)
baseQuery = baseQuery.Where(c => c.Status != WalkStatus.Requested);
if (ignoreDeclined)
baseQuery = baseQuery.Where(c => c.Status != WalkStatus.Declined);
}
if (serviceType.HasValue)
baseQuery = baseQuery.Where(c => c.ServiceType == serviceType);
if (paymentStatus.HasValue)
baseQuery = baseQuery.Where(c => c.PaymentStatus == paymentStatus && c.PaymentStatus >= PaymentStatus.Authorized);
else
baseQuery = baseQuery.Where(c => c.PaymentStatus >= PaymentStatus.Authorized || (c.PaymentStatus == PaymentStatus.None && (c.Status == WalkStatus.Requested || c.Status == WalkStatus.Declined)));
if (!string.IsNullOrWhiteSpace(dogFilter))
baseQuery = baseQuery.Where(c => c.DogsJson.Contains(dogFilter));
if (!string.IsNullOrWhiteSpace(walkerFilter))
baseQuery = baseQuery.Where(c => c.DogWalkerName.Contains(walkerFilter));
if (!string.IsNullOrWhiteSpace(ownerFilter))
baseQuery = baseQuery.Where(c => c.DogOwnerName.Contains(ownerFilter));
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.PickupLocation.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Start).ThenBy(c => c.PickupLocation.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
/// <summary>
/// Gibt eine Liste von Walks mit Namen aufgelöst zurück für Hundebesitzer die anstehen, laufen, nicht abgeschlossen sind, nicht gestartet wurden oder zu bezahlen sind
/// </summary>
/// <param name="dogOwnerId">Id des Hundebesitzers</param>
/// <param name="date">Datum ab dem gesucht werden soll oder NULL wenn alle</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste gefudene Walks</returns>
public async Task<(int total, List<WalkWithNames> list)> GetNextWithNamesForOwnerAsync(string dogOwnerId, DateTimeOffset date, int skip, int take)
{
var total = 0;
var list = new List<WalkWithNames>();
//1. Aktuell laufende Walks
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogOwnerId == dogOwnerId && c.Deleted == false && c.Status == WalkStatus.Started);
//baseQuery = baseQuery.Where(c => c.Start <= date && c.End >= date);
//Anpassung, Walks auch dann anzeigen, wenn gestartet aber das Startdatum noch nicht erreicht ist
//baseQuery = baseQuery.Where(c => c.Start <= date && c.End >= date);
baseQuery = baseQuery.Where(c => c.End >= date);
list.AddRange(await baseQuery.OrderBy(c => c.Start).ToListAsync().ConfigureAwait(false));
//2. Nicht gestartete Walks
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogOwnerId == dogOwnerId && c.Deleted == false && c.Status == WalkStatus.Accepted && c.PaymentStatus == PaymentStatus.Authorized);
baseQuery = baseQuery.Where(c => c.Start <= date && c.End >= date);
list.AddRange(await baseQuery.OrderBy(c => c.Start).ToListAsync().ConfigureAwait(false));
//3. Zu bezahlende Walks
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogOwnerId == dogOwnerId && c.Deleted == false && c.Status == WalkStatus.Accepted && c.PaymentStatus == PaymentStatus.Pending);
list.AddRange(await baseQuery.OrderBy(c => c.Start).ToListAsync().ConfigureAwait(false));
//4. Nicht abgeschlossene Walks
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogOwnerId == dogOwnerId && c.Deleted == false && (c.Status == WalkStatus.Accepted || c.Status == WalkStatus.Started) && c.PaymentStatus == PaymentStatus.Authorized);
baseQuery = baseQuery.Where(c => c.End <= date);
list.AddRange(await baseQuery.OrderBy(c => c.Start).ToListAsync().ConfigureAwait(false));
//5. Nicht bestätigte Walks
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogOwnerId == dogOwnerId && c.Deleted == false && c.Status == WalkStatus.Completed && c.PaymentStatus == PaymentStatus.Authorized);
baseQuery = baseQuery.Where(c => c.End <= date);
list.AddRange(await baseQuery.OrderBy(c => c.Start).ToListAsync().ConfigureAwait(false));
//6. Nächster anstehender Walk
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogOwnerId == dogOwnerId && c.Deleted == false && c.Status == WalkStatus.Accepted && c.PaymentStatus == PaymentStatus.Authorized);
baseQuery = baseQuery.Where(c => c.Start >= date).OrderBy(c => c.Start);
var nextWaks = (await baseQuery.ToListAsync().ConfigureAwait(false)).Skip(0).Take(3);
if(nextWaks.Any())
list.AddRange(nextWaks);
total = list.Count;
list = list.Skip(skip).Take(take).ToList();
return (total, list);
}
/// <summary>
/// Gibt eine Liste von Walks mit Namen aufgelöst zurück für Walker die anstehen, laufen, nicht abgeschlossen sind oder nicht gestartet wurden
/// </summary>
/// <param name="dogWalkerId">Id des Walkers</param>
/// <param name="date">Datum ab dem gesucht werden soll oder NULL wenn alle</param>
/// <param name="skip">Datensätze auslassen</param>
/// <param name="take">Datensätze nehmen</param>
/// <returns>Liste gefudene Walks</returns>
public async Task<(int total, List<WalkWithNames> list)> GetNextWithNamesForWalkerAsync(string dogWalkerId, DateTimeOffset date, int skip, int take)
{
var total = 0;
var list = new List<WalkWithNames>();
//1. Aktuell laufende Walks
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogWalkerId == dogWalkerId && c.Deleted == false && c.Status == WalkStatus.Started);
//baseQuery = baseQuery.Where(c => c.Start <= date && c.End >= date);
//Anpassung, Walks auch dann anzeigen, wenn gestartet aber das Startdatum noch nicht erreicht ist
baseQuery = baseQuery.Where(c => c.End >= date);
list.AddRange(await baseQuery.OrderBy(c => c.Start).ToListAsync().ConfigureAwait(false));
//2. Nicht gestartete Walks
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogWalkerId == dogWalkerId && c.Deleted == false && c.Status == WalkStatus.Accepted && c.PaymentStatus == PaymentStatus.Authorized);
baseQuery = baseQuery.Where(c => c.Start <= date && c.End >= date);
list.AddRange(await baseQuery.OrderBy(c => c.Start).ToListAsync().ConfigureAwait(false));
//3. Nicht abgeschlossene Walks
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogWalkerId == dogWalkerId && c.Deleted == false && (c.Status == WalkStatus.Accepted || c.Status == WalkStatus.Started) && c.PaymentStatus == PaymentStatus.Authorized);
baseQuery = baseQuery.Where(c => c.End <= date);
list.AddRange(await baseQuery.OrderBy(c => c.Start).ToListAsync().ConfigureAwait(false));
//5. Nächster anstehender Walk
baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.DogWalkerId == dogWalkerId && c.Deleted == false && c.Status == WalkStatus.Accepted && c.PaymentStatus == PaymentStatus.Authorized);
baseQuery = baseQuery.Where(c => c.Start >= date).OrderBy(c => c.Start);
var nextWaks = (await baseQuery.ToListAsync().ConfigureAwait(false)).Skip(0).Take(3);
if (nextWaks.Any())
list.AddRange(nextWaks);
total = list.Count;
list = list.Skip(skip).Take(take).ToList();
return (total, list);
}
/// <summary>
/// Gibt einen Walk mit Namen aufgelöst zurück
/// </summary>
/// <param name="walkId">Id des Walks</param>
/// <returns>Walk oder null, wenn nicht gefunden</returns>
public async Task<WalkWithNames> GetWalkWithNamesAsync(string walkId)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_WalksWithNames()", c => c.Deleted == false);
baseQuery = baseQuery.Where(c => c.Id == walkId && c.Deleted == false);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
/// <summary>
/// Zurückgeben einer Liste von Walks die einen bestimmten Status haben
/// </summary>
/// <param name="status">Gesuchter Status</param>
/// <returns>Liste von Walks</returns>
public async Task<List<Walk>> GetByStatusAsync(WalkStatus status)
{
var walks = await Repository.FindAsync(c => c.Status == status && c.Deleted == false).ConfigureAwait(false);
return walks.ToList();
}
/// <summary>
/// Gibt einen Walk zurück der mit einer öffentlichen Anfrage verknüpft ist
/// </summary>
/// <param name="publicWalkRequestId">Id der öffentlichen Anfrage</param>
/// <returns>Walk oder null, wenn nicht gefunden</returns>
public async Task<Walk> GetWalkByPublicRequestAsync(string publicWalkRequestId)
{
var walk = await Repository.FirstOrDefaultAsync(c => c.PublicWalkRequestId == publicWalkRequestId && c.Deleted == false).ConfigureAwait(false);
return walk;
}
/// <summary>
/// Prüft ob ein Walk für einen Zeitraum gebucht werden kann.
/// Sittings werden dabei ignoriert!
/// </summary>
/// <param name="dogWalkerId">Id des dogwalkers</param>
/// <param name="start">Startdatum</param>
/// <param name="end">Enddatum</param>
/// <returns>true wenn verfügbar, false sonst</returns>
public async Task<bool> IsWalkPossibleAsync(string dogWalkerId, DateTimeOffset start, DateTimeOffset end)
{
var walkFound = await Repository.FirstOrDefaultAsync(c => c.DogWalkerId == dogWalkerId && c.ServiceType == WalkServiceType.Walking && start < c.End && end > c.Start && c.Deleted == false && (c.Status == WalkStatus.Accepted || c.Status == WalkStatus.Started)).ConfigureAwait(false);
return walkFound == null;
}
/// <summary>
/// Setzt Walks die im Status "Request" sind auf Cancel.
/// Wird verwendet wenn eine FIXE buchung gemacht wird und andere noch auf eine Antwort warten.
/// </summary>
/// <param name="dogWalkerId">Id des dogwalkers</param>
/// <param name="start">Startdatum</param>
/// <param name="end">Enddatum</param>
/// <returns>Anzahl betroffene Walks</returns>
public async Task<int> CancelWalkRequestsAsync(string dogWalkerId, DateTimeOffset start, DateTimeOffset end)
{
var walks = await Repository.FindAsync(c => c.DogWalkerId == dogWalkerId && c.ServiceType == WalkServiceType.Walking && start < c.End && end > c.Start && c.Deleted == false && c.Status == WalkStatus.Requested).ConfigureAwait(false);
foreach (var walk in walks)
{
walk.Status = WalkStatus.Cancelled;
walk.CancelledBy = CancellationSource.GehGassi;
walk.CancelledReason = string.Empty;
walk.UpdatedAt = DateTimeOffset.UtcNow;
}
return walks.Count();
}
/// <summary>
/// Walks stornieren welche im Status Request sind und das Startdatum erreicht wurde
/// </summary>
/// <param name="date">Datum für Prüfung</param>
/// <returns>Anzahl timed out</returns>
public async Task<int> CancelTimedOutAsync(DateTimeOffset date)
{
var walks = await Repository.FindAsync(c => c.Start < DateTimeOffset.UtcNow && c.Deleted == false && c.Status == WalkStatus.Requested).ConfigureAwait(false);
foreach (var walk in walks)
{
walk.Status = WalkStatus.Cancelled;
walk.CancelledBy = CancellationSource.GehGassi;
walk.CancelledReason = string.Empty;
walk.UpdatedAt = DateTimeOffset.UtcNow;
}
return walks.Count();
}
/// <summary>
/// Stornieren von Walks die im Zahlungsstatus Pending sind und länger als X Minuten alt sind
/// </summary>
/// <param name="date">Datum für Prüfung</param>
/// <param name="minutes">Ablaufzeit in Minuten</param>
/// <returns>Anzahl storniert</returns>
public async Task<int> CancelPendingAsync(DateTimeOffset date, int minutes)
{
var publicWalkRequestService = (IPublicWalkRequestService)_serviceProvider.GetService(typeof(IPublicWalkRequestService));
var publicWalkResponseService = (IPublicWalkResponseService)_serviceProvider.GetService(typeof(IPublicWalkResponseService));
var voucherCampaignService = (IVoucherCampaignService)_serviceProvider.GetService(typeof(IVoucherCampaignService));
var customerService = (ICustomerService)_serviceProvider.GetService(typeof(ICustomerService));
var mangoPayService = (IMangoPayService)_serviceProvider.GetService(typeof(IMangoPayService));
var compareDate = date.AddMinutes(-minutes);
var walks = await Repository.FindAsync(c => c.PaymentStatus == PaymentStatus.Pending && c.Deleted == false && c.Status == WalkStatus.Accepted && c.Created < compareDate).ConfigureAwait(false);
foreach (var walk in walks)
{
if (!string.IsNullOrWhiteSpace(walk.VoucherId) && walk.VoucherAmmount > 0)
{
var usage = await voucherCampaignService.GetUsageAsync(walk.VoucherId, walk.DogOwnerId, walk.Id);
if (usage != null)
{
var voucher = await voucherCampaignService.GetVoucherAsync(usage.VoucherId);
if (voucher != null && voucher.Id == usage.VoucherId)
{
var campaign = await voucherCampaignService.GetAsync(voucher.VoucherCampaignId);
if (campaign != null)
{
var customer = await customerService.GetAsync(campaign.CustomerId);
if (customer != null)
{
//Geld vom Transferkonto der Kamapgne auf das Guthabenkonto überweisen mit Verweis auf den Walk und Transaktion erfassen
var ammount = (int)(usage.VoucherAmmountUsed * 100);
var transferResult = await mangoPayService.TransferMoneyAsync(customer.MangopayPaymentId, campaign.WalletFeeId,
customer.MangopayPaymentId, campaign.WalletCreditId, walk.Id, ammount, 0, $"VoucherId_{voucher.Id}_WalkId_{walk.Id}", "", "");
if (transferResult.Success)
{
//Usage beim voucher zurücksetzen (UsedCount, IsValid wenn Einzelgutschein)
voucher.UsedCount -= 1;
if (voucher.UsedCount < 0)
voucher.UsedCount = 0;
voucher.IsValid = true;
//Usage in der Kampagne zurücksetzen (VouchersUsed, BudgetUsed)
campaign.VouchersUsed -= 1;
if (campaign.VouchersUsed < 0)
campaign.VouchersUsed = 0;
campaign.BudgetUsed -= usage.VoucherAmmountUsed;
if (campaign.BudgetUsed < 0)
campaign.BudgetUsed = 0;
walk.VoucherId = null;
walk.VoucherAmmount = 0;
//Reservieren durch die Anlage einer VoucherUsage
voucherCampaignService.RemoveUsage(usage);
}
}
}
}
}
}
walk.Status = WalkStatus.Cancelled;
walk.CancelledBy = CancellationSource.GehGassi;
walk.CancelledReason = string.Empty;
walk.UpdatedAt = DateTimeOffset.UtcNow;
if (walk.Type == WalkType.OpenRequest)
{
var resetSuccess = await publicWalkRequestService.ResetAsync(walk.PublicWalkRequestId);
if (resetSuccess)
{
var reopenedCount = await publicWalkResponseService.SetOpenForClosedOrAcceptedAsync(walk.PublicWalkRequestId);
}
walk.PublicWalkRequestId = string.Empty;
}
}
return walks.Count();
}
/// <summary>
/// Abschliessen eines Walks, falls das vergessen wurde
/// </summary>
/// <param name="date">Datum für Prüfung</param>
/// <param name="minutes">Ablaufzeit in Minuten</param>
/// <returns>Anzahl abgeschlossen</returns>
public async Task<int> CompleteAutoAsync(DateTimeOffset date, int minutes)
{
var compareDate = date.AddMinutes(-minutes);
var walks = await Repository.FindAsync(c => c.PaymentStatus == PaymentStatus.Authorized && c.Deleted == false && (c.Status == WalkStatus.Accepted || c.Status == WalkStatus.Started) && c.End < compareDate).ConfigureAwait(false);
foreach (var walk in walks)
{
walk.Status = WalkStatus.Completed;
walk.UpdatedAt = DateTimeOffset.UtcNow;
}
return walks.Count();
}
/// <summary>
/// Bestätigen von Walks die im Status Abgeschlossen sind und länger als X Minuten alt sind.
/// Löst die Überweisung von den Kosten für den Walk vom hundebesitzer an den Dogwalker aus.
/// </summary>
/// <param name="date">Datum für Prüfung</param>
/// <param name="minutes">Ablaufzeit in Minuten</param>
/// <returns>Anzahl bestätigt</returns>
public async Task<int> ConfirmCompletedAsync(DateTimeOffset date, int minutes)
{
var compareDate = date.AddMinutes(-minutes);
var mangoPayService = (IMangoPayService)_serviceProvider.GetService(typeof(IMangoPayService));
var gehGassiTransactionService = (ITransactionFeeService)_serviceProvider.GetService(typeof(ITransactionFeeService));
var walletService = (IWalletService)_serviceProvider.GetService(typeof(IWalletService));
var appUserService = (IAppUserService)_serviceProvider.GetService(typeof(IAppUserService));
var voucherCampaignService = (IVoucherCampaignService)_serviceProvider.GetService(typeof(IVoucherCampaignService));
var customerService = (ICustomerService)_serviceProvider.GetService(typeof(ICustomerService));
var walks = await Repository.FindAsync(c => c.Status == WalkStatus.Completed && c.Deleted == false && c.PaymentStatus == PaymentStatus.Authorized && c.Completed< compareDate).ConfigureAwait(false);
foreach (var walk in walks)
{
walk.Status = WalkStatus.Confirmed;
walk.Confirmed = DateTimeOffset.UtcNow;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.PaymentStatus = PaymentStatus.Paid;
await CommitAsync("System");
decimal feeDecimal = 0;
long feeFromUser = 0;
long feeFromVoucher = 0;
var gehGassiFee = await gehGassiTransactionService.GetMinGehGassiFeeAsync(walk.Price);
if (gehGassiFee != null)
{
if (gehGassiFee.Percent > 0)
{
var percent = walk.Price * gehGassiFee.Percent / 100;
feeDecimal = percent;
}
if (gehGassiFee.Fixed > 0)
{
feeDecimal += gehGassiFee.Fixed;
}
}
if (feeDecimal > 0)
{
feeFromUser = (long)(feeDecimal * 100);
}
//Wenn es einen gutschein gibt, dann Geld vom Transferkontos des Gutscheins auf das transferkonto des Hundebesitzers überweisen
if (!string.IsNullOrWhiteSpace(walk.VoucherId) && walk.VoucherAmmount > 0)
{
var voucherUsed = await voucherCampaignService.GetUsageAsync(walk.VoucherId, walk.DogOwnerId, walk.Id);
if (voucherUsed != null)
{
var ammountFromVoucher = (long)(voucherUsed.VoucherAmmountUsed * 100);
voucherUsed.UsageStatus = VoucherUsageStatus.Used;
await CommitAsync("System");
var voucher = await voucherCampaignService.GetVoucherAsync(voucherUsed.VoucherId);
if (voucher != null)
{
var campaign = await voucherCampaignService.GetAsync(voucher.VoucherCampaignId);
if (campaign != null)
{
var customer = await customerService.GetAsync(campaign.CustomerId);
if (customer != null)
{
var dogOwner = await appUserService.GetAsync(walk.DogOwnerId);
if (dogOwner != null)
{
var wallet = await walletService.GetAsync(walk.DogOwnerId, WalletType.Fees);
if (wallet != null)
{
var transferResult = await mangoPayService.TransferMoneyAsync(customer.MangopayPaymentId, campaign.WalletFeeId, dogOwner.PaymentId, wallet.Id, walk.Id, ammountFromVoucher, 0, $"VoucherId_{voucher.Id}_WalkId_{walk.Id}", targetAppUserId: dogOwner.Id);
}
}
}
}
}
}
}
var sourceWallet = await walletService.GetAsync(walk.DogOwnerId, WalletType.Fees);
var targetWallet = await walletService.GetAsync(walk.DogWalkerId, WalletType.Credits);
if (sourceWallet != null && targetWallet != null)
{
var sourceUser = await appUserService.GetAsync(walk.DogOwnerId);
var targetUser = await appUserService.GetAsync(walk.DogWalkerId);
if (sourceUser != null && targetUser != null)
{
var ammount = (long)(walk.Price * 100);
var transcationSuccess = await mangoPayService.TransferMoneyAsync(walk.DogOwnerId, walk.DogWalkerId, walk.Id, ammount, feeFromUser, $"WalkId_Payment_{walk.Id}");
//Wallets aktualisieren
await mangoPayService.GetWalletBalanceAsync(sourceUser.Id, WalletType.Credits);
await mangoPayService.GetWalletBalanceAsync(sourceUser.Id, WalletType.Fees);
await mangoPayService.GetWalletBalanceAsync(targetUser.Id, WalletType.Credits);
await mangoPayService.GetWalletBalanceAsync(targetUser.Id, WalletType.Fees);
}
}
}
return walks.Count();
}
/// <summary>
/// Bestätigen von Walks die im Status Abgeschlossen sind und länger als X Minuten alt sind.
/// Löst die Überweisung von den Kosten für den Walk vom hundebesitzer an den Dogwalker aus.
/// </summary>
/// <param name="date">Datum für Prüfung</param>
/// <param name="minutes">Ablaufzeit in Minuten</param>
/// <returns>Anzahl bestätigt</returns>
public async Task<int> ConfirmCompletedReminderAsync(DateTimeOffset date, int minutes)
{
var pushNotificationService = (IPushNotificationService)_serviceProvider.GetService(typeof(IPushNotificationService));
var compareDateLower = date.AddMinutes(-minutes - 1);
var compareDateUpper = date.AddMinutes(-minutes);
var walks = await Repository.FindAsync(c => c.Status == WalkStatus.Completed && c.Deleted == false && c.PaymentStatus == PaymentStatus.Authorized && c.Completed >= compareDateLower && c.Completed <= compareDateUpper).ConfigureAwait(false);
foreach (var walk in walks)
{
await pushNotificationService.SendWalkCompletedReminderAsync(walk.DogOwnerId, walk.Id);
}
return walks.Count();
}
/// <summary>
/// Storniert Liste von Walks zurück, welche für 2 App-User in Beziehung stehen wenn eine Blockierung stattgefunden hat.
/// Wird bidirektional gesucht, da nicht immer sichergestellt ist, dass einer Walker und einer Owner ist.
/// Es werden nur Walks im Status Requested oder Accepted berücksichtigt
/// </summary>
/// <param name="blockingAppUserId">Id des App-Users der blockiert hat</param>
/// <param name="blockedAppUserId">Id des App-Users der blockiert wurde</param>
/// <returns>Anzahl stornierte Walks</returns>
public async Task<int> CancelWalksForBlockAsync(string blockingAppUserId, string blockedAppUserId)
{
var mangoPayService = (IMangoPayService)_serviceProvider.GetService(typeof(IMangoPayService));
var systemMessageService = (ISystemMessageService)_serviceProvider.GetService(typeof(ISystemMessageService));
var pushNotificationService = (IPushNotificationService)_serviceProvider.GetService(typeof(IPushNotificationService));
var walks = await Repository.FindAsync(c => c.Status <= WalkStatus.Accepted && c.Deleted == false && ((c.DogOwnerId == blockingAppUserId && c.DogWalkerId == blockedAppUserId) || (c.DogOwnerId == blockedAppUserId && c.DogWalkerId == blockingAppUserId))).ConfigureAwait(false);
foreach (var walk in walks)
{
walk.Status = WalkStatus.Cancelled;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.CancelledBy = CancellationSource.GehGassi;
walk.CancelledReason = "User blocked";
//Jetzt noch wegen Zahlungsstatus prüfen
if (walk.PaymentStatus == PaymentStatus.Paid || walk.PaymentStatus == PaymentStatus.Authorized)
{
//Zurücküberweisen der Summe vom Transaktionskonto auf das Guthabenkonto
if (walk.Price > 0)
{
var ammount = (long)(walk.Price * 100);
var tag = $"Walk_Cancel_{walk.Id}";
var refundResult = await mangoPayService.TransferMoneyFromFeeToCreditAccountAsync(walk.DogOwnerId, ammount, tag);
if (refundResult.Success)
walk.PaymentStatus = PaymentStatus.Refunded;
else
walk.PaymentStatus = PaymentStatus.Voided;
}
else
walk.PaymentStatus = PaymentStatus.Voided;
await CommitAsync("System");
//Wallets aktualisieren
await mangoPayService.GetWalletBalanceAsync(walk.DogOwnerId, WalletType.Credits);
await mangoPayService.GetWalletBalanceAsync(walk.DogOwnerId, WalletType.Fees);
systemMessageService.Add(walk.DogOwnerId, AppUserType.DogOwner, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkCancelled, DateTimeOffset.UtcNow.AddDays(14));
systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkCancelled, DateTimeOffset.UtcNow.AddDays(14));
await systemMessageService.CommitAsync("System");
await pushNotificationService.SendWalkCancelledAsync(walk.DogOwnerId, walk.Id, AppMode.DogOwner);
await pushNotificationService.SendWalkCancelledAsync(walk.DogWalkerId, walk.Id, AppMode.DogWalker);
}
}
return walks.Count();
}
/// <summary>
/// Stornieren aller Walks eines Benutzers der gesperrt wurde.
/// Wird bidirektional gesucht, da nicht immer sichergestellt ist, dass einer Walker und einer Owner ist.
/// Es werden nur Walks im Status Requested oder Accepted berücksichtigt
/// </summary>
/// <param name="appUserId">Id des App-Users</param>
/// <returns>Anzahl stornierte Walks</returns>
public async Task<int> CancelWalksForLockedAsync(string appUserId)
{
var mangoPayService = (IMangoPayService)_serviceProvider.GetService(typeof(IMangoPayService));
var systemMessageService = (ISystemMessageService)_serviceProvider.GetService(typeof(ISystemMessageService));
var pushNotificationService = (IPushNotificationService)_serviceProvider.GetService(typeof(IPushNotificationService));
var walks = await Repository.FindAsync(c => c.Status <= WalkStatus.Accepted && c.Deleted == false && (c.DogOwnerId == appUserId || c.DogWalkerId == appUserId)).ConfigureAwait(false);
foreach (var walk in walks)
{
walk.Status = WalkStatus.Cancelled;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.CancelledBy = CancellationSource.GehGassi;
walk.CancelledReason = "User locked";
//Jetzt noch wegen Zahlungsstatus prüfen
if (walk.PaymentStatus == PaymentStatus.Paid || walk.PaymentStatus == PaymentStatus.Authorized)
{
//Zurücküberweisen der Summe vom Transaktionskonto auf das Guthabenkonto
if (walk.Price > 0)
{
var ammount = (long)(walk.Price * 100);
var tag = $"Walk_Cancel_{walk.Id}";
var refundResult = await mangoPayService.TransferMoneyFromFeeToCreditAccountAsync(walk.DogOwnerId, ammount, tag);
if (refundResult.Success)
walk.PaymentStatus = PaymentStatus.Refunded;
else
walk.PaymentStatus = PaymentStatus.Voided;
}
else
walk.PaymentStatus = PaymentStatus.Voided;
await CommitAsync("System");
//Wallets aktualisieren
await mangoPayService.GetWalletBalanceAsync(walk.DogOwnerId, WalletType.Credits);
await mangoPayService.GetWalletBalanceAsync(walk.DogOwnerId, WalletType.Fees);
//Es werden nur Nachrichten an den nicht gesperrten Benutzer gesendet
if(walk.DogOwnerId != appUserId)
systemMessageService.Add(walk.DogOwnerId, AppUserType.DogOwner, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkCancelled, DateTimeOffset.UtcNow.AddDays(14));
else
systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkCancelled, DateTimeOffset.UtcNow.AddDays(14));
await systemMessageService.CommitAsync("System");
if (walk.DogOwnerId != appUserId)
await pushNotificationService.SendWalkCancelledAsync(walk.DogOwnerId, walk.Id, AppMode.DogOwner);
else
await pushNotificationService.SendWalkCancelledAsync(walk.DogWalkerId, walk.Id, AppMode.DogWalker);
}
}
return walks.Count();
}
/// <summary>
/// Fügt die Verwendung eines Gutscheins zu einem Walk hinzu
/// </summary>
/// <param name="walkId">Id des Walks</param>
/// <param name="voucherId">Id des Gutscheins</param>
/// <param name="ammount">Verwendeter Gutscheinwert</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> AddVoucherToWalk(string walkId, string voucherId, decimal ammount)
{
var walk = await Repository.FirstOrDefaultAsync(c => c.Id == walkId && c.Deleted == false).ConfigureAwait(false);
if (walk != null)
{
walk.VoucherId = voucherId;
walk.VoucherAmmount = ammount;
return true;
}
return false;
}
/// <summary>
/// Entfernt die Verwendung eines Gutscheins zu einem Walk
/// </summary>
/// <param name="walkId">Id des Walks</param>
/// <param name="voucherId">Id des Gutscheins</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> RemoveVoucherFromWalk(string walkId, string voucherId)
{
var walk = await Repository.FirstOrDefaultAsync(c => c.Id == walkId && c.Deleted == false).ConfigureAwait(false);
if (walk != null && walk.VoucherId == voucherId)
{
walk.VoucherId = null;
walk.VoucherAmmount = 0;
return true;
}
return false;
}
/// <summary>
/// Gibt die Anzahl von Walks für einen Dogwalker mit dem Status Bestätigt in einem bestimmten Zeitraum zurück
/// </summary>
/// <param name="dogWalkerId">Id des DogWalkers</param>
/// <param name="startDate">StartDatum</param>
/// <param name="endDate">Enddatum</param>
/// <returns>Anzahl Walks</returns>
public async Task<int> CountConfirmedByTimeRangeAsync(string dogWalkerId, DateTimeOffset startDate, DateTimeOffset endDate)
{
var count = await Repository.CountAsync(c => c.DogWalkerId == dogWalkerId && c.Confirmed >= startDate && c.Confirmed <= endDate && c.Status == WalkStatus.Confirmed && c.Deleted == false).ConfigureAwait(false);
return count;
}
}
}

View File

@ -0,0 +1,70 @@
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Walks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Dogs;
using Microsoft.EntityFrameworkCore;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Walking-Schedules ermöglicht.
/// Das bedeutet die einstellung an welchen Wochentagen ein Walker zu welchen Zeiten zur Verfügung steht
/// </summary>
public class WalkingTimeService : ServiceBase<WalkingTime>, IWalkingTimeService
{
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public WalkingTimeService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
/// <summary>
/// Erstellt eine Walkingtime Einstellung
/// </summary>
/// <param name="dogWalkerId">Id des Dogwalkers</param>
/// <returns>WalkingTime</returns>
public WalkingTime Create(string dogWalkerId)
{
var walk = new WalkingTime()
{
Id = Guid.NewGuid().ToString("N"),
DogWalkerId = dogWalkerId
};
return walk;
}
/// <summary>
/// Gibt eine Liste der Walkintimes eines Benutzers für das Syncing mit der App zurück.
/// </summary>
/// <param name="dogWalkerId">Id des DogWalkers</param>
/// <param name="lastUpdate">Letztes Update</param>
/// <returns>Liste der öffentlichen Anfragen</returns>
public async Task<List<WalkingTime>> GetForSyncAppAsync(string dogWalkerId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.DogWalkerId == dogWalkerId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
/// <summary>
/// Gibt eine Liste der Walkintimes eines Benutzers zurück.
/// </summary>
/// <param name="dogWalkerId">Id des DogWalkers</param>
/// <returns>Liste der öffentlichen Anfragen</returns>
public async Task<List<WalkingTime>> GetAllAsync(string dogWalkerId)
{
var query = Repository.Query(c => c.DogWalkerId == dogWalkerId && c.Deleted == false);
return await query.OrderBy(c => c.Day).ThenBy(c => c.Start).ToListAsync().ConfigureAwait(false);
}
}
}

Some files were not shown because too many files have changed in this diff Show More