Compare commits

...

5 Commits

Author SHA1 Message Date
fafbc55732 Change authorization policy for EditCustomer method
Updated the authorization policy for the `EditCustomer` method in the `AdvertisementController` to allow access only to users with the "Customer" role, replacing the previous "Power User" role requirement.
2025-08-11 18:01:46 +02:00
555273aed1 Make PaymentType nullable and handle null cases
Updated the PaymentType property in PublicWalkRequestAcceptDto and WalkCreateDto to be nullable.
Modified ApiWalksController to safely assign PaymentType, using a default value when null to prevent runtime errors.
2025-08-06 16:46:32 +02:00
b481a3999a Refactor WalkService and enhance API account features
Significant updates to WalkService.cs for improved fee handling and voucher processing. Introduced API version 3 in ApiAccountController.cs with new user registration and external login endpoints, supporting Google and Apple. Added functionality for setting user types without creating MangoPay users.
2025-06-30 17:17:09 +02:00
38142f2435 Add payment type handling for walks
- Updated `Walk` class to include `PaymentType` property.
- Introduced `WalkPaymentTypeDto` enum for payment types.
- Modified DTOs (`PublicWalkRequestAcceptDto`, `WalkCreateDto`, `WalkDto`, `WalkWithNamesDto`) to include `PaymentType`.
- Enhanced `ApiWalksController` with new endpoints for accepting and canceling walks, and confirming walks with ratings.
- Updated `MappingProfile` for seamless conversion between `WalkPaymentType` and `WalkPaymentTypeDto`.
2025-06-30 15:08:57 +02:00
ada8b7a200 Add payment types for walks and update related models
This commit introduces new payment types, including "MangoPay" and "Cash", to the localization resource files for both German and English. A new enum `WalkPaymentType` is created to represent these payment types, and the `Walk` class is updated to include a required `PaymentType` property of this enum type.

The database context is modified to add a `PaymentType` column to the `Walks` table, accompanied by a migration to implement this change and set a default value. The `WalkController` is updated to support filtering by the new `PaymentType` field, and the mapping profile is enhanced to include mappings for `WalkPaymentType` and its view model counterpart.

View models are also updated to incorporate the new `PaymentType` and its display name. Front-end TypeScript files are adjusted to utilize the new `WalkPaymentType` enum and related properties, while the UI is enhanced to display the payment type in the details view of walks. Version numbers in various configuration files are incremented to reflect these changes.
2025-06-30 11:06:30 +02:00
28 changed files with 11075 additions and 68 deletions

View File

@ -3933,4 +3933,13 @@
<data name="Subscription_MaxRegisteredDate" xml:space="preserve"> <data name="Subscription_MaxRegisteredDate" xml:space="preserve">
<value>Max. Registrierungsdatum</value> <value>Max. Registrierungsdatum</value>
</data> </data>
<data name="WalkPaymentType_MangoPay" xml:space="preserve">
<value>Mangopay</value>
</data>
<data name="WalkPaymentType_Cash" xml:space="preserve">
<value>Barzahlung</value>
</data>
<data name="Walk_PaymentType" xml:space="preserve">
<value>Zahlungsart</value>
</data>
</root> </root>

View File

@ -3935,4 +3935,13 @@
<data name="Subscription_MaxRegisteredDate" xml:space="preserve"> <data name="Subscription_MaxRegisteredDate" xml:space="preserve">
<value>Max. registered date</value> <value>Max. registered date</value>
</data> </data>
<data name="WalkPaymentType_MangoPay" xml:space="preserve">
<value>Mangopay</value>
</data>
<data name="WalkPaymentType_Cash" xml:space="preserve">
<value>Cash</value>
</data>
<data name="Walk_PaymentType" xml:space="preserve">
<value>Payment tye</value>
</data>
</root> </root>

View File

@ -3934,4 +3934,13 @@
<data name="Subscription_MaxRegisteredDate" xml:space="preserve"> <data name="Subscription_MaxRegisteredDate" xml:space="preserve">
<value /> <value />
</data> </data>
<data name="WalkPaymentType_MangoPay" xml:space="preserve">
<value />
</data>
<data name="WalkPaymentType_Cash" xml:space="preserve">
<value />
</data>
<data name="Walk_PaymentType" xml:space="preserve">
<value />
</data>
</root> </root>

View File

@ -50,6 +50,7 @@ namespace gehGassi.Core.Services
var walk = new Walk() var walk = new Walk()
{ {
Id = Guid.NewGuid().ToString("N"), Id = Guid.NewGuid().ToString("N"),
PaymentType = WalkPaymentType.MangoPay,
Created = DateTimeOffset.UtcNow Created = DateTimeOffset.UtcNow
}; };
return walk; return walk;
@ -591,6 +592,8 @@ namespace gehGassi.Core.Services
walk.PaymentStatus = PaymentStatus.Paid; walk.PaymentStatus = PaymentStatus.Paid;
await CommitAsync("System"); await CommitAsync("System");
if (walk.PaymentType == WalkPaymentType.MangoPay)
{
decimal feeDecimal = 0; decimal feeDecimal = 0;
long feeFromUser = 0; long feeFromUser = 0;
long feeFromVoucher = 0; long feeFromVoucher = 0;
@ -669,6 +672,7 @@ namespace gehGassi.Core.Services
} }
} }
} }
}
return walks.Count(); return walks.Count();
} }

View File

@ -303,6 +303,21 @@ namespace gehGassi.Domain.Common
PayPal = 30 PayPal = 30
} }
/// <summary>
/// Zahlungsart für einen Walk
/// </summary>
public enum WalkPaymentType
{
/// <summary>
/// Mit MangoPay bezahlen (z.B. Kreditkarte)
/// </summary>
MangoPay = 1,
/// <summary>
/// Barzahlung
/// </summary>
Cash = 10
}
/// <summary> /// <summary>
/// Zahlungsstatus /// Zahlungsstatus
/// </summary> /// </summary>

View File

@ -178,6 +178,12 @@ namespace gehGassi.Domain.Walks
[Required] [Required]
public PaymentStatus PaymentStatus { get; set; } public PaymentStatus PaymentStatus { get; set; }
/// <summary>
/// Welche Art von Bezahlung wurde verwendet
/// </summary>
[Required]
public WalkPaymentType PaymentType { get; set; }
/// <summary> /// <summary>
/// Id des Gutscheins der verwendet wurde, wenn einer verwendet wurde /// Id des Gutscheins der verwendet wurde, wenn einer verwendet wurde
/// </summary> /// </summary>
@ -473,6 +479,11 @@ namespace gehGassi.Domain.Walks
/// </summary> /// </summary>
public PaymentStatus PaymentStatus { get; set; } public PaymentStatus PaymentStatus { get; set; }
/// <summary>
/// Welche Art von Bezahlung wurde verwendet
/// </summary>
public WalkPaymentType PaymentType { get; set; }
/// <summary> /// <summary>
/// Id des Gutscheins der verwendet wurde, wenn einer verwendet wurde /// Id des Gutscheins der verwendet wurde, wenn einer verwendet wurde
/// </summary> /// </summary>

View File

@ -584,6 +584,21 @@ namespace gehGassi.Dto.Common
RefundendAndPayed = 60 RefundendAndPayed = 60
} }
/// <summary>
/// Zahlungsart für einen Walk
/// </summary>
public enum WalkPaymentTypeDto
{
/// <summary>
/// Mit MangoPay bezahlen (z.B. Kreditkarte)
/// </summary>
MangoPay = 1,
/// <summary>
/// Barzahlung
/// </summary>
Cash = 10
}
/// <summary> /// <summary>
/// Sortierreihenfolge /// Sortierreihenfolge
/// </summary> /// </summary>

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Text; using System.Text;
using gehGassi.Dto.Common;
namespace gehGassi.Dto.Walks namespace gehGassi.Dto.Walks
{ {
@ -36,5 +37,9 @@ namespace gehGassi.Dto.Walks
/// </summary> /// </summary>
public DateTimeOffset UpdatedAt { get; set; } public DateTimeOffset UpdatedAt { get; set; }
/// <summary>
/// Zahlungsart für den Walk
/// </summary>
public WalkPaymentTypeDto? PaymentType { get; set; }
} }
} }

View File

@ -101,5 +101,10 @@ namespace gehGassi.Dto.Walks
/// </summary> /// </summary>
[MaxLength(500)] [MaxLength(500)]
public string Info { get; set; } public string Info { get; set; }
/// <summary>
/// Zahlungsart für den Walk
/// </summary>
public WalkPaymentTypeDto? PaymentType { get; set; }
} }
} }

View File

@ -179,6 +179,11 @@ namespace gehGassi.Dto.Walks
[Required] [Required]
public PaymentStatusDto PaymentStatus { get; set; } public PaymentStatusDto PaymentStatus { get; set; }
/// <summary>
/// Zahlungsart für den Walk
/// </summary>
public WalkPaymentTypeDto PaymentType { get; set; }
/// <summary> /// <summary>
/// Datum der Erstellung des Walks /// Datum der Erstellung des Walks
/// </summary> /// </summary>

View File

@ -204,6 +204,11 @@ namespace gehGassi.Dto.Walks
/// </summary> /// </summary>
public PaymentStatusDto PaymentStatus { get; set; } public PaymentStatusDto PaymentStatus { get; set; }
/// <summary>
/// Zahlungsart für den Walk
/// </summary>
public WalkPaymentTypeDto PaymentType { get; set; }
/// <summary> /// <summary>
/// Id des Gutscheins der verwendet wurde, wenn einer verwendet wurde /// Id des Gutscheins der verwendet wurde, wenn einer verwendet wurde
/// </summary> /// </summary>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
#nullable disable
namespace gehGassi.Persistence.Migrations
{
/// <inheritdoc />
public partial class Walk_PaymentType_Added : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "PaymentType",
table: "Walks",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.Sql("Update Walks SET PaymentType = 1");
var viewName = "tv_WalksWithNames";
var sql = @"CREATE FUNCTION tv_WalksWithNames() RETURNS TABLE AS RETURN
SELECT W.*,
CONCAT(DW.FirstName, ' ' , DW.LastName) AS DogWalkerName, DW.Photo AS DogWalkerPhoto,
IsNull(DW.Locked,0) AS DogWalkerLocked, CAST((SELECT CASE WHEN AUBDW.Id IS NOT NULL THEN 1 ELSE 0 END) AS BIT) AS DogWalkerBlocked,
CONCAT(DO.FirstName, ' ' , DO.LastName) AS DogOwnerName, DO.Photo AS DogOwnerPhoto,
IsNull(DO.Locked,0) AS DogOwnerLocked, CAST((SELECT CASE WHEN AUBDO.Id IS NOT NULL THEN 1 ELSE 0 END) AS BIT) AS DogOwnerBlocked
FROM Walks W
LEFT JOIN AppUsers DW ON W.DogWalkerId = DW.Id
LEFT JOIN AppUsers DO ON W.DogOwnerId = DO.Id
LEFT OUTER JOIN AppUserBlocks AUBDW ON AUBDW.BlockedAppUserId = DW.Id AND AUBDW.BlockingAppUserId = DO.Id
LEFT OUTER JOIN AppUserBlocks AUBDO ON AUBDO.BlockedAppUserId = DO.Id AND AUBDO.BlockingAppUserId = DW.Id";
migrationBuilder.Sql($"IF(exists (SELECT 1 FROM sys.objects WHERE Name = '{viewName}')) BEGIN DROP FUNCTION [dbo].[{viewName}] END {Environment.NewLine} GO");
migrationBuilder.Sql($"{sql} {Environment.NewLine} GO");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PaymentType",
table: "Walks");
var viewName = "tv_WalksWithNames";
var sql = @"CREATE FUNCTION tv_WalksWithNames() RETURNS TABLE AS RETURN
SELECT W.*,
CONCAT(DW.FirstName, ' ' , DW.LastName) AS DogWalkerName, DW.Photo AS DogWalkerPhoto,
IsNull(DW.Locked,0) AS DogWalkerLocked, CAST((SELECT CASE WHEN AUBDW.Id IS NOT NULL THEN 1 ELSE 0 END) AS BIT) AS DogWalkerBlocked,
CONCAT(DO.FirstName, ' ' , DO.LastName) AS DogOwnerName, DO.Photo AS DogOwnerPhoto,
IsNull(DO.Locked,0) AS DogOwnerLocked, CAST((SELECT CASE WHEN AUBDO.Id IS NOT NULL THEN 1 ELSE 0 END) AS BIT) AS DogOwnerBlocked
FROM Walks W
LEFT JOIN AppUsers DW ON W.DogWalkerId = DW.Id
LEFT JOIN AppUsers DO ON W.DogOwnerId = DO.Id
LEFT OUTER JOIN AppUserBlocks AUBDW ON AUBDW.BlockedAppUserId = DW.Id AND AUBDW.BlockingAppUserId = DO.Id
LEFT OUTER JOIN AppUserBlocks AUBDO ON AUBDO.BlockedAppUserId = DO.Id AND AUBDO.BlockingAppUserId = DW.Id";
migrationBuilder.Sql($"IF(exists (SELECT 1 FROM sys.objects WHERE Name = '{viewName}')) BEGIN DROP FUNCTION [dbo].[{viewName}] END {Environment.NewLine} GO");
migrationBuilder.Sql($"{sql} {Environment.NewLine} GO");
}
}
}

View File

@ -7818,6 +7818,9 @@ namespace gehGassi.Persistence.Migrations
b.Property<int>("PaymentStatus") b.Property<int>("PaymentStatus")
.HasColumnType("int"); .HasColumnType("int");
b.Property<int>("PaymentType")
.HasColumnType("int");
b.Property<Point>("PickupLocation") b.Property<Point>("PickupLocation")
.HasColumnType("geography"); .HasColumnType("geography");
@ -8091,6 +8094,9 @@ namespace gehGassi.Persistence.Migrations
b.Property<int>("PaymentStatus") b.Property<int>("PaymentStatus")
.HasColumnType("int"); .HasColumnType("int");
b.Property<int>("PaymentType")
.HasColumnType("int");
b.Property<string>("PickupAddress_AddressLine1") b.Property<string>("PickupAddress_AddressLine1")
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");

View File

@ -776,7 +776,7 @@ namespace gehGassi.Web.Controllers
/// </summary> /// </summary>
/// <param name="model">Model</param> /// <param name="model">Model</param>
/// <returns>Json</returns> /// <returns>Json</returns>
[Authorize(Policy = Policies.PowerUserOnly)] [Authorize(Policy = Policies.CustomerOnly)]
[HasPermission(Permission.AdvertisementsManage, Permission.AdvertisementsEdit)] [HasPermission(Permission.AdvertisementsManage, Permission.AdvertisementsEdit)]
[CustomerAuthorize("CustomerId")] [CustomerAuthorize("CustomerId")]
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]

View File

@ -56,6 +56,7 @@ namespace gehGassi.Web.Controllers.Api
[ApiController] [ApiController]
[ApiVersion(1)] [ApiVersion(1)]
[ApiVersion(2)] [ApiVersion(2)]
[ApiVersion(3)]
[Route("api/account")] [Route("api/account")]
[Route("api/v{v:apiVersion}/account")] [Route("api/v{v:apiVersion}/account")]
public class ApiAccountController : ApiBaseController public class ApiAccountController : ApiBaseController
@ -465,6 +466,131 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid); return BadRequest(CommunicationErrors.Common_Model_Invalid);
} }
/// <summary>
/// Registrieren eines App-Users
/// </summary>
/// <param name="model">Model</param>
/// <returns>HTTP 200 OK wenn erfolgreich</returns>
[AllowAnonymous]
[HttpPost]
[MapToApiVersion(3)]
[Route("Register")]
public async Task<IActionResult> RegisterV3(RegisterV2Dto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
var user = await _userService.GetByUsernameAsync(model.UserName);
if (user == null)
{
user = _userService.Create();
user.Id = Guid.NewGuid().ToString();
user.UserName = model.UserName;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.FullName = $"{model.FirstName} {model.LastName}";
user.RegistrationDate = DateTimeOffset.UtcNow;
user.Photo = string.Empty;
user.Email = model.UserName;
user.EmailConfirmed = !_authOptions.Value.MustConfirmEmail;
user.Permissions = PermissionHelper.GetForRole("AppUser").PackPermissionsIntoString();
var createResult = await _userManager.CreateAsync(user, model.Password);
if (createResult.Succeeded)
{
await _userManager.AddToRoleAsync(user, "AppUser");
user = await _userService.GetAsync(user.Id);
//Nun den AppUser anlegen und dann mit dem Benutzer verknüpfen
var appUser = AppUserService.Create();
appUser.Type = (AppUserType)model.AppUserType;
appUser.Number = AppUserService.GetNextNumber();
appUser.FirstName = model.FirstName;
appUser.LastName = model.LastName;
appUser.Sex = Sex.Undefined;
appUser.BirthDate = model.BirthDate;
appUser.Photo = string.Empty;
appUser.Contact.Email = model.UserName;
appUser.Address.City = model.City;
appUser.Address.Zip = model.Zip;
appUser.Address.CountryCode = model.CountryCode;
appUser.Address.State = model.State;
appUser.TermsAccepted = true;
appUser.TermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.PrivacyAccepted = true;
appUser.PrivacyAcceptedDate = DateTimeOffset.UtcNow;
appUser.Verified = false;
appUser.VerifiedDate = null;
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = string.Empty;
appUser.MainResidenceCode = string.Empty;
if (appUser.Type == AppUserType.DogWalker)
{
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCountryCode;
}
var location = await _geoLocationService.GetLocationAsync($"{model.Zip},{model.City},{model.CountryCode},{model.State}", CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
if (location.Success)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(location.Longitude, location.Latitude));
appUser.Location = geoLocation;
}
AppUserService.Add(appUser);
await AppUserService.CommitAsync("System");
user.AppUserId = appUser.Id;
await _userService.CommitAsync("System");
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync("System");
//Nun für den Walker Mangopay User anlegen
//var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
//if (mangoPayResult.Success)
//{
// //Wallets anlegen
// var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
// var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
//}
}
if (_authOptions.Value.MustConfirmEmail)
{
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmailApp", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(user.UserName, callbackUrl, _localizer, _licenseOptions);
}
return Ok();
}
else
{
if (createResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (createResult.Errors.FirstOrDefault(c => c.Code.StartsWith("IdentityError_Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return BadRequest(CommunicationErrors.Register_Failed);
}
return BadRequest(CommunicationErrors.Register_Email_Exists);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary> /// <summary>
/// Registrieren eines App-Users mittels external Provider /// Registrieren eines App-Users mittels external Provider
/// </summary> /// </summary>
@ -958,6 +1084,254 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid); return BadRequest(CommunicationErrors.Common_Model_Invalid);
} }
/// <summary>
/// Registrieren eines App-Users mittels external Provider
/// </summary>
/// <param name="model">Model</param>
/// <returns>HTTP 200 OK wenn erfolgreich</returns>
[AllowAnonymous]
[HttpPost]
[MapToApiVersion(3)]
[Route("RegisterExternal")]
public async Task<IActionResult> RegisterExternalV3(RegisterExternalV2Dto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
//Zuerst holen wir nochmal die Infos für den User. Token prüfen... //TODO: Je nach Provider vorgehen. Derzeit nur google. Apple einführen...
var tokenValid = false;
ExternalLoginGoogleResponse userInfo = null;
AppleResponseDto appleResponse = null;
if (model.LoginProvider == "Google")
{
HttpStatusCode tokenResponseCode = HttpStatusCode.OK;
var httpClient = new HttpClient();
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", model.AccessToken);
var tokenResult = await httpClient.GetAsync($"https://www.googleapis.com/oauth2/v3/userinfo");
if (tokenResult.IsSuccessStatusCode)
{
string jsonString = tokenResult.Content.ReadAsStringAsync().Result;
userInfo = JsonSerializer.Deserialize<ExternalLoginGoogleResponse>(jsonString);
tokenValid = true;
}
else
{
tokenResponseCode = tokenResult.StatusCode;
}
}
catch (Exception ex)
{
tokenValid = false;
}
}
else if (model.LoginProvider == "Apple")
{
var clientSecret = await GenerateAppleClientSecretAsync();
var parameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", _appleOptions.Value.ClientId),
new KeyValuePair<string, string>("client_secret", clientSecret),
new KeyValuePair<string, string>("refresh_token", model.AccessToken),
new KeyValuePair<string, string>("grant_type", "refresh_token")
};
var httpClient = new HttpClient();
try
{
var tokenResult = await httpClient.PostAsync($"https://appleid.apple.com/auth/token", new FormUrlEncodedContent(parameters));
if (tokenResult.IsSuccessStatusCode)
{
var successResponse = await tokenResult.Content.ReadAsStringAsync();
appleResponse = JsonSerializer.Deserialize<AppleResponseDto>(successResponse);
tokenValid = true;
}
else
{
var errorResponse = await tokenResult.Content.ReadAsStringAsync();
_logger.LogError(errorResponse);
}
}
catch (Exception ex)
{
tokenValid = false;
}
}
if (tokenValid)
{
var user = await _userService.GetByUsernameAsync(model.UserName);
if (user == null)
{
user = _userService.Create();
user.Id = Guid.NewGuid().ToString();
user.UserName = model.UserName;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.FullName = $"{model.FirstName} {model.LastName}";
user.RegistrationDate = DateTimeOffset.UtcNow;
user.Photo = string.Empty;
user.Email = model.UserName;
user.EmailConfirmed = true; //Weil über externen Provider
user.Permissions = PermissionHelper.GetForRole("AppUser").PackPermissionsIntoString();
IdentityResult createResult;
if (!string.IsNullOrEmpty(model.Password))
createResult = await _userManager.CreateAsync(user, model.Password);
else
createResult = await _userManager.CreateAsync(user);
if (createResult.Succeeded)
{
await _userManager.AddToRoleAsync(user, "AppUser");
user = await _userManager.FindByEmailAsync(user.UserName);
//Nun den AppUser anlegen und dann mit dem Benutzer verknüpfen
var appUser = AppUserService.Create();
appUser.Type = (AppUserType)model.AppUserType;
appUser.Number = AppUserService.GetNextNumber();
appUser.FirstName = model.FirstName;
appUser.LastName = model.LastName;
appUser.Sex = Sex.Undefined;
appUser.BirthDate = model.BirthDate;
appUser.Photo = string.Empty;
appUser.Contact.Email = model.UserName;
appUser.Address.City = model.City;
appUser.Address.Zip = model.Zip;
appUser.Address.CountryCode = model.CountryCode;
appUser.Address.State = model.State;
appUser.TermsAccepted = true;
appUser.TermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.PrivacyAccepted = true;
appUser.PrivacyAcceptedDate = DateTimeOffset.UtcNow;
appUser.Verified = false;
appUser.VerifiedDate = null;
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = string.Empty;
appUser.MainResidenceCode = string.Empty;
if (appUser.Type == AppUserType.DogWalker)
{
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCountryCode;
}
var location = await _geoLocationService.GetLocationAsync($"{model.Zip},{model.City},{model.CountryCode},{model.State}", CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
if (location.Success)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(location.Longitude, location.Latitude));
appUser.Location = geoLocation;
}
AppUserService.Add(appUser);
await AppUserService.CommitAsync("System");
user.AppUserId = appUser.Id;
await _userManager.UpdateAsync(user);
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync("System");
//Nun für den Walker Mangopay User anlegen
//var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
//if (mangoPayResult.Success)
//{
// //Wallets anlegen
// var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
// var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
//}
}
//Da wir den User gleich einloggen möchten, geben wir alles nötige zurück
var usersClaims = new List<Claim>()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.UserData, user.Photo ?? ""),
new Claim(ClaimConstants.AppUserIdClaimType, user.AppUserId ?? ""),
};
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
usersClaims.Add(new Claim(ClaimTypes.Role, role));
}
var token = _tokenService.GenerateAccessToken(usersClaims);
await _refreshTokenService.RemoveExpiredAsync(user.Id, user.UserName);
var refreshToken = _refreshTokenService.Create();
refreshToken.UserId = user.Id;
_refreshTokenService.Add(refreshToken);
await _refreshTokenService.CommitAsync(user.UserName);
var userDto = Mapper.Map<UserDto>(user);
userDto.AccessToken = token;
userDto.AccessTokenExpires = DateTimeOffset.UtcNow.AddMinutes(_jwtTokenOptions.Value.TokenMinutes);
userDto.RefreshToken = refreshToken.Token;
userDto.RefreshTokenExpires = refreshToken.Expires;
userDto.AppUserType = (AppUserTypeDto)appUser.Type;
var roleList = roles.ToList();
userDto.Roles = string.Join(";", roleList);
user.LastLoginDate = DateTimeOffset.UtcNow;
await _userManager.UpdateAsync(user);
if (model.LoginProvider == "Google" && userInfo != null)
{
var createdUser = await _userManager.FindByEmailAsync(user.UserName);
if (createdUser != null)
{
await _userManager.AddLoginAsync(createdUser, new UserLoginInfo("Google", userInfo.Sub, user.FullName));
}
}
else if (model.LoginProvider == "Apple" && appleResponse != null)
{
var createdUser = await _userManager.FindByEmailAsync(user.UserName);
if (createdUser != null)
{
var appleToken = new JwtSecurityToken(appleResponse.IdToken);
if (appleToken != null)
{
var appleUserId = appleToken.Claims.First(c => c.Type == "sub").Value;
await _userManager.AddLoginAsync(createdUser, new UserLoginInfo("Apple", appleUserId, user.FullName));
}
}
}
return Ok(userDto);
}
else
{
if (createResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (createResult.Errors.FirstOrDefault(c => c.Code.StartsWith("Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return BadRequest(CommunicationErrors.Register_Failed);
}
return BadRequest(CommunicationErrors.Register_Email_Exists);
}
return BadRequest(CommunicationErrors.Register_TokenInvalid);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary> /// <summary>
/// Anmelden via API. Erstellt ein Access-Token und ein Refresh-Token wenn erfolgreich /// Anmelden via API. Erstellt ein Access-Token und ein Refresh-Token wenn erfolgreich
/// </summary> /// </summary>
@ -2041,6 +2415,62 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound); return NotFound(CommunicationErrors.Common_NotFound);
} }
/// <summary>
/// Setzt den Modus eines App-Users auf "Beides" - von Owner aufgerufen.
/// Erstellt keinen MangoPay User und legt keine Wallets an
/// </summary>
/// <returns>200 OK</returns>
[HttpPost]
[Route("SetAppUserTypeNoMango")]
public async Task<IActionResult> SetAppUserTypeNoMango(SetAppUserTypeExDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (ModelState.IsValid)
{
var appUser = await AppUserService.GetAsync(model.Id);
if (appUser != null)
{
if (model.UpdatedAt > appUser.UpdatedAt)
{
appUser.Type = (AppUserType)model.Type;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCode;
appUser.PaymentTermsAccepted = model.PaymentTermsAccepted;
appUser.PaymentTermsAcceptedDate = model.PaymentTermsAcceptedDate;
appUser.UpdatedAt = model.UpdatedAt;
await AppUserService.CommitAsync(User.Identity.Name);
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync(User.Identity.Name);
}
var appUserDto = Mapper.Map<AppUserDto>(appUser);
var baseAddress = GetBaseAddress();
if (!string.IsNullOrWhiteSpace(appUserDto.Photo))
{
appUserDto.Photo = $"{baseAddress}/file/documents/thumbnails/{200}/{appUserDto.Photo}";
}
return Ok(appUserDto);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary> /// <summary>
/// Hinzufügen eines PaymentUsers zu einem AppUser /// Hinzufügen eines PaymentUsers zu einem AppUser
/// </summary> /// </summary>

View File

@ -43,6 +43,7 @@ namespace gehGassi.Web.Controllers.Api
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController] [ApiController]
[ApiVersion(1)] [ApiVersion(1)]
[ApiVersion(2)]
[Route("api/walks")] [Route("api/walks")]
[Route("api/v{v:apiVersion}/walks")] [Route("api/v{v:apiVersion}/walks")]
public class ApiWalksController : ApiBaseController public class ApiWalksController : ApiBaseController
@ -954,6 +955,120 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound); return NotFound(CommunicationErrors.Common_NotFound);
} }
/// <summary>
/// Akzeptieren einer Antwort zu einer öffentlichen Anfrage
/// </summary>
/// <param name="model">PublicWalkRequestDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("AcceptPublicWalkRequest")]
[MapToApiVersion(2)]
public async Task<IActionResult> AcceptPublicWalkRequestV2(PublicWalkRequestAcceptDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var request = await _publicWalkRequestService.GetAsync(model.PublicWalkRequestId);
if (request != null && request.DogOwnerId == model.DogOwnerId)
{
var response = await _publicWalkResponseService.GetAsync(model.PublicWalkResponseId);
if (response != null && response.PublicWalkRequestId == request.Id)
{
response.Status = PublicWalkResponseStatus.Accepted;
response.UpdatedAt = model.UpdatedAt;
await _publicWalkResponseService.CommitAsync(User.Identity.Name);
//Nun Completed setzen für alle anderen die Warten...
await _publicWalkResponseService.SetCompletedForWaitingAsync(request.Id);
await _publicWalkResponseService.CommitAsync(User.Identity.Name);
request.Status = PublicWalkRequestStatus.Placed;
request.PlacedDate = model.UpdatedAt;
request.PublicWalkResponseId = response.Id;
request.DogWalkerId = response.DogWalkerId;
request.UpdatedAt = model.UpdatedAt;
await _publicWalkResponseService.CommitAsync(User.Identity.Name);
//Nachricht an alle Angebote senden...
var responses = await _publicWalkResponseService.FindAsync(c => c.PublicWalkRequestId == request.Id);
foreach (var publicWalkResponse in responses.Where(c => c.DogWalkerId != request.DogWalkerId))
{
_systemMessageService.Add(publicWalkResponse.DogWalkerId, AppUserType.DogWalker, request.Id, SystemMessageTables.PublicWalkRequest, SystemMessageType.PublicWalkRequestChanged, DateTimeOffset.UtcNow.AddDays(14));
await _pushNotificationService.SendPublicWalkRequestChangedAsync(publicWalkResponse.DogWalkerId, request.Id);
}
await _systemMessageService.CommitAsync(User.Identity.Name);
foreach (var publicWalkResponse in responses.Where(c => c.DogWalkerId != request.DogWalkerId))
{
await _appHubSender.SystemMessageAddedAsync(publicWalkResponse.DogWalkerId);
}
//Nun einen Walk anlgegen!
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(request.Location.X, request.Location.Y));
var walk = _walkService.Create();
walk.Type = WalkType.OpenRequest;
walk.ServiceType = request.RequestType == PublicWalkRequestType.Sitting ? WalkServiceType.Sitting : WalkServiceType.Walking;
walk.DogWalkerId = request.DogWalkerId;
walk.DogOwnerId = request.DogOwnerId;
walk.PublicWalkRequestId = request.Id;
walk.DogsJson = request.DogsJson;
walk.DogCount = request.DogCount;
walk.Start = request.StartDate;
walk.End = request.EndDate;
walk.PickupAddress = request.PickupAddress.Clone();
walk.PickupLocation = geoLocation;
walk.ReturnAddress = request.PickupAddress.Clone();
walk.ReturnLocation = geoLocation;
walk.Price = response.Price;
walk.Info = request.Info;
walk.Status = WalkStatus.Accepted;
walk.Started = null;
walk.Completed = null;
walk.Confirmed = null;
walk.Complained = null;
walk.ComplainReason = string.Empty;
walk.DeclinedReason = string.Empty;
walk.CancelledReason = string.Empty;
walk.CancelledBy = CancellationSource.None;
walk.CompletedInfo = string.Empty;
walk.Defactation = false;
walk.PaymentStatus = PaymentStatus.Pending;
walk.PaymentType = model.PaymentType != null ? (WalkPaymentType)model.PaymentType : WalkPaymentType.MangoPay;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.Created = DateTimeOffset.UtcNow;
if (walk.Price == 0 || walk.PaymentType == WalkPaymentType.Cash)
walk.PaymentStatus = PaymentStatus.Authorized;
_walkService.Add(walk);
await _walkService.CommitAsync(User.Identity.Name);
if (walk.PaymentStatus is PaymentStatus.Authorized or PaymentStatus.Paid)
{
//Nachricht an den dogWalker senden
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkAdded, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendNewWalkAsync(walk.DogWalkerId, walk.Id);
}
var walkDto = Mapper.Map<WalkDto>(walk);
return Ok(walkDto);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary> /// <summary>
/// Stornieren einer öffentlichen Anfrage /// Stornieren einer öffentlichen Anfrage
/// </summary> /// </summary>
@ -2224,6 +2339,128 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound); return NotFound(CommunicationErrors.Common_NotFound);
} }
/// <summary>
/// Stornieren eines Walks
/// </summary>
/// <param name="model">PublicWalkRequestCancelDto</param>
/// <returns>HTTP 200 OK, Fehler sonst</returns>
[HttpPost]
[Route("CancelWalk")]
[MapToApiVersion(2)]
public async Task<IActionResult> CancelWalkV2(WalkCancelDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var walk = await _walkService.GetAsync(model.WalkId);
if (walk != null)
{
if (!string.IsNullOrWhiteSpace(walk.VoucherId) && walk.VoucherAmmount > 0 && walk.PaymentType == WalkPaymentType.MangoPay)
{
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.UpdatedAt = model.UpdatedAt;
walk.CancelledBy = (CancellationSource)model.CancellationSource;
walk.CancelledReason = model.CancelledReason;
//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 && walk.PaymentType == WalkPaymentType.MangoPay)
{
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 _walkService.CommitAsync(User.Identity.Name);
//Wallets aktualisieren
if (walk.PaymentType == WalkPaymentType.MangoPay)
{
await _mangoPayService.GetWalletBalanceAsync(walk.DogOwnerId, WalletType.Credits);
await _mangoPayService.GetWalletBalanceAsync(walk.DogOwnerId, WalletType.Fees);
}
if (walk.CancelledBy == CancellationSource.DogWalker)
{
_systemMessageService.Add(walk.DogOwnerId, AppUserType.DogOwner, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkCancelled, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogOwnerId);
await _pushNotificationService.SendWalkCancelledAsync(walk.DogOwnerId, walk.Id, AppMode.DogOwner);
}
else
{
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkCancelled, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendWalkCancelledAsync(walk.DogWalkerId, walk.Id, AppMode.DogWalker);
}
return Ok();
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary> /// <summary>
/// Starten eines Walks /// Starten eines Walks
/// </summary> /// </summary>
@ -2604,6 +2841,190 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound); return NotFound(CommunicationErrors.Common_NotFound);
} }
/// <summary>
/// Bestätigen eines Walks
/// </summary>
/// <param name="model">WalkConfirmDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("ConfirmWalk")]
[MapToApiVersion(2)]
public async Task<IActionResult> ConfirmWalkV2(WalkConfirmDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var walk = await _walkService.GetAsync(model.WalkId);
if (walk != null && walk.DogOwnerId == model.AppUserId)
{
var appUser = await AppUserService.GetAsync(walk.DogOwnerId);
if (appUser is { Locked: true })
{
return BadRequest(CommunicationErrors.Login_LockedOut);
}
var dogWalker = await AppUserService.GetAsync(walk.DogWalkerId);
if (dogWalker is { Locked: true })
{
return BadRequest(CommunicationErrors.Login_LockedOut);
}
if (walk.Status == WalkStatus.Completed && walk.Deleted == false)
{
walk.Status = WalkStatus.Confirmed;
walk.Confirmed = DateTimeOffset.UtcNow;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.PaymentStatus = PaymentStatus.Paid;
await _walkService.CommitAsync(User.Identity.Name);
//Wenn der Dogwalker in 30 Minuten mehr als 4 Walks bestätigt bekommt, dann Sperren wegen Betrugsverdacht
var numberOfConfirmedWalksTwentyMinutes = await _walkService.CountConfirmedByTimeRangeAsync(walk.DogWalkerId, DateTimeOffset.UtcNow.AddMinutes(-30), DateTimeOffset.UtcNow);
if (numberOfConfirmedWalksTwentyMinutes >= 4)
{
if (dogWalker != null)
{
dogWalker.Locked = true;
dogWalker.LockedReason = "Suspicious behavior - verächtiges Verhalten";
dogWalker.LockedUntil = DateTimeOffset.UtcNow.AddYears(5);
await AppUserService.CommitAsync("System");
//Echten Benutzer sperren
var user = await _userService.GetByAppUserAsync(dogWalker.Id);
if (user != null)
{
user.LockoutEnd = DateTimeOffset.UtcNow.AddYears(5);
await _userService.CommitAsync("System");
await _ticketStore.RemoveAsync(user.UserName);
await _refreshTokenService.RemoveAllAsync(user.Id, User.Identity.Name);
await _refreshTokenService.CommitAsync("System");
}
}
}
if (walk.PaymentType == WalkPaymentType.MangoPay)
{
//Zahlung veranlassen....
if (walk.Price > 0)
{
decimal feeDecimal = 0;
long fee = 0;
var gehGassiFee = await _transactionFeeService.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)
{
fee = (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 _voucherCampaignService.CommitAsync(User.Identity.Name);
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, fee, $"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);
}
}
}
}
//Coins für den Walker gutschreiben
var coins = walk.ServiceType switch
{
WalkServiceType.Walking => _coinsOptions.Value.WalkForDogwalker,
WalkServiceType.Sitting => _coinsOptions.Value.SittingForDowgWalker,
WalkServiceType.DayCare => _coinsOptions.Value.DayCareForDowgWalker,
_ => 0m
};
await _coinsService.DepositAsync(walk.DogWalkerId, AppUserType.DogWalker, coins, walk.Id);
//Coins für den Besitzer gutschreiben
var coinsOwner = walk.ServiceType switch
{
WalkServiceType.Walking => _coinsOptions.Value.WalkForOwner,
WalkServiceType.Sitting => _coinsOptions.Value.SittingForOwner,
WalkServiceType.DayCare => _coinsOptions.Value.DayCareForOwner,
_ => 0m
};
await _coinsService.DepositAsync(walk.DogOwnerId, AppUserType.DogOwner, coinsOwner, walk.Id);
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkConfirmed, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendWalkConfirmedAsync(walk.DogWalkerId, walk.Id);
var walkDto = Mapper.Map<WalkDto>(walk);
return Ok(walkDto);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary> /// <summary>
/// Bestätigen eines Walks mit Rating /// Bestätigen eines Walks mit Rating
/// </summary> /// </summary>
@ -2798,6 +3219,205 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound); return NotFound(CommunicationErrors.Common_NotFound);
} }
/// <summary>
/// Bestätigen eines Walks mit Rating
/// </summary>
/// <param name="model">WalkConfirmDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("ConfirmWalkWithRating")]
[MapToApiVersion(2)]
public async Task<IActionResult> ConfirmWalkWithRatingV2(WalkConfirmWithRatingDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var walk = await _walkService.GetAsync(model.WalkId);
if (walk != null && walk.DogOwnerId == model.AppUserId)
{
var appUser = await AppUserService.GetAsync(walk.DogOwnerId);
if (appUser is { Locked: true })
{
return BadRequest(CommunicationErrors.Login_LockedOut);
}
var dogWalker = await AppUserService.GetAsync(walk.DogWalkerId);
if (dogWalker is { Locked: true })
{
return BadRequest(CommunicationErrors.Login_LockedOut);
}
if (walk.Status == WalkStatus.Completed && walk.Deleted == false)
{
walk.Status = WalkStatus.Confirmed;
walk.Confirmed = DateTimeOffset.UtcNow;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.PaymentStatus = PaymentStatus.Paid;
await _walkService.CommitAsync(User.Identity.Name);
//Wenn der Dogwalker in 30 Minuten mehr als 4 Walks bestätigt bekommt, dann Sperren wegen Betrugsverdacht
var numberOfConfirmedWalksTwentyMinutes = await _walkService.CountConfirmedByTimeRangeAsync(walk.DogWalkerId, DateTimeOffset.UtcNow.AddMinutes(-30), DateTimeOffset.UtcNow);
if (numberOfConfirmedWalksTwentyMinutes >= 4)
{
if (dogWalker != null)
{
dogWalker.Locked = true;
dogWalker.LockedReason = "Suspicious behavior - verächtiges Verhalten";
dogWalker.LockedUntil = DateTimeOffset.UtcNow.AddYears(5);
await AppUserService.CommitAsync("System");
//Echten Benutzer sperren
var user = await _userService.GetByAppUserAsync(dogWalker.Id);
if (user != null)
{
user.LockoutEnd = DateTimeOffset.UtcNow.AddYears(5);
await _userService.CommitAsync("System");
await _ticketStore.RemoveAsync(user.UserName);
await _refreshTokenService.RemoveAllAsync(user.Id, User.Identity.Name);
await _refreshTokenService.CommitAsync("System");
}
}
}
if (walk.PaymentType == WalkPaymentType.MangoPay)
{
//Zahlung veranlassen....
if (walk.Price > 0)
{
decimal feeDecimal = 0;
long fee = 0;
var gehGassiFee = await _transactionFeeService.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)
{
fee = (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 _voucherCampaignService.CommitAsync(User.Identity.Name);
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, fee, $"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);
}
}
}
}
//Coins für den Walker gutschreiben
var coins = walk.ServiceType switch
{
WalkServiceType.Walking => _coinsOptions.Value.WalkForDogwalker,
WalkServiceType.Sitting => _coinsOptions.Value.SittingForDowgWalker,
WalkServiceType.DayCare => _coinsOptions.Value.DayCareForDowgWalker,
_ => 0m
};
await _coinsService.DepositAsync(walk.DogWalkerId, AppUserType.DogWalker, coins, walk.Id);
//Coins für den Besitzer gutschreiben
var coinsOwner = walk.ServiceType switch
{
WalkServiceType.Walking => _coinsOptions.Value.WalkForOwner,
WalkServiceType.Sitting => _coinsOptions.Value.SittingForOwner,
WalkServiceType.DayCare => _coinsOptions.Value.DayCareForOwner,
_ => 0m
};
await _coinsService.DepositAsync(walk.DogOwnerId, AppUserType.DogOwner, coinsOwner, walk.Id);
//Rating erstellen
//var appUser = await AppUserService.GetAsync(walk.DogOwnerId);
if (appUser != null)
{
var rating = _ratingService.Create(walk.DogOwnerId, appUser.Type, walk.DogWalkerId, RatingTarget.Walker, model.RatingPoints, model.RatingInfo);
_ratingService.Add(rating);
await _ratingService.CommitAsync(User.Identity.Name);
//Statistik Hundebesitzer aktualisieren
await _ratingService.UpdateRatingStatisticsAsync(walk.DogWalkerId, RatingTarget.Walker);
await _ratingService.CommitAsync(User.Identity.Name);
}
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkConfirmed, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendWalkConfirmedAsync(walk.DogWalkerId, walk.Id);
var walkDto = Mapper.Map<WalkDto>(walk);
return Ok(walkDto);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary> /// <summary>
/// Zahlungsstatus eines Walks setzen /// Zahlungsstatus eines Walks setzen
/// </summary> /// </summary>
@ -2947,6 +3567,102 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid); return BadRequest(CommunicationErrors.Common_Model_Invalid);
} }
/// <summary>
/// Anlegen eines Walks wenn DIREKT! buchen möglich ist
/// </summary>
/// <param name="model">WalkCreateDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("CreateWalkDirect")]
[MapToApiVersion(2)]
public async Task<IActionResult> CreateWalkDirectV2(WalkCreateDto model)
{
var clientOffset = GetClientDateOffset();
var result = new CreateResponseDto<WalkDto>
{
Status = CreateStatusDto.Error,
Value = null
};
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var appUserId = User.AppUserId();
var isLocked = await AppUserService.IsLockedAsync(appUserId);
var isBlocked = await AppUserService.IsBlockedAsync(model.DogWalkerId, appUserId);
var walkPossible = await _walkService.IsWalkPossibleAsync(model.DogWalkerId, model.Start, model.End);
if (walkPossible && !isLocked && !isBlocked)
{
//Nun einen Walk anlgegen!
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocationPickup = geometryFactory.CreatePoint(new Coordinate(model.PickupAddressLng, model.PickupAddressLat));
var geoLocationReturn = geometryFactory.CreatePoint(new Coordinate(model.ReturnAddressLng, model.ReturnAddressLat));
var walk = _walkService.Create();
walk.Type = (WalkType)model.Type;
walk.ServiceType = (WalkServiceType)model.ServiceType;
walk.DogWalkerId = model.DogWalkerId;
walk.DogOwnerId = model.DogOwnerId;
walk.PublicWalkRequestId = string.Empty;
walk.DogsJson = model.DogsJson;
walk.DogCount = model.DogCount;
walk.Start = model.Start;
walk.End = model.End;
walk.PickupAddress = Mapper.Map<Address>(model.PickupAddress);
walk.PickupLocation = geoLocationPickup;
walk.ReturnAddress = Mapper.Map<Address>(model.ReturnAddress);
walk.ReturnLocation = geoLocationReturn;
walk.Price = model.Price;
walk.Info = model.Info;
walk.Status = WalkStatus.Accepted;
walk.Started = null;
walk.Completed = null;
walk.Confirmed = null;
walk.Complained = null;
walk.ComplainReason = string.Empty;
walk.DeclinedReason = string.Empty;
walk.CancelledReason = string.Empty;
walk.CancelledBy = CancellationSource.None;
walk.CompletedInfo = string.Empty;
walk.Defactation = false;
walk.PaymentStatus = PaymentStatus.Pending;
walk.PaymentType = model.PaymentType != null ? (WalkPaymentType)model.PaymentType : WalkPaymentType.MangoPay;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.Created = DateTimeOffset.UtcNow;
if (walk.Price == 0 || walk.PaymentType == WalkPaymentType.Cash)
walk.PaymentStatus = PaymentStatus.Authorized;
_walkService.Add(walk);
await _walkService.CommitAsync(User.Identity.Name);
//Überlappende Walks die im Status "Request" sind, stornieren
if (walk.PaymentStatus is PaymentStatus.Authorized or PaymentStatus.Paid)
{
//Wenn der Preis eines Walks 0 ist, dann wird dieser direkt angelegt und gilt.
await _walkService.CancelWalkRequestsAsync(walk.DogWalkerId, walk.Start, walk.End);
await _walkService.CommitAsync(User.Identity.Name);
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkAdded, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendNewWalkAsync(walk.DogWalkerId, walk.Id);
}
result.Status = CreateStatusDto.Success;
result.Value = Mapper.Map<WalkDto>(walk);
return Ok(result);
}
return NotFound(CommunicationErrors.Walk_NotPossible);
}
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary> /// <summary>
/// Anlegen eines Walks als Anfrage /// Anlegen eines Walks als Anfrage
/// </summary> /// </summary>
@ -3031,6 +3747,92 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid); return BadRequest(CommunicationErrors.Common_Model_Invalid);
} }
/// <summary>
/// Anlegen eines Walks als Anfrage
/// </summary>
/// <param name="model">WalkCreateDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("CreateWalkRequest")]
[MapToApiVersion(2)]
public async Task<IActionResult> CreateWalkRequestV2(WalkCreateDto model)
{
var clientOffset = GetClientDateOffset();
var result = new CreateResponseDto<WalkDto>
{
Status = CreateStatusDto.Error,
Value = null
};
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var appUserId = User.AppUserId();
var isLocked = await AppUserService.IsLockedAsync(appUserId);
var isBlocked = await AppUserService.IsBlockedAsync(model.DogWalkerId, appUserId);
var walkPossible = await _walkService.IsWalkPossibleAsync(model.DogWalkerId, model.Start, model.End);
if (walkPossible && !isLocked && !isBlocked)
{
//Nun einen Walk anlgegen!
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocationPickup = geometryFactory.CreatePoint(new Coordinate(model.PickupAddressLng, model.PickupAddressLat));
var geoLocationReturn = geometryFactory.CreatePoint(new Coordinate(model.ReturnAddressLng, model.ReturnAddressLat));
var walk = _walkService.Create();
walk.Type = (WalkType)model.Type;
walk.ServiceType = (WalkServiceType)model.ServiceType;
walk.DogWalkerId = model.DogWalkerId;
walk.DogOwnerId = model.DogOwnerId;
walk.PublicWalkRequestId = string.Empty;
walk.DogsJson = model.DogsJson;
walk.DogCount = model.DogCount;
walk.Start = model.Start;
walk.End = model.End;
walk.PickupAddress = Mapper.Map<Address>(model.PickupAddress);
walk.PickupLocation = geoLocationPickup;
walk.ReturnAddress = Mapper.Map<Address>(model.ReturnAddress);
walk.ReturnLocation = geoLocationReturn;
walk.Price = model.Price;
walk.Info = model.Info;
walk.Status = WalkStatus.Requested;
walk.Started = null;
walk.Completed = null;
walk.Confirmed = null;
walk.Complained = null;
walk.ComplainReason = string.Empty;
walk.DeclinedReason = string.Empty;
walk.CancelledReason = string.Empty;
walk.CancelledBy = CancellationSource.None;
walk.CompletedInfo = string.Empty;
walk.Defactation = false;
walk.PaymentStatus = PaymentStatus.None;
walk.PaymentType = model.PaymentType != null ? (WalkPaymentType)model.PaymentType : WalkPaymentType.MangoPay;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.Created = DateTimeOffset.UtcNow;
_walkService.Add(walk);
await _walkService.CommitAsync(User.Identity.Name);
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkRequested, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendWalkRequestedAsync(walk.DogWalkerId, walk.Id);
result.Status = CreateStatusDto.Success;
result.Value = Mapper.Map<WalkDto>(walk);
return Ok(result);
}
return NotFound(CommunicationErrors.Walk_NotPossible);
}
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary> /// <summary>
/// Akzeptieren eines Walks /// Akzeptieren eines Walks
/// </summary> /// </summary>
@ -3071,6 +3873,47 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound); return NotFound(CommunicationErrors.Common_NotFound);
} }
/// <summary>
/// Akzeptieren eines Walks
/// </summary>
/// <param name="model">WalkAcceptDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("AcceptWalk")]
[MapToApiVersion(2)]
public async Task<IActionResult> AcceptWalkV2(WalkAcceptDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var walk = await _walkService.GetAsync(model.WalkId);
if (walk != null)
{
walk.Status = WalkStatus.Accepted;
walk.PaymentStatus = PaymentStatus.Pending;
walk.UpdatedAt = model.UpdatedAt;
if (walk.Price == 0 || walk.PaymentType == WalkPaymentType.Cash)
walk.PaymentStatus = PaymentStatus.Authorized;
await _walkService.CommitAsync(User.Identity.Name);
_systemMessageService.Add(walk.DogOwnerId, AppUserType.DogOwner, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkAccepted, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogOwnerId);
await _pushNotificationService.SendWalkAcceptedAsync(walk.DogOwnerId, walk.Id);
return Ok();
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary> /// <summary>
/// Ablehnen eines Walks /// Ablehnen eines Walks
/// </summary> /// </summary>

View File

@ -98,6 +98,8 @@ namespace gehGassi.Web.Controllers
whereFilterPredicate.value = (WalkStatus)((int)((long)whereFilterPredicate.value)); whereFilterPredicate.value = (WalkStatus)((int)((long)whereFilterPredicate.value));
if (whereFilterPredicate.Field == "paymentStatus") if (whereFilterPredicate.Field == "paymentStatus")
whereFilterPredicate.value = (PaymentStatus)((int)((long)whereFilterPredicate.value)); whereFilterPredicate.value = (PaymentStatus)((int)((long)whereFilterPredicate.value));
if (whereFilterPredicate.Field == "paymentType")
whereFilterPredicate.value = (WalkPaymentType)((int)((long)whereFilterPredicate.value));
} }
} }
} }
@ -120,6 +122,7 @@ namespace gehGassi.Web.Controllers
itemVm.ServiceTypeText = itemVm.ServiceType.GetDisplayName(AnnotationsLocalizer); itemVm.ServiceTypeText = itemVm.ServiceType.GetDisplayName(AnnotationsLocalizer);
itemVm.StatusText = itemVm.Status.GetDisplayName(AnnotationsLocalizer); itemVm.StatusText = itemVm.Status.GetDisplayName(AnnotationsLocalizer);
itemVm.PaymentStatusText = itemVm.PaymentStatus.GetDisplayName(AnnotationsLocalizer); itemVm.PaymentStatusText = itemVm.PaymentStatus.GetDisplayName(AnnotationsLocalizer);
itemVm.PaymentTypeText = itemVm.PaymentType.GetDisplayName(AnnotationsLocalizer);
} }
//FilterPreview? //FilterPreview?

View File

@ -135,6 +135,9 @@ namespace gehGassi.Web.Mapper
CreateMap<PaymentStatus, PaymentStatusVm>(); CreateMap<PaymentStatus, PaymentStatusVm>();
CreateMap<PaymentStatusVm, PaymentStatus>(); CreateMap<PaymentStatusVm, PaymentStatus>();
CreateMap<WalkPaymentType, WalkPaymentTypeVm>();
CreateMap<WalkPaymentTypeVm, WalkPaymentType>();
CreateMap<OrderStatus, OrderStatusVm>(); CreateMap<OrderStatus, OrderStatusVm>();
CreateMap<OrderStatusVm, OrderStatus>(); CreateMap<OrderStatusVm, OrderStatus>();
@ -638,6 +641,9 @@ namespace gehGassi.Web.Mapper
CreateMap<PaymentStatus, PaymentStatusDto>(); CreateMap<PaymentStatus, PaymentStatusDto>();
CreateMap<PaymentStatusDto, PaymentStatus>(); CreateMap<PaymentStatusDto, PaymentStatus>();
CreateMap<WalkPaymentType, WalkPaymentTypeDto>();
CreateMap<WalkPaymentTypeDto, WalkPaymentType>();
CreateMap<RatingTarget, RatingTargetDto>(); CreateMap<RatingTarget, RatingTargetDto>();
CreateMap<RatingTargetDto, RatingTarget>(); CreateMap<RatingTargetDto, RatingTarget>();

View File

@ -378,6 +378,23 @@ namespace gehGassi.Web.Models
RefundendAndPayed = 60 RefundendAndPayed = 60
} }
/// <summary>
/// Zahlungsart für einen Walk
/// </summary>
public enum WalkPaymentTypeVm
{
/// <summary>
/// Mit MangoPay bezahlen (z.B. Kreditkarte)
/// </summary>
[Display(Name = "WalkPaymentType_MangoPay")]
MangoPay = 1,
/// <summary>
/// Barzahlung
/// </summary>
[Display(Name = "WalkPaymentType_Cash")]
Cash = 10
}
/// <summary> /// <summary>
/// Status einer Bestellung /// Status einer Bestellung
/// </summary> /// </summary>

View File

@ -220,6 +220,11 @@ namespace gehGassi.Web.Models
/// </summary> /// </summary>
public PaymentStatusVm PaymentStatus { get; set; } public PaymentStatusVm PaymentStatus { get; set; }
/// <summary>
/// Zahlungsart des Walks
/// </summary>
public WalkPaymentTypeVm PaymentType { get; set; }
#endregion #endregion
/// <summary> /// <summary>
@ -380,6 +385,16 @@ namespace gehGassi.Web.Models
/// </summary> /// </summary>
public string PaymentStatusText { get; set; } public string PaymentStatusText { get; set; }
/// <summary>
/// Welche Art von Bezahlung wurde verwendet
/// </summary>
public WalkPaymentTypeVm PaymentType { get; set; }
/// <summary>
/// Welche Art von Bezahlung wurde verwendet
/// </summary>
public string PaymentTypeText { get; set; }
#endregion #endregion
/// <summary> /// <summary>

View File

@ -229,7 +229,15 @@ export enum PaymentStatus {
/**Entwertet */ /**Entwertet */
Voided = 50, Voided = 50,
/**Rückerstattet gehgassi */ /**Rückerstattet gehgassi */
RefundendAndPayed = 60, RefundendAndPayed = 60
}
/**Zahlungsarten Walk */
export enum WalkPaymentType {
/**MangoPay */
MangoPay = 1,
/**Cash */
Cash = 10
} }
/**Geoeinschränkungen für Listungen und Werbungen */ /**Geoeinschränkungen für Listungen und Werbungen */
@ -2473,6 +2481,9 @@ export class WalkWithNamesListItem extends SyncEntity {
/**Zahlungsstatus des Walks */ /**Zahlungsstatus des Walks */
paymentStatus: PaymentStatus; paymentStatus: PaymentStatus;
/**Zahlungsart des Walks */
paymentType: WalkPaymentType;
/**Datum der Erstellung des Walks */ /**Datum der Erstellung des Walks */
created: Date | string; created: Date | string;
} }

View File

@ -15,6 +15,7 @@ import WalkType = Models.WalkType;
import WalkStatus = Models.WalkStatus; import WalkStatus = Models.WalkStatus;
import WalkServiceType = Models.WalkServiceType; import WalkServiceType = Models.WalkServiceType;
import PaymentStatus = Models.PaymentStatus; import PaymentStatus = Models.PaymentStatus;
import WalkPaymentType = Models.WalkPaymentType;
class WalksModule { class WalksModule {
@ -40,6 +41,9 @@ class WalksModule {
private _paymentStatusList; private _paymentStatusList;
private _filterPaymentStatus: DropDownList; private _filterPaymentStatus: DropDownList;
private _paymentTypeList;
private _filterPaymentType: DropDownList;
constructor() { constructor() {
this._global = Global.getInstance(); this._global = Global.getInstance();
this._eventManager = EventManager.getInstance(); this._eventManager = EventManager.getInstance();
@ -95,6 +99,10 @@ class WalksModule {
{ value: PaymentStatus.Voided, text: self._localizationMananger.get("PaymentStatus_Voided") }, { value: PaymentStatus.Voided, text: self._localizationMananger.get("PaymentStatus_Voided") },
{ value: PaymentStatus.RefundendAndPayed, text: self._localizationMananger.get("PaymentStatus_RefundendAndPayed") }]; { value: PaymentStatus.RefundendAndPayed, text: self._localizationMananger.get("PaymentStatus_RefundendAndPayed") }];
self._paymentTypeList = [
{ value: WalkPaymentType.MangoPay, text: self._localizationMananger.get("WalkPaymentType_MangoPay")},
{ value: WalkPaymentType.Cash, text: self._localizationMananger.get("WalkPaymentType_Cash") }];
self._dataManager = new DataManager({ self._dataManager = new DataManager({
url: <string>$(self.prefix("#jsGetUrl")).val(), url: <string>$(self.prefix("#jsGetUrl")).val(),
adaptor: new UrlAdaptor(), adaptor: new UrlAdaptor(),
@ -201,6 +209,32 @@ class WalksModule {
} }
} }
}, },
{
field: "paymentType", headerText: self._localizationMananger.get("Walk_PaymentType"), type: "number", template: "${paymentTypeText}",
filter: {
ui: {
create: (args: { target: Element, column: Object }) => {
let flValInput: HTMLElement = createElement("input", { className: "flm-input" });
args.target.appendChild(flValInput);
self._filterPaymentType = new DropDownList({
dataSource: new DataManager(self._paymentTypeList),
fields: { text: "text", value: "value" },
placeholder: self._localizationMananger.get("Common_Select"),
popupHeight: "200px"
});
self._filterPaymentType.appendTo(flValInput);
},
write: (args: {
column: Object, target: Element, parent: any, filteredValue: number | string
}) => {
self._filterPaymentType.value = args.filteredValue;
},
read: (args: { target: Element, column: any, operator: string, fltrObj: Filter }) => {
args.fltrObj.filterByColumn(args.column.field, args.operator, self._filterPaymentType.value);
}
}
}
},
{ {
field: "paymentStatus", headerText: self._localizationMananger.get("Walk_PaymentStatus"), type: "number", template: "${paymentStatusText}", field: "paymentStatus", headerText: self._localizationMananger.get("Walk_PaymentStatus"), type: "number", template: "${paymentStatusText}",
filter: { filter: {

View File

@ -65,6 +65,13 @@
<div class="h5 mb-1">@annotationsLocalizer["Walk_Status"]</div> <div class="h5 mb-1">@annotationsLocalizer["Walk_Status"]</div>
@Model.Status.GetDisplayName(annotationsLocalizer) @Model.Status.GetDisplayName(annotationsLocalizer)
</div> </div>
</div>
<div class="row mb-3">
<div class="col-sm-3">
<div class="h5 mb-1">@annotationsLocalizer["Walk_PaymentType"]</div>
@Model.PaymentType.GetDisplayName(annotationsLocalizer)
</div>
<div class="col-sm-3"> <div class="col-sm-3">
<div class="h5 mb-1">@annotationsLocalizer["Walk_PaymentStatus"]</div> <div class="h5 mb-1">@annotationsLocalizer["Walk_PaymentStatus"]</div>
@Model.PaymentStatus.GetDisplayName(annotationsLocalizer) @Model.PaymentStatus.GetDisplayName(annotationsLocalizer)

View File

@ -22,10 +22,10 @@
"LicenseOptions": { "LicenseOptions": {
"Address": "https://gehgassi.com", "Address": "https://gehgassi.com",
"Software": "gehgassi", "Software": "gehgassi",
"Version": "1.0.99" "Version": "1.0.100"
}, },
"SystemJsOptions": { "SystemJsOptions": {
"FileVersion": "1.0.99" "FileVersion": "1.0.100"
}, },
"SessionSettings": { "SessionSettings": {
"TimeOut": "60", "TimeOut": "60",

View File

@ -23,10 +23,10 @@
"LicenseOptions": { "LicenseOptions": {
"Address": "https://creativeBITS.com", "Address": "https://creativeBITS.com",
"Software": "gehgassi", "Software": "gehgassi",
"Version": "1.0.99" "Version": "1.0.100"
}, },
"SystemJsOptions": { "SystemJsOptions": {
"FileVersion": "1.0.99" "FileVersion": "1.0.100"
}, },
"SessionSettings": { "SessionSettings": {
"TimeOut": "60", "TimeOut": "60",

View File

@ -22,10 +22,10 @@
"LicenseOptions": { "LicenseOptions": {
"Address": "https://gehGassi.creativeBITS.eu", "Address": "https://gehGassi.creativeBITS.eu",
"Software": "gehgassi", "Software": "gehgassi",
"Version": "1.0.99" "Version": "1.0.100"
}, },
"SystemJsOptions": { "SystemJsOptions": {
"FileVersion": "1.0.99" "FileVersion": "1.0.100"
}, },
"SessionSettings": { "SessionSettings": {
"TimeOut": "60", "TimeOut": "60",

View File

@ -23,10 +23,10 @@
"LicenseOptions": { "LicenseOptions": {
"Address": "https://creativeBITS.com", "Address": "https://creativeBITS.com",
"Software": "gehgassi", "Software": "gehgassi",
"Version": "1.0.99" "Version": "1.0.100"
}, },
"SystemJsOptions": { "SystemJsOptions": {
"FileVersion": "1.0.99" "FileVersion": "1.0.100"
}, },
"SessionSettings": { "SessionSettings": {
"TimeOut": "60", "TimeOut": "60",