RC build v161.0

FEAT more info stored in localStorage - TODO check browser storage limit
This commit is contained in:
Frede Hundewadt 2023-06-06 16:53:01 +02:00
parent c523e9a475
commit 3c738e3313
43 changed files with 1242 additions and 723 deletions

View file

@ -13,6 +13,7 @@
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
//
using System.Reflection.Metadata;
using System.Text.Json;
using Blazored.LocalStorage;
using Blazored.Toast.Services;
@ -22,30 +23,45 @@ using Microsoft.VisualBasic;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository;
using Wonky.Client.Local.Services;
using Wonky.Client.Models;
using Wonky.Client.Shared;
using Wonky.Entity.DTO;
using Wonky.Entity.Models;
using Wonky.Entity.Views;
#pragma warning disable CS8618
namespace Wonky.Client.Components;
public partial class AdvisorLandingComponent
{
// ##############################################################
[Inject] public UserPreferenceService PreferenceService { get; set; }
[Inject] public IDrawerCabinetService DrawerCabinetService { get; set; }
[Inject] public IUserInfoService UserInfo { get; set; }
[Inject] public ICountryCatalogRepository CatalogRepo { get; set; }
private readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
// ##############################################################
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
private UserPreference Profiles { get; set; } = new();
private DateTime SelectedDate { get; set; }
// private DateTime SelectedDate { get; set; }
protected override async Task OnInitializedAsync()
{
Profiles = await PreferenceService.GetProfile();
SelectedDate = string.IsNullOrWhiteSpace(Profiles.WorkDate) ? DateTime.Now : DateTime.Parse(Profiles.WorkDate);
// SelectedDate = string.IsNullOrWhiteSpace(Profiles.WorkDate) ? DateTime.Parse(Profiles.WorkDate) : DateTime.Today;
}
// protected override async Task OnAfterRenderAsync(bool firstRender)
// {
// if (firstRender)
// {
// var countryCode = await UserInfo.GetCountryCode();
// var _ = await DrawerCabinetService.GetCatalogDrawer(countryCode);
// }
// }
}

View file

@ -15,7 +15,7 @@
@using Wonky.Entity.Views
@using System.Linq.Expressions
@if (InvoiceList.Any())
@if (Invoices.Any())
{
<div class="list-group">
<div class="list-group-item">
@ -37,7 +37,7 @@
</div>
</div>
</div>
@foreach (var invoice in InvoiceList)
@foreach (var invoice in Invoices)
{
<div class="list-group-item list-group-item-action" @onclick="@(() => ShowInvoice(invoice.ArchiveHeadId))">
<div class="row">

View file

@ -20,10 +20,17 @@ namespace Wonky.Client.Components;
public partial class CustomerInvoiceListComponent
{
[Parameter] public string CompanyId { get; set; } = "";
[Parameter] public List<InvoiceListItemView> InvoiceList { get; set; } = new();
[Parameter] public List<InvoiceListItemView> Invoices { get; set; } = new();
[Parameter] public EventCallback<string> OnShowInvoice { get; set; }
protected override void OnParametersSet()
{
if (Invoices.Any())
{
Invoices = Invoices.OrderByDescending(x => x.DocumentDate).ToList();
}
}
private async Task ShowInvoice(string invoiceId)
{
await OnShowInvoice.InvokeAsync(invoiceId);

View file

@ -50,7 +50,7 @@ public class CrmContactRepository : ICrmContactRepository
/// </summary>
/// <param name="contact"></param>
/// <returns></returns>
public async Task<string> CreateContact(ContactDto contact)
public async Task<string> PostContact(ContactDto contact)
{
var response = await _client.PostAsJsonAsync(
$"{_conf.CrmCustomers}/{contact.CompanyId}/contacts", contact, _options);
@ -100,7 +100,7 @@ public class CrmContactRepository : ICrmContactRepository
/// </summary>
/// <param name="contact"></param>
/// <returns></returns>
public async Task UpdateContact(ContactDto contact)
public async Task PutContact(ContactDto contact)
{
await _client.PutAsJsonAsync(
$"{_conf.CrmCustomers}/{contact.CompanyId}/contacts/{contact.ContactId}", contact, _options);

View file

@ -45,8 +45,20 @@ public class CrmCustomerHistoryRepository : ICrmCustomerHistoryRepository
_api = configuration.Value;
}
public async Task<List<InvoiceListItemView>> GetInvoiceList(string companyId)
{
var response = await _client.GetAsync($"{_api.CrmCustomers}/{companyId}/invoices/list");
var content = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode || string.IsNullOrWhiteSpace(content))
{
return new List<InvoiceListItemView>();
}
return JsonSerializer.Deserialize<List<InvoiceListItemView>>(content, _options) ?? new List<InvoiceListItemView>();
}
public async Task<InvoiceListView> FetchInvoiceList(string companyId)
public async Task<InvoiceListView> GetInvoiceListHeader(string companyId)
{
var response = await _client.GetAsync($"{_api.CrmCustomers}/{companyId}/invoices");
var content = await response.Content.ReadAsStringAsync();
@ -58,7 +70,7 @@ public class CrmCustomerHistoryRepository : ICrmCustomerHistoryRepository
}
public async Task<InvoiceView> FetchInvoice(string companyId, string invoiceId)
public async Task<InvoiceView> GetInvoice(string companyId, string invoiceId)
{
var content = await _client
.GetFromJsonAsync<InvoiceView>($"{_api.CrmCustomers}/{companyId}/invoices/{invoiceId}", _options);
@ -66,7 +78,7 @@ public class CrmCustomerHistoryRepository : ICrmCustomerHistoryRepository
}
public async Task<List<ProductInventoryItemView>> FetchInventory(string companyId)
public async Task<List<ProductInventoryItemView>> GetInventory(string companyId)
{
var response = await _client.GetAsync($"{_api.CrmCustomers}/{companyId}/{_api.CrmInventoryExt}");
var content = await response.Content.ReadAsStringAsync();
@ -112,7 +124,7 @@ public class CrmCustomerHistoryRepository : ICrmCustomerHistoryRepository
}
public async Task<string> GetRecycledInvoiceList(string companyId, string syncDate, bool force)
public async Task<string> RequestErpSync(string companyId, string syncDate, bool force)
{
var x = await _client.GetAsync($"{_api.SyncInvoice}/{companyId}/{syncDate}?force={force}");
var content = await x.Content.ReadAsStringAsync();

View file

@ -28,7 +28,7 @@ public interface ICrmContactRepository
/// </summary>
/// <param name="contact"></param>
/// <returns></returns>
Task<string> CreateContact(ContactDto contact);
Task<string> PostContact(ContactDto contact);
/// <summary>
/// Get Contact
@ -58,5 +58,5 @@ public interface ICrmContactRepository
/// </summary>
/// <param name="contact"></param>
/// <returns></returns>
Task UpdateContact(ContactDto contact);
Task PutContact(ContactDto contact);
}

View file

@ -22,12 +22,19 @@ namespace Wonky.Client.HttpRepository;
/// </summary>
public interface ICrmCustomerHistoryRepository
{
/// <summary>
/// Return invoice list without company information
/// </summary>
/// <param name="companyId"></param>
/// <returns></returns>
Task<List<InvoiceListItemView>> GetInvoiceList(string companyId);
/// <summary>
/// Fetch Invoice LIst
/// </summary>
/// <param name="companyId"></param>
/// <returns></returns>
Task<InvoiceListView> FetchInvoiceList(string companyId);
Task<InvoiceListView> GetInvoiceListHeader(string companyId);
/// <summary>
/// Fetch given invoice for given customer
@ -35,14 +42,14 @@ public interface ICrmCustomerHistoryRepository
/// <param name="companyId"></param>
/// <param name="invoiceId"></param>
/// <returns></returns>
Task<InvoiceView> FetchInvoice(string companyId, string invoiceId);
Task<InvoiceView> GetInvoice(string companyId, string invoiceId);
/// <summary>
/// Fetch inventory from given customer
/// </summary>
/// <param name="companyId"></param>
/// <returns></returns>
Task<List<ProductInventoryItemView>> FetchInventory(string companyId);
Task<List<ProductInventoryItemView>> GetInventory(string companyId);
/// <summary>
/// Fetch History for given customer
@ -58,7 +65,7 @@ public interface ICrmCustomerHistoryRepository
/// <param name="months"></param>
/// <returns></returns>
Task<List<ProductHistoryView>> GetProductInvoiceLines(string companyId, int months);
/// <summary>
/// Fetch history for given customer and a given product
/// </summary>
@ -74,5 +81,5 @@ public interface ICrmCustomerHistoryRepository
/// <param name="syncDate"></param>
/// <param name="force"></param>
/// <returns></returns>
Task<string> GetRecycledInvoiceList(string companyId, string syncDate, bool force);
Task<string> RequestErpSync(string companyId, string syncDate, bool force);
}

View file

@ -0,0 +1,291 @@
using System.Text.Json;
using Blazored.LocalStorage;
using Wonky.Client.HttpRepository;
using Wonky.Client.Models;
namespace Wonky.Client.Local.Services;
public class DrawerCabinetService : IDrawerCabinetService
{
private readonly JsonSerializerOptions _options = new ()
{
PropertyNameCaseInsensitive = true
};
private readonly ILogger<DrawerCabinetService> _logger;
private readonly ILocalStorageService _asyncStorageService;
private readonly ISyncLocalStorageService _syncStorageService;
private readonly ICrmActivityRepository _activityRepo;
private readonly ICrmCustomerRepository _customerRepo;
private readonly ICrmCustomerHistoryRepository _historyRepo;
private readonly ICountryCatalogRepository _catalogRepo;
/// <summary>
/// Constructor
/// </summary>
/// <param name="logger"></param>
/// <param name="asyncStorageService"></param>
/// <param name="syncStorageService"></param>
/// <param name="catalogRepo"></param>
/// <param name="historyRepo"></param>
/// <param name="customerRepo"></param>
/// <param name="activityRepo"></param>
public DrawerCabinetService(
ILogger<DrawerCabinetService> logger,
ILocalStorageService asyncStorageService,
ISyncLocalStorageService syncStorageService,
ICountryCatalogRepository catalogRepo,
ICrmCustomerHistoryRepository historyRepo,
ICrmCustomerRepository customerRepo,
ICrmActivityRepository activityRepo)
{
_logger = logger;
_asyncStorageService = asyncStorageService;
_syncStorageService = syncStorageService;
_catalogRepo = catalogRepo;
_historyRepo = historyRepo;
_customerRepo = customerRepo;
_activityRepo = activityRepo;
}
/// <summary>
/// Activity
/// </summary>
/// <param name="companyId"></param>
/// <param name="force"></param>
/// <returns></returns>
public async Task<ActivityDrawer> GetActivityDrawerAsync(string companyId, bool force = false)
{
var drawer = await _asyncStorageService
.GetItemAsync<ActivityDrawer>($"{companyId}.{ActivityDrawer.Label}");
if (drawer == null) force = true;
if (force)
{
var result = await _activityRepo.GetCustomerActivities(companyId);
drawer = new ActivityDrawer
{
LastDateModified = DateTime.Today,
Content = result
};
await StoreActivityDrawerAsync(companyId, drawer);
}
_logger.LogDebug("ActivityDrawer {}", JsonSerializer.Serialize(drawer, _options));
return drawer ?? new ActivityDrawer();
}
/// <summary>
/// Sales Catalog
/// </summary>
/// <param name="countryCode"></param>
/// <param name="force"></param>
/// <returns></returns>
public async Task<CatalogDrawer> GetCatalogDrawerAsync(string countryCode, bool force = false)
{
var drawer = await _asyncStorageService
.GetItemAsync<CatalogDrawer>($"{countryCode}.{CatalogDrawer.Label}");
if (drawer == null) force = true;
if (force)
{
var result = await _catalogRepo.GetPriceList(countryCode);
drawer = new CatalogDrawer
{
LastDateModified = DateTime.Today,
Content = result
};
await StoreCatalogDrawerAsync(countryCode, drawer);
}
_logger.LogDebug("CatalogDrawer {}", JsonSerializer.Serialize(drawer, _options));
return drawer ?? new CatalogDrawer();
}
/// <summary>
/// Company Info
/// </summary>
/// <param name="companyId"></param>
/// <param name="force"></param>
/// <returns></returns>
public async Task<InfoDrawer> GetInfoDrawerAsync(string companyId, bool force = false)
{
var drawer = await _asyncStorageService
.GetItemAsync<InfoDrawer>($"{companyId}.{InfoDrawer.Label}");
if (drawer == null) force = true;
if (force)
{
var result = await _customerRepo.GetCompanyById(companyId);
drawer = new InfoDrawer
{
LastDateModified = DateTime.Today,
Content = result
};
await StoreInfoDrawerAsync(companyId, drawer);
}
_logger.LogDebug("InfoDrawer {}", JsonSerializer.Serialize(drawer, _options));
return drawer ?? new InfoDrawer();
}
/// <summary>
/// Inventory
/// </summary>
/// <param name="companyId"></param>
/// <param name="force"></param>
/// <returns></returns>
public async Task<InventoryDrawer> GetInventoryDrawerAsync(string companyId, bool force = false)
{
var drawer = await _asyncStorageService
.GetItemAsync<InventoryDrawer>($"{companyId}.{InventoryDrawer.Label}");
if (drawer == null) force = true;
if (force)
{
var result = await _historyRepo.GetInventory(companyId);
drawer = new InventoryDrawer
{
LastDateModified = DateTime.Today,
Content = result
};
await StoreInventoryDrawerAsync(companyId, drawer);
}
_logger.LogDebug("InventoryDrawer {}", JsonSerializer.Serialize(drawer, _options));
return drawer ?? new InventoryDrawer();
}
/// <summary>
/// Invoices
/// </summary>
/// <param name="companyId"></param>
/// <param name="force"></param>
/// <returns></returns>
public async Task<InvoiceDrawer> GetInvoiceDrawerAsync(string companyId, bool force = false)
{
var drawer = await _asyncStorageService
.GetItemAsync<InvoiceDrawer>($"{companyId}.{InvoiceDrawer.Label}");
if (drawer == null) force = true;
if (force)
{
var result = await _historyRepo.GetInvoiceList(companyId);
drawer = new InvoiceDrawer
{
LastDateModified = DateTime.Today,
Content = result
};
await StoreInvoiceDrawerAsync(companyId, drawer);
}
_logger.LogDebug("InvoiceDrawer {}", JsonSerializer.Serialize(drawer, _options));
return drawer ?? new InvoiceDrawer();
}
/// <summary>
/// Statistic (invoice lines)
/// </summary>
/// <param name="companyId"></param>
/// <param name="force"></param>
/// <returns></returns>
public async Task<StatisticDrawer> GetStatisticDrawerAsync(string companyId, bool force = false)
{
var drawer = await _asyncStorageService
.GetItemAsync<StatisticDrawer>($"{companyId}.{StatisticDrawer.Label}");
if (drawer == null) force = true;
if (force)
{
var result = await _historyRepo.GetProductInvoiceLines(companyId);
drawer = new StatisticDrawer
{
LastDateModified = DateTime.Today,
Content = result
};
await StoreStatisticDrawerAsync(companyId, drawer);
}
_logger.LogDebug("StatisticDrawer {}", JsonSerializer.Serialize(drawer, _options));
return drawer ?? new StatisticDrawer();
}
/// <summary>
/// Store Activity
/// </summary>
/// <param name="companyId"></param>
/// <param name="drawer"></param>
public async Task StoreActivityDrawerAsync(string companyId, ActivityDrawer drawer)
{
if (drawer.Content.Any())
{
drawer.Content = drawer.Content.OrderByDescending(x => x.OrderDate).ToList();
}
await _asyncStorageService.SetItemAsync($"{companyId}.{ActivityDrawer.Label}", drawer);
}
/// <summary>
/// Store Sales Catalog
/// </summary>
/// <param name="countryCode"></param>
/// <param name="drawer"></param>
public async Task StoreCatalogDrawerAsync(string countryCode, CatalogDrawer drawer)
{
await _asyncStorageService.SetItemAsync($"{countryCode}.{CatalogDrawer.Label}", drawer);
}
/// <summary>
/// Store Company Info
/// </summary>
/// <param name="companyId"></param>
/// <param name="drawer"></param>
public async Task StoreInfoDrawerAsync(string companyId, InfoDrawer drawer)
{
await _asyncStorageService.SetItemAsync($"{companyId}.{InfoDrawer.Label}", drawer);
}
/// <summary>
/// Store Inventory
/// </summary>
/// <param name="companyId"></param>
/// <param name="drawer"></param>
public async Task StoreInventoryDrawerAsync(string companyId, InventoryDrawer drawer)
{
if (drawer.Content.Any())
{
drawer.Content = drawer.Content.OrderByDescending(x => x.LastInvoiceDate).ToList();
}
await _asyncStorageService.SetItemAsync($"{companyId}.{InventoryDrawer.Label}", drawer);
}
/// <summary>
/// Store Invoices
/// </summary>
/// <param name="companyId"></param>
/// <param name="drawer"></param>
public async Task StoreInvoiceDrawerAsync(string companyId, InvoiceDrawer drawer)
{
if (drawer.Content.Any())
{
drawer.Content = drawer.Content.OrderByDescending(x => x.DocumentDate).ToList();
}
await _asyncStorageService.SetItemAsync($"{companyId}.{InvoiceDrawer.Label}", drawer);
}
/// <summary>
/// Store statistic (invoice lines)
/// </summary>
/// <param name="companyId"></param>
/// <param name="drawer"></param>
public async Task StoreStatisticDrawerAsync(string companyId, StatisticDrawer drawer)
{
if (drawer.Content.Any())
{
drawer.Content = drawer.Content.OrderByDescending(x => x.DeliveryDate).ToList();
}
await _asyncStorageService.SetItemAsync($"{companyId}.{StatisticDrawer.Label}", drawer);
}
}

View file

@ -0,0 +1,30 @@
using System.Security.Authentication.ExtendedProtection;
using Wonky.Client.Models;
using Wonky.Entity.DTO;
namespace Wonky.Client.Local.Services;
public interface IDrawerCabinetService
{
Task<ActivityDrawer> GetActivityDrawerAsync(string companyId, bool force = false);
Task<CatalogDrawer> GetCatalogDrawerAsync(string countryCode, bool force = false);
Task<InfoDrawer> GetInfoDrawerAsync(string companyId, bool force = false);
Task<InventoryDrawer> GetInventoryDrawerAsync(string companyId, bool force = false);
Task<InvoiceDrawer> GetInvoiceDrawerAsync(string companyId, bool force = false);
Task<StatisticDrawer> GetStatisticDrawerAsync(string companyId, bool force = false);
Task StoreActivityDrawerAsync(string companyId, ActivityDrawer drawer);
Task StoreCatalogDrawerAsync(string countryCode, CatalogDrawer drawer);
Task StoreInfoDrawerAsync(string companyId, InfoDrawer drawer);
Task StoreInventoryDrawerAsync(string companyId, InventoryDrawer drawer);
Task StoreInvoiceDrawerAsync(string companyId, InvoiceDrawer drawer);
Task StoreStatisticDrawerAsync(string companyId, StatisticDrawer drawer);
// void StoreActivityDrawer(string companyId, ActivityDrawer drawer);
// void StoreCatalogDrawer(string countryCode, CatalogDrawer drawer);
// void StoreInfoDrawer(string companyId, InfoDrawer drawer);
// void StoreInventoryDrawer(string companyId, InventoryDrawer drawer);
// void StoreInvoiceDrawer(string companyId, InvoiceDrawer drawer);
// void StoreStatisticDrawer(string companyId, StatisticDrawer drawer);
}

View file

@ -21,6 +21,7 @@ namespace Wonky.Client.Local.Services;
public interface IUserInfoService
{
Task<string> GetUserId();
Task<string> GetCountryCode();
Task<UserManagerEditView> GetUserInfo();
Task SetUserInfo(UserManagerEditView userInfo);
Task<bool> IsSupervisor();

View file

@ -24,10 +24,10 @@ namespace Wonky.Client.Local.Services;
public class UserInfoService : IUserInfoService
{
private const string _infoKey = "_xui";
private const string _refreshKey = "_xr";
private const string _accessKey = "_xa";
private const string _expiryKey = "_xe";
private const string InfoKey = "_xui";
private const string RefreshKey = "_xr";
private const string AccessKey = "_xa";
private const string ExpiryKey = "_xe";
private readonly ILocalStorageService _localStorageService;
private readonly JsonSerializerOptions _options = new() { PropertyNameCaseInsensitive = true };
@ -43,6 +43,12 @@ public class UserInfoService : IUserInfoService
var x = await GetUserInfo();
return x.UserId;
}
public async Task<string> GetCountryCode()
{
var x = await GetUserInfo();
return x.CountryCode;
}
public async Task<bool> IsSupervisor()
@ -54,48 +60,48 @@ public class UserInfoService : IUserInfoService
public async Task<UserManagerEditView> GetUserInfo()
{
return await _localStorageService.GetItemAsync<UserManagerEditView>(_infoKey);
return await _localStorageService.GetItemAsync<UserManagerEditView>(InfoKey);
}
public async Task SetUserInfo(UserManagerEditView userInfo)
{
await _localStorageService.SetItemAsync(_infoKey, userInfo);
await _localStorageService.SetItemAsync(InfoKey, userInfo);
}
public async Task<string> GetRefreshToken()
{
return await _localStorageService.GetItemAsStringAsync(_refreshKey);
return await _localStorageService.GetItemAsStringAsync(RefreshKey);
}
public async Task SetRefreshToken(string token)
{
await _localStorageService.SetItemAsStringAsync(_refreshKey, token);
await _localStorageService.SetItemAsStringAsync(RefreshKey, token);
}
public async Task<string> GetAccessToken()
{
return await _localStorageService.GetItemAsStringAsync(_accessKey);
return await _localStorageService.GetItemAsStringAsync(AccessKey);
}
public async Task SetAccessToken(string token)
{
await _localStorageService.SetItemAsStringAsync(_accessKey, token);
await _localStorageService.SetItemAsStringAsync(AccessKey, token);
}
public async Task<long> GetExpiration()
{
return await _localStorageService.GetItemAsync<long>(_expiryKey);
return await _localStorageService.GetItemAsync<long>(ExpiryKey);
}
public async Task SetExpiration(long expiration)
{
await _localStorageService.SetItemAsync(_expiryKey, expiration);
await _localStorageService.SetItemAsync(ExpiryKey, expiration);
}
}

View file

@ -0,0 +1,11 @@
using Wonky.Entity.Views;
namespace Wonky.Client.Models;
public class ActivityDrawer
{
public const string Label = "Activity";
public DateTime LastDateModified { get; set; }
public List<ReportItemView> Content { get; set; } = new();
}

View file

@ -0,0 +1,10 @@
using Wonky.Entity.Views;
namespace Wonky.Client.Models;
public class CatalogDrawer
{
public const string Label = "Catalog";
public DateTime LastDateModified { get; set; }
public List<SalesItemView> Content { get; set; } = new();
}

View file

@ -0,0 +1,10 @@
using Wonky.Entity.DTO;
namespace Wonky.Client.Models;
public class InfoDrawer
{
public const string Label = "Info";
public DateTime LastDateModified { get; set; }
public CompanyDto Content { get; set; } = new();
}

View file

@ -0,0 +1,10 @@
using Wonky.Entity.Views;
namespace Wonky.Client.Models;
public class InventoryDrawer
{
public const string Label = "Inventory";
public DateTime LastDateModified { get; set; }
public List<ProductInventoryItemView> Content { get; set; } = new();
}

View file

@ -0,0 +1,10 @@
using Wonky.Entity.Views;
namespace Wonky.Client.Models;
public class InvoiceDrawer
{
public const string Label = "Invoice";
public DateTime LastDateModified { get; set; }
public List<InvoiceListItemView> Content { get; set; } = new();
}

View file

@ -0,0 +1,10 @@
using Wonky.Entity.Views;
namespace Wonky.Client.Models;
public class StatisticDrawer
{
public const string Label = "Statistic";
public DateTime LastDateModified { get; set; }
public List<ProductHistoryView> Content { get; set; } = new();
}

View file

@ -26,7 +26,23 @@ public partial class CustomerActivityListOverlay
private string _modalDisplay = "";
private bool _showBackdrop;
/// <summary>
/// On Paramters Set
/// </summary>
protected override void OnParametersSet()
{
if (Activities.Any())
{
Activities = Activities.OrderByDescending(x => x.OrderDate).ToList();
}
}
/// <summary>
/// Show Self
/// </summary>
public void Show()
{
_modalDisplay = "block;";
@ -34,6 +50,10 @@ public partial class CustomerActivityListOverlay
StateHasChanged();
}
/// <summary>
/// Hide Self
/// </summary>
private void Hide()
{
_modalDisplay = "none;";

View file

@ -18,84 +18,89 @@
<div class="modal-content">
<div class="modal-header">
<h3>Besøg</h3>
<button type="button" class="btn btn-danger" @onclick="@Hide" data-bs-dismiss="modal" aria-label="Luk"><i class="bi-x-lg"></i></button>
<button type="button" class="btn btn-danger" @onclick="@Hide" data-bs-dismiss="modal" aria-label="Luk">
<i class="bi-x-lg"></i>
</button>
</div>
<div class="modal-body">
<div class="card">
<div class="card-body">
<div class="card-title mb-2">
<h3>@ReportItem.Company.Name</h3>
</div>
<div class="row g-3">
<label for="date" class="col-form-label-sm col-sm-1">Dato</label>
<div class="col-sm-3">
<input id="date" class="form-control" type="text" value="@($"{DateTime.Parse(ReportItem.CreateTimestamp):yyyy-MM-dd hh:mm:ss}")" readonly/>
@if (!string.IsNullOrWhiteSpace(ReportItem.Company.Name))
{
<div class="card-title mb-2">
<h3>@ReportItem.Company.Name</h3>
</div>
<div class="row g-3">
<label for="date" class="col-form-label-sm col-sm-1">Dato</label>
<div class="col-sm-3">
<input id="date" class="form-control" type="text" value="@($"{DateTime.Parse(ReportItem.CreateTimestamp):yyyy-MM-dd hh:mm:ss}")" readonly/>
</div>
<label for="account" class="col-form-label-sm col-sm-1">Konto</label>
<div class="col-sm-3">
<input id="account" class="form-control" type="text" value="@ReportItem.Company.Account" readonly/>
</div>
<label for="account" class="col-form-label-sm col-sm-1">Konto</label>
<div class="col-sm-3">
<input id="account" class="form-control" type="text" value="@ReportItem.Company.Account" readonly/>
</div>
<label for="vatNumber" class="col-form-label-sm col-sm-1">Cvr Org</label>
<div class="col-sm-3">
<input id="vatNumber" class="form-control" type="text" value="@ReportItem.Company.VatNumber" readonly/>
</div>
<label for="vatNumber" class="col-form-label-sm col-sm-1">Cvr Org</label>
<div class="col-sm-3">
<input id="vatNumber" class="form-control" type="text" value="@ReportItem.Company.VatNumber" readonly/>
</div>
<label for="eSalesNumber" class="col-form-label-sm col-sm-1">EOrdre</label>
<div class="col-sm-3">
<input id="eSalesNumber" class="form-control" type="text" value="@ReportItem.ESalesNumber" readonly/>
</div>
<label for="eSalesNumber" class="col-form-label-sm col-sm-1">EOrdre</label>
<div class="col-sm-3">
<input id="eSalesNumber" class="form-control" type="text" value="@ReportItem.ESalesNumber" readonly/>
</div>
<label for="ourRef" class="col-form-label-sm col-sm-1">Vor Ref.</label>
<div class="col-sm-3">
<input id="ourRef" class="form-control" type="text" value="@ReportItem.OurRef" readonly/>
</div>
<hr/>
<label for="referenceNumber" class="col-form-label-sm col-sm-1">Rekv.</label>
<div class="col-sm-3">
<input id="referenceNumber" class="form-control" type="text" value="@ReportItem.ReferenceNumber" readonly/>
</div>
<label for="ourRef" class="col-form-label-sm col-sm-1">Vor Ref.</label>
<div class="col-sm-3">
<input id="ourRef" class="form-control" type="text" value="@ReportItem.OurRef" readonly/>
</div>
<hr/>
<label for="referenceNumber" class="col-form-label-sm col-sm-1">Rekv.</label>
<div class="col-sm-3">
<input id="referenceNumber" class="form-control" type="text" value="@ReportItem.ReferenceNumber" readonly/>
</div>
<label for="yourRef" class="col-form-label-sm col-sm-1">Kunde Ref.</label>
<div class="col-sm-7">
<input id="yourRef" class="form-control" type="text" value="@ReportItem.YourRef" readonly/>
</div>
<label for="yourRef" class="col-form-label-sm col-sm-1">Kunde Ref.</label>
<div class="col-sm-7">
<input id="yourRef" class="form-control" type="text" value="@ReportItem.YourRef" readonly/>
</div>
<label for="officeNote" class="col-form-label-sm col-sm-1">Ordre Note</label>
<div class="col-sm-5">
<textarea id="officeNote" class="form-control" value="@ReportItem.OfficeNote" readonly/>
</div>
<label for="officeNote" class="col-form-label-sm col-sm-1">Ordre Note</label>
<div class="col-sm-5">
<textarea id="officeNote" class="form-control" value="@ReportItem.OfficeNote" readonly/>
</div>
<label for="crmNote" class="col-form-label-sm col-sm-1">Crm Note</label>
<div class="col-sm-5">
<textarea id="crmNote" class="form-control" value="@ReportItem.CrmNote" readonly/>
</div>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Varenr</th>
<th scope="col">Beskrivelse</th>
<th class="text-end" scope="col">Antal</th>
<th class="text-end" scope="col">Pris</th>
<th class="text-end" scope="col">Rabat</th>
</tr>
</thead>
<tbody>
@foreach (var line in ReportItem.Lines)
{
<label for="crmNote" class="col-form-label-sm col-sm-1">Crm Note</label>
<div class="col-sm-5">
<textarea id="crmNote" class="form-control" value="@ReportItem.CrmNote" readonly/>
</div>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<td>@line.Sku</td>
<td>@line.Description</td>
<td class="text-end">@line.Quantity</td>
<td class="text-end">@line.Price</td>
<td class="text-end">@line.Discount</td>
<th scope="col">Varenr</th>
<th scope="col">Beskrivelse</th>
<th class="text-end" scope="col">Antal</th>
<th class="text-end" scope="col">Pris</th>
<th class="text-end" scope="col">Rabat</th>
</tr>
}
</tbody>
</table>
</div>
</thead>
<tbody>
@foreach (var line in ReportItem.Lines)
{
<tr>
<td>@line.Sku</td>
<td>@line.Description</td>
<td class="text-end">@line.Quantity</td>
<td class="text-end">@line.Price</td>
<td class="text-end">@line.Discount</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
</div>
</div>

View file

@ -25,6 +25,10 @@ public partial class CustomerActivityViewOverlay
private string _modalDisplay = "";
private bool _showBackdrop;
/// <summary>
/// Show Self
/// </summary>
public void Show()
{
_modalDisplay = "block;";
@ -32,6 +36,10 @@ public partial class CustomerActivityViewOverlay
StateHasChanged();
}
/// <summary>
/// Hide Self
/// </summary>
private void Hide()
{
_modalDisplay = "none;";

View file

@ -25,31 +25,47 @@ namespace Wonky.Client.OverlayCustomer;
public partial class CustomerInventoryListOverlay : IDisposable
{
/*
* Dependency Injection
*/
[Inject] public ILogger<CustomerInventoryListOverlay> Logger { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public ICountryCatalogRepository CatalogRepo { get; set; }
[Inject] public ILogger<CustomerInventoryListOverlay> Logger { get; set; }
/*
* Parameters
*/
[Parameter] public string CompanyName { get; set; } = "";
[Parameter] public string CompanyId { get; set; } = "";
[Parameter] public string CountryCode { get; set; } = "";
[Parameter] public List<ProductInventoryItemView> Inventory { get; set; } = new();
[Parameter] public EventCallback<DraftItem> OnSelected { get; set; }
/*
* Private Variables
*/
private string _modalDisplay = "";
private bool _showBackdrop;
private CompanyDto Company { get; set; } = new();
// private List<ProductInventoryView> ProductList { get; set; } = new();
private DraftItem DraftItem { get; set; } = new();
private SalesItemView SalesItem { get; set; } = new();
private CustomerInventoryReorderOverlay ReorderOverlay { get; set; } = new();
/// <summary>
/// On Parameters Set
/// </summary>
protected override void OnParametersSet()
{
if(Inventory.Any())
Inventory = Inventory.OrderBy(x => x.Description).ToList();
{
Inventory = Inventory
.OrderByDescending(x => x.LastInvoiceDate)
.ToList();
}
}
/// <summary>
/// On Initialized
/// </summary>
protected override void OnInitialized()
{
Interceptor.RegisterEvent();
@ -57,18 +73,32 @@ public partial class CustomerInventoryListOverlay : IDisposable
StateHasChanged();
}
/// <summary>
/// Inventory Reorder Overlay Callback
/// </summary>
/// <param name="sku"></param>
private async Task OnReorderCallback(string sku)
{
SalesItem = await CatalogRepo.GetSalesItemSku(CountryCode.ToLower(), sku);
ReorderOverlay.Show();
}
/// <summary>
/// SelectedItem Callback
/// </summary>
/// <param name="draftItem"></param>
private async Task OnSelectedItem(DraftItem draftItem)
{
await OnSelected.InvokeAsync(draftItem);
Hide();
}
/// <summary>
/// Show Self
/// </summary>
public void Show()
{
_modalDisplay = "block;";
@ -76,6 +106,10 @@ public partial class CustomerInventoryListOverlay : IDisposable
StateHasChanged();
}
/// <summary>
/// Hide Self
/// </summary>
private void Hide()
{
_modalDisplay = "none;";
@ -83,6 +117,10 @@ public partial class CustomerInventoryListOverlay : IDisposable
StateHasChanged();
}
/// <summary>
/// Dispose Self
/// </summary>
public void Dispose()
{
Interceptor.DisposeEvent();

View file

@ -42,6 +42,9 @@ public partial class CustomerInventoryReorderOverlay
private bool ShowDraft { get; set; }
/// <summary>
/// On Parameters Set Async
/// </summary>
protected override async Task OnParametersSetAsync()
{
if (string.IsNullOrWhiteSpace(SalesItem.Sku))
@ -57,6 +60,11 @@ public partial class CustomerInventoryReorderOverlay
SelectedItem.Price = decimal.Parse(SalesItem.Rates[0].Rate, CultureInfo.InvariantCulture);
}
/// <summary>
/// Invoke Callback Adding Selected Item To Draft
/// </summary>
/// <param name="item"></param>
private async Task SendToOrder(DraftItem item)
{
SelectedItem = new DraftItem();
@ -64,6 +72,12 @@ public partial class CustomerInventoryReorderOverlay
Hide();
}
/// <summary>
/// Apply Selected Price To Item
/// </summary>
/// <param name="quantity"></param>
/// <param name="rate"></param>
private void SelectPrice(string quantity, string rate)
{
SelectedItem.Discount = 0;
@ -72,6 +86,11 @@ public partial class CustomerInventoryReorderOverlay
StateHasChanged();
}
/// <summary>
/// Apply Selected Historical Price To Item
/// </summary>
/// <param name="item"></param>
private void SelectHistory(ProductHistoryView item)
{
SelectedItem.Discount = item.Discount;
@ -80,6 +99,10 @@ public partial class CustomerInventoryReorderOverlay
StateHasChanged();
}
/// <summary>
/// Show Self
/// </summary>
public void Show()
{
_modalDisplay = "block;";
@ -87,6 +110,10 @@ public partial class CustomerInventoryReorderOverlay
StateHasChanged();
}
/// <summary>
/// Reset SelectedItem And Hide Self
/// </summary>
private void Hide()
{
SelectedItem = new DraftItem();

View file

@ -24,7 +24,7 @@
<button type="button" class="btn btn-danger" @onclick="@Hide" data-bs-dismiss="modal" aria-label="Luk"><i class="bi-x-lg"></i></button>
</div>
<div class="modal-body">
<CustomerInvoiceListComponent OnShowInvoice="@CallInvoiceModal" CompanyId="@Company.CompanyId" InvoiceList="@Invoices"/>
<CustomerInvoiceListComponent OnShowInvoice="@CallInvoiceModal" Invoices="@Invoices"/>
</div>
</div>
</div>

View file

@ -16,6 +16,7 @@
using System.Text.Json;
using Microsoft.AspNetCore.Components;
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
#pragma warning disable CS8618
@ -23,36 +24,46 @@ namespace Wonky.Client.OverlayCustomer;
public partial class CustomerInvoiceListOverlay
{
[Parameter] public InvoiceListView CustomerInvoices { get; set; } = new();
// ##############################################################
[Inject] public ILogger<CustomerInvoiceListOverlay> Logger { get; set; }
// ##############################################################
[Parameter] public List<InvoiceListItemView> Invoices { get; set; } = new();
[Parameter] public CompanyDto Company { get; set; } = new();
// ##############################################################
private CustomerInvoiceViewOverlay CustomerInvoiceView { get; set; } = new();
private string InvoiceId { get; set; } = "";
private string _modalDisplay = "";
private bool _showBackdrop;
private List<InvoiceListItemView> Invoices { get; set; } = new();
private InvoiceCompanyView Company { get; set; } = new();
/// <summary>
/// On Parameters Set
/// </summary>
protected override void OnParametersSet()
{
Company = CustomerInvoices.Company;
Logger.LogDebug("company => {}", JsonSerializer.Serialize(Company));
if (CustomerInvoices.Invoices.Any())
Invoices = CustomerInvoices.Invoices.OrderByDescending(x => x.DocumentDate).ToList();
Logger.LogDebug("invoices => {}", JsonSerializer.Serialize(Invoices));
if (Invoices.Any())
Invoices = Invoices
.OrderByDescending(x => x.DocumentDate)
.ToList();
}
protected override void OnInitialized()
{
StateHasChanged();
}
/// <summary>
/// Show Invoice View Overlay
/// </summary>
/// <param name="invoiceId"></param>
private void CallInvoiceModal(string invoiceId)
{
InvoiceId = invoiceId;
CustomerInvoiceView.Show();
}
/// <summary>
/// Show Self
/// </summary>
public void Show()
{
_modalDisplay = "block;";
@ -60,6 +71,10 @@ public partial class CustomerInvoiceListOverlay
StateHasChanged();
}
/// <summary>
/// Hide Self
/// </summary>
private void Hide()
{
_modalDisplay = "none;";

View file

@ -37,6 +37,10 @@ public partial class CustomerInvoiceViewOverlay : IDisposable
private bool _showBackdrop;
private InvoiceView Invoice { get; set; } = new();
/// <summary>
/// On Parameters Set Async
/// </summary>
protected override async Task OnParametersSetAsync()
{
Interceptor.RegisterEvent();
@ -44,10 +48,14 @@ public partial class CustomerInvoiceViewOverlay : IDisposable
if (!string.IsNullOrWhiteSpace(InvoiceId))
{
Invoice = await HistoryRepo.FetchInvoice(CompanyId, InvoiceId);
Invoice = await HistoryRepo.GetInvoice(CompanyId, InvoiceId);
}
}
/// <summary>
/// Show Self
/// </summary>
public void Show()
{
_modalDisplay = "block;";
@ -55,6 +63,10 @@ public partial class CustomerInvoiceViewOverlay : IDisposable
StateHasChanged();
}
/// <summary>
/// Hide Self
/// </summary>
private void Hide()
{
_modalDisplay = "none;";

View file

@ -25,7 +25,7 @@
</div>
<div class="modal-body">
@* component listing invoices*@
<CustomerInvoiceListComponent OnShowInvoice="@CallInvoiceModal" CompanyId="@Company.CompanyId" InvoiceList="@Invoices"/>
<CustomerInvoiceListComponent OnShowInvoice="@CallInvoiceModal" CompanyId="@Company.CompanyId" Invoices="@Invoices"/>
</div>
</div>
</div>

View file

@ -19,7 +19,7 @@ using Wonky.Entity.Views;
namespace Wonky.Client.OverlayOrderCreate;
public partial class ProductCheckConfirmationOverlay
public partial class ConfirmCheckOverlay
{
// ##############################################################
[Parameter] public string BodyMessage { get; set; } = "";

View file

@ -57,7 +57,7 @@ else
<label for="activityType" class="col-sm-2 col-form-label-sm">Ordre Type</label>
<div class="col-sm-4">
<InputSelect id="activityType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityTypeEnum">
@if (Kanvas)
@if (_kanvas)
{
<option value="canvas" selected>Kanvas</option>
}
@ -74,7 +74,7 @@ else
<label for="statusType" class="col-sm-2 col-form-label-sm">Status</label>
<div class="col-sm-4">
<InputSelect id="statusType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityStatusEnum">
@if (Kanvas)
@if (_kanvas)
{
<option value="canvas" selected>Kanvas</option>
}
@ -113,7 +113,7 @@ else
}
</div>
@if (Kanvas)
@if (_kanvas)
{
<label for="demo" class="col-sm-2 col-form-label-sm">Demo</label>
<div class="col-sm-10">
@ -188,7 +188,7 @@ else
}
</div>
@if (!Kanvas)
@if (!_kanvas)
{
<div class="row g-2 mb-3">
<div class="col-sm-3 d-grid mx-auto">
@ -201,7 +201,7 @@ else
@*
***************** Visit hisotry overlay *****************************
*@
<button class="btn btn-warning" disabled="@string.IsNullOrWhiteSpace(Activity.ActivityTypeEnum)" @onclick="@ShowVisitOverlay">Tidl. besøg</button>
<button class="btn btn-warning" disabled="@string.IsNullOrWhiteSpace(Activity.ActivityTypeEnum)" @onclick="@ShowActivitiesOverlay">Tidl. besøg</button>
</div>
<div class="col-sm-3 d-grid mx-auto">
@*
@ -272,7 +272,7 @@ else
@*
***************** Price catalog overlay button *****************************
*@
<button class="btn btn-primary" type="button" @onclick="@ShowPriceListOverlay">
<button class="btn btn-primary" type="button" @onclick="@ShowPriceCatalogOverlay">
<i class="bi-plus"></i> Ny linje
</button>
</td>
@ -345,7 +345,7 @@ else
Leveringsadresse
</button>
</div>
<div class="@(HideDeliveryAddress ? "inno-hidden" : "inno-display")">
<div class="@(_hideDeliveryAddress ? "inno-hidden" : "inno-display")">
<div class="card-body">
<div class="row mb-1">
<label for="dlvName" class="col-sm-2 col-form-label-sm">Lev. Navn</label>
@ -380,51 +380,6 @@ else
</div>
</div>
</div>
@*</div>*@
@*<div class="accordion" id="crmActivity">
$1$ Delivery address #1#
<div class="accordion-item @(Activity.ActivityStatusEnum == "order" ? "inno-display" : "inno-hidden")">
<h2 class="accordion-header" id="deliveryHeader">
<button class="accordion-button bg-light" type="button" data-bs-toggle="collapse" data-bs-target="#deliveryBody" aria-expanded="false" aria-controls="deliveryBody">
Leveringsadresse
</button>
</h2>
<div id="deliveryBody" class="accordion-collapse collapsed" aria-labelledby="deliveryHeader" data-bs-parent="#crmActivity">
<div class="accordion-body">
<div class="row mb-1">
<label for="dlvName" class="col-sm-2 col-form-label-sm">Lev. Navn</label>
<div class="col-md-10">
<InputText id="dlvName" class="form-control" @bind-Value="Activity.DlvName"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvAddress1" class="col-sm-2 col-form-label-sm">Lev. Adresse</label>
<div class="col-md-10">
<InputText id="dlvAddress1" class="form-control" @bind-Value="Activity.DlvAddress1"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvAddress2" class="col-sm-2 col-form-label-sm">Lev. Adresse</label>
<div class="col-md-10">
<InputText id="dlvAddress2" class="form-control" @bind-Value="Activity.DlvAddress2"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvZipCode" class="col-sm-2 col-form-label-sm">Lev. Postnr</label>
<div class="col-md-10">
<InputText id="dlvZipCode" class="form-control" @bind-Value="Activity.DlvZipCode"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvCity" class="col-sm-2 col-form-label-sm">Lev. Bynavn</label>
<div class="col-md-10">
<InputText id="dlvCity" class="form-control" @bind-Value="Activity.DlvCity"/>
</div>
</div>
</div>
</div>
</div>
</div>*@
}
</EditForm>
@ -437,27 +392,35 @@ else
***************** Confirm product check overlay button *****************************
***************** Continue by submitton order to erp *****************************
*@
<button type="button" class="btn btn-warning" @onclick="@CallConfirmCheckOverlay" disabled="@(PoFormInvalid || Working)">
<button type="button" class="btn btn-warning" @onclick="@ShowProductCheckOverlay" disabled="@(PoFormInvalid || Working)">
<i class="bi-cloud-arrow-up"></i> @ButtonText
</button>
</div>
</div>
}
<ProductCheckConfirmationOverlay BodyMessage="" CompanyId="@CompanyId" Products="CheckList"
OnOkClicked="ConfirmProductCheckCallback" @ref="ConfirmationCheckOverlay"/>
<ConfirmWorkDateModal BodyMessage="@PromptDateConfirm"
OnOkClicked="WorkDateConfirmCallback" @ref="ConfirmWorkDate"/>
<ConfirmWorkDateOverlay BodyMessage="@PromptDateConfirm"
OnOkClicked="WorkDateOverlayCallback" @ref="WorkDateOverlay"/>
<ProductHistoryOverlay CompanyId="@CompanyId" ItemSku="@SelectedItem.Sku" @ref="ProductOverlay"/>
<CatalogPagedOverlay CountryCode="@Company.CountryCode.ToLower()" OnSelected="PriceListCallback" @ref="CatalogOverlay"/>
<CatalogPagedOverlay CountryCode="@Company.CountryCode.ToLower()"
OnSelected="PriceCatalogOverlayCallback" @ref="CatalogOverlay"/>
<ProductPriceHistoryOverlay CompanyId="@CompanyId" Sku="@SelectedItem.Sku" OnSelected="PriceHistoryCallback" @ref="PriceOverlay"/>
<ProductPriceHistoryOverlay CompanyId="@CompanyId" Sku="@SelectedItem.Sku"
OnSelected="PriceHistoryOverlayCallback" @ref="PriceOverlay"/>
<CustomerInvoiceListOverlay CustomerInvoices="CompanyInvoices" @ref="InvoiceListOverlay"/>
<ConfirmCheckOverlay BodyMessage="" CompanyId="@CompanyId"
Products="_inventoryDrawer.Content"
OnOkClicked="ProductCheckOverlayCallback" @ref="ProductCheckOverlay"/>
<CustomerActivityListOverlay Activities="Activities" CompanyName="@Company.Name" @ref="ActivityListOverlay"/>
<CustomerActivityListOverlay CompanyName="@Company.Name"
Activities="_activityDrawer.Content" @ref="ActivityListOverlay"/>
<CustomerInventoryListOverlay CompanyName="@Company.Name" CompanyId="@CompanyId" CountryCode="@Company.CountryCode"
OnSelected="OnInventoryCallback" Inventory="Inventory" @ref="InventoryListOverlay"/>
Inventory="_inventoryDrawer.Content"
OnSelected="InventoryOverlayCallback" @ref="InventoryListOverlay"/>
<CustomerInvoiceListOverlay Company="Company"
Invoices="_invoiceDrawer.Content" @ref="InvoiceListOverlay"/>

View file

@ -50,6 +50,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
[Inject] public ICrmSalesReportRepository ReportRepo { get; set; }
[Inject] public ICrmCustomerHistoryRepository HistoryRepo { get; set; }
[Inject] public IUserInfoService UserInfo { get; set; }
[Inject] public IDrawerCabinetService CabinetService { get; set; }
// #############################################################
[CascadingParameter] private DraftStateProvider DraftProvider { get; set; } = new();
@ -74,31 +75,26 @@ public partial class AdvisorActivityCreatePage : IDisposable
private bool Working { get; set; } = true;
private UserManagerEditView SalesRep { get; set; } = new();
private DateTime SelectedDate { get; set; }
// private string OldPhone { get; set; } = "";
private string PromptDateConfirm { get; set; } = "";
private string ButtonText { get; set; } = "Gem besøg";
private bool OrgWarning { get; set; }
private const string PromptDemoForgotten = "Har du glemt demo?";
// #############################################################
private CatalogPagedOverlay CatalogOverlay { get; set; } = new();
private ProductHistoryOverlay ProductOverlay { get; set; } = new();
private ProductPriceHistoryOverlay PriceOverlay { get; set; } = new();
private ConfirmWorkDateModal ConfirmWorkDate { get; set; } = new();
private ProductCheckConfirmationOverlay ConfirmationCheckOverlay { get; set; } = new();
private ConfirmWorkDateOverlay WorkDateOverlay { get; set; } = new();
private ConfirmCheckOverlay ProductCheckOverlay { get; set; } = new();
private CustomerInvoiceListOverlay InvoiceListOverlay { get; set; } = new();
private CustomerInventoryListOverlay InventoryListOverlay { get; set; } = new();
private CustomerActivityListOverlay ActivityListOverlay { get; set; } = new();
private bool _kanvas;
private bool _hideDeliveryAddress = true;
private InfoDrawer _infoDrawer = new();
private ActivityDrawer _activityDrawer = new();
private InventoryDrawer _inventoryDrawer = new();
private InvoiceDrawer _invoiceDrawer = new();
// #############################################################
private List<ProductInventoryItemView> Inventory { get; set; } = new();
private List<ProductInventoryItemView> CheckList { get; set; } = new();
private InvoiceListView CompanyInvoices { get; set; } = new();
private List<ReportItemView> Activities { get; set; } = new();
private bool Kanvas { get; set; }
private bool HideDeliveryAddress { get; set; } = true;
/// <summary>
/// Page initialization
/// </summary>
@ -114,12 +110,17 @@ public partial class AdvisorActivityCreatePage : IDisposable
UserPreference = await PreferenceService.GetProfile();
// User Info
SalesRep = await UserInfo.GetUserInfo();
// Fetch Customer from http
Company = await CompanyRepo.GetCompanyById(CompanyId);
_infoDrawer = await CabinetService.GetInfoDrawerAsync(CompanyId);
_activityDrawer = await CabinetService.GetActivityDrawerAsync(CompanyId);
_inventoryDrawer = await CabinetService.GetInventoryDrawerAsync(CompanyId);
_invoiceDrawer = await CabinetService.GetInvoiceDrawerAsync(CompanyId);
Company = _infoDrawer.Content;
if (Company.Account.StartsWith("KANVAS"))
{
Kanvas = true;
_kanvas = true;
Activity.ActivityStatusEnum = "canvas";
Activity.ActivityTypeEnum = "canvas";
Activity.ActivityVisitEnum = "new";
@ -130,28 +131,21 @@ public partial class AdvisorActivityCreatePage : IDisposable
if (Company.HasFolded == 1)
{
// Company has shut down
Activity.OrderMessage = "BEMÆRK: CVR nummer er ophørt.";
Activity.OrderMessage = "BEMÆRK: CVR nummer er ophørt.";
}
//
// if (Company.CountryCode.ToLower() == "dk"
// && string.IsNullOrWhiteSpace(Company.Phone)
// && !string.IsNullOrWhiteSpace(Company.Account)
// && !Company.Account.StartsWith("NY")
// && Company.Account.Length > 7)
// {
// Company.Phone = Company.Account[..8];
// }
Activity.ActivityStatusEnum = "noSale";
// decide if new or recall
Activity.ActivityVisitEnum = Company.Account.StartsWith("NY")
? "new" : "recall";
Activity.ActivityVisitEnum = Company.Account.StartsWith("NY")
? "new"
: "recall";
if (string.IsNullOrWhiteSpace(Company.Segment) && SalesRep.CountryCode.ToLower() == "dk")
{
Toaster.ShowError("Der mangler information om Segment. Ret kunde segment, gem erp data og prøv igen.");
Navigator.NavigateTo($"/advisor/customers/{CompanyId}");
}
}
// Populate base activity information
Activity.BcId = Company.BcId;
Activity.CompanyId = Company.CompanyId;
@ -175,24 +169,24 @@ public partial class AdvisorActivityCreatePage : IDisposable
Activity.DlvCity = Company.City;
Activity.Segment = Company.Segment;
Activity.EanNumber = Company.EanNumber;
// Initialize date variable
Logger.LogDebug("AdvisorActivityCreatePage => DateTime parser => {}", UserPreference.WorkDate);
SelectedDate = string.IsNullOrWhiteSpace(UserPreference.WorkDate)
? DateTime.Now
: DateTime.Parse(UserPreference.WorkDate);
// raise flag if report is closed
ReportClosed = await ReportRepo.ReportExist($"{SelectedDate:yyyy-MM-dd}");
// Ask for confirmation of date
Logger.LogDebug("Preferences.DateConfirmed => {}", UserPreference.DateConfirmed);
if (!UserPreference.DateConfirmed)
{
PromptDateConfirm = $"Aktiviteter oprettes med dato {SelectedDate.ToShortDateString()}. Er dette OK?";
ConfirmWorkDate.Show();
WorkDateOverlay.Show();
}
// Lines may already have been added from the company inventory page
if (DraftProvider.Draft.DraftType == "order")
{
@ -201,58 +195,52 @@ public partial class AdvisorActivityCreatePage : IDisposable
{
Activity.ActivityTypeEnum = "onSite";
}
Activity.ActivityStatusEnum = "order";
PoFormInvalid = false;
}
Working = false;
StateHasChanged();
}
// #############################################################
// overlays
private async Task ShowVisitOverlay()
/// <summary>
/// Show Activity Overlay
/// </summary>
private void ShowActivitiesOverlay()
{
Logger.LogDebug("ShowInventoryOverlay - wait for visits");
ActivityListOverlay.Show();
Activities = await ActivityRepo.GetCustomerActivities(CompanyId);
await Task.Delay(500);
}
private async Task ShowInventoryOverlay()
/// <summary>
/// Show Inventory Overlay
/// </summary>
private void ShowInventoryOverlay()
{
Logger.LogDebug("ShowInventoryOverlay - wait for inventory");
InventoryListOverlay.Show();
Inventory = await HistoryRepo.FetchInventory(CompanyId);
Inventory = Inventory.OrderBy(x => x.Description).ToList();
await Task.Delay(500);
}
private async Task ShowInvoiceOverlay()
/// <summary>
/// Show Invoice Overlay
/// </summary>
private void ShowInvoiceOverlay()
{
Logger.LogDebug("ShowInvoiceOverlay - wait for invoices");
InvoiceListOverlay.Show();
CompanyInvoices = await FetchCompanyInvoices();
await Task.Delay(500);
}
private void ShowPriceListOverlay()
{
CatalogOverlay.Show();
}
private async Task CallConfirmCheckOverlay()
/// <summary>
/// Show Product Check Overlay
/// </summary>
private async Task ShowProductCheckOverlay()
{
// check if new account
if (string.IsNullOrWhiteSpace(Company.Account)
|| Company.Account.StartsWith("NY")
|| Activity.ActivityStatusEnum.ToLower() == "quote"
|| Activity.ActivityStatusEnum.ToLower() == "quote"
|| Activity.ActivityStatusEnum.ToLower() == "canvas")
{
// proceed to create activity - as there is no product check to be done
@ -260,58 +248,22 @@ public partial class AdvisorActivityCreatePage : IDisposable
return;
}
// check if product has been checked
// fetch products from storage
var pStorage = await Storage.GetItemAsStringAsync($"{CompanyId}-products");
Logger.LogDebug("pStorage => {}", pStorage);
// fetch pDate from storage
var pDate = await Storage.GetItemAsync<string>($"{CompanyId}-pDate");
if (string.IsNullOrWhiteSpace(pDate))
{
pDate = $"{DateTime.Now.AddDays(-1):yyyy-MM-dd}";
}
Logger.LogDebug("pDate => {}", pDate);
// check if product data is valid and updated today
if (string.IsNullOrWhiteSpace(pStorage) || pDate.Replace("\"", "") != $"{DateTime.Now:yyyy-MM-dd}")
{
Working = true;
// pop a message
Toaster.ShowInfo(
"Produkt gennemgang mangler. Vent mens produkt oversigt indlæses. Gå ikke væk fra siden!");
// product inventory has not been updated
// request backend to sync ERP to CRM
Toaster.ShowInfo("Vent mens data synkroniseres ...");
var ts = await HistoryRepo.GetRecycledInvoiceList(CompanyId, Company.HistorySync, false);
await Task.Delay(250);
// save pDate
await Storage.SetItemAsync($"{CompanyId}-pDate", ts);
// request products from backend
Toaster.ShowInfo("Vent mens produkt oversigt hentes");
CheckList = await HistoryRepo.FetchInventory(CompanyId);
if (CheckList.Any())
{
CheckList = CheckList.OrderBy(x => x.Description).ToList();
}
await Storage.SetItemAsync($"{CompanyId}-products", CheckList);
Working = false;
}
else
{
// deserialize storage data
CheckList = JsonSerializer.Deserialize<List<ProductInventoryItemView>>(pStorage) ?? new List<ProductInventoryItemView>();
if (CheckList.Any())
{
CheckList = CheckList.OrderBy(x => x.Description).ToList();
}
}
// Show CheckList modal
ConfirmationCheckOverlay.Show();
ProductCheckOverlay.Show();
}
/// <summary>
/// Show Price Catalog Overlay
/// </summary>
private void ShowPriceCatalogOverlay()
{
CatalogOverlay.Show();
}
/// <summary>
/// Show Product Price History Overlay
/// </summary>
private void ShowPriceHistoryOverlay()
{
if (ShowItem)
@ -320,17 +272,19 @@ public partial class AdvisorActivityCreatePage : IDisposable
}
}
// #############################################################
// callbacks
// pricelist callback
private async Task PriceListCallback(SelectedSku sku)
/// <summary>
/// Price Catalog Overlay Callback
/// </summary>
/// <param name="sku"></param>
private async Task PriceCatalogOverlayCallback(SelectedSku sku)
{
// get selected item
if (string.IsNullOrWhiteSpace(sku.ItemId))
{
return;
}
SelectedItem = await CatalogRepo.GetSalesItemId(SalesRep.CountryCode.ToLower(), sku.ItemId);
ShowItem = true;
Price = sku.Rate;
@ -338,19 +292,28 @@ public partial class AdvisorActivityCreatePage : IDisposable
StateHasChanged();
}
// price history callback
private void PriceHistoryCallback(decimal price)
/// <summary>
/// Product Price History Overlay Callback
/// </summary>
/// <param name="price"></param>
private void PriceHistoryOverlayCallback(decimal price)
{
if (price == 0)
{
return;
}
Price = price.ToString("N2", CultureInfo.InvariantCulture);
StateHasChanged();
}
// inventory callback
private void OnInventoryCallback(DraftItem item)
/// <summary>
/// Inventory Overlay Callback
/// </summary>
/// <param name="item"></param>
private void InventoryOverlayCallback(DraftItem item)
{
Activity.ActivityStatusEnum = "order";
DraftProvider.Draft.DraftType = "order";
@ -358,28 +321,38 @@ public partial class AdvisorActivityCreatePage : IDisposable
StateHasChanged();
}
// confirm product check callback
private async Task ConfirmProductCheckCallback()
/// <summary>
/// Product Check Overlay Callback
/// </summary>
private async Task ProductCheckOverlayCallback()
{
ConfirmationCheckOverlay.Hide();
foreach (var item in CheckList)
ProductCheckOverlay.Hide();
foreach (var item in _inventoryDrawer.Content)
{
item.Check = false;
}
await Storage.SetItemAsync($"{CompanyId}-products", CheckList);
await CabinetService.StoreInventoryDrawerAsync(CompanyId, _inventoryDrawer);
await CreateActivity();
}
// workdate confirm callback
private async Task WorkDateConfirmCallback()
/// <summary>
/// Workdate Overlay Callback
/// </summary>
private async Task WorkDateOverlayCallback()
{
await PreferenceService.SetDateConfirmed(true);
Activity.ActivityDate = $"{SelectedDate:yyyy-MM-dd}";
ConfirmWorkDate.Hide();
WorkDateOverlay.Hide();
StateHasChanged();
}
// workdate changed callback
/// <summary>
/// Workdate Changed Callback
/// </summary>
/// <param name="workDate"></param>
private async Task WorkDateChangedCallback(string workDate)
{
ReportClosed = await ReportRepo.ReportExist(workDate);
@ -387,45 +360,10 @@ public partial class AdvisorActivityCreatePage : IDisposable
Activity.ActivityDate = workDate;
}
// ################################################################################################
// fetch invoices for customer
// save invoices to storage
private async Task<InvoiceListView> FetchCompanyInvoices()
{
// no need to do for kanvas entry
if (Kanvas)
{
return new InvoiceListView();
}
// fetch from storage
var storage = await Storage.GetItemAsStringAsync($"{CompanyId}-invoices");
await Task.Delay(250);
var iDate = await Storage.GetItemAsStringAsync($"{CompanyId}-iDate");
// if we have a list and iDate was today return the list
if (!string.IsNullOrWhiteSpace(storage)
&& !string.IsNullOrWhiteSpace(iDate)
&& DateTime.Parse(iDate.Replace("\"", "")) >= DateTime.Now)
{
Logger.LogDebug("fetching invoices from storage");
Logger.LogDebug("storage contains <= {}", storage);
return JsonSerializer.Deserialize<InvoiceListView>(storage);
}
Logger.LogDebug("pulling invoices from backend");
// pull invoices
var companyInvoices = await HistoryRepo.FetchInvoiceList(CompanyId);
// send invoices to storage
await Storage.SetItemAsync($"{CompanyId}-invoices", companyInvoices);
await Storage.SetItemAsync($"{CompanyId}-iDate", $"{DateTime.Now:yyyy-MM-dd}");
Logger.LogDebug(" --> return invoices from backend");
Working = false;
Logger.LogDebug("backend contains <= {}", JsonSerializer.Serialize(companyInvoices));
return companyInvoices;
}
// ##################################################################################################
// create activity
/// <summary>
/// Create Activity
/// </summary>
private async Task CreateActivity()
{
// avoid duplication
@ -433,9 +371,10 @@ public partial class AdvisorActivityCreatePage : IDisposable
{
return;
}
// working flag is raised after the following validations
Logger.LogDebug("view kanvas activity => {}", JsonSerializer.Serialize(Activity));
switch (Kanvas)
switch (_kanvas)
{
// validate customer address1
// - this is a required input
@ -466,6 +405,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
Toaster.ShowError("Ved tilbud skal en gyldig email adresse angives.");
return;
}
// every checks
// raise working flag
Working = true;
@ -486,6 +426,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
{
Activity.OurRef = $"E{Activity.OurRef}";
}
// begin lines
Activity.Lines = new List<ActivityLineDto>();
var ln = 0;
@ -507,6 +448,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
.ToList();
Activity.Lines = lines;
}
// debug logging
Logger.LogDebug("CrmNewActivityPage => \n {}", JsonSerializer.Serialize(Activity));
// post to api
@ -529,40 +471,54 @@ public partial class AdvisorActivityCreatePage : IDisposable
}
/// <summary>
/// Toggle Delivery Address Input
/// </summary>
private void ToggleDeliveryAddress()
{
HideDeliveryAddress = !HideDeliveryAddress;
_hideDeliveryAddress = !_hideDeliveryAddress;
}
/// <summary>
/// Show Incomplete Organization Number Warning
/// </summary>
private void ShowOrgWarning()
{
if (Kanvas)
if (_kanvas)
{
return;
}
if (OrgWarning)
{
return;
}
OrgWarning = true;
if (Company.CountryCode.ToLower() == "se"
&& Utils.StringToDigits(Activity.VatNumber).Length < 10
if (Company.CountryCode.ToLower() == "se"
&& Utils.StringToDigits(Activity.VatNumber).Length < 10
&& Activity.ActivityStatusEnum == "order")
{
Toaster.ShowWarning("Org nummer er ufuldstændig. Skal opdateres før bestilling kan sendes. ");
}
}
// ###############################################################################
// draft functions
/// <summary>
/// Remove Current Draft
/// </summary>
private async Task DeleteDraft()
{
await DraftProvider.DeleteDraftAsync();
Activity.ActivityStatusEnum = "noSale";
}
/// <summary>
/// Add Item To Draft
/// </summary>
/// <param name="salesItem"></param>
private async Task AddItem(SalesItemView salesItem)
{
ShowItem = false;
@ -586,11 +542,16 @@ public partial class AdvisorActivityCreatePage : IDisposable
{
Activity.ActivityStatusEnum = "order";
}
// save the item using the CartStateProvider's save method
await DraftProvider.SaveChangesAsync();
}
/// <summary>
/// Remove Item From Draft
/// </summary>
/// <param name="item"></param>
private async Task RemoveItem(DraftItem item)
{
// remove item
@ -603,9 +564,13 @@ public partial class AdvisorActivityCreatePage : IDisposable
}
}
// ##################################################################################################
// form validation
/// <summary>
/// EditContext Field Changed Callback
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
{
Logger.LogDebug("ActivityNewPage => HandleFieldChanged => ActivityStatusEnum <= '{}'",
@ -629,10 +594,10 @@ public partial class AdvisorActivityCreatePage : IDisposable
InvalidActivity = InvalidActivityType
|| PoFormInvalid
|| DraftProvider.Draft.Items.Count == 0
|| (Activity.ActivityStatusEnum == "offer"
|| (Activity.ActivityStatusEnum == "offer"
&& string.IsNullOrWhiteSpace(Activity.Email));
if (Activity.YourRef.Length > 35
|| Activity.ReferenceNumber.Length > 20
if (Activity.YourRef.Length > 35
|| Activity.ReferenceNumber.Length > 20
|| InvalidActivity)
{
PoFormInvalid = true;
@ -643,7 +608,12 @@ public partial class AdvisorActivityCreatePage : IDisposable
StateHasChanged();
}
/// <summary>
/// EditContext ValdiationChanged Callback
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidationChanged(object sender, ValidationStateChangedEventArgs e)
{
if (string.IsNullOrEmpty(Activity.ActivityTypeEnum) && !ReportClosed)
@ -670,9 +640,10 @@ public partial class AdvisorActivityCreatePage : IDisposable
ActivityContext.OnValidationStateChanged += ValidationChanged;
}
// #########################################################################################################
// dispose
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Interceptor.DisposeEvent();

View file

@ -35,7 +35,7 @@
</div>
</div>
<CustomerActivityListComponent Activities="ActivityList"/>
<CustomerActivityListComponent Activities="Activities"/>
}
@if (Working)
{

View file

@ -17,6 +17,8 @@
using Microsoft.AspNetCore.Components;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository;
using Wonky.Client.Local.Services;
using Wonky.Client.Models;
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
@ -30,13 +32,15 @@ public partial class AdvisorCustomerActivityListPage : IDisposable
[Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public ICrmActivityRepository CrmActivityRepo { get; set; }
[Inject] public ICrmCustomerRepository CompanyRepo { get; set; }
[Inject] public IDrawerCabinetService CabinetService { get; set; }
[Inject] public ILogger<AdvisorCustomerActivityListPage> Logger { get; set; }
// ######################################################################
[Parameter] public string CompanyId { get; set; } = "";
// ######################################################################
private List<ReportItemView> ActivityList { get; set; } = new();
private CompanyDto Company { get; set; } = new();
private List<ReportItemView> Activities { get; set; } = new();
private bool Working { get; set; } = true;
protected override async Task OnInitializedAsync()
@ -53,9 +57,14 @@ public partial class AdvisorCustomerActivityListPage : IDisposable
private async Task GetActivities()
{
ActivityList = await CrmActivityRepo.GetCustomerActivities(CompanyId);
if (ActivityList.Any())
ActivityList = ActivityList.OrderByDescending(x => x.OrderDate).ToList();
Activities = new List<ReportItemView>();
var drawer = await CabinetService.GetActivityDrawerAsync(CompanyId);
if (drawer.Content.Any())
{
Activities = drawer.Content
.OrderByDescending(x => x.OrderDate)
.ToList();
}
}
public void Dispose()

View file

@ -23,6 +23,7 @@ using Wonky.Client.Enums;
using Wonky.Client.Helpers;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository;
using Wonky.Client.Local.Services;
using Wonky.Client.Models;
using Wonky.Client.OverlayCustomer;
using Wonky.Client.Shared;
@ -34,6 +35,7 @@ namespace Wonky.Client.Pages;
public partial class AdvisorCustomerInventoryListPage : IDisposable
{
// ##############################################################
[Inject] public ICrmCustomerHistoryRepository HistoryRepo { get; set; }
[Inject] public ICrmCustomerRepository CustomerRepo { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; }
@ -41,11 +43,13 @@ public partial class AdvisorCustomerInventoryListPage : IDisposable
[Inject] public ILogger<AdvisorCustomerInventoryListPage> Logger { get; set; }
[Inject] public ILocalStorageService Storage { get; set; }
[Inject] public ICountryCatalogRepository CatalogRepo { get; set; }
[Inject] public IDrawerCabinetService CabinetService { get; set; }
// ##############################################################
[CascadingParameter] public DraftStateProvider DraftStateProvider { get; set; } = new();
[Parameter] public string CompanyId { get; set; } = "";
// ##############################################################
private readonly JsonSerializerOptions _options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
private CompanyDto Company { get; set; } = new();
private bool Working { get; set; } = true;
@ -53,14 +57,14 @@ public partial class AdvisorCustomerInventoryListPage : IDisposable
private CustomerInventoryReorderOverlay ReorderOverlay { get; set; } = new();
private List<ProductInventoryItemView> Inventory { get; set; } = new();
protected override async Task OnInitializedAsync()
{
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
Company = await CustomerRepo.GetCompanyById(CompanyId);
var drawer = await CabinetService.GetInfoDrawerAsync(CompanyId);
Company = drawer.Content;
// fetch product inventory
await FetchProductInventory();
@ -89,11 +93,11 @@ public partial class AdvisorCustomerInventoryListPage : IDisposable
private async Task FetchProductInventory()
{
Inventory = new List<ProductInventoryItemView>();
Inventory = await HistoryRepo.FetchInventory(CompanyId);
if (Inventory.Any())
Inventory = Inventory.OrderBy(x => x.Description).ToList();
var drawer = await CabinetService.GetInventoryDrawerAsync(CompanyId);
if (drawer.Content.Any())
{
Inventory = drawer.Content;
}
}

View file

@ -18,26 +18,26 @@
@using Wonky.Client.OverlayCustomer
@page "/advisor/customers/{CompanyId}/invoices"
@attribute [Authorize(Roles = "Advisor")]
<PageTitle>Faktura Oversigt for @Company.Name</PageTitle>
@if (!string.IsNullOrWhiteSpace(Company.Name))
<PageTitle>Faktura Oversigt for @_company.Name</PageTitle>
@if (!string.IsNullOrWhiteSpace(_company.Name))
{
<div class="row pt-2 pb-1 rounded-2 bg-dark text-white">
<div class="col-sm-6">
<h4 class="pt-1">@Company.Name</h4>
<h4 class="pt-1">@_company.Name</h4>
</div>
<div class="col-sm-3 align-content-end">
<a class="btn btn-primary d-block" href="/advisor/customers/@Company.CompanyId"><i class="bi-chevron-left"></i> Stamkort</a>
<a class="btn btn-primary d-block" href="/advisor/customers/@_company.CompanyId"><i class="bi-chevron-left"></i> Stamkort</a>
</div>
<div class="col-sm-3 align-content-end">
<a class="btn btn-primary d-block" href="/advisor/customers/@Company.CompanyId/activities/new"><i class="bi-chevron-right"></i> Besøg</a>
<a class="btn btn-primary d-block" href="/advisor/customers/@_company.CompanyId/activities/new"><i class="bi-chevron-right"></i> Besøg</a>
</div>
</div>
<CustomerInvoiceListComponent OnShowInvoice="CallInvoiceModal" CompanyId="@CompanyId" InvoiceList="@CompanyInvoices.Invoices"/>
<CustomerInvoiceListComponent OnShowInvoice="ShowInvoiceModal" Invoices="@_invoiceDrawer.Content"/>
}
@if (Working)
@if (_working)
{
<WorkingThreeDots />
}
<CustomerInvoiceViewOverlay CompanyId="@CompanyId" InvoiceId="@InvoiceId" @ref="CustomerInvoiceView" />
<CustomerInvoiceViewOverlay CompanyId="@CompanyId" InvoiceId="@_invoiceId" @ref="CustomerInvoiceView" />

View file

@ -19,6 +19,8 @@ using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository;
using Wonky.Client.Local.Services;
using Wonky.Client.Models;
using Wonky.Client.OverlayCustomer;
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
@ -28,87 +30,46 @@ namespace Wonky.Client.Pages;
public partial class AdvisorCustomerInvoiceListPage : IDisposable
{
// ##############################################################
[Inject] public ILogger<AdvisorCustomerInvoiceListPage> Logger { get; set; }
[Inject] public ICrmCustomerRepository CompanyRepo { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public ICrmCustomerHistoryRepository HistoryRepo { get; set; }
[Inject] public IToastService Toaster { get; set; }
[Inject] public ILocalStorageService Storage { get; set; }
[Inject] public ILogger<AdvisorCustomerInvoiceListPage> Logger { get; set; }
[Inject] public IDrawerCabinetService CabinetService { get; set; }
// ##############################################################
[Parameter] public string CompanyId { get; set; } = "";
private InvoiceListView CompanyInvoices { get; set; } = new();
private CompanyDto Company { get; set; } = new();
// ##############################################################
private CustomerInvoiceViewOverlay CustomerInvoiceView { get; set; } = new();
private string InvoiceId { get; set; } = "";
private bool Working { get; set; }
private bool AllSet { get; set; }
private string _companyId = "";
protected override void OnInitialized()
{
_companyId = CompanyId;
Working = true;
}
private string _invoiceId = "";
private bool _working = true;
private CompanyDto _company = new();
private InvoiceDrawer _invoiceDrawer = new();
private InfoDrawer _infoDrawer = new();
private List<InvoiceListItemView> _invoices = new();
protected override async Task OnInitializedAsync()
{
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
Company = await CompanyRepo.GetCompanyById(_companyId);
_infoDrawer = await CabinetService.GetInfoDrawerAsync(CompanyId);
_invoiceDrawer = await CabinetService.GetInvoiceDrawerAsync(CompanyId);
_company = _infoDrawer.Content;
_invoices = _invoiceDrawer.Content;
// while (string.IsNullOrWhiteSpace(Company.HistorySync))
// await Task.Delay(500);
//
// var iDate = await Storage.GetItemAsStringAsync($"{_companyId}-iDate");
// if (string.IsNullOrWhiteSpace(iDate) || (iDate == Company.HistorySync && iDate != $"{DateTime.Now:yyyy-MM-dd}"))
// {
// // send rpc to sync invoices from ERP to CRM
// var ts = await HistoryRepo.ErpInvoiceToCrmRpc(_companyId, Company.HistorySync);
// // wait until we have the result
// while (string.IsNullOrWhiteSpace(ts))
// await Task.Delay(500);
//
// await Storage.SetItemAsync($"{_companyId}-iDate", ts);
// }
CompanyInvoices = await FetchCompanyInvoices();
AllSet = true;
_working = false;
}
private void CallInvoiceModal(string invoiceId)
private void ShowInvoiceModal(string invoiceId)
{
InvoiceId = invoiceId;
_invoiceId = invoiceId;
CustomerInvoiceView.Show();
}
private async Task<InvoiceListView> FetchCompanyInvoices()
{
// fetch from storage
var storage = await Storage.GetItemAsStringAsync($"{_companyId}-invoices");
var iDate = await Storage.GetItemAsStringAsync($"{_companyId}-iDate");
// if we have a list and iDate was today return the list
if (!string.IsNullOrWhiteSpace(storage) && (!string.IsNullOrWhiteSpace(iDate) && DateTime.Parse(iDate.Replace("\"", "")) >= DateTime.Now))
{
Logger.LogDebug("fetching invoices from storage");
return JsonSerializer.Deserialize<InvoiceListView>(storage);
}
Logger.LogDebug("pulling invoices from backend");
// pull invoices
var companyInvoices = await HistoryRepo.FetchInvoiceList(_companyId);
if (companyInvoices.Invoices.Any())
companyInvoices.Invoices = companyInvoices.Invoices
.OrderByDescending(x => x.DocumentDate)
.ToList();
// send invoices to storage
await Storage.SetItemAsync($"{_companyId}-invoices", companyInvoices);
await Storage.SetItemAsync($"{_companyId}-iDate", $"{DateTime.Now:yyyy-MM-dd}");
Logger.LogDebug("return invoices from backend");
Working = false;
return companyInvoices;
}
public void Dispose()
{

View file

@ -17,28 +17,28 @@
@using Wonky.Client.Components
@attribute [Authorize(Roles = "Advisor")]
@page "/advisor/customers/{CompanyId}"
<PageTitle>Stamkort for @Company.Name</PageTitle>
<PageTitle>Stamkort for @_infoDrawer.Content.Name</PageTitle>
@if (!string.IsNullOrWhiteSpace(Company.Account))
@if (!string.IsNullOrWhiteSpace(_infoDrawer.Content.Account))
{
@if (!string.IsNullOrWhiteSpace(Company.Blocked))
@if (!string.IsNullOrWhiteSpace(_infoDrawer.Content.Blocked))
{
<div class="alert alert-danger">
<h4>Ring til kontoret. Denne konto er spærret med kode '@Company.Blocked'</h4>
<h4>Ring til kontoret. Denne konto er spærret med kode '@_infoDrawer.Content.Blocked'</h4>
</div>
}
<div class="row pt-2 pb-2 mb-2 rounded rounded-2 bg-dark text-white">
<div class="col-sm-7">
<div class="mt-1">
<span class="h3">@Company.Name</span> <span>(@Company.Account)</span>
<span class="h3">@_infoDrawer.Content.Name</span> <span>(@_infoDrawer.Content.Account)</span>
</div>
</div>
<div class="col-sm-5 text-end">
@if (UserInfo.CountryCode is "DK")
@if (_userInfo.CountryCode is "DK")
{
<a class="btn btn-secondary me-2" href="/advisor/customers/@CompanyId/workplaces/new"><i class="bi-plus-lg"></i> Arbejdssted</a>
}
@* <button class="btn btn-secondary me-2" @onclick="@ConfirmReloadHistory" disabled="@Working"><i class="bi-recycle"></i> Varelinjer</button> *@
<button class="btn btn-secondary me-2" @onclick="@ReloadHistory" disabled="@Working"><i class="bi-repeat"></i> Historik</button>
<a class="btn btn-primary me-2" href="/advisor/customers"><i class="bi-chevron-left"></i> Tilbage</a>
</div>
@ -50,78 +50,78 @@
@* Company Name *@
<label for="name" class="col-sm-1 col-form-label-sm">Navn</label>
<div class="col-sm-5">
<InputText id="name" class="form-control" @bind-Value="Company.Name" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.Name)"></ValidationMessage>
<InputText id="name" class="form-control" @bind-Value="_infoDrawer.Content.Name" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.Name)"></ValidationMessage>
</div>
@* Company Attention *@
<label for="attention" class="col-sm-1 col-form-label-sm">Att.</label>
<div class="col-sm-5">
<InputText id="attention" class="form-control" @bind-Value="Company.Attention" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.Attention)"></ValidationMessage>
<InputText id="attention" class="form-control" @bind-Value="_infoDrawer.Content.Attention" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.Attention)"></ValidationMessage>
</div>
@* Address 1 *@
<label for="address1" class="col-sm-1 col-form-label-sm">Adresse</label>
<div class="col-sm-5">
<InputText id="address1" class="form-control" @bind-Value="Company.Address1" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.Address1)"></ValidationMessage>
<InputText id="address1" class="form-control" @bind-Value="_infoDrawer.Content.Address1" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.Address1)"></ValidationMessage>
</div>
@* Address 2 *@
<label for="address2" class="col-sm-1 col-form-label-sm">Adresse</label>
<div class="col-sm-5">
<InputText id="address2" class="form-control" @bind-Value="Company.Address2" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.Address2)"></ValidationMessage>
<InputText id="address2" class="form-control" @bind-Value="_infoDrawer.Content.Address2" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.Address2)"></ValidationMessage>
</div>
@* Post Code *@
<label for="zipCode" class="col-sm-1 col-form-label-sm">PostNr</label>
<div class="col-sm-1">
<InputText id="zipCode" class="form-control" @bind-Value="Company.ZipCode" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.ZipCode)"></ValidationMessage>
<InputText id="zipCode" class="form-control" @bind-Value="_infoDrawer.Content.ZipCode" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.ZipCode)"></ValidationMessage>
</div>
@* City Name *@
<label for="city" class="col-sm-1 col-form-label-sm">Bynavn</label>
<div class="col-sm-3">
<InputText id="city" class="form-control" @bind-Value="Company.City" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.City)"></ValidationMessage>
<InputText id="city" class="form-control" @bind-Value="_infoDrawer.Content.City" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.City)"></ValidationMessage>
</div>
@* Email *@
<label for="email" class="col-sm-1 col-form-label-sm">Epost</label>
<div class="col-sm-5">
<InputText id="email" class="form-control" @bind-Value="Company.Email" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.Email)"></ValidationMessage>
<InputText id="email" class="form-control" @bind-Value="_infoDrawer.Content.Email" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.Email)"></ValidationMessage>
</div>
@* Phone *@
<label for="phone" class="col-sm-1 col-form-label-sm">Telefon</label>
<div class="col-sm-2">
<InputText id="phone" class="form-control" @bind-Value="Company.Phone" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.Phone)"></ValidationMessage>
<InputText id="phone" class="form-control" @bind-Value="_infoDrawer.Content.Phone" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.Phone)"></ValidationMessage>
</div>
@* Mobile *@
<label for="mobile" class="col-sm-1 col-form-label-sm">Mobil</label>
<div class="col-sm-2">
<InputText id="mobile" class="form-control" @bind-Value="Company.Mobile" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.Mobile)"></ValidationMessage>
<InputText id="mobile" class="form-control" @bind-Value="_infoDrawer.Content.Mobile" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.Mobile)"></ValidationMessage>
</div>
@* Email *@
<label for="eanNumber" class="col-sm-1 col-form-label-sm">EAN</label>
<div class="col-sm-5">
<InputText id="eanNumber" class="form-control" @bind-Value="Company.EanNumber" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.EanNumber)"></ValidationMessage>
<InputText id="eanNumber" class="form-control" @bind-Value="_infoDrawer.Content.EanNumber" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.EanNumber)"></ValidationMessage>
</div>
@if (!Kanvas)
{
@* entity segment *@
@if (UserInfo.CountryCode.ToLower() == "dk")
@if (_userInfo.CountryCode.ToLower() == "dk")
{
<label for="segment" class="col-sm-1 col-form-label-sm">Segment</label>
<div class="col-sm-2">
<InputSelect id="segment" class="form-select bg-warning text-bg-warning"
@bind-Value="@Company.Segment" disabled="@(ErpEditDisabled)">
@bind-Value="@_infoDrawer.Content.Segment" disabled="@(ErpEditDisabled)">
<option value="" disabled>segment</option>
<option value="1">AUTO</option>
<option value="2">INDUSTRI</option>
</InputSelect>
<ValidationMessage For="@(() => Company.Segment)"></ValidationMessage>
<ValidationMessage For="@(() => _infoDrawer.Content.Segment)"></ValidationMessage>
</div>
<div class="col-sm-1">
<div class="led-box @(DateTime.Now < DateTime.Parse("2023-12-31") ? "inno-display" : "inno-hidden")">
@ -145,7 +145,7 @@
</div>
@* Save erp data *@
<div class="col-sm-3 d-grid mx-auto">
<button type="button" class="btn btn-danger d-block" @onclick="@UpdateErpData" disabled="@(Working || Company.Name == "ERROR" || ErpEditDisabled)"><i class="bi-cloud-arrow-up"></i> STAM data </button>
<button type="button" class="btn btn-danger d-block" @onclick="@UpdateErpData" disabled="@(Working || _infoDrawer.Content.Name == "ERROR" || ErpEditDisabled)"><i class="bi-cloud-arrow-up"></i> STAM data </button>
</div>
@* vat number*@
<hr class="mb-3"/>
@ -155,8 +155,8 @@
<span class="input-group-text">
<DisplayStateComponent StateClass="@VatState"/>
</span>
<InputText id="vatNumber" class="form-control" @bind-Value="Company.VatNumber" readonly="@(VatEditDisabled)"/>
<ValidationMessage For="@(() => Company.VatNumber)"></ValidationMessage>
<InputText id="vatNumber" class="form-control" @bind-Value="_infoDrawer.Content.VatNumber" readonly="@(VatEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.VatNumber)"></ValidationMessage>
</div>
</div>
@* Enable edit/save vatnumber *@
@ -174,7 +174,7 @@
<a class="btn btn-info" href="https://brreg.no/" target="_blank"><i class="bi-search"></i> brreg.no</a>
break;
case "se":
<a class="btn btn-info" href="https://www.allabolag.se/what/@Company.Name" target="_blank"><i class="bi-search"></i> allabolag.se</a>
<a class="btn btn-info" href="https://www.allabolag.se/what/@_infoDrawer.Content.Name" target="_blank"><i class="bi-search"></i> allabolag.se</a>
break;
}
</div>
@ -232,19 +232,19 @@
<div class="row mb-2">
<label for="note" class="col-sm-1 col-form-label-sm">OBS</label>
<div class="col-sm-8">
@if (string.IsNullOrWhiteSpace(Company.Note))
@if (string.IsNullOrWhiteSpace(_infoDrawer.Content.Note))
{
<InputText name="note" id="note" class="form-control" @bind-Value="Company.Note"/>
<InputText name="note" id="note" class="form-control" @bind-Value="_infoDrawer.Content.Note"/>
}
else
{
<InputText name="note" id="note" class="form-control bg-warning text-black" @bind-Value="Company.Note"/>
<InputText name="note" id="note" class="form-control bg-warning text-black" @bind-Value="_infoDrawer.Content.Note"/>
}
<ValidationMessage For="@(() => Company.Note)"></ValidationMessage>
<ValidationMessage For="@(() => _infoDrawer.Content.Note)"></ValidationMessage>
</div>
@* Save CRM data button *@
<div class="col-sm-3 d-grid mx-auto">
<button type="button" class="btn btn-warning" disabled="@(Company.Name == "ERROR")" @onclick="@UpdateCrmData"><i class="bi-cloud-arrow-up"></i> CRM data</button>
<button type="button" class="btn btn-warning" disabled="@(_infoDrawer.Content.Name == "ERROR")" @onclick="@PostCrmData"><i class="bi-cloud-arrow-up"></i> CRM data</button>
</div>
</div>
@* crm context - contacts *@
@ -300,14 +300,14 @@
</div>
<label for="interval" class="col-sm-2 col-form-label-sm">Uge Interval</label>
<div class="col-sm-2">
<InputNumber id="interval" class="form-control" @bind-Value="Company.Interval"/>
<ValidationMessage For="@(() => Company.Interval)"></ValidationMessage>
<InputNumber id="interval" class="form-control" @bind-Value="_infoDrawer.Content.Interval"/>
<ValidationMessage For="@(() => _infoDrawer.Content.Interval)"></ValidationMessage>
</div>
</div>
<div class="row mb-2">
<label for="crmNotes" class="col-sm-1 col-form-label-sm">Noter</label>
<div class="col-sm-11">
<InputTextArea id="crmNotes" class="form-control" @bind-Value="Company.CrmNotes"/>
<InputTextArea id="crmNotes" class="form-control" @bind-Value="_infoDrawer.Content.CrmNotes"/>
</div>
</div>
}
@ -318,7 +318,7 @@
<button type="button" class="btn btn-outline-dark" @onclick="@ToggleVisibility">@ToggleButtonText</button>
</div>
<div class="col-sm-4 d-grid">
@if (UserInfo.CountryCode is "DK")
@if (_userInfo.CountryCode is "DK")
{
<a class="btn btn-info" href="@($"/advisor/customers/{CompanyId}/workplaces")">Kemi Dokumentation</a>
}
@ -331,10 +331,8 @@
<WorkingThreeDots/>
}
<VatLookupDkModal VatAddress="CompanyVatAddress" EntityName="@Company.Name" VatNumber="@Company.VatNumber"
<VatLookupDkModal VatAddress="CompanyVatAddress" EntityName="@_infoDrawer.Content.Name" VatNumber="@_infoDrawer.Content.VatNumber"
@ref="VatLookupPopup" OnSelectedCompany="SelectedCompanyCallback"/>
<ContactViewEditModal SelectedContact="SelectedContact" CompanyName="@Company.Name"
<ContactViewEditModal SelectedContact="SelectedContact" CompanyName="@_infoDrawer.Content.Name"
@ref="ContactViewPopup" OnSaveClicked="WriteContactCallback" OnDeleteClicked="DeleteContactCallback"/>
<ConfirmActionModal BodyMessage="@RemoveHistoryWarning" OnOkClicked="ReloadHistory" @ref="ConfirmActionHistory" />

View file

@ -14,7 +14,6 @@
//
using System.Text.Json;
using System.Text.RegularExpressions;
using Blazored.LocalStorage;
using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components;
@ -40,25 +39,32 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
[Inject] public IToastService Toaster { get; set; }
[Inject] public ILogger<AdvisorCustomerViewEditPage> Logger { get; set; }
[Inject] public NavigationManager Navigator { get; set; }
[Inject] public ICrmActivityRepository ActivityRepo { get; set; }
[Inject] public ICrmCustomerRepository CustomerRepo { get; set; }
[Inject] public ICrmCustomerHistoryRepository HistoryRepo { get; set; }
[Inject] public ICrmContactRepository CrmContactRepo { get; set; }
[Inject] public ICrmContactRepository ContactRepo { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public VatInfoLookupService VatService { get; set; }
[Inject] public ILocalStorageService Storage { get; set; }
[Inject] public IUserInfoService UserInfoService { get; set; }
[Inject] public IOptions<AppInfo?>? AppInfo { get; set; }
[Inject] public IDrawerCabinetService CabinetService { get; set; }
// ###########################################################################
[Parameter] public string CompanyId { get; set; } = "";
// ###########################################################################
private readonly JsonSerializerOptions _options = new() { PropertyNameCaseInsensitive = true };
private CompanyDto Company { get; set; } = new();
private EditContext ErpContext { get; set; }
private DateTime LastVisit { get; set; }
private DateTime NextVisit { get; set; }
private VatAddress CompanyVatAddress { get; set; } = new();
private ContactDto SelectedContact { get; set; } = new();
private ContactDto DefaultContact { get; set; } = new();
private ContactViewEditModal ContactViewPopup { get; set; } = new();
private VatLookupDkModal VatLookupPopup { get; set; } = new();
private List<ContactDto> Contacts { get; set; } = new();
private UserManagerEditView _userInfo = new();
private InfoDrawer _infoDrawer = new();
private string VatState { get; set; } = "the-ugly";
private bool ValidVat { get; set; }
private bool HasFolded { get; set; }
@ -70,119 +76,168 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
private bool CountryIsDk { get; set; } = true;
private bool ErpEditDisabled { get; set; } = true;
private bool VatEditDisabled { get; set; } = true;
private List<ContactDto> Contacts { get; set; } = new();
private UserManagerEditView UserInfo { get; set; } = new();
private string ToggleButtonText { get; set; } = "";
private bool Kanvas { get; set; }
private ContactDto SelectedContact { get; set; } = new();
private ContactDto DefaultContact { get; set; } = new();
private ContactViewEditModal ContactViewPopup { get; set; } = new();
private VatLookupDkModal VatLookupPopup { get; set; } = new();
private string RemoveHistoryWarning { get; set; } = "";
private ConfirmActionModal ConfirmActionHistory { get; set; }
private string InventoryLink { get; set; } = "";
private string ActivityLink { get; set; } = "";
private string InvoiceLink { get; set; } = "";
private string NewActivityLink { get; set; } = "";
private int EnableLink { get; set; } = 1;
private ActivityDrawer _activityDrawer = new();
private InventoryDrawer _inventoryDrawer = new();
private InvoiceDrawer _invoiceDrawer = new();
private StatisticDrawer _statisticDrawer = new();
private string _companyId = "";
protected override async Task OnInitializedAsync()
{
// setup interceptor
/*
* setup interceptor
*/
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
// initialize default contact
/*
* default contact
*/
DefaultContact = new ContactDto { CompanyId = CompanyId, ContactId = "", FirstName = "" };
// navigation button links
/*
* navigation button links
*/
InventoryLink = $"/advisor/customers/{CompanyId}/history/inventory";
ActivityLink = $"/advisor/customers/{CompanyId}/activities";
InvoiceLink = $"/advisor/customers/{CompanyId}/invoices";
NewActivityLink = $"/advisor/customers/{CompanyId}/activities/new";
// setup form context
ErpContext = new EditContext(Company);
// assign event handlers to context
/*
* setup form context
*/
ErpContext = new EditContext(_infoDrawer.Content);
/*
* assign event handlers to context
*/
ErpContext.OnFieldChanged += HandleFieldChanged;
ErpContext.OnValidationStateChanged += ValidationChanged!;
// fetch user info from local storage
UserInfo = await UserInfoService.GetUserInfo();
CountryCode = UserInfo.CountryCode.ToLower();
/*
* fetch user info from local storage
*/
_userInfo = await UserInfoService.GetUserInfo();
CountryCode = _userInfo.CountryCode.ToLower();
CountryIsDk = CountryCode == "dk";
Logger.LogDebug("companyId => {}", CompanyId);
Company = await CustomerRepo.GetCompanyById(CompanyId);
// internal flag
EnableActivity = Company.ValidVat;
// override if canvas which has account property as empty string or "NY"
if (Company.Account.StartsWith("NY") || Company.Account.StartsWith("KANVAS") || string.IsNullOrWhiteSpace(Company.Account))
/*
* get InfoDrawer.Company from drawer
*/
_infoDrawer = await CabinetService.GetInfoDrawerAsync(CompanyId);
_infoDrawer.Content = _infoDrawer.Content;
/*
* internal EnableActivity flag
*/
EnableActivity = _infoDrawer.Content.ValidVat;
/*
* if KANVAS or NY override EnableActivity
*/
if (_infoDrawer.Content.Account.StartsWith("NY") || _infoDrawer.Content.Account.StartsWith("KANVAS") || string.IsNullOrWhiteSpace(_infoDrawer.Content.Account))
EnableActivity = 1;
if (Company.Account.StartsWith("KANVAS"))
if (_infoDrawer.Content.Account.StartsWith("KANVAS"))
Kanvas = true;
// only execute if the company a 'real' customer
/*
* only execute if the InfoDrawer.Company is not KANVAS
*/
if (!Kanvas)
{
Logger.LogDebug("company => {}", JsonSerializer.Serialize(Company));
// toggle view button text
ToggleButtonText = Company.IsHidden == 0 ? "Udelad kunde i oversigt" : "Brug Normal Visning";
CurrentVat = Company.VatNumber;
Company.CountryCode = UserInfo.CountryCode.ToLower();
// visit interval init
if (Company.Interval == 0)
Company.Interval = 8;
// visit date init
LastVisit = DateTime.Parse(Company.LastVisit);
NextVisit = DateTime.Parse(Company.NextVisit);
// if no previous visit is registered - force last visit date to 2020
Logger.LogDebug("InfoDrawer.Company => {}", JsonSerializer.Serialize(_infoDrawer.Content));
/*
* toggle view button text
*/
ToggleButtonText = _infoDrawer.Content.IsHidden == 0 ? "Udelad kunde i oversigt" : "Brug Normal Visning";
CurrentVat = _infoDrawer.Content.VatNumber;
_infoDrawer.Content.CountryCode = _userInfo.CountryCode.ToLower();
/*
* visit interval init
*/
if (_infoDrawer.Content.Interval == 0)
_infoDrawer.Content.Interval = 8;
/*
* visit date init
*/
LastVisit = DateTime.Parse(_infoDrawer.Content.LastVisit);
NextVisit = DateTime.Parse(_infoDrawer.Content.NextVisit);
/*
* if no previous visit is registered - force last visit date to 2020
*/
if (LastVisit.Year < 2020)
LastVisit = DateTime.Parse("2020-01-01");
// set next visit according to last visit and interval
if (!Company.ValidDateSpan())
NextVisit = LastVisit.AddDays(Company.Interval * 7);
// display urgency of next visit
/*
* set next visit according to last visit and interval
*/
if (!_infoDrawer.Content.ValidDateSpan())
NextVisit = LastVisit.AddDays(_infoDrawer.Content.Interval * 7);
/*
* display urgency of next visit
*/
VisitState = Utils.GetVisitState($"{NextVisit:yyyy-MM-dd}");
// handle company out of business case
if (Company.HasFolded == 1)
/*
* handle InfoDrawer.Company out of business case
*/
if (_infoDrawer.Content.HasFolded == 1)
{
// this is only used if user has selected to show closed companies
/*
* this is only used if user has selected to show closed companies
*/
HasFolded = true;
VatState = "the-dead";
VisitState = "the-dead";
}
else
{
// valid vat enum
Company.ValidVat = VatUtils.ValidateFormat(Company.CountryCode, Company.VatNumber) ? 1 : 0;
// valid vat flag
ValidVat = Company.ValidVat == 1; // true/false flag set if company has a valid vatNumber
// vat state css class
VatState = Company.ValidVat == 1 ? "the-good" : "no-vat"; // assign css class
/*
* vat validation flags
*/
_infoDrawer.Content.ValidVat = VatUtils.ValidateFormat(_infoDrawer.Content.CountryCode, _infoDrawer.Content.VatNumber) ? 1 : 0;
ValidVat = _infoDrawer.Content.ValidVat == 1; // true/false flag set if InfoDrawer.Company has a valid vatNumber
VatState = _infoDrawer.Content.ValidVat == 1 ? "the-good" : "no-vat"; // assign css class
}
// create search address from address
/*
* create search address from address
*/
if (CountryIsDk)
{
CompanyVatAddress = PrepareVatAddress(Company);
CompanyVatAddress = PrepareVatAddress(_infoDrawer.Content);
}
await FetchContacts(CompanyId);
Working = false;
await RequestErpUpdate();
await GetContacts(CompanyId);
}
// remove loading image
/*
* remove loading image
*/
Working = false;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (string.IsNullOrWhiteSpace(_companyId))
{
_companyId = CompanyId;
await CabinetService.GetInvoiceDrawerAsync(CompanyId, true);
await CabinetService.GetInventoryDrawerAsync(CompanyId, true);
await CabinetService.GetActivityDrawerAsync(CompanyId, true);
await CabinetService.GetStatisticDrawerAsync(CompanyId, true);
}
}
private async Task ReloadHistory()
{
Toaster.ShowWarning("Arbejder på sagen ...");
var newSync = await HistoryRepo.RequestErpSync(CompanyId, _infoDrawer.Content.HistorySync, false);
if (!string.IsNullOrWhiteSpace(newSync))
{
_infoDrawer.Content.HistorySync = newSync;
_infoDrawer.Content = _infoDrawer.Content;
await CabinetService.StoreInfoDrawerAsync(CompanyId, _infoDrawer);
}
await CabinetService.GetInvoiceDrawerAsync(CompanyId, true);
await CabinetService.GetInventoryDrawerAsync(CompanyId, true);
await CabinetService.GetActivityDrawerAsync(CompanyId, true);
await CabinetService.GetStatisticDrawerAsync(CompanyId, true);
Toaster.ShowSuccess("Data er snart klar ....");
}
private void ToggleErpEdit()
{
@ -196,60 +251,30 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
}
private void ConfirmReloadHistory()
{
// $"Bekræft at du sletter<br/><strong>{Workplace.Name}</strong> fra <strong>{Workplace.CompanyName}</strong>?<br/>AL INFORMATION slettes og handlingen er uigenkaldelig.";
RemoveHistoryWarning = $"Denne process kan tage lang tid.<br/>Bekræft at al historik gendannes for<br/>{Company.Account} {Company.Name}";
ConfirmActionHistory.Show();
}
private async Task ReloadHistory()
{
if (Working)
return;
Toaster.ShowWarning("Vent venligst ....");
Working = true;
EnableLink = 0;
EnableActivity = 0;
var result = await HistoryRepo.GetRecycledInvoiceList(CompanyId, Company.HistorySync, true);
await Task.Delay(1000);
Working = false;
if (!string.IsNullOrWhiteSpace(result))
{
Toaster.ShowInfo("Historik gendannelse er færdig");
EnableLink = 1;
EnableActivity = 1;
}
}
private async Task ToggleVisibility()
{
Company.IsHidden = Company.IsHidden == 0 ? 1 : 0;
// toggle view button text
ToggleButtonText = Company.IsHidden == 0 ? "Udelad kunde i oversigt" : "Brug Normal Visning";
Logger.LogDebug("ToggleVisibility => Company.IsHidden == {}", Company.IsHidden);
await CustomerRepo.UpdateCrmData(CompanyId, Company);
/*
* toggle view button text
*/
_infoDrawer.Content.IsHidden = _infoDrawer.Content.IsHidden == 0 ? 1 : 0;
ToggleButtonText = _infoDrawer.Content.IsHidden == 0 ? "Udelad kunde i oversigt" : "Brug Normal Visning";
/*
* send update reqeust
*/
await CustomerRepo.UpdateCrmData(CompanyId, _infoDrawer.Content);
}
private async Task RequestErpUpdate()
private async Task GetContacts(string companyId)
{
if (Working)
return;
Working = true;
Company.HistorySync = await HistoryRepo.GetRecycledInvoiceList(CompanyId, Company.HistorySync, false);
Working = false;
}
private async Task FetchContacts(string companyId)
{
// load contacts
Contacts = await CrmContactRepo.GetContacts(companyId);
/*
* load contacts
*/
Contacts = await ContactRepo.GetContacts(companyId);
if (Contacts.Any() && Contacts.Count > 1)
{
Contacts = Contacts.OrderBy(x => x.FirstName).ToList();
}
}
@ -261,83 +286,95 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
private void SelectedCompanyCallback(VirkRegInfo regInfo)
{
Logger.LogDebug("CrmCompanyView => SelectCompanyCallback => {}", JsonSerializer.Serialize(regInfo));
// this can be removed in favor of the new data returned from updating the VatNumber
ValidVat = regInfo.States[0].State.ToLower() == "normal";
Company.HasFolded = ValidVat ? 1 : 0;
_infoDrawer.Content.HasFolded = ValidVat ? 1 : 0;
EnableActivity = ValidVat ? 1 : 0;
VatState = regInfo.States[0].State.ToLower() == "normal" ? "the-good" : "the-dead";
// set new properties
/*
* set new properties if flagged
*/
if (regInfo.SyncAll)
{
Company.Name = regInfo.Name;
Company.Address1 = regInfo.Address;
Company.Address2 = regInfo.CoName;
Company.ZipCode = regInfo.ZipCode;
Company.City = regInfo.City;
_infoDrawer.Content.Name = regInfo.Name;
_infoDrawer.Content.Address1 = regInfo.Address;
_infoDrawer.Content.Address2 = regInfo.CoName;
_infoDrawer.Content.ZipCode = regInfo.ZipCode;
_infoDrawer.Content.City = regInfo.City;
}
Company.VatNumber = regInfo.VatNumber;
_infoDrawer.Content.VatNumber = regInfo.VatNumber;
}
private void OpenContactPopup(ContactDto contact)
{
// write contact to debug log
Logger.LogDebug("CompanyView => OpenContactPopup => contact => {}", JsonSerializer.Serialize(contact));
// object to pass on to the popup
/*
* pass contact to popup
*/
SelectedContact = contact;
Logger.LogDebug("CompanyView => OpenContactPopup => SelectedContact => {}", JsonSerializer.Serialize(SelectedContact));
// show the popup
/*
* show the popup
*/
ContactViewPopup.Show();
}
/// <summary>
/// Create request to update contact information
/// </summary>
/// <param name="contact"></param>
private async Task WriteContactCallback(ContactDto contact)
{
if (Working)
return;
Working = true;
// write contact to debug log
var jsonContact = JsonSerializer.Serialize(contact);
Logger.LogDebug("CompanyView => SaveContactCallback <= {}", jsonContact);
/*
* if ContactId is empty it is a new contact
*/
if (string.IsNullOrWhiteSpace(contact.ContactId))
{
// new contact created
Logger.LogDebug("create => {}", jsonContact);
// send post request to backend
await CrmContactRepo.CreateContact(contact);
/*
* contact created - send post request to backend
*/
await ContactRepo.PostContact(contact);
}
else
{
// contact modified
Logger.LogDebug("update => {}", jsonContact);
// send put request to backend
await CrmContactRepo.UpdateContact(contact);
/*
* contact modified - send put request
*/
await ContactRepo.PutContact(contact);
}
// reset selected contact
/*
* reset default contact
*/
SelectedContact = new ContactDto();
// reload contacts from backend
await FetchContacts(CompanyId);
/*
* reload contacts from backend
*/
await GetContacts(CompanyId);
Working = false;
}
/// <summary>
/// Delete contact callback
/// Delete contact information
/// </summary>
/// <param name="contactId"></param>
private async Task DeleteContactCallback(string contactId)
{
if (Working)
return;
Working = true;
Logger.LogDebug("delete {}", contactId);
// send delete request to backend
await CrmContactRepo.DeleteContact(CompanyId, contactId);
// reset selected contact
/*
* send delete request to backend
*/
await ContactRepo.DeleteContact(CompanyId, contactId);
/*
* reset default contact
*/
SelectedContact = new ContactDto();
// reload contacts from backend
await FetchContacts(CompanyId);
/*
* reload contacts from backend
*/
await GetContacts(CompanyId);
Working = false;
}
@ -345,22 +382,21 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/// Update CRM data
/// </summary>
/// <returns>true/false</returns>
private async Task UpdateCrmData()
private async Task PostCrmData()
{
if (Working)
return;
Working = true;
Toaster.ShowInfo("Vent venligst ...");
Company.LastVisit = $"{LastVisit:yyyy-MM-dd}";
Company.NextVisit = $"{NextVisit:yyyy-MM-dd}";
Company.IsHidden = 0;
var result = await CustomerRepo.UpdateCrmData(CompanyId, Company);
if (!string.IsNullOrWhiteSpace(result.CompanyId))
_infoDrawer.Content.LastVisit = $"{LastVisit:yyyy-MM-dd}";
_infoDrawer.Content.NextVisit = $"{NextVisit:yyyy-MM-dd}";
_infoDrawer.Content.IsHidden = 0;
var company = await CustomerRepo.UpdateCrmData(CompanyId, _infoDrawer.Content);
if (!string.IsNullOrWhiteSpace(company.CompanyId))
{
Company = result;
await UpdateInfoDrawer(company);
StateHasChanged();
}
Working = false;
Toaster.ClearAll();
}
@ -376,13 +412,12 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
ErpEditDisabled = true;
Working = true;
Toaster.ShowInfo("Vent venligst ...");
var result = await CustomerRepo.UpdateErpData(CompanyId, Company);
if (!string.IsNullOrWhiteSpace(result.CompanyId))
var company = await CustomerRepo.UpdateErpData(CompanyId, _infoDrawer.Content);
if (!string.IsNullOrWhiteSpace(company.CompanyId))
{
Company = result;
await UpdateInfoDrawer(company);
StateHasChanged();
}
Working = false;
Toaster.ClearAll();
}
@ -393,8 +428,10 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/// <returns>true/false</returns>
private async Task UpdateVatNumber()
{
// VAT format validation
if (!VatUtils.ValidateFormat(Company.CountryCode, Company.VatNumber))
/*
* VAT format validation
*/
if (!VatUtils.ValidateFormat(_infoDrawer.Content.CountryCode, _infoDrawer.Content.VatNumber))
{
Toaster.ShowError($"Moms Nummer ugyldigt");
return;
@ -402,29 +439,30 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
if (Working)
return;
VatEditDisabled = true;
Working = true;
VatEditDisabled = true;
Toaster.ShowInfo("Vent venligst ...");
var result = await CustomerRepo.UpdateCompanyVat(CompanyId, Company.VatNumber);
if (!string.IsNullOrWhiteSpace(result.CompanyId))
var company = await CustomerRepo.UpdateCompanyVat(CompanyId, _infoDrawer.Content.VatNumber);
if (!string.IsNullOrWhiteSpace(company.CompanyId))
{
Company = result;
await UpdateInfoDrawer(company);
StateHasChanged();
}
Toaster.ClearAll();
Working = false;
}
/// <summary>
/// Prepare vat address from company model
/// Prepare vat address from InfoDrawer.Company model
/// </summary>
/// <param name="company"></param>
/// <returns></returns>
private static VatAddress PrepareVatAddress(CompanyDto company)
{
var digits = "1234567890".ToCharArray();
// process address1
/*
* process address1
*/
var pos1 = company.Address1.IndexOfAny(digits);
if (pos1 > 0)
{
@ -432,11 +470,12 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
{
ZipCode = company.ZipCode.Trim(),
StreetName = company.Address1[..pos1].Trim(),
HouseNumber = Regex.Replace(company.Address1[pos1..], "[^0-9]", "").Trim()
HouseNumber = company.Address1.Split('-')[0]
};
}
// process address2
/*
* process address2
*/
var pos2 = company.Address2.IndexOfAny(digits);
if (pos2 > 0)
{
@ -444,17 +483,22 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
{
ZipCode = company.ZipCode.Trim(),
StreetName = company.Address2[..pos2].Trim(),
HouseNumber = Regex.Replace(company.Address2[pos2..], "[^0-9]", "").Trim()
HouseNumber = company.Address2.Split('-')[0]
};
}
// return empty model
/*
* return empty model
*/
return new VatAddress();
}
/// <summary>
/// Change activity enabled state
/// </summary>
private async Task UpdateInfoDrawer(CompanyDto company)
{
_infoDrawer.Content = company;
await CabinetService.StoreInfoDrawerAsync(CompanyId, _infoDrawer);
}
private void ForceActivity()
{
EnableActivity = EnableActivity == 0 ? 1 : 0;
@ -468,11 +512,15 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
private void HandleFieldChanged(object? sender, FieldChangedEventArgs? e)
{
NextVisit = LastVisit.AddDays(Company.Interval * 7);
// avoid nesting if by assuming ValidVat is false
NextVisit = LastVisit.AddDays(_infoDrawer.Content.Interval * 7);
/*
* avoid nesting if by assuming ValidVat is false
*/
ValidVat = false;
// set ValidVat true if validation succeed
if (VatUtils.ValidateFormat(Company.CountryCode, Company.VatNumber))
/*
* set ValidVat true if validation succeed
*/
if (VatUtils.ValidateFormat(_infoDrawer.Content.CountryCode, _infoDrawer.Content.VatNumber))
{
ValidVat = true;
EnableActivity = 1;
@ -491,7 +539,7 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
ErpContext.OnFieldChanged -= HandleFieldChanged;
ErpContext.OnValidationStateChanged -= ValidationChanged!;
ErpContext = new EditContext(Company);
ErpContext = new EditContext(_infoDrawer.Content);
ErpContext.OnFieldChanged += HandleFieldChanged;
ErpContext.OnValidationStateChanged += ValidationChanged!;

View file

@ -24,7 +24,7 @@
<h2>Support Dokumentation</h2>
</div>
<div class="col-sm-2 text-end">
<a class="btn btn-primary" href="/supervisor" ><i class="bi-chevron-left"></i> Supervisor</a>
<a class="btn btn-primary" href="/supervisor"><i class="bi-chevron-left"></i> Supervisor</a>
</div>
<div class="col-sm-2 text-end">
<a class="btn btn-primary" href="/supervisor/advisors/@AdvisorId/documents"><i class="bi-chevron-left"></i> Tilbage</a>
@ -38,7 +38,7 @@
@* placeholder *@
</div>
<div class="col-sm-2 text-end">
<div class="busy-signal @(Working ? "inno-visible" : "inno-hidden")" >
<div class="busy-signal @(Working ? "inno-visible" : "inno-hidden")">
<div class="spinner-grow text-info" role="status">
<span class="visually-hidden">Loading...</span>
</div>
@ -60,7 +60,7 @@
<label for="countryCode" class="col-sm-1 col-form-label-sm" readonly="">Land</label>
<div class="col-1">
<InputText id="countryCode" class="form-control" @bind-Value="Document.CountryCode"></InputText>
</div>
</div>
<label for="documentDate" class="col-sm-1 col-form-label-sm">Dato</label>
<div class="col-sm-5">
<InputText id="documentDate" class="form-control" @bind-Value="Document.DocumentDate" readonly></InputText>
@ -81,13 +81,15 @@
</div>
<div class="card-footer">
<div class="row">
<Authorized Context="Management">
<div class="col text-start">
<button type="button" class="btn btn-warning" @onclick="@RemoveDocument"><i class="bi-trash"></i> Slet</button>
</div>
</Authorized>
<div class="col text-end">
<button type="submit" class="btn btn-primary"><i class="bi-cloud-upload"></i> Gem</button>
</div>
</div>
</div>
</div>
</EditForm>
</EditForm>

View file

@ -28,7 +28,6 @@ using Wonky.Client.HttpRepository;
using Wonky.Client.Local.Services;
using Wonky.Client.Shared;
using Wonky.Entity.Configuration;
using Wonky.Entity.DTO;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
@ -88,8 +87,6 @@ builder.Services.AddScoped<ISystemSendMailService, SystemSendMailService>();
builder.Services.AddScoped<ISystemSendSmsService, SystemSendSmsService>();
// interceptor
builder.Services.AddScoped<HttpInterceptorService>();
// storage
builder.Services.AddBlazoredLocalStorage();
// authorization
builder.Services.AddAuthorizationCore();
// authentication state provider
@ -102,7 +99,10 @@ builder.Services.AddScoped<RefreshTokenService>();
builder.Services.AddScoped<VatInfoLookupService>();
// activity draft service
builder.Services.AddScoped<OrderDraftService>();
// cabinet service
builder.Services.AddScoped<IDrawerCabinetService, DrawerCabinetService>();
// storage
builder.Services.AddBlazoredLocalStorage();
// ---------------------------------------

View file

@ -31,7 +31,8 @@ public class AuthStateProvider : AuthenticationStateProvider
private readonly IUserInfoService _infoService;
private readonly ILogger<AuthStateProvider> _logger;
private bool DebugMe { get; set; } = false;
public AuthStateProvider(HttpClient client, IUserInfoService infoService, ILogger<AuthStateProvider> logger)
{
_client = client;
@ -44,25 +45,29 @@ public class AuthStateProvider : AuthenticationStateProvider
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var token = await _infoService.GetAccessToken();
_logger.LogDebug("accessToken {}", token);
if (DebugMe) _logger.LogDebug("accessToken {}", token);
if (string.IsNullOrEmpty(token))
{
return _anonymous;
}
var userInfo = await _infoService.GetUserInfo();
_logger.LogDebug("userInfo {}", JsonSerializer.Serialize(userInfo));
if (DebugMe) _logger.LogDebug("userInfo {}", JsonSerializer.Serialize(userInfo));
if (userInfo == null || string.IsNullOrWhiteSpace(userInfo.FirstName))
{
NotifyUserLogout();
return _anonymous;
}
_logger.LogDebug("userInfo.FirstName {}", userInfo.FirstName);
if (DebugMe) _logger.LogDebug("userInfo.FirstName {}", userInfo.FirstName);
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);
var exp = await _infoService.GetExpiration();
_logger.LogDebug("expiration {}", exp);
if (DebugMe) _logger.LogDebug("expiration {}", exp);
var claims = new List<Claim>
{
@ -95,7 +100,9 @@ public class AuthStateProvider : AuthenticationStateProvider
}
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);
var userInfo = await _infoService.GetUserInfo();
if (string.IsNullOrWhiteSpace(userInfo.UserId))
{
NotifyUserLogout();

View file

@ -18,7 +18,7 @@ using Microsoft.AspNetCore.Components;
namespace Wonky.Client.Shared;
public partial class ConfirmWorkDateModal
public partial class ConfirmWorkDateOverlay
{
private string _modalDisplay = "";
private bool _showBackdrop;

View file

@ -1,15 +1,15 @@
{
"appInfo": {
"name": "Wonky Online",
"version": "158.0",
"version": "161.0",
"rc": true,
"sandBox": false,
"image": "grumpy-coder.png"
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Debug",
"Default": "Information",
"System": "Information",
"Microsoft": "Information"
},
"Debug": {
@ -19,7 +19,7 @@
}
},
"apiConfig": {
"baseUrl": "https://dev.innotec.dk",
"baseUrl": "https://zeta.innotec.dk",
"catalog": "api/v2/catalog/country",
"crmCustomers": "api/v2/crm/companies",
"crmInventoryExt": "history/inventory",