diff --git a/gehGassi.Core/Services/WalkService.cs b/gehGassi.Core/Services/WalkService.cs
index de3a1d4..f9874ac 100644
--- a/gehGassi.Core/Services/WalkService.cs
+++ b/gehGassi.Core/Services/WalkService.cs
@@ -50,6 +50,7 @@ namespace gehGassi.Core.Services
var walk = new Walk()
{
Id = Guid.NewGuid().ToString("N"),
+ PaymentType = WalkPaymentType.MangoPay,
Created = DateTimeOffset.UtcNow
};
return walk;
diff --git a/gehGassi.Dto/Common/Enums.cs b/gehGassi.Dto/Common/Enums.cs
index 15e9066..d44fd4d 100644
--- a/gehGassi.Dto/Common/Enums.cs
+++ b/gehGassi.Dto/Common/Enums.cs
@@ -584,6 +584,21 @@ namespace gehGassi.Dto.Common
RefundendAndPayed = 60
}
+ ///
+ /// Zahlungsart für einen Walk
+ ///
+ public enum WalkPaymentTypeDto
+ {
+ ///
+ /// Mit MangoPay bezahlen (z.B. Kreditkarte)
+ ///
+ MangoPay = 1,
+ ///
+ /// Barzahlung
+ ///
+ Cash = 10
+ }
+
///
/// Sortierreihenfolge
///
diff --git a/gehGassi.Dto/Walks/PublicWalkRequestAcceptDto.cs b/gehGassi.Dto/Walks/PublicWalkRequestAcceptDto.cs
index 3b91f1d..54bb222 100644
--- a/gehGassi.Dto/Walks/PublicWalkRequestAcceptDto.cs
+++ b/gehGassi.Dto/Walks/PublicWalkRequestAcceptDto.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
+using gehGassi.Dto.Common;
namespace gehGassi.Dto.Walks
{
@@ -36,5 +37,9 @@ namespace gehGassi.Dto.Walks
///
public DateTimeOffset UpdatedAt { get; set; }
+ ///
+ /// Zahlungsart für den Walk
+ ///
+ public WalkPaymentTypeDto PaymentType { get; set; }
}
}
diff --git a/gehGassi.Dto/Walks/WalkCreateDto.cs b/gehGassi.Dto/Walks/WalkCreateDto.cs
index 36bd004..9cc8ebb 100644
--- a/gehGassi.Dto/Walks/WalkCreateDto.cs
+++ b/gehGassi.Dto/Walks/WalkCreateDto.cs
@@ -101,5 +101,10 @@ namespace gehGassi.Dto.Walks
///
[MaxLength(500)]
public string Info { get; set; }
+
+ ///
+ /// Zahlungsart für den Walk
+ ///
+ public WalkPaymentTypeDto PaymentType { get; set; }
}
}
diff --git a/gehGassi.Dto/Walks/WalkDto.cs b/gehGassi.Dto/Walks/WalkDto.cs
index 7bac30c..cb6cc16 100644
--- a/gehGassi.Dto/Walks/WalkDto.cs
+++ b/gehGassi.Dto/Walks/WalkDto.cs
@@ -179,6 +179,11 @@ namespace gehGassi.Dto.Walks
[Required]
public PaymentStatusDto PaymentStatus { get; set; }
+ ///
+ /// Zahlungsart für den Walk
+ ///
+ public WalkPaymentTypeDto PaymentType { get; set; }
+
///
/// Datum der Erstellung des Walks
///
diff --git a/gehGassi.Dto/Walks/WalkWithNamesDto.cs b/gehGassi.Dto/Walks/WalkWithNamesDto.cs
index 250637b..b55db7f 100644
--- a/gehGassi.Dto/Walks/WalkWithNamesDto.cs
+++ b/gehGassi.Dto/Walks/WalkWithNamesDto.cs
@@ -204,6 +204,11 @@ namespace gehGassi.Dto.Walks
///
public PaymentStatusDto PaymentStatus { get; set; }
+ ///
+ /// Zahlungsart für den Walk
+ ///
+ public WalkPaymentTypeDto PaymentType { get; set; }
+
///
/// Id des Gutscheins der verwendet wurde, wenn einer verwendet wurde
///
diff --git a/gehGassi.Web/Controllers/Api/ApiWalksController.cs b/gehGassi.Web/Controllers/Api/ApiWalksController.cs
index 2d011a4..6977608 100644
--- a/gehGassi.Web/Controllers/Api/ApiWalksController.cs
+++ b/gehGassi.Web/Controllers/Api/ApiWalksController.cs
@@ -43,6 +43,7 @@ namespace gehGassi.Web.Controllers.Api
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[ApiVersion(1)]
+ [ApiVersion(2)]
[Route("api/walks")]
[Route("api/v{v:apiVersion}/walks")]
public class ApiWalksController : ApiBaseController
@@ -954,6 +955,120 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
+ ///
+ /// Akzeptieren einer Antwort zu einer öffentlichen Anfrage
+ ///
+ /// PublicWalkRequestDto
+ /// HTTP 200 OK, Felher sonst
+ [HttpPost]
+ [Route("AcceptPublicWalkRequest")]
+ [MapToApiVersion(2)]
+ public async Task 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 = (WalkPaymentType)model.PaymentType;
+ 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(walk);
+ return Ok(walkDto);
+ }
+ }
+ return NotFound(CommunicationErrors.Common_NotFound);
+ }
+ return BadRequest(CommunicationErrors.Common_Model_Invalid);
+ }
+ return NotFound(CommunicationErrors.Common_NotFound);
+ }
+
///
/// Stornieren einer öffentlichen Anfrage
///
@@ -2224,6 +2339,128 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
+ ///
+ /// Stornieren eines Walks
+ ///
+ /// PublicWalkRequestCancelDto
+ /// HTTP 200 OK, Fehler sonst
+ [HttpPost]
+ [Route("CancelWalk")]
+ [MapToApiVersion(2)]
+ public async Task 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);
+ }
+
///
/// Starten eines Walks
///
@@ -2604,6 +2841,190 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
+ ///
+ /// Bestätigen eines Walks
+ ///
+ /// WalkConfirmDto
+ /// HTTP 200 OK, Felher sonst
+ [HttpPost]
+ [Route("ConfirmWalk")]
+ [MapToApiVersion(2)]
+ public async Task 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(walk);
+ return Ok(walkDto);
+
+ }
+ }
+ return NotFound(CommunicationErrors.Common_NotFound);
+ }
+ return BadRequest(CommunicationErrors.Common_Model_Invalid);
+ }
+
+ return NotFound(CommunicationErrors.Common_NotFound);
+ }
+
///
/// Bestätigen eines Walks mit Rating
///
@@ -2798,6 +3219,205 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
+ ///
+ /// Bestätigen eines Walks mit Rating
+ ///
+ /// WalkConfirmDto
+ /// HTTP 200 OK, Felher sonst
+ [HttpPost]
+ [Route("ConfirmWalkWithRating")]
+ [MapToApiVersion(2)]
+ public async Task 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(walk);
+ return Ok(walkDto);
+
+ }
+ }
+ return NotFound(CommunicationErrors.Common_NotFound);
+ }
+ return BadRequest(CommunicationErrors.Common_Model_Invalid);
+ }
+
+ return NotFound(CommunicationErrors.Common_NotFound);
+ }
+
///
/// Zahlungsstatus eines Walks setzen
///
@@ -2947,6 +3567,102 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
+ ///
+ /// Anlegen eines Walks wenn DIREKT! buchen möglich ist
+ ///
+ /// WalkCreateDto
+ /// HTTP 200 OK, Felher sonst
+ [HttpPost]
+ [Route("CreateWalkDirect")]
+ [MapToApiVersion(2)]
+ public async Task CreateWalkDirectV2(WalkCreateDto model)
+ {
+ var clientOffset = GetClientDateOffset();
+
+ var result = new CreateResponseDto
+ {
+ 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(model.PickupAddress);
+ walk.PickupLocation = geoLocationPickup;
+ walk.ReturnAddress = Mapper.Map(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 = (WalkPaymentType)model.PaymentType;
+ 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(walk);
+ return Ok(result);
+ }
+ return NotFound(CommunicationErrors.Walk_NotPossible);
+ }
+ }
+ return BadRequest(CommunicationErrors.Common_Model_Invalid);
+ }
+
///
/// Anlegen eines Walks als Anfrage
///
@@ -3031,6 +3747,92 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
+ ///
+ /// Anlegen eines Walks als Anfrage
+ ///
+ /// WalkCreateDto
+ /// HTTP 200 OK, Felher sonst
+ [HttpPost]
+ [Route("CreateWalkRequest")]
+ [MapToApiVersion(2)]
+ public async Task CreateWalkRequestV2(WalkCreateDto model)
+ {
+ var clientOffset = GetClientDateOffset();
+
+ var result = new CreateResponseDto
+ {
+ 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(model.PickupAddress);
+ walk.PickupLocation = geoLocationPickup;
+ walk.ReturnAddress = Mapper.Map(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 = (WalkPaymentType)model.PaymentType;
+ 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(walk);
+ return Ok(result);
+ }
+
+ return NotFound(CommunicationErrors.Walk_NotPossible);
+ }
+ }
+ return BadRequest(CommunicationErrors.Common_Model_Invalid);
+ }
+
///
/// Akzeptieren eines Walks
///
@@ -3071,6 +3873,47 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
+ ///
+ /// Akzeptieren eines Walks
+ ///
+ /// WalkAcceptDto
+ /// HTTP 200 OK, Felher sonst
+ [HttpPost]
+ [Route("AcceptWalk")]
+ [MapToApiVersion(2)]
+ public async Task 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);
+ }
+
///
/// Ablehnen eines Walks
///
diff --git a/gehGassi.Web/Mapper/MappingProfile.cs b/gehGassi.Web/Mapper/MappingProfile.cs
index 0f0b726..134ea92 100644
--- a/gehGassi.Web/Mapper/MappingProfile.cs
+++ b/gehGassi.Web/Mapper/MappingProfile.cs
@@ -641,6 +641,9 @@ namespace gehGassi.Web.Mapper
CreateMap();
CreateMap();
+ CreateMap();
+ CreateMap();
+
CreateMap();
CreateMap();