FEAT: add statistic page, add history filter class

REN: drawer cabinet service -> cabinet drawer service
FIX: page size component - prefix function call
WIP: create user with web shop permission - attach to company
FEAT: add workaround for Swedish moms numbers - new api call
FIX: advisor create activity page
FIX: advisor customer CRM data edit and reload
FEAT: add button to statistic on customer view page
FIX: advisor customer create - set correct segment for no and se
This commit is contained in:
Frede Hundewadt 2023-06-09 13:38:36 +02:00
parent 7d21b834fc
commit 2d9ee3c9ce
33 changed files with 703 additions and 450 deletions

View file

@ -35,7 +35,7 @@ public partial class AdvisorLandingComponent
{
// ##############################################################
[Inject] public UserPreferenceService PreferenceService { get; set; }
[Inject] public IDrawerCabinetService DrawerCabinetService { get; set; }
[Inject] public ICabinetDrawerService CabinetDrawerService { get; set; }
[Inject] public IUserInfoService UserInfo { get; set; }
[Inject] public ICountryCatalogRepository CatalogRepo { get; set; }
@ -60,7 +60,7 @@ public partial class AdvisorLandingComponent
// if (firstRender)
// {
// var countryCode = await UserInfo.GetCountryCode();
// var _ = await DrawerCabinetService.GetCatalogDrawer(countryCode);
// var _ = await CabinetDrawerService.GetCatalogDrawer(countryCode);
// }
// }
}

View file

@ -1,72 +0,0 @@
@*
// Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
*@
@if (Inventory.Any())
{
<table class="table table-striped">
<thead>
<tr class="rounded-2 border border-1">
<th scope="col">
Dato
</th>
<th scope="col">
Antal
</th>
<th scope="col">
Vare
</th>
<th scope="col">
Varenummer
</th>
<th scope="col" class="align-content-end">
Rabat
</th>
<th scope="col" class="align-content-end">
Pris
</th>
</tr>
</thead>
<tbody>
@foreach (var line in Inventory)
{
<tr class="align-content-center">
<td>
@line.DeliveryDate
</td>
<td>
@line.Quantity
</td>
<td>
@line.Description
</td>
<td>
@line.Sku
</td>
<td class="align-content-end">
@(line.Discount)%
</td>
<td class="align-content-end">
@line.Price
</td>
</tr>
}
</tbody>
</table>
}
else
{
<div>Ingen data</div>
}

View file

@ -1,30 +0,0 @@
// Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
//
using Microsoft.AspNetCore.Components;
using Wonky.Entity.Views;
namespace Wonky.Client.Components;
public partial class CustomerProductLineListComponent
{
[Parameter] public List<ProductHistoryView> Inventory { get; set; } = new();
protected override void OnParametersSet()
{
if(Inventory.Any())
Inventory = Inventory.OrderBy(x => x.Description).ToList();
}
}

View file

@ -14,7 +14,7 @@
*@
<select class="form-select bg-info text-bg-info" @bind-value="PageSize" @bind-value:event="oninput" @onchange="OnSelectChanged">
<select class="form-select bg-info text-bg-info" @bind-value="PageSize" @bind-value:event="oninput" @onchange="@OnSelectChanged">
<option value="-1" selected disabled>ANTAL svar</option>
<option value="5">5 svar</option>
<option value="10">10 svar</option>

View file

@ -6,9 +6,9 @@ namespace Wonky.Client.Helpers;
public class Mapper
{
public static UserManagerCreateView MapCreateUser(CreateUserInputModel model)
public static ManagerNewUserDto MapCreateUser(NewUserForm model)
{
return new UserManagerCreateView
return new ManagerNewUserDto
{
Description = model.Description,
Email = model.Email,

View file

@ -90,10 +90,13 @@ public class VatUtils
var vatToCheck = vatNumber;
if (long.Parse(vatToCheck) == 0)
return false;
switch (vatToCheck.Length)
{
// if less than 10 chars validate as SSI
/*
* if less than 10 chars validate as SSI
* only the first 6 chars are known
* yyMMdd
*/
case 6:
return ValidateFormatSeExt(vatToCheck);
case < 10:
@ -102,11 +105,16 @@ public class VatUtils
vatNumber = vatNumber[..10];
break;
}
// calculate check digit
/*
* calculate check digit
*/
var c10 = C10(vatToCheck);
// return comparison
/*
* return comparison
*/
return $"{vatToCheck[..9]}{c10}" == vatNumber;
}
/// <summary>
/// Calculate check digit for swedish org number
@ -115,14 +123,15 @@ public class VatUtils
/// <returns></returns>
private static int C10(string orgNumber)
{
// https://wiki.scn.sap.com/wiki/display/CRM/Sweden
// 12 digits 0 to 9
// C10 = (10 (18 + 5 + 1 + 8 + 4)MOD10 10) MOD10
// R = S1 + S3 + S5 + S7 + S9
// Si = int(Ci/5) + (Ci*2)MOD10)
// https://www.skatteverket.se/skatter/mervardesskattmoms/momsregistreringsnummer.4.18e1b10334ebe8bc80002649.html
// C11 C12 == 01 (De två sista siffrorna är alltid 01)
/*
* https://wiki.scn.sap.com/wiki/display/CRM/Sweden
* 12 digits 0 to 9
* C10 = (10 (18 + 5 + 1 + 8 + 4)MOD10 10) MOD10
* R = S1 + S3 + S5 + S7 + S9
* Si = int(Ci/5) + (Ci*2)MOD10)
* https://www.skatteverket.se/skatter/mervardesskattmoms/momsregistreringsnummer.4.18e1b10334ebe8bc80002649.html
* C11 C12 == 01 (De två sista siffrorna är alltid 01)
*/
var r = new[] { 0, 2, 4, 6, 8 }
.Sum(m => (int)char.GetNumericValue(orgNumber[m]) / 5 +
(int)char.GetNumericValue(orgNumber[m]) * 2 % 10);
@ -130,8 +139,9 @@ public class VatUtils
var c10 = (10 - (r + c1) % 10) % 10;
return c10;
// end check digit calculation
}
/// <summary>
///
/// </summary>
@ -173,6 +183,7 @@ public class VatUtils
}
}
/// <summary>
/// Modulus11 validator
/// </summary>

View file

@ -47,7 +47,7 @@ public interface ISystemUserRepository
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
Task<UserManagerEditView> CreateUser(UserManagerCreateView model);
Task<UserManagerEditView> PostNewUser(ManagerNewUserDto model);
/// <summary>
/// Update User using userId and updated data

View file

@ -72,7 +72,7 @@ public class SystemUserRepository : ISystemUserRepository
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public async Task<UserManagerEditView> CreateUser(UserManagerCreateView model)
public async Task<UserManagerEditView> PostNewUser(ManagerNewUserDto model)
{
var result = await _client.PostAsJsonAsync($"{_api.UserManager}", model, _options);
if (!result.IsSuccessStatusCode)

View file

@ -5,15 +5,14 @@ using Wonky.Client.Models;
namespace Wonky.Client.Local.Services;
public class DrawerCabinetService : IDrawerCabinetService
public class CabinetDrawerService : ICabinetDrawerService
{
private readonly JsonSerializerOptions _options = new ()
{
PropertyNameCaseInsensitive = true
};
private readonly ILogger<DrawerCabinetService> _logger;
private readonly ILogger<CabinetDrawerService> _logger;
private readonly ILocalStorageService _asyncStorageService;
private readonly ISyncLocalStorageService _syncStorageService;
private readonly IAdvisorActivityRepository _activityRepo;
private readonly IAdvisorCustomerRepository _customerRepo;
private readonly IAdvisorCustomerHistoryRepository _historyRepo;
@ -24,15 +23,13 @@ public class DrawerCabinetService : IDrawerCabinetService
/// </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,
public CabinetDrawerService(
ILogger<CabinetDrawerService> logger,
ILocalStorageService asyncStorageService,
ISyncLocalStorageService syncStorageService,
ICountryCatalogRepository catalogRepo,
IAdvisorCustomerHistoryRepository historyRepo,
IAdvisorCustomerRepository customerRepo,
@ -40,7 +37,6 @@ public class DrawerCabinetService : IDrawerCabinetService
{
_logger = logger;
_asyncStorageService = asyncStorageService;
_syncStorageService = syncStorageService;
_catalogRepo = catalogRepo;
_historyRepo = historyRepo;
_customerRepo = customerRepo;
@ -196,10 +192,19 @@ public class DrawerCabinetService : IDrawerCabinetService
if (force)
{
var result = await _historyRepo.GetProductInvoiceLines(companyId);
var content = result.Select(x => new HistoryFilter
{
DeliveryDate = DateTime.Parse(x.DeliveryDate),
Description = x.Description,
Discount = x.Discount,
Price = x.Price,
Quantity = x.Quantity,
Sku = x.Sku
}).ToList();
drawer = new StatisticDrawer
{
LastDateModified = DateTime.Today,
Content = result
Content = content
};
await StoreStatisticDrawerAsync(companyId, drawer);
}

View file

@ -4,7 +4,7 @@ using Wonky.Entity.DTO;
namespace Wonky.Client.Local.Services;
public interface IDrawerCabinetService
public interface ICabinetDrawerService
{
Task<ActivityDrawer> GetActivityDrawerAsync(string companyId, bool force = false);
Task<CatalogDrawer> GetCatalogDrawerAsync(string countryCode, bool force = false);

View file

@ -0,0 +1,8 @@
using Wonky.Client.Models;
namespace Wonky.Client.Local.Services;
public interface ISwedishPersonalOrgService
{
Task<PersonalOrgNumber> GetPersonalOrgNumber(string companyName);
}

View file

@ -0,0 +1,47 @@
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Options;
using Wonky.Client.Models;
using Wonky.Entity.Configuration;
using Wonky.Entity.Views;
namespace Wonky.Client.Local.Services;
public class SwedishPersonalOrgService : ISwedishPersonalOrgService
{
private readonly ILogger<SwedishPersonalOrgService> _logger;
private readonly HttpClient _client;
private readonly ApiConfig _api;
public SwedishPersonalOrgService(HttpClient client, ILogger<SwedishPersonalOrgService> logger, IOptions<ApiConfig> api)
{
_client = client;
_logger = logger;
_api = api.Value;
}
public async Task<PersonalOrgNumber> GetPersonalOrgNumber(string companyName)
{
var result = new PersonalOrgNumber
{
CompanyName = companyName,
PersonalId = ""
};
var response = await _client.GetAsync($"{_api.ServicesVatSe}?companyName={companyName}");
var content = await response.Content.ReadAsStringAsync();
_logger.LogDebug("response {}", content);
if (!response.IsSuccessStatusCode)
{
return result;
}
result.PersonalId = content.Replace("\"", "");
return result;
}
}

View file

@ -0,0 +1,11 @@
namespace Wonky.Client.Models;
public class HistoryFilter
{
public DateTime DeliveryDate { get; set; }
public string Description { get; set; } = "";
public string Sku { get; set; } = "";
public int Quantity { get; set; }
public decimal Price { get; set; }
public decimal Discount { get; set; }
}

View file

@ -3,7 +3,7 @@ using Wonky.Entity.DTO;
namespace Wonky.Client.Models;
public class CreateUserInputModel
public class NewUserForm
{
[MaxLength(128)] public string CompanyId { get; set; } = "";

View file

@ -0,0 +1,7 @@
namespace Wonky.Client.Models;
public class PersonalOrgNumber
{
public string CompanyName { get; set; } = "";
public string PersonalId { get; set; } = "";
}

View file

@ -6,5 +6,5 @@ public class StatisticDrawer
{
public const string Label = "Statistic";
public DateTime LastDateModified { get; set; }
public List<ProductHistoryView> Content { get; set; } = new();
public List<HistoryFilter> Content { get; set; } = new();
}

View file

@ -170,10 +170,16 @@ else
</div>
<div class="col-sm-6"></div>
<label for="vatNumber" class="col-sm-2 col-form-label-sm">Cvr/Org nr.</label>
<div class="col-sm-4">
<div class="col-sm-2">
<InputText id="vatNumber" class="form-control" @bind-Value="Activity.VatNumber"/>
<ValidationMessage For="@(() => Activity.VatNumber)"/>
</div>
<div class="col-sm-2">
@if (Activity.CountryCode.ToLower() == "se")
{
<button type="button" class="btn btn-warning" @onclick="@TestOrgNumber">ORG Nr.</button>
}
</div>
<label for="orderMessage" class="col-sm-2 col-form-label-sm">Note /Kontor</label>
<div class="col-sm-4">
<InputTextArea id="orderMessage" class="form-control" @bind-Value="Activity.OrderMessage"/>

View file

@ -50,7 +50,8 @@ public partial class AdvisorActivityCreatePage : IDisposable
[Inject] public IAdvisorSalesReportRepository ReportRepo { get; set; }
[Inject] public IAdvisorCustomerHistoryRepository HistoryRepo { get; set; }
[Inject] public IUserInfoService UserInfo { get; set; }
[Inject] public IDrawerCabinetService CabinetService { get; set; }
[Inject] public ICabinetDrawerService DrawerService { get; set; }
[Inject] public ISwedishPersonalOrgService OrgService { get; set; }
// #############################################################
[CascadingParameter] private DraftStateProvider DraftProvider { get; set; } = new();
@ -93,7 +94,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
private ActivityDrawer _activityDrawer = new();
private InventoryDrawer _inventoryDrawer = new();
private InvoiceDrawer _invoiceDrawer = new();
/// <summary>
/// Page initialization
@ -101,8 +102,8 @@ public partial class AdvisorActivityCreatePage : IDisposable
protected override async Task OnInitializedAsync()
{
ActivityContext = new EditContext(Activity);
ActivityContext.OnFieldChanged += HandleFieldChanged;
ActivityContext.OnValidationStateChanged += ValidationChanged;
ActivityContext.OnFieldChanged += ContextHandleFieldChanged;
ActivityContext.OnValidationStateChanged += ContextValidationChanged;
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
@ -111,13 +112,13 @@ public partial class AdvisorActivityCreatePage : IDisposable
// User Info
SalesRep = await UserInfo.GetUserInfo();
_infoDrawer = await CabinetService.GetInfoDrawerAsync(CompanyId);
_activityDrawer = await CabinetService.GetActivityDrawerAsync(CompanyId);
_inventoryDrawer = await CabinetService.GetInventoryDrawerAsync(CompanyId);
_invoiceDrawer = await CabinetService.GetInvoiceDrawerAsync(CompanyId);
_infoDrawer = await DrawerService.GetInfoDrawerAsync(CompanyId);
_activityDrawer = await DrawerService.GetActivityDrawerAsync(CompanyId);
_inventoryDrawer = await DrawerService.GetInventoryDrawerAsync(CompanyId);
_invoiceDrawer = await DrawerService.GetInvoiceDrawerAsync(CompanyId);
Company = _infoDrawer.Content;
if (Company.Account.StartsWith("KANVAS"))
{
_kanvas = true;
@ -139,11 +140,12 @@ public partial class AdvisorActivityCreatePage : IDisposable
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}");
}
// 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
@ -170,8 +172,15 @@ public partial class AdvisorActivityCreatePage : IDisposable
Activity.Segment = Company.Segment;
Activity.EanNumber = Company.EanNumber;
// Initialize date variable
/*
* debug loggin
*/
Logger.LogDebug("company.segment = {}", Company.Segment);
Logger.LogDebug("activity.segment = {}", Activity.Segment);
Logger.LogDebug("AdvisorActivityCreatePage => DateTime parser => {}", UserPreference.WorkDate);
/*
* set selected date
*/
SelectedDate = string.IsNullOrWhiteSpace(UserPreference.WorkDate)
? DateTime.Now
: DateTime.Parse(UserPreference.WorkDate);
@ -199,12 +208,28 @@ public partial class AdvisorActivityCreatePage : IDisposable
Activity.ActivityStatusEnum = "order";
PoFormInvalid = false;
}
if (SalesRep.CountryCode.ToLower() == "se")
{
await TestOrgNumber();
}
Working = false;
StateHasChanged();
}
private async Task TestOrgNumber()
{
var result = await OrgService.GetPersonalOrgNumber(Activity.Name);
if (!string.IsNullOrWhiteSpace(result.PersonalId))
{
// Toaster.ShowSuccess($"PersonId for {result.CompanyName} er fundet til at være {result.PersonalId}");
Activity.VatNumber = result.PersonalId;
}
}
/// <summary>
/// Show Activity Overlay
/// </summary>
@ -332,7 +357,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
{
item.Check = false;
}
await CabinetService.StoreInventoryDrawerAsync(CompanyId, _inventoryDrawer);
await DrawerService.StoreInventoryDrawerAsync(CompanyId, _inventoryDrawer);
await CreateActivity();
}
@ -371,7 +396,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
{
return;
}
// working flag is raised after the following validations
Logger.LogDebug("view kanvas activity => {}", JsonSerializer.Serialize(Activity));
switch (_kanvas)
@ -483,25 +508,33 @@ public partial class AdvisorActivityCreatePage : IDisposable
/// <summary>
/// Show Incomplete Organization Number Warning
/// </summary>
private void ShowOrgWarning()
private async Task ShowOrgWarning()
{
if (_kanvas)
{
return;
}
if (OrgWarning)
{
return;
}
OrgWarning = true;
if (Company.CountryCode.ToLower() == "se"
// if (OrgWarning)
// {
// return;
// }
// OrgWarning = true;
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. ");
Toaster.ShowWarning("Org nummer er ufuldstændig. Skal opdateres før bestilling kan sendes.");
Toaster.ShowInfo("Vent mens vi soger at finde de manglende cifre ...");
var result = await OrgService.GetPersonalOrgNumber(Activity.Name);
if (!string.IsNullOrWhiteSpace(result.PersonalId))
{
Activity.VatNumber = result.PersonalId;
Toaster.ShowSuccess($"Org nummer for {result.CompanyName} er fundet at være {result.PersonalId}.");
}
}
// OrgWarning = false;
}
@ -571,9 +604,9 @@ public partial class AdvisorActivityCreatePage : IDisposable
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
private void ContextHandleFieldChanged(object sender, FieldChangedEventArgs e)
{
Logger.LogDebug("ActivityNewPage => HandleFieldChanged => ActivityStatusEnum <= '{}'",
Logger.LogDebug("ActivityNewPage => ContextHandleFieldChanged => ActivityStatusEnum <= '{}'",
Activity.ActivityStatusEnum);
DraftProvider.Draft.DraftType = Activity.ActivityStatusEnum;
if (Activity.ActivityStatusEnum == "noSale")
@ -614,7 +647,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidationChanged(object sender, ValidationStateChangedEventArgs e)
private void ContextValidationChanged(object sender, ValidationStateChangedEventArgs e)
{
if (string.IsNullOrEmpty(Activity.ActivityTypeEnum) && !ReportClosed)
{
@ -627,17 +660,17 @@ public partial class AdvisorActivityCreatePage : IDisposable
&& Company.CountryCode.ToLower() == "se"
&& Utils.StringToDigits(Activity.VatNumber).Length < 10)
{
ShowOrgWarning();
PoFormInvalid = true;
return;
Toaster.ShowError("Org nummer er ufuldstændig ...");
// ShowOrgWarning();
// return;
}
PoFormInvalid = false;
ActivityContext.OnFieldChanged -= HandleFieldChanged;
ActivityContext.OnValidationStateChanged -= ValidationChanged;
ActivityContext.OnFieldChanged -= ContextHandleFieldChanged;
ActivityContext.OnValidationStateChanged -= ContextValidationChanged;
ActivityContext = new EditContext(Activity);
ActivityContext.OnFieldChanged += HandleFieldChanged;
ActivityContext.OnValidationStateChanged += ValidationChanged;
ActivityContext.OnFieldChanged += ContextHandleFieldChanged;
ActivityContext.OnValidationStateChanged += ContextValidationChanged;
}
@ -647,7 +680,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
public void Dispose()
{
Interceptor.DisposeEvent();
ActivityContext.OnFieldChanged -= HandleFieldChanged;
ActivityContext.OnValidationStateChanged -= ValidationChanged;
ActivityContext.OnFieldChanged -= ContextHandleFieldChanged;
ActivityContext.OnValidationStateChanged -= ContextValidationChanged;
}
}

View file

@ -32,7 +32,7 @@ public partial class AdvisorCustomerActivityListPage : IDisposable
[Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public IAdvisorActivityRepository AdvisorActivityRepo { get; set; }
[Inject] public IAdvisorCustomerRepository CompanyRepo { get; set; }
[Inject] public IDrawerCabinetService CabinetService { get; set; }
[Inject] public ICabinetDrawerService DrawerService { get; set; }
[Inject] public ILogger<AdvisorCustomerActivityListPage> Logger { get; set; }
// ######################################################################
@ -58,7 +58,7 @@ public partial class AdvisorCustomerActivityListPage : IDisposable
private async Task GetActivities()
{
Activities = new List<ReportItemView>();
var drawer = await CabinetService.GetActivityDrawerAsync(CompanyId);
var drawer = await DrawerService.GetActivityDrawerAsync(CompanyId);
if (drawer.Content.Any())
{
Activities = drawer.Content

View file

@ -137,7 +137,7 @@
else
{
<div class="col-sm-3">
@* placeholder *@
@(Company.Segment = UserInfo.CountryCode.ToLower() == "no" ? "10" : "11")
</div>
}
@* entity mobile *@

View file

@ -35,21 +35,25 @@ namespace Wonky.Client.Pages;
public partial class AdvisorCustomerInventoryListPage : IDisposable
{
// ##############################################################
/**
* Injected services
*/
[Inject] public ILogger<AdvisorCustomerInventoryListPage> Logger { get; set; }
[Inject] public IAdvisorCustomerHistoryRepository HistoryRepo { get; set; }
[Inject] public IAdvisorCustomerRepository CustomerRepo { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public IToastService Toaster { get; set; }
[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; }
// ##############################################################
[Inject] public ICabinetDrawerService DrawerService { get; set; }
/*
* Parameters
*/
[CascadingParameter] public DraftStateProvider DraftStateProvider { get; set; } = new();
[Parameter] public string CompanyId { get; set; } = "";
// ##############################################################
/*
* Variables
*/
private readonly JsonSerializerOptions _options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
private CompanyDto Company { get; set; } = new();
private bool Working { get; set; } = true;
@ -63,7 +67,7 @@ public partial class AdvisorCustomerInventoryListPage : IDisposable
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
var drawer = await CabinetService.GetInfoDrawerAsync(CompanyId);
var drawer = await DrawerService.GetInfoDrawerAsync(CompanyId);
Company = drawer.Content;
// fetch product inventory
@ -93,7 +97,7 @@ public partial class AdvisorCustomerInventoryListPage : IDisposable
private async Task FetchProductInventory()
{
var drawer = await CabinetService.GetInventoryDrawerAsync(CompanyId);
var drawer = await DrawerService.GetInventoryDrawerAsync(CompanyId);
if (drawer.Content.Any())
{
Inventory = drawer.Content;

View file

@ -32,6 +32,7 @@
<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="ShowInvoiceModal" Invoices="@_invoiceDrawer.Content"/>
}

View file

@ -36,7 +36,7 @@ public partial class AdvisorCustomerInvoiceListPage : IDisposable
[Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public IAdvisorCustomerHistoryRepository HistoryRepo { get; set; }
[Inject] public IToastService Toaster { get; set; }
[Inject] public IDrawerCabinetService CabinetService { get; set; }
[Inject] public ICabinetDrawerService DrawerService { get; set; }
// ##############################################################
[Parameter] public string CompanyId { get; set; } = "";
@ -55,8 +55,8 @@ public partial class AdvisorCustomerInvoiceListPage : IDisposable
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
_infoDrawer = await CabinetService.GetInfoDrawerAsync(CompanyId);
_invoiceDrawer = await CabinetService.GetInvoiceDrawerAsync(CompanyId);
_infoDrawer = await DrawerService.GetInfoDrawerAsync(CompanyId);
_invoiceDrawer = await DrawerService.GetInvoiceDrawerAsync(CompanyId);
_company = _infoDrawer.Content;
_invoices = _invoiceDrawer.Content;

View file

@ -0,0 +1,99 @@
@* Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
*@
@using Wonky.Client.Components
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Advisor")]
@page "/advisor/customers/{CompanyId}/statistic"
<PageTitle>Produktkøb @_company.Name</PageTitle>
<div class="row d-print-none pb-3 mb-3 border-bottom">
<div class="col-sm-5"></div>
<div class="col-sm-2 text-end">
<select class="form-select bg-info text-bg-info me-3" @bind-value="Months" @bind-value:event="oninput" @onchange="@OnSelectChanged">
<option value="6">Vis 6 måneder</option>
<option value="12">Vis 12 måneder</option>
<option value="18">Vis 18 måneder</option>
<option value="24">Vis 24 måneder</option>
</select>
</div>
<div class="col-sm-5 text-end">
<button type="button" class="btn btn-primary me-3" onclick="window.print();"><i class="bi-printer"></i> Udskriv (24 md.)</button>
<a class="btn btn-primary" href="/advisor/customers/@CompanyId"><i class="bi-chevron-left"></i> Tilbage</a>
</div>
</div>
@if (DisplayList.Any())
{
<table class="table table-striped d-print-table">
<thead>
<tr>
<th class="text-center" colspan="6">
<h2>Produktkøb @_company.Name</h2>
<p>Dannet @DateTime.Today.ToLongDateString() (@Months md.)</p>
</th>
</tr>
<tr class="rounded-2 border border-1">
<th scope="col">
Beskrivelse
</th>
<th scope="col">
Vare Nr.
</th>
<th scope="col">
Dato
</th>
<th scope="col" class="text-end">
Antal
</th>
<th scope="col" class="text-end">
Pris
</th>
<th scope="col" class="text-end">
Afslag
</th>
</tr>
</thead>
<tbody>
@foreach (var item in DisplayList)
{
<tr class="align-content-center">
<td>
@item.Description
</td>
<td>
@item.Sku
</td>
<td>
@item.DeliveryDate.ToShortDateString()
</td>
<td class="text-end">
@item.Quantity
</td>
<td class="text-end">
@item.Price
</td>
<td class="text-end">
@(item.Discount)%
</td>
</tr>
}
</tbody>
</table>
}
else
{
<div>Ingen data</div>
}

View file

@ -0,0 +1,54 @@
using Microsoft.AspNetCore.Components;
using Wonky.Client.Local.Services;
using Wonky.Client.Models;
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
#pragma warning disable CS8618
namespace Wonky.Client.Pages;
public partial class AdvisorCustomerStatisticPage
{
[Inject] public ICabinetDrawerService DrawerService { get; set; }
[Parameter] public string CompanyId { get; set; } = "";
private InfoDrawer _infoDrawer = new();
private StatisticDrawer _statisticDrawer = new();
private List<HistoryFilter> DisplayList { get; set; } = new();
private CompanyDto _company = new();
private int Months { get; set; }
protected override async Task OnInitializedAsync()
{
_infoDrawer = await DrawerService.GetInfoDrawerAsync(CompanyId);
_statisticDrawer = await DrawerService.GetStatisticDrawerAsync(CompanyId);
_company = _infoDrawer.Content;
if (Months == 0)
{
Months = 24;
}
FilterDisplayList(Months);
}
private void FilterDisplayList(int months)
{
if (_statisticDrawer.Content.Any())
{
DisplayList = _statisticDrawer.Content
.Where(x => x.DeliveryDate > DateTime.Now.AddMonths(-months))
.OrderBy(x => x.Description).ThenByDescending(x => x.DeliveryDate)
.ToList();
}
}
private void OnSelectChanged(ChangeEventArgs e)
{
var val = e.Value?.ToString();
var x = Convert.ToInt32(val);
if (x > 24) x = 24;
FilterDisplayList(x);
}
}

View file

@ -28,87 +28,88 @@
</div>
}
<div class="row pt-2 pb-2 mb-2 rounded rounded-2 bg-dark text-white">
<div class="col-sm-7">
<div class="col-sm-6">
<div class="mt-1">
<span class="h3">@_infoDrawer.Content.Name</span> <span>(@_infoDrawer.Content.Account)</span>
</div>
</div>
<div class="col-sm-5 text-end">
<div class="col-sm-6 text-end">
@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>
<a class="btn btn-secondary btn-sm me-2" href="/advisor/customers/@CompanyId/workplaces/new"><i class="bi-plus-lg"></i> Arbejdssted</a>
}
<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>
<a class="btn btn-secondary btn-sm me-2" href="/advisor/customers/@CompanyId/statistic"><i class="bi-activity"></i> Statistik</a>
<button class="btn btn-secondary btn-sm me-2" @onclick="@ReloadHistory" disabled="@_working"><i class="bi-repeat"></i> Historik</button>
<a class="btn btn-primary btn-sm me-2" href="/advisor/customers"><i class="bi-chevron-left"></i> Tilbage</a>
</div>
</div>
// erp context
<EditForm EditContext="ErpContext">
<EditForm EditContext="_erpContext">
<DataAnnotationsValidator/>
<div class="row g-1">
@* 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="_infoDrawer.Content.Name" readonly="@(ErpEditDisabled)"/>
<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="_infoDrawer.Content.Attention" readonly="@(ErpEditDisabled)"/>
<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="_infoDrawer.Content.Address1" readonly="@(ErpEditDisabled)"/>
<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="_infoDrawer.Content.Address2" readonly="@(ErpEditDisabled)"/>
<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="_infoDrawer.Content.ZipCode" readonly="@(ErpEditDisabled)"/>
<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="_infoDrawer.Content.City" readonly="@(ErpEditDisabled)"/>
<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="_infoDrawer.Content.Email" readonly="@(ErpEditDisabled)"/>
<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="_infoDrawer.Content.Phone" readonly="@(ErpEditDisabled)"/>
<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="_infoDrawer.Content.Mobile" readonly="@(ErpEditDisabled)"/>
<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="_infoDrawer.Content.EanNumber" readonly="@(ErpEditDisabled)"/>
<InputText id="eanNumber" class="form-control" @bind-Value="_infoDrawer.Content.EanNumber" readonly="@(_erpEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.EanNumber)"></ValidationMessage>
</div>
@if (!Kanvas)
@if (!_kanvas)
{
@* entity segment *@
@if (_userInfo.CountryCode.ToLower() == "dk")
@ -116,7 +117,7 @@
<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="@_infoDrawer.Content.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>
@ -145,7 +146,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 || _infoDrawer.Content.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,7 +156,7 @@
<span class="input-group-text">
<DisplayStateComponent StateClass="@VatState"/>
</span>
<InputText id="vatNumber" class="form-control" @bind-Value="_infoDrawer.Content.VatNumber" readonly="@(VatEditDisabled)"/>
<InputText id="vatNumber" class="form-control" @bind-Value="_infoDrawer.Content.VatNumber" readonly="@(_vatEditDisabled)"/>
<ValidationMessage For="@(() => _infoDrawer.Content.VatNumber)"></ValidationMessage>
</div>
</div>
@ -165,10 +166,10 @@
</div>
@* vat lookup *@
<div class="col-sm-3 d-grid mx-auto">
@switch (CountryCode)
@switch (_countryCode)
{
case "dk":
<button type="button" class="btn btn-info" @onclick="@OpenVatLookupModal" disabled="@(VatEditDisabled)"><i class="bi-search"></i> CVR</button>
<button type="button" class="btn btn-info" @onclick="@OpenVatLookupModal" disabled="@(_vatEditDisabled)"><i class="bi-search"></i> CVR</button>
break;
case "no":
<a class="btn btn-info" href="https://brreg.no/" target="_blank"><i class="bi-search"></i> brreg.no</a>
@ -180,7 +181,7 @@
</div>
@* save vat number *@
<div class="col-sm-3 d-grid mx-auto">
<button type="button" class="btn btn-warning d-block" @onclick="@UpdateVatNumber" disabled="@(VatEditDisabled)"><i class="bi-cloud-arrow-up"></i> Moms/Org Nr.</button>
<button type="button" class="btn btn-warning d-block" @onclick="@UpdateVatNumber" disabled="@(_vatEditDisabled)"><i class="bi-cloud-arrow-up"></i> Moms/Org Nr.</button>
</div>
}
@ -190,42 +191,42 @@
@* activity buttons *@
<div class="row mt-3 mb-3">
<div class="col-sm-3">
@if (!Kanvas)
@if (!_kanvas)
{
<ActionButton ActionLink="@InvoiceLink"
<ActionButton ActionLink="@_invoiceLink"
ButtonText="Faktura"
ButtonType="danger"
Enabled="@EnableLink" />
Enabled="@_enableLink" />
}
</div>
<div class="col-sm-3">
@if (!Kanvas)
@if (!_kanvas)
{
<ActionButton ActionLink="@ActivityLink"
<ActionButton ActionLink="@_activitiesLink"
ButtonText="Tidl. Besøg"
ButtonType="warning"
Enabled="@EnableLink" />
Enabled="@_enableLink" />
}
</div>
<div class="col-sm-3">
@if (!Kanvas )
@if (!_kanvas )
{
<ActionButton ActionLink="@InventoryLink"
<ActionButton ActionLink="@_inventoryLink"
ButtonText="Produkter"
ButtonType="success"
Enabled="@EnableLink" />
Enabled="@_enableLink" />
}
</div>
<div class="col-sm-3">
<ActionButton ActionLink="@NewActivityLink"
<ActionButton ActionLink="@_newActivityLink"
ButtonText="Nyt Besøg"
ButtonType="primary"
Enabled="@EnableActivity">
Enabled="@_enableActivity">
</ActionButton>
</div>
</div>
@if (!Kanvas)
@if (!_kanvas)
{
<hr class="mb-3"/>
@* crm context - OBS note *@
@ -252,7 +253,7 @@
<label for="contacts" class="col-sm-1 col-form-label-sm">Kontakt</label>
<div id="contacts" class="col-sm-11">
<div class="list-group">
<div class="list-group-item list-group-item-action bg-dark text-white" @onclick="@(() => OpenContactPopup(DefaultContact))">
<div class="list-group-item list-group-item-action bg-dark text-white" @onclick="@(() => OpenContactPopup(_defaultContact))">
<div class="row">
<div class="col-sm-4">Stilling</div>
<div class="col-sm-4">Navn</div>
@ -262,9 +263,9 @@
</div>
</div>
</div>
@if (Contacts.Any())
@if (_contacts.Any())
{
@foreach (var contact in Contacts)
@foreach (var contact in _contacts)
{
<div class="list-group-item list-group-item-action" @onclick="@(() => OpenContactPopup(contact))">
<div class="row g-2">
@ -289,14 +290,14 @@
<div class="col-sm-3">
<div class="input-group">
<span class="input-group-text">
<DisplayStateComponent StateClass="@VisitState"/>
<DisplayStateComponent StateClass="@_visitStateCss"/>
</span>
<InputDate id="nextVisit" class="form-control" @bind-Value="@(NextVisit)"/>
<InputDate id="nextVisit" class="form-control" @bind-Value="@(_nextVisit)"/>
</div>
</div>
<label for="lastVisit" class="col-sm-1 col-form-label-sm">Sidste besøg</label>
<div class="col-sm-3">
<InputDate id="lastVisit" class="form-control" @bind-Value="@LastVisit"/>
<InputDate id="lastVisit" class="form-control" @bind-Value="@_lastVisit"/>
</div>
<label for="interval" class="col-sm-2 col-form-label-sm">Uge Interval</label>
<div class="col-sm-2">
@ -315,7 +316,7 @@
<hr class="mb-3"/>
<div class="row pt-3 mb-5">
<div class="col-sm-4 d-grid">
<button type="button" class="btn btn-outline-dark" @onclick="@ToggleVisibility">@ToggleButtonText</button>
<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")
@ -326,13 +327,13 @@
</div>
}
@if (Working)
@if (_working)
{
<WorkingThreeDots/>
}
<VatLookupDkModal VatAddress="CompanyVatAddress" EntityName="@_infoDrawer.Content.Name" VatNumber="@_infoDrawer.Content.VatNumber"
@ref="VatLookupPopup" OnSelectedCompany="SelectedCompanyCallback"/>
<VatLookupDkModal VatAddress="_companyVatAddress" EntityName="@_infoDrawer.Content.Name" VatNumber="@_infoDrawer.Content.VatNumber"
@ref="_vatLookupPopup" OnSelectedCompany="SelectedCompanyCallback"/>
<ContactViewEditModal SelectedContact="SelectedContact" CompanyName="@_infoDrawer.Content.Name"
@ref="ContactViewPopup" OnSaveClicked="WriteContactCallback" OnDeleteClicked="DeleteContactCallback"/>
<ContactViewEditModal SelectedContact="_selectedContact" CompanyName="@_infoDrawer.Content.Name"
@ref="_contactViewPopup" OnSaveClicked="WriteContactCallback" OnDeleteClicked="DeleteContactCallback"/>

View file

@ -48,45 +48,46 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
[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; }
[Inject] public ICabinetDrawerService DrawerService { get; set; }
// ###########################################################################
[Parameter] public string CompanyId { get; set; } = "";
// ###########################################################################
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 EditContext _erpContext;
private DateTime _lastVisit;
private DateTime _nextVisit;
private VatAddress _companyVatAddress = new();
private ContactDto _selectedContact = new();
private ContactDto _defaultContact = new();
private ContactViewEditModal _contactViewPopup = new();
private VatLookupDkModal _vatLookupPopup = new();
private List<ContactDto> _contacts = new();
private string VatState { get; set; } = "the-ugly";
private bool ValidVat { get; set; }
private bool HasFolded { get; set; }
private string CurrentVat { get; set; } = "";
private string CountryCode { get; set; } = "dk";
private string VisitState { get; set; } = "the-ugly";
private int EnableActivity { get; set; } = 1;
private bool Working { get; set; } = true;
private bool CountryIsDk { get; set; } = true;
private bool ErpEditDisabled { get; set; } = true;
private bool VatEditDisabled { get; set; } = true;
private string ToggleButtonText { get; set; } = "";
private bool Kanvas { 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 bool _validVat;
private bool _hasFolded;
private string _currentVat = "";
private string _countryCode = "dk";
private string _visitStateCss = "the-ugly";
private int _enableActivity = 1;
private bool _working = true;
private bool _countryIsDk = true;
private bool _erpEditDisabled = true;
private bool _vatEditDisabled = true;
private string _toggleButtonText = "";
private bool _kanvas;
private string _inventoryLink = "";
private string _activitiesLink = "";
private string _invoiceLink = "";
private string _newActivityLink = "";
private int _enableLink = 1;
private ActivityDrawer _activityDrawer = new();
private InventoryDrawer _inventoryDrawer = new();
private InvoiceDrawer _invoiceDrawer = new();
private StatisticDrawer _statisticDrawer = new();
private UserManagerEditView _userInfo = new();
private InfoDrawer _infoDrawer = new();
private CompanyDto _company = new();
private string _companyId = "";
protected override async Task OnInitializedAsync()
{
@ -98,115 +99,126 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/*
* default contact
*/
DefaultContact = new ContactDto { CompanyId = CompanyId, ContactId = "", FirstName = "" };
_defaultContact = new ContactDto { CompanyId = CompanyId, ContactId = "", FirstName = "" };
/*
* 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";
_inventoryLink = $"/advisor/customers/{CompanyId}/history/inventory";
_activitiesLink = $"/advisor/customers/{CompanyId}/activities";
_invoiceLink = $"/advisor/customers/{CompanyId}/invoices";
_newActivityLink = $"/advisor/customers/{CompanyId}/activities/new";
/*
* setup form context
*/
ErpContext = new EditContext(_infoDrawer.Content);
_erpContext = new EditContext(_company);
/*
* assign event handlers to context
*/
ErpContext.OnFieldChanged += HandleFieldChanged;
ErpContext.OnValidationStateChanged += ValidationChanged!;
_erpContext.OnFieldChanged += HandleFieldChanged;
_erpContext.OnValidationStateChanged += ValidationChanged!;
/*
* fetch user info from local storage
*/
_userInfo = await UserInfoService.GetUserInfo();
CountryCode = _userInfo.CountryCode.ToLower();
CountryIsDk = CountryCode == "dk";
_countryCode = _userInfo.CountryCode.ToLower();
_countryIsDk = _countryCode == "dk";
/*
* get InfoDrawer.Company from drawer
*/
_infoDrawer = await CabinetService.GetInfoDrawerAsync(CompanyId);
_infoDrawer.Content = _infoDrawer.Content;
_infoDrawer = await DrawerService.GetInfoDrawerAsync(CompanyId, force:true);
_company = _infoDrawer.Content;
/*
* internal EnableActivity flag
* internal _enableActivity flag
*/
EnableActivity = _infoDrawer.Content.ValidVat;
_enableActivity = _company.ValidVat;
/*
* if KANVAS or NY override EnableActivity
* 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 (_infoDrawer.Content.Account.StartsWith("KANVAS"))
Kanvas = true;
if (_company.Account.StartsWith("NY") || _company.Account.StartsWith("KANVAS") ||
string.IsNullOrWhiteSpace(_company.Account))
{
_enableActivity = 1;
}
if (_company.Account.StartsWith("KANVAS"))
{
_kanvas = true;
}
/*
* only execute if the InfoDrawer.Company is not KANVAS
*/
if (!Kanvas)
if (!_kanvas)
{
Logger.LogDebug("InfoDrawer.Company => {}", JsonSerializer.Serialize(_infoDrawer.Content));
Logger.LogDebug("_company => {}", JsonSerializer.Serialize(_company));
/*
* 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();
_toggleButtonText = _company.IsHidden == 0 ? "Udelad kunde i oversigt" : "Brug Normal Visning";
_currentVat = _company.VatNumber;
_company.CountryCode = _userInfo.CountryCode.ToLower();
/*
* visit interval init
*/
if (_infoDrawer.Content.Interval == 0)
_infoDrawer.Content.Interval = 8;
if (_company.Interval == 0)
{
_company.Interval = 8;
}
/*
* visit date init
*/
LastVisit = DateTime.Parse(_infoDrawer.Content.LastVisit);
NextVisit = DateTime.Parse(_infoDrawer.Content.NextVisit);
_lastVisit = DateTime.Parse(_company.LastVisit);
_nextVisit = DateTime.Parse(_company.NextVisit);
/*
* if no previous visit is registered - force last visit date to 2020
*/
if (LastVisit.Year < 2020)
LastVisit = DateTime.Parse("2020-01-01");
if (_lastVisit.Year < 2020)
{
_lastVisit = DateTime.Parse("2020-01-01");
}
/*
* set next visit according to last visit and interval
*/
if (!_infoDrawer.Content.ValidDateSpan())
NextVisit = LastVisit.AddDays(_infoDrawer.Content.Interval * 7);
if (!_company.ValidDateSpan())
{
_nextVisit = _lastVisit.AddDays(_company.Interval * 7);
}
/*
* display urgency of next visit
*/
VisitState = Utils.MapVisitState($"{NextVisit:yyyy-MM-dd}");
_visitStateCss = Utils.MapVisitState($"{_nextVisit:yyyy-MM-dd}");
/*
* handle InfoDrawer.Company out of business case
*/
if (_infoDrawer.Content.HasFolded == 1)
if (_company.HasFolded == 1)
{
/*
* this is only used if user has selected to show closed companies
*/
HasFolded = true;
_hasFolded = true;
VatState = "the-dead";
VisitState = "the-dead";
_visitStateCss = "the-dead";
}
else
{
/*
* 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
_company.ValidVat = VatUtils.ValidateFormat(_company.CountryCode, _company.VatNumber) ? 1 : 0;
_validVat = _company.ValidVat == 1; // true/false flag set if InfoDrawer.Company has a valid vatNumber
VatState = _company.ValidVat == 1 ? "the-good" : "no-vat"; // assign css class
}
/*
* create search address from address
*/
if (CountryIsDk)
if (_countryIsDk)
{
CompanyVatAddress = PrepareVatAddress(_infoDrawer.Content);
_companyVatAddress = PrepareVatAddress(_company);
}
await GetContacts(CompanyId);
}
/*
* remove loading image
*/
Working = false;
_working = false;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
@ -214,40 +226,42 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
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);
_invoiceDrawer = await DrawerService.GetInvoiceDrawerAsync(CompanyId, true);
_inventoryDrawer = await DrawerService.GetInventoryDrawerAsync(CompanyId, true);
_activityDrawer = await DrawerService.GetActivityDrawerAsync(CompanyId, true);
_statisticDrawer = await DrawerService.GetStatisticDrawerAsync(CompanyId, true);
}
}
private async Task ReloadHistory()
{
_enableLink = 0;
_enableActivity = 0;
Toaster.ShowWarning("Arbejder på sagen ...");
var newSync = await HistoryRepo.RequestErpSync(CompanyId, _infoDrawer.Content.HistorySync, false);
var newSync = await HistoryRepo.RequestErpSync(CompanyId, _company.HistorySync, false);
if (!string.IsNullOrWhiteSpace(newSync))
{
_infoDrawer.Content.HistorySync = newSync;
_infoDrawer.Content = _infoDrawer.Content;
await CabinetService.StoreInfoDrawerAsync(CompanyId, _infoDrawer);
_infoDrawer = await DrawerService.GetInfoDrawerAsync(CompanyId, true);
_invoiceDrawer = await DrawerService.GetInvoiceDrawerAsync(CompanyId, true);
_inventoryDrawer = await DrawerService.GetInventoryDrawerAsync(CompanyId, true);
_activityDrawer = await DrawerService.GetActivityDrawerAsync(CompanyId, true);
_statisticDrawer = await DrawerService.GetStatisticDrawerAsync(CompanyId, true);
}
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 ....");
_enableLink = 1;
_enableActivity = _company.ValidVat;
}
private void ToggleErpEdit()
{
ErpEditDisabled = !ErpEditDisabled;
_erpEditDisabled = !_erpEditDisabled;
}
private void ToggleVatEdit()
{
VatEditDisabled = !VatEditDisabled;
_vatEditDisabled = !_vatEditDisabled;
}
@ -256,12 +270,12 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/*
* 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";
_company.IsHidden = _company.IsHidden == 0 ? 1 : 0;
_toggleButtonText = _company.IsHidden == 0 ? "Udelad kunde i oversigt" : "Brug Normal Visning";
/*
* send update reqeust
*/
await CustomerRepo.UpdateCrmData(CompanyId, _infoDrawer.Content);
await CustomerRepo.UpdateCrmData(CompanyId, _company);
}
@ -270,38 +284,38 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/*
* load contacts
*/
Contacts = await ContactRepo.GetContacts(companyId);
if (Contacts.Any() && Contacts.Count > 1)
_contacts = await ContactRepo.GetContacts(companyId);
if (_contacts.Any() && _contacts.Count > 1)
{
Contacts = Contacts.OrderBy(x => x.FirstName).ToList();
_contacts = _contacts.OrderBy(x => x.FirstName).ToList();
}
}
private void OpenVatLookupModal()
{
VatLookupPopup.Show();
_vatLookupPopup.Show();
}
private void SelectedCompanyCallback(VirkRegInfo regInfo)
{
ValidVat = regInfo.States[0].State.ToLower() == "normal";
_infoDrawer.Content.HasFolded = ValidVat ? 1 : 0;
EnableActivity = ValidVat ? 1 : 0;
_validVat = regInfo.States[0].State.ToLower() == "normal";
_company.HasFolded = _validVat ? 1 : 0;
_enableActivity = _validVat ? 1 : 0;
VatState = regInfo.States[0].State.ToLower() == "normal" ? "the-good" : "the-dead";
/*
* set new properties if flagged
*/
if (regInfo.SyncAll)
{
_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.Name = regInfo.Name;
_company.Address1 = regInfo.Address;
_company.Address2 = regInfo.CoName;
_company.ZipCode = regInfo.ZipCode;
_company.City = regInfo.City;
}
_infoDrawer.Content.VatNumber = regInfo.VatNumber;
_company.VatNumber = regInfo.VatNumber;
}
@ -310,11 +324,11 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/*
* pass contact to popup
*/
SelectedContact = contact;
_selectedContact = contact;
/*
* show the popup
*/
ContactViewPopup.Show();
_contactViewPopup.Show();
}
/// <summary>
@ -323,9 +337,9 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/// <param name="contact"></param>
private async Task WriteContactCallback(ContactDto contact)
{
if (Working)
if (_working)
return;
Working = true;
_working = true;
/*
* if ContactId is empty it is a new contact
*/
@ -346,12 +360,12 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/*
* reset default contact
*/
SelectedContact = new ContactDto();
_selectedContact = new ContactDto();
/*
* reload contacts from backend
*/
await GetContacts(CompanyId);
Working = false;
_working = false;
}
/// <summary>
@ -360,9 +374,9 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/// <param name="contactId"></param>
private async Task DeleteContactCallback(string contactId)
{
if (Working)
if (_working)
return;
Working = true;
_working = true;
/*
* send delete request to backend
*/
@ -370,12 +384,12 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/*
* reset default contact
*/
SelectedContact = new ContactDto();
_selectedContact = new ContactDto();
/*
* reload contacts from backend
*/
await GetContacts(CompanyId);
Working = false;
_working = false;
}
/// <summary>
@ -384,21 +398,22 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/// <returns>true/false</returns>
private async Task PostCrmData()
{
if (Working)
if (_working)
return;
Working = true;
_working = true;
Toaster.ShowInfo("Vent venligst ...");
_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.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))
{
await UpdateInfoDrawer(company);
StateHasChanged();
_infoDrawer = await DrawerService.GetInfoDrawerAsync(CompanyId, true);
_company = _infoDrawer.Content;
}
Working = false;
Toaster.ClearAll();
StateHasChanged();
_working = false;
Toaster.ShowSuccess("Dine CRM data er opdateret.");
}
/// <summary>
@ -407,19 +422,20 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/// <returns></returns>
private async Task UpdateErpData()
{
if (Working)
if (_working)
return;
ErpEditDisabled = true;
Working = true;
_erpEditDisabled = true;
_working = true;
Toaster.ShowInfo("Vent venligst ...");
var company = await CustomerRepo.UpdateErpData(CompanyId, _infoDrawer.Content);
if (!string.IsNullOrWhiteSpace(company.CompanyId))
var result = await CustomerRepo.UpdateErpData(CompanyId, _company);
if (!string.IsNullOrWhiteSpace(result.CompanyId))
{
await UpdateInfoDrawer(company);
StateHasChanged();
_infoDrawer = await DrawerService.GetInfoDrawerAsync(CompanyId, true);
_company = _infoDrawer.Content;
}
Working = false;
Toaster.ClearAll();
StateHasChanged();
_working = false;
Toaster.ShowSuccess("ERP data er opdateret. Notifikation til kontoret er sendt.");
}
/// <summary>
@ -431,25 +447,26 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/*
* VAT format validation
*/
if (!VatUtils.ValidateFormat(_infoDrawer.Content.CountryCode, _infoDrawer.Content.VatNumber))
if (!VatUtils.ValidateFormat(_company.CountryCode, _company.VatNumber))
{
Toaster.ShowError($"Moms Nummer ugyldigt");
return;
}
if (Working)
if (_working)
return;
Working = true;
VatEditDisabled = true;
_working = true;
_vatEditDisabled = true;
Toaster.ShowInfo("Vent venligst ...");
var company = await CustomerRepo.UpdateCompanyVat(CompanyId, _infoDrawer.Content.VatNumber);
if (!string.IsNullOrWhiteSpace(company.CompanyId))
var result = await CustomerRepo.UpdateCompanyVat(CompanyId, _company.VatNumber);
if (!string.IsNullOrWhiteSpace(result.CompanyId))
{
await UpdateInfoDrawer(company);
StateHasChanged();
_infoDrawer = await DrawerService.GetInfoDrawerAsync(CompanyId, true);
_company = _infoDrawer.Content;
}
Toaster.ClearAll();
Working = false;
StateHasChanged();
_working = false;
Toaster.ShowSuccess("Moms Nr. er opdateret.");
}
/// <summary>
@ -491,17 +508,14 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
*/
return new VatAddress();
}
private async Task UpdateInfoDrawer(CompanyDto company)
{
_infoDrawer.Content = company;
await CabinetService.StoreInfoDrawerAsync(CompanyId, _infoDrawer);
}
/// <summary>
/// Force enable activity
/// </summary>
private void ForceActivity()
{
EnableActivity = EnableActivity == 0 ? 1 : 0;
_enableActivity = _enableActivity == 0 ? 1 : 0;
}
/// <summary>
@ -512,18 +526,18 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
private void HandleFieldChanged(object? sender, FieldChangedEventArgs? e)
{
NextVisit = LastVisit.AddDays(_infoDrawer.Content.Interval * 7);
_nextVisit = _lastVisit.AddDays(_company.Interval * 7);
/*
* avoid nesting if by assuming ValidVat is false
* avoid nesting if by assuming _validVat is false
*/
ValidVat = false;
_validVat = false;
/*
* set ValidVat true if validation succeed
* set _validVat true if validation succeed
*/
if (VatUtils.ValidateFormat(_infoDrawer.Content.CountryCode, _infoDrawer.Content.VatNumber))
if (VatUtils.ValidateFormat(_company.CountryCode, _company.VatNumber))
{
ValidVat = true;
EnableActivity = 1;
_validVat = true;
_enableActivity = 1;
}
StateHasChanged();
@ -536,13 +550,13 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/// <param name="e"></param>
private void ValidationChanged(object sender, ValidationStateChangedEventArgs e)
{
ErpContext.OnFieldChanged -= HandleFieldChanged;
ErpContext.OnValidationStateChanged -= ValidationChanged!;
_erpContext.OnFieldChanged -= HandleFieldChanged;
_erpContext.OnValidationStateChanged -= ValidationChanged!;
ErpContext = new EditContext(_infoDrawer.Content);
_erpContext = new EditContext(_company);
ErpContext.OnFieldChanged += HandleFieldChanged;
ErpContext.OnValidationStateChanged += ValidationChanged!;
_erpContext.OnFieldChanged += HandleFieldChanged;
_erpContext.OnValidationStateChanged += ValidationChanged!;
}
/// <summary>
@ -551,7 +565,7 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
public void Dispose()
{
Interceptor.DisposeEvent();
ErpContext.OnFieldChanged -= HandleFieldChanged;
ErpContext.OnValidationStateChanged -= ValidationChanged!;
_erpContext.OnFieldChanged -= HandleFieldChanged;
_erpContext.OnValidationStateChanged -= ValidationChanged!;
}
}

View file

@ -28,59 +28,59 @@
</div>
</div>
<div class="card-body">
<EditForm EditContext="FormContext" OnValidSubmit="CreateUserRequest">
<EditForm EditContext="FormContext" OnValidSubmit="PostNewUser">
<DataAnnotationsValidator/>
<div class="row g-3 mb-3">
@* firstName *@
<div class="col-sm-6">
<div class="form-floating">
<InputText id="firstName" class="form-control" @bind-Value="CreateUserInput.FirstName" placeholder="Fornavn"/>
<ValidationMessage For="@(() => CreateUserInput.FirstName)"></ValidationMessage>
<InputText id="firstName" class="form-control" @bind-Value="UserForm.FirstName" placeholder="Fornavn"/>
<ValidationMessage For="@(() => UserForm.FirstName)"></ValidationMessage>
<label for="firstName">Fornavn</label>
</div>
</div>
@* lastName *@
<div class="col-sm-6">
<div class="form-floating">
<InputText id="lastName" class="form-control" @bind-Value="CreateUserInput.LastName" placeholder="Efternavn"/>
<ValidationMessage For="@(() => CreateUserInput.LastName)"></ValidationMessage>
<InputText id="lastName" class="form-control" @bind-Value="UserForm.LastName" placeholder="Efternavn"/>
<ValidationMessage For="@(() => UserForm.LastName)"></ValidationMessage>
<label for="lastName">Efternavn</label>
</div>
</div>
@* email *@
<div class="col-sm-6">
<div class="form-floating">
<InputText id="email" class="form-control" @bind-Value="CreateUserInput.Email" placeholder="Email"/>
<ValidationMessage For="@(() => CreateUserInput.Email)"></ValidationMessage>
<InputText id="email" class="form-control" @bind-Value="UserForm.Email" placeholder="Email"/>
<ValidationMessage For="@(() => UserForm.Email)"></ValidationMessage>
<label for="email">Email</label>
</div>
</div>
@* phoneNumber *@
<div class="col-sm-6">
<div class="form-floating">
<InputText id="phoneNumber" class="form-control" @bind-Value="CreateUserInput.PhoneNumber" placeholder="Direkte Telefon Nr."/>
<ValidationMessage For="@(() => CreateUserInput.PhoneNumber)"></ValidationMessage>
<InputText id="phoneNumber" class="form-control" @bind-Value="UserForm.PhoneNumber" placeholder="Direkte Telefon Nr."/>
<ValidationMessage For="@(() => UserForm.PhoneNumber)"></ValidationMessage>
<label for="phoneNumber">Direkte Telefon Nr.</label>
</div>
</div>
@* salesRep *@
<div class="col-sm-6">
<div class="form-floating">
<InputText id="salesRep" class="form-control" @bind-Value="CreateUserInput.SalesRep" placeholder="Sælger"/>
<ValidationMessage For="@(() => CreateUserInput.SalesRep)"></ValidationMessage>
<InputText id="salesRep" class="form-control" @bind-Value="UserForm.SalesRep" placeholder="Sælger"/>
<ValidationMessage For="@(() => UserForm.SalesRep)"></ValidationMessage>
<label for="salesRep">Sælger</label>
</div>
</div>
@* countrycode *@
<div class="col-sm-6">
<div class="form-floating">
<InputSelect id="countryCode" class="form-control" @bind-Value="CreateUserInput.CountryCode" placeholder="Landekode">
<InputSelect id="countryCode" class="form-control" @bind-Value="UserForm.CountryCode" placeholder="Landekode">
<option value="" disabled>Klik for valg</option>
<option value="DK">Danmark</option>
<option value="NO">Norge</option>
<option value="SE">Sverige</option>
</InputSelect>
<ValidationMessage For="@(() => CreateUserInput.CountryCode)"></ValidationMessage>
<ValidationMessage For="@(() => UserForm.CountryCode)"></ValidationMessage>
<label for="countryCode">Landekode</label>
</div>
</div>
@ -116,12 +116,12 @@
</div>
</div>
<div class="row @(_webShop && !string.IsNullOrWhiteSpace(CreateUserInput.CountryCode) ? "inno-display" : "inno-hidden")">
<div class="row @(UserForm.EShop && !string.IsNullOrWhiteSpace(UserForm.CountryCode) ? "inno-display" : "inno-hidden")">
<div class="col-sm-10">
@_company.Name - @_company.Account - @_company.Phone
</div>
<div class="col-sm-2">
<button type="button" class="btn btn-primary" @onclick="@ShowSearchOverlay">
<button type="button" class="btn btn-primary" @onclick="@ShowCompanySearchOverlay">
Tilknyt kunde ...
</button>
</div>
@ -143,14 +143,14 @@
<div class="col-sm-6">
<div class="form-floating">
<InputText id="newPasswd" class="form-control" @bind-Value="@PasswdInput.NewPassword" placeholder="Ny Adgangskode"/>
<ValidationMessage For="@(() => CreateUserInput.NewPassword)"></ValidationMessage>
<ValidationMessage For="@(() => UserForm.NewPassword)"></ValidationMessage>
<label for="newPasswd">Ny Adgangskode</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-floating">
<InputText id="verifyPasswd" class="form-control" @bind-Value="@PasswdInput.ConfirmPassword" placeholder="Gentag Adgangskode"/>
<ValidationMessage For="@(() => CreateUserInput.ConfirmPassword)"></ValidationMessage>
<ValidationMessage For="@(() => UserForm.ConfirmPassword)"></ValidationMessage>
<label for="verifyPasswd">Gentag Adgangskode</label>
</div>
</div>
@ -165,4 +165,4 @@
<WorkingThreeDots/>
}
<CustomerSearchOverlay CountryCode="@CreateUserInput.CountryCode" OnSelected="@OnSelectCompany" @ref="SearchOverlay" />
<CustomerSearchOverlay CountryCode="@UserForm.CountryCode" OnSelected="@OnSelectCompany" @ref="SearchOverlay" />

View file

@ -38,8 +38,8 @@ public partial class SystemUserCreatePage : IDisposable
[Inject] public IToastService Toaster { get; set; }
// #############################################################
private CreateUserInputModel CreateUserInput { get; set; } = new();
private UserManagerCreateView CreateUserDto { get; set; } = new();
private NewUserForm UserForm { get; set; } = new();
private ManagerNewUserDto NewUserDto { get; set; } = new();
private EditContext FormContext { get; set; }
private bool ContextInvalid { get; set; } = true;
private bool Working { get; set; } = true;
@ -48,55 +48,93 @@ public partial class SystemUserCreatePage : IDisposable
private RoleAssignment AssignedRoles { get; set; } = new();
private readonly JsonSerializerOptions _options = new() { PropertyNameCaseInsensitive = true };
private bool _webShop;
private CompanyDto _company = new();
private CustomerSearchOverlay SearchOverlay { get; set; }
protected override void OnParametersSet()
protected override void OnInitialized()
{
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
FormContext = new EditContext(CreateUserInput);
FormContext = new EditContext(UserForm);
FormContext.OnFieldChanged += ContextHandleFieldChanged!;
FormContext.OnFieldChanged += ContextHandleFieldChanged;
FormContext.OnValidationStateChanged += ContextValidationChanged;
Working = false;
}
private async Task CreateUserRequest()
/// <summary>
/// Post New User To Backend
/// </summary>
private async Task PostNewUser()
{
ReadOnly = true;
Working = true;
CreateUserInput.AssignedRoles = Utils.MapSaveAssignedRoles(AssignedRoles);
CreateUserInput.EShop = _webShop;
CreateUserInput.CompanyId = !_webShop ? "" : _company.CompanyId;
/*
* UI message
*/
Toaster.ShowInfo("Sender data til server ...");
CreateUserDto = Mapper.MapCreateUser(CreateUserInput);
await UserRepo.CreateUser(CreateUserDto);
/*
* Make fields reaonly
*/
ReadOnly = true;
/*
* Raise working flag
*/
Working = true;
/*
* ensure companyId is set correct
*/
UserForm.CompanyId = !UserForm.EShop ? "" : _company.CompanyId;
/*
* Map RoleAssignment
*/
UserForm.AssignedRoles = Utils.MapSaveAssignedRoles(AssignedRoles);
/*
* Map form input to a model the backend expects
*/
NewUserDto = Mapper.MapCreateUser(UserForm);
/*
* Send Post Request
*/
await UserRepo.PostNewUser(NewUserDto);
/*
* Lower working flag
*/
Working = false;
/*
* UI message
*/
Toaster.ShowInfo("Bruger er oprettet ...");
}
private void ShowSearchOverlay()
/// <summary>
/// Show Company Search Overlay
/// </summary>
private void ShowCompanySearchOverlay()
{
SearchOverlay.Show();
}
/// <summary>
/// Company Selected Callback
/// </summary>
/// <param name="company"></param>
private void OnSelectCompany(CompanyDto company)
{
_company = company;
CreateUserInput.CompanyId = _company.CompanyId;
UserForm.CompanyId = _company.CompanyId;
SearchOverlay.Hide();
}
/// <summary>
/// Callback Handling Context Field Changes
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ContextHandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
Logger.LogDebug("contextHandleFieldChanged => e.FieldIdentifier.FieldName {}", e.FieldIdentifier.FieldName);
@ -118,11 +156,11 @@ public partial class SystemUserCreatePage : IDisposable
AssignedRoles.Office = false;
AssignedRoles.Supervisor = false;
AssignedRoles.Warehouse = false;
_webShop = true;
UserForm.EShop = true;
}
else
{
_webShop = false;
UserForm.EShop = false;
}
ContextInvalid = !FormContext.Validate();
@ -130,6 +168,11 @@ public partial class SystemUserCreatePage : IDisposable
}
/// <summary>
/// Callback Handling Context Validation Changes
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ContextValidationChanged(object? sender, ValidationStateChangedEventArgs e)
{
ContextInvalid = true;
@ -137,17 +180,20 @@ public partial class SystemUserCreatePage : IDisposable
FormContext.OnFieldChanged -= ContextHandleFieldChanged;
FormContext.OnValidationStateChanged -= ContextValidationChanged;
FormContext = new EditContext(CreateUserInput);
FormContext = new EditContext(UserForm);
FormContext.OnFieldChanged += ContextHandleFieldChanged;
FormContext.OnValidationStateChanged += ContextValidationChanged;
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Interceptor.DisposeEvent();
FormContext.OnFieldChanged -= ContextHandleFieldChanged!;
FormContext.OnFieldChanged -= ContextHandleFieldChanged;
FormContext.OnValidationStateChanged -= ContextValidationChanged;
}
}

View file

@ -100,9 +100,11 @@ builder.Services.AddScoped<VatInfoLookupService>();
// activity draft service
builder.Services.AddScoped<OrderDraftService>();
// cabinet service
builder.Services.AddScoped<IDrawerCabinetService, DrawerCabinetService>();
builder.Services.AddScoped<ICabinetDrawerService, CabinetDrawerService>();
// storage
builder.Services.AddBlazoredLocalStorage();
// Swedisd Personal Company OrgNo Search
builder.Services.AddScoped<ISwedishPersonalOrgService, SwedishPersonalOrgService>();
// ---------------------------------------

View file

@ -1,15 +1,15 @@
{
"appInfo": {
"name": "Wonky Online",
"version": "163.0",
"version": "168.1",
"rc": true,
"sandBox": true,
"sandBox": false,
"image": "grumpy-coder.png"
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Debug",
"Default": "None",
"System": "None",
"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",
@ -42,6 +42,7 @@
"servicesVatDk": "api/v2/services/virk",
"serviceVatEu": "api/v2/services/vies",
"servicesVatNo": "api/v2/services/brReg",
"servicesVatSe": "api/v2/services/allabolag",
"servicesAuth": "v2/token",
"sync": "api/v2/sync",
"syncInvoice": "api/v2/sync/invoices",

View file

@ -132,6 +132,11 @@ public class ApiConfig
/// </summary>
public string ServicesVatNo { get; set; } = "";
/// <summary>
/// VAT registrar url EU
/// </summary>
public string ServicesVatSe { get; set; } = "";
/// <summary>
/// VAT registrar url EU
/// </summary>

View file

@ -19,7 +19,7 @@ using System.ComponentModel.DataAnnotations;
namespace Wonky.Entity.DTO;
public class UserManagerCreateView
public class ManagerNewUserDto
{
[MaxLength(128)] public string CompanyId { get; set; } = "";