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