This commit is contained in:
Frede Hundewadt 2022-04-06 12:25:21 +02:00
parent 18058a4230
commit 38b247f048
16 changed files with 209 additions and 229 deletions

View file

@ -28,7 +28,7 @@
<td>@company.Name</td> <td>@company.Name</td>
<td>@company.Account</td> <td>@company.Account</td>
<td>@company.City</td> <td>@company.City</td>
<td><a class="btn btn-primary mb-1" href="/company/account/@company.Account">Vis</a></td> <td><a class="btn btn-primary mb-1" href="/company/@company.CompanyId">Vis</a></td>
</tr> </tr>
} }
</tbody> </tbody>

View file

@ -30,15 +30,6 @@ namespace Wonky.Client.Components
private Confirmation _confirmation = new (); private Confirmation _confirmation = new ();
private string _companyId = string.Empty; private string _companyId = string.Empty;
private static string VisitState(string nextVisit)
{
var theUgly = DateTime.Parse(nextVisit);
var theBad = theUgly.AddDays(-14);
if (DateTime.Now >= theUgly)
return "the-ugly";
return DateTime.Now >= theBad ? "the-bad" : "the-good";
}
private void CallConfirmationModal(string companyId) private void CallConfirmationModal(string companyId)
{ {
_companyId = companyId; _companyId = companyId;

View file

@ -22,7 +22,7 @@ namespace Wonky.Client.Components;
public partial class RegInfoCompany public partial class RegInfoCompany
{ {
[Inject] public VirkRegistryService VirkRegistryService { get; set; } = null!; [Inject] public VatOwnerLookupService VatOwnerLookupService { get; set; } = null!;
[Parameter] public string VatNumber { get; set; } = ""; [Parameter] public string VatNumber { get; set; } = "";
private VirkRegInfo VirkRegInfo { get; set; } = new(); private VirkRegInfo VirkRegInfo { get; set; } = new();
private bool _hideMe = true; private bool _hideMe = true;
@ -34,7 +34,7 @@ public partial class RegInfoCompany
_virkParams.VatNumber = VatNumber; _virkParams.VatNumber = VatNumber;
if (string.IsNullOrWhiteSpace(VirkRegInfo.VatNumber)) if (string.IsNullOrWhiteSpace(VirkRegInfo.VatNumber))
{ {
var result = await VirkRegistryService.QueryVirkRegistry(_virkParams); var result = await VatOwnerLookupService.QueryVirkRegistry(_virkParams);
if (result.Any()) if (result.Any())
{ {
_currentState = VirkRegInfo.States[^1].State; _currentState = VirkRegInfo.States[^1].State;

View file

@ -1,128 +1,99 @@
namespace Wonky.Client.Helpers; namespace Wonky.Client.Helpers;
public class VatUtils public static class VatUtils
{ {
public static bool CheckVat(string countryCode, string vatNumber) // https://ec.europa.eu/taxation_customs/vies/faqvies.do#item_11
// https://ec.europa.eu/taxation_customs/vies/
public static bool ValidateFormat(string countryCode, string vatNumber)
{ {
return countryCode.ToUpperInvariant() switch return countryCode.ToUpperInvariant() switch
{ {
"DK" => CheckVatNumberDenmark(vatNumber), "DK" => ValidateFormatDk(vatNumber),
"NO" => CheckVatNumberNorway(vatNumber), "NO" => ValidateFormatNo(vatNumber),
"SE" => CheckVatNumberSweden(vatNumber), "SE" => ValidateFormatSe(vatNumber),
_ => false _ => false
}; };
} }
private static bool CheckVatNumberNorway(string vatNumber) private static bool ValidateFormatDk(string vatNumber)
{
// https://wiki.scn.sap.com/wiki/display/CRM/Norway
// 12 digits
// C1..C8 random 0 to 9
// C9 Check MOD11
// C10 C11 C12 chars == MVA
return vatNumber.Length >= 9 && CheckModolus11(vatNumber.Substring(0, 8));
}
private static bool CheckVatNumberDenmark(string vatNumber)
{ {
// https://wiki.scn.sap.com/wiki/display/CRM/Denmark // https://wiki.scn.sap.com/wiki/display/CRM/Denmark
// 8 digits 0 to 9 // 8 digits 0 to 9
// C1..C7 // C1..C7
// C8 Modulo 11 check digit // C8 check-digit MOD11
// C1 > 0 // C1 > 0
// R = (2*C1 + 7*C2 + 6*C3 + 5*C4 + 4*C5 + 3*C6 + 2*C7 + C8) // R = (2*C1 + 7*C2 + 6*C3 + 5*C4 + 4*C5 + 3*C6 + 2*C7 + C8)
if ((int)char.GetNumericValue(vatNumber[0]) < 1 || vatNumber.Length > 8) var vatToCheck = SanitizeVatNumber(vatNumber);
if (vatToCheck.Length != 8)
return false; return false;
return CheckModolus11(vatNumber); if ((int)char.GetNumericValue(vatToCheck[0]) < 1 || vatToCheck.Length > 8)
return false;
return ValidateMod11(vatToCheck);
} }
private static bool CheckVatNumberSweden(string vatNumber) private static bool ValidateFormatNo(string vatNumber)
{
// https://wiki.scn.sap.com/wiki/display/CRM/Norway
// 12 digits
// C1..C8 random 0 to 9
// C9 check-digit MOD11
// C10 C11 C12 chars == MVA
var vatToCheck = SanitizeVatNumber(vatNumber);
if (vatToCheck.Length != 9)
return false;
return long.Parse(vatToCheck) != 0 && ValidateMod11(vatNumber);
}
private static bool ValidateFormatSe(string vatNumber)
{ {
// https://wiki.scn.sap.com/wiki/display/CRM/Sweden // https://wiki.scn.sap.com/wiki/display/CRM/Sweden
// 12 digits 0 to 9 // 12 digits 0 to 9
// C1 = (10 - (R + C2 + C4 + C6 + C8) modulo 10) modulo 10 // C10 = (10 (18 + 5 + 1 + 8 + 4)MOD10 10) MOD10
// R = S1 + S3 + S5 + S7 + S9 // R = S1 + S3 + S5 + S7 + S9
// Si = int(Ci/5) + (Ci*2) modulo 10) // Si = int(Ci/5) + (Ci*2)MOD10)
// C11 C12 >= 01 <= 94 // https://www.skatteverket.se/skatter/mervardesskattmoms/momsregistreringsnummer.4.18e1b10334ebe8bc80002649.html
// // C11 C12 == 01 (De två sista siffrorna är alltid 01)
// Validate full string using Luhn algoritm var vatToCheck = SanitizeVatNumber(vatNumber);
// 12 chars if (vatToCheck.Length < 10 || long.Parse(vatToCheck) == 0)
if (vatNumber.Length != 12)
return false; return false;
// validate if number
if (!int.TryParse(vatNumber, out var result)) var r = new[] { 0, 2, 4, 6, 8 }
return false; .Sum(m => (int)char.GetNumericValue(vatToCheck[m]) / 5 +
//validate char 11 and 12 (int)char.GetNumericValue(vatToCheck[m]) * 2 % 10);
var valid = int.TryParse(vatNumber.Substring(10, 2), out var v2); var c1 = new[] { 1, 3, 5, 7 }.Sum(m => (int)char.GetNumericValue(vatToCheck[m]));
if(valid && v2 is < 1 or > 94 ) var c10 = (10 - (r + c1) % 10) % 10;
return false; if (vatToCheck.Length == 10)
// check vatnumber
return CheckLuhn(vatNumber);
}
private static bool CheckModolus11(string number)
{
int[] factors;
switch (number.Length)
{ {
case 8: return $"{vatToCheck[..9]}{c10}" == vatNumber;
factors = new [] { 2, 7, 6, 5, 4, 3, 2, 1 };
break;
case 9:
factors = new[] { 3, 2, 7, 6, 5, 4, 3, 2, 1 };
break;
case 10:
factors = new[] { 4, 3, 2, 7, 6, 5, 4, 3, 2, 1 };
break;
default:
return false;
} }
var i = 0; return $"{vatToCheck[..9]}{c10}01" == vatNumber;
var r = factors.Sum(m => (int)char.GetNumericValue(number[i++]) * m);
return r % 11 == 0;
} }
private static bool CheckLuhn(string vatNumber) private static bool ValidateMod11(string number)
{ {
// https://www.geeksforgeeks.org/luhn-algorithm/ if (long.Parse(number) == 0)
return false;
var nDigits = vatNumber.Length;
var nSum = 0;
var isSecond = false;
for (var i = nDigits - 1; i >= 0; i--)
{
var d = (int)char.GetNumericValue(vatNumber[i]) - '0';
if (isSecond)
d *= 2;
// We add two digits to handle
// cases that make two digits
// after doubling
nSum += d / 10;
nSum += d % 10;
isSecond = !isSecond;
}
return (nSum % 10 == 0);
}
private static string AddSelfCheckDigit(string number)
{
// modulus 11
var multi = number.Length;
var sum = 0; var sum = 0;
for (int i = number.Length - 1, multiplier = 2; i >= 0; i--) for (int i = number.Length - 1, multiplier = 1; i >= 0; i--)
{ {
sum += (int)char.GetNumericValue(number[i]) * multiplier; sum += (int)char.GetNumericValue(number[i]) * multiplier;
if (++multiplier == multi) multiplier = 2; if (++multiplier > 7) multiplier = 2;
} }
var validator = (11 - (sum % 11)).ToString();
validator = validator switch return sum % 11 == 0;
}
private static string SanitizeVatNumber(string vatNumber)
{
var washing = vatNumber.Replace(" ", "").Replace("-", "").ToUpperInvariant();
var result = washing.Replace("DK", "").Replace("NO", "").Replace("SE","") ;
if (result.Contains("MVA"))
result = result.Replace("MVA", "");
return result.Length switch
{ {
"11" => "0", >= 10 => vatNumber[..10],
"10" => "X", < 10 => result
_ => validator
}; };
return number + validator;
} }
} }

View file

@ -26,7 +26,7 @@ namespace Wonky.Client.Models
[Required(ErrorMessage = "Sælger skal udfyldes")] public string SalesRep { get; set; } = ""; [Required(ErrorMessage = "Sælger skal udfyldes")] public string SalesRep { get; set; } = "";
// From company row // From company row
[Required(ErrorMessage = "Konto skal udfyldes")] public string Account { get; set; } = ""; [Required(ErrorMessage = "Konto skal udfyldes")] public string Account { get; set; } = "";
[Required(ErrorMessage = "Moms nummer skal udfyldes")] public string VatNumber { get; set; } = ""; [Required(ErrorMessage = "CVR / ORG nummer skal udfyldes")] public string VatNumber { get; set; } = "";
[Required(ErrorMessage = "Navn skal udfyldes")] public string Name { get; set; } = ""; [Required(ErrorMessage = "Navn skal udfyldes")] public string Name { get; set; } = "";
public string Address { get; set; } = ""; public string Address { get; set; } = "";
public string Address2 { get; set; } = ""; public string Address2 { get; set; } = "";

View file

@ -25,6 +25,7 @@ using Wonky.Client.Services;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Wonky.Client.Helpers;
using Wonky.Client.Models; using Wonky.Client.Models;
using Wonky.Entity.DTO; using Wonky.Entity.DTO;
using Wonky.Entity.Models; using Wonky.Entity.Models;
@ -39,7 +40,7 @@ namespace Wonky.Client.Pages
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public IToastService ToastService { get; set; } [Inject] public IToastService ToastService { get; set; }
[Inject] public ILogger<CompanyCreate> Logger { get; set; } [Inject] public ILogger<CompanyCreate> Logger { get; set; }
[Inject] public VirkRegistryService VirkRegistryService { get; set; } [Inject] public VatOwnerLookupService VatOwnerLookupService { get; set; }
[Inject] public ILocalStorageService StorageService { get; set; } [Inject] public ILocalStorageService StorageService { get; set; }
[Inject] public NavigationManager Navigation { get; set; } [Inject] public NavigationManager Navigation { get; set; }
private CompanyDto _companyDto = new(); private CompanyDto _companyDto = new();
@ -55,14 +56,14 @@ namespace Wonky.Client.Pages
var ux = await StorageService.GetItemAsync<UserInfoDto>("_ux"); var ux = await StorageService.GetItemAsync<UserInfoDto>("_ux");
_companyDto.SalesRepId = ux.Id; _companyDto.SalesRepId = ux.Id;
_companyDto.CountryCode = ux.CountryCode;
Interceptor.RegisterEvent(); Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent(); Interceptor.RegisterBeforeSendEvent();
} }
private async Task GetInfoFromAddress(VatAddress address) private async Task GetInfoFromAddress(VatAddress address)
{ {
VInfos = await VirkRegistryService.QueryVirkRegistry( VInfos = await VatOwnerLookupService.QueryVirkRegistry(
new VirkParams new VirkParams
{ {
StreetName = address.StreetName, StreetName = address.StreetName,
@ -76,9 +77,8 @@ namespace Wonky.Client.Pages
} }
private async Task GetInfoFromVat(string vatNumber) private async Task GetInfoFromVat(string vatNumber)
{ {
VInfos = await VirkRegistryService VInfos = await VatOwnerLookupService
.QueryVirkRegistry(new VirkParams {VatNumber = vatNumber}); .QueryVirkRegistry(new VirkParams {VatNumber = vatNumber});
if (!VInfos.Any()) if (!VInfos.Any())
{ {
ToastService.ShowError($"Firma med CVR '{vatNumber}' findes ikke."); ToastService.ShowError($"Firma med CVR '{vatNumber}' findes ikke.");
@ -107,7 +107,14 @@ namespace Wonky.Client.Pages
private void HandleFieldChanged(object sender, FieldChangedEventArgs e) private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
{ {
_formInvalid = !_editContext.Validate(); if (!VatUtils.ValidateFormat(_companyDto.CountryCode, _companyDto.VatNumber))
{
_formInvalid = false;
}
else
{
_formInvalid = !_editContext.Validate();
}
StateHasChanged(); StateHasChanged();
} }
private void ValidationChanged(object sender, ValidationStateChangedEventArgs e) private void ValidationChanged(object sender, ValidationStateChangedEventArgs e)

View file

@ -14,24 +14,17 @@
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html] // along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
// //
*@ *@
@page "/company/{account}/update"
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components @using Microsoft.AspNetCore.Components
@attribute [Authorize(Roles = "Adviser")]
@using Wonky.Client.Components @using Wonky.Client.Components
@attribute [Authorize(Roles = "Adviser")]
@page "/company/{companyId}/update"
@if (!string.IsNullOrEmpty(_companyDto.Name)) @if (_company.Name != "")
{ {
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<div><DisplayStateComponent StateClass="@RegState"></DisplayStateComponent> CVR status</div> <div class="h2">@_company.Account - @_company.Name</div>
<div>Konto @_companyDto.Account</div>
<div>CompanyId @_companyDto.CompanyId</div>
<div>EDIT Næste besøg @NextVisit</div>
<div>EDIT Sidst besøgt @LastVisit</div>
<div>DB Næste besøg @_companyDto.NextVisit</div>
<div>DB Sidst besøgt @_companyDto.LastVisit</div>
</div> </div>
<div class="card-body"> <div class="card-body">
<EditForm EditContext="_editContext" OnValidSubmit="Update"> <EditForm EditContext="_editContext" OnValidSubmit="Update">
@ -39,61 +32,65 @@
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="name" class="col-md-2 col-form-label">Firmanavn</label> <label for="name" class="col-md-2 col-form-label">Firmanavn</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="name" class="form-control" @bind-Value="_companyDto.Name"/> <InputText id="name" class="form-control" @bind-Value="_company.Name"/>
<ValidationMessage For="@(() => _company.Name)"></ValidationMessage>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="address1" class="col-md-2 col-form-label">Adresse</label> <label for="address1" class="col-md-2 col-form-label">Adresse</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="address1" class="form-control" @bind-Value="_companyDto.Address1"/> <InputText id="address1" class="form-control" @bind-Value="_company.Address1"/>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="address2" class="col-md-2 col-form-label">Adresse</label> <label for="address2" class="col-md-2 col-form-label">Adresse</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="address2" class="form-control" @bind-Value="_companyDto.Address2"/> <InputText id="address2" class="form-control" @bind-Value="_company.Address2"/>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="zipCode" class="col-md-2 col-form-label">Postnr</label> <label for="zipCode" class="col-md-2 col-form-label">Postnr</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="zipCode" class="form-control" @bind-Value="_companyDto.ZipCode"/> <InputText id="zipCode" class="form-control" @bind-Value="_company.ZipCode"/>
<ValidationMessage For="@(() => _company.ZipCode)"></ValidationMessage>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="city" class="col-md-2 col-form-label">Bynavn</label> <label for="city" class="col-md-2 col-form-label">Bynavn</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="city" class="form-control" @bind-Value="_companyDto.City"/> <InputText id="city" class="form-control" @bind-Value="_company.City"/>
<ValidationMessage For="@(() => _company.City)"></ValidationMessage>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="vatNumber" class="col-md-2 col-form-label">CVR/ORG</label> <label for="vatNumber" class="col-md-2 col-form-label">CVR/ORG</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="vatNumber" class="form-control" @bind-Value="_companyDto.VatNumber"/> <InputText id="vatNumber" class="form-control" @bind-Value="_company.VatNumber"/>
<ValidationMessage For="@(() => _company.VatNumber)"></ValidationMessage>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="phone" class="col-md-2 col-form-label">Telefon nummer</label> <label for="phone" class="col-md-2 col-form-label">Telefon nummer</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="phone" class="form-control" @bind-Value="_companyDto.Phone"/> <InputText id="phone" class="form-control" @bind-Value="_company.Phone"/>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="mobile" class="col-md-2 col-form-label">Mobil nummer</label> <label for="mobile" class="col-md-2 col-form-label">Mobil nummer</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="mobile" class="form-control" @bind-Value="_companyDto.Mobile"/> <InputText id="mobile" class="form-control" @bind-Value="_company.Mobile"/>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="email" class="col-md-2 col-form-label">Email</label> <label for="email" class="col-md-2 col-form-label">Email</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="email" class="form-control" @bind-Value="_companyDto.Email"/> <InputText id="email" class="form-control" @bind-Value="_company.Email"/>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="attention" class="col-md-2 col-form-label">Attention</label> <label for="attention" class="col-md-2 col-form-label">Attention</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="attention" class="form-control" @bind-Value="_companyDto.Attention"/> <InputText id="attention" class="form-control" @bind-Value="_company.Attention"/>
</div> </div>
</div> </div>
<div class="form-group row mb-2"> <div class="form-group row mb-2">
@ -111,7 +108,7 @@
<div class="form-group row mb-2"> <div class="form-group row mb-2">
<label for="interval" class="col-form-label col-md-2">Besøgs interval Interval</label> <label for="interval" class="col-form-label col-md-2">Besøgs interval Interval</label>
<div class="col-md-3"> <div class="col-md-3">
<InputNumber id="interval" class="form-control" @bind-Value="_companyDto.Interval"/> <InputNumber id="interval" class="form-control" @bind-Value="_company.Interval"/>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
@ -132,5 +129,5 @@
} }
else else
{ {
<div><img class="spinner" src="loader.gif" alt="Vent venligst..."/> Henter data...</div> @* <AppSpinner/> *@
} }

View file

@ -13,11 +13,14 @@
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html] // along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
// //
using System.Text.Json;
using System.Text.Json.Serialization;
using Blazored.Toast.Services; using Blazored.Toast.Services;
using Wonky.Client.HttpInterceptors; using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository; using Wonky.Client.HttpRepository;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Forms;
using Wonky.Client.Helpers;
using Wonky.Client.Models; using Wonky.Client.Models;
using Wonky.Client.Services; using Wonky.Client.Services;
using Wonky.Entity.DTO; using Wonky.Entity.DTO;
@ -33,44 +36,59 @@ public partial class CompanyUpdate : IDisposable
[Inject] public IToastService ToastService { get; set; } [Inject] public IToastService ToastService { get; set; }
[Inject] public ILogger<CompanyCreate> Logger { get; set; } [Inject] public ILogger<CompanyCreate> Logger { get; set; }
[Inject] public NavigationManager Navigation { get; set; } [Inject] public NavigationManager Navigation { get; set; }
[Inject] public VirkRegistryService VirkRegistryService { get; set; } [Inject] public VatOwnerLookupService VatOwnerLookupService { get; set; }
[Parameter] public string Account { get; set; } = null!; [Parameter] public string Account { get; set; } = "";
private CompanyDto _companyDto; [Parameter] public string CompanyId { get; set; } = "";
private EditContext _editContext; private CompanyDto _company { get; set; }
private EditContext _editContext { get; set; }
private List<VirkRegInfo> VInfos { get; set; } = new(); private List<VirkRegInfo> VInfos { get; set; } = new();
private VirkRegInfo _virkRegInfo { get; set; } = new(); private VirkRegInfo _virkRegInfo { get; set; } = new();
private DateTime LastVisit { get; set; } private DateTime LastVisit { get; set; }
private DateTime NextVisit { get; set; } private DateTime NextVisit { get; set; }
private string RegState { get; set; } = "the-ugly"; private string _vatState { get; set; } = "the-ugly";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_companyDto = await CompanyRepo.GetCompanyByAccount(Account);
LastVisit = DateTime.Parse(_companyDto.LastVisit);
NextVisit = DateTime.Parse(_companyDto.NextVisit);
_editContext = new EditContext(_companyDto);
Interceptor.RegisterEvent(); Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent(); Interceptor.RegisterBeforeSendEvent();
if(!string.IsNullOrWhiteSpace(_companyDto.VatNumber)) _company = await CompanyRepo.GetCompanyById(CompanyId);
await GetInfoFromVat(_companyDto.VatNumber);
//_companyDto.CompanyId = Squid.DecodeSquid(_companyDto.CompanyId).ToString(); LastVisit = DateTime.Parse(_company.LastVisit);
NextVisit = DateTime.Parse(_company.NextVisit);
_editContext = new EditContext(_company);
Console.WriteLine(JsonSerializer.Serialize(_company));
if(_company.HasFolded == 1)
{
_vatState = "the-dead";
}
else
{
_vatState = VatUtils.ValidateFormat(_company.CountryCode, _company.VatNumber) ? "the-good" : "the-draw";
}
} }
private async Task Update() private async Task Update()
{ {
if (!VatUtils.ValidateFormat(_company.CountryCode, _company.VatNumber))
var lv = DateTime.TryParse(_companyDto.LastVisit, out var lvValidated); {
var nv = DateTime.TryParse(_companyDto.NextVisit, out var nvValidated); ToastService.ShowError($"CVR/VAT/ORG nummer er ugyldig.");
StateHasChanged();
return;
}
var lv = DateTime.TryParse(_company.LastVisit, out var lvValidated);
var nv = DateTime.TryParse(_company.NextVisit, out var nvValidated);
if (lv && nv) if (lv && nv)
{ {
_companyDto.LastVisit = $"{lvValidated:yyyy-MM-dd}"; _company.LastVisit = $"{lvValidated:yyyy-MM-dd}";
_companyDto.NextVisit = $"{nvValidated:yyyy-MM-dd}"; _company.NextVisit = $"{nvValidated:yyyy-MM-dd}";
await CompanyRepo.UpdateCompany(_companyDto); await CompanyRepo.UpdateCompany(_company);
ToastService.ShowSuccess($"Godt så. Firma '{_companyDto!.Name}' er opdateret.");
Navigation.NavigateTo($"/company/id/{_companyDto.CompanyId}"); ToastService.ShowSuccess($"Godt så. Firma '{_company!.Name}' er opdateret.");
Navigation.NavigateTo($"/company/{_company.CompanyId}");
} }
} }
private async Task GetInfoFromVat(string vatNumber) private async Task GetInfoFromVat(string vatNumber)
{ {
@ -79,7 +97,7 @@ public partial class CompanyUpdate : IDisposable
ToastService.ShowError($"CVR nummer mangler."); ToastService.ShowError($"CVR nummer mangler.");
return; return;
} }
VInfos = await VirkRegistryService VInfos = await VatOwnerLookupService
.QueryVirkRegistry( .QueryVirkRegistry(
new VirkParams new VirkParams
{ {
@ -94,7 +112,7 @@ public partial class CompanyUpdate : IDisposable
private async Task GetInfoFromAddress(VatAddress address) private async Task GetInfoFromAddress(VatAddress address)
{ {
VInfos = await VirkRegistryService.QueryVirkRegistry( VInfos = await VatOwnerLookupService.QueryVirkRegistry(
new VirkParams new VirkParams
{ {
StreetName = address.StreetName, StreetName = address.StreetName,
@ -109,12 +127,12 @@ public partial class CompanyUpdate : IDisposable
private void SelectCompany(string vatNumber) private void SelectCompany(string vatNumber)
{ {
_virkRegInfo = (from x in VInfos where x.VatNumber == vatNumber select x).First(); _virkRegInfo = (from x in VInfos where x.VatNumber == vatNumber select x).First();
_companyDto.Name = _virkRegInfo.Name; _company.Name = _virkRegInfo.Name;
_companyDto.Address1 = _virkRegInfo.CoName; _company.Address1 = _virkRegInfo.CoName;
_companyDto.Address2 = _virkRegInfo.Address; _company.Address2 = _virkRegInfo.Address;
_companyDto.ZipCode = _virkRegInfo.ZipCode; _company.ZipCode = _virkRegInfo.ZipCode;
_companyDto.City = _virkRegInfo.City; _company.City = _virkRegInfo.City;
_companyDto.VatNumber = _virkRegInfo.VatNumber; _company.VatNumber = _virkRegInfo.VatNumber;
} }
public void Dispose() public void Dispose()

View file

@ -15,61 +15,63 @@
// //
*@ *@
@page "/company/account/{account}" @page "/company/{companyId}"
@page "/company/id/{companyId}"
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Wonky.Client.Components; @using Wonky.Client.Components;
@using Wonky.Client.Helpers @using Wonky.Client.Helpers
@using Wonky.Entity.Models
@attribute [Authorize(Roles = "Adviser")] @attribute [Authorize(Roles = "Adviser")]
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<div class="card-header">
<div class="h2">@(_hasFolded ? @_company.Account : "LUKKET" ) - @_company.Name</div>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
<table class="table table-sm table-striped table-bordered"> <table class="table table-sm table-striped table-bordered">
<tbody> <tbody>
<tr> <tr>
<th scope="row">Navn</th> <th scope="row">Navn</th>
<td colspan="2">@CompanyDto.Name</td> <td colspan="2">@_company.Name</td>
</tr> </tr>
<tr> <tr>
<th scope="row">CO navn</th> <th scope="row">CO navn</th>
<td colspan="2">@CompanyDto.Address1</td> <td colspan="2">@_company.Address1</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Adresse</th> <th scope="row">Adresse</th>
<td colspan="2">@CompanyDto.Address2</td> <td colspan="2">@_company.Address2</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Postnummer</th> <th scope="row">Postnummer</th>
<td colspan="2">@CompanyDto.ZipCode</td> <td colspan="2">@_company.ZipCode</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Bynavn</th> <th scope="row">Bynavn</th>
<td colspan="2">@CompanyDto.City</td> <td colspan="2">@_company.City</td>
</tr> </tr>
<tr> <tr>
<th scope="row">CVR</th> <th scope="row">CVR</th>
<td><RegStateVatNumber VatNumber="@CompanyDto.VatNumber"></RegStateVatNumber></td> <td class="state"><DisplayStateComponent StateClass="@_vatState"></DisplayStateComponent></td>
<td>@CompanyDto.VatNumber</td> <td>@_company.VatNumber</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Telefon</th> <th scope="row">Telefon</th>
<td colspan="2">@CompanyDto.Phone</td> <td colspan="2">@_company.Phone</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Email</th> <th scope="row">Email</th>
<td colspan="2">@CompanyDto.Email</td> <td colspan="2">@_company.Email</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Sidste besøg</th> <th scope="row">Sidste besøg</th>
<td colspan="2">@CompanyDto.LastVisit</td> <td colspan="2">@_company.LastVisit</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Næste besøg</th> <th scope="row">Næste besøg</th>
<td><DisplayStateComponent StateClass="@(Utils.GetVisitState(CompanyDto.NextVisit))"></DisplayStateComponent></td> <td class="state"><DisplayStateComponent StateClass="@(Utils.GetVisitState(_company.NextVisit))"></DisplayStateComponent></td>
<td>@CompanyDto.NextVisit</td> <td>@_company.NextVisit</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -78,10 +80,10 @@
<div class="row flex-row align-items-end"> <div class="row flex-row align-items-end">
<div class="col flex-column"> <div class="col flex-column">
<a class="btn btn-primary float-start mx-2" href="/companies">Tilbage</a> <a class="btn btn-primary float-start mx-2" href="/companies">Tilbage</a>
<a class="btn btn-primary float-start" href="/company/@CompanyDto.Account/update">Rediger</a> <a class="btn btn-primary float-start" href="/company/@_company.CompanyId/update">Rediger</a>
</div> </div>
<div class="col flex-column"> <div class="col flex-column">
<a class="btn btn-primary float-end mx-2" href="/company/@CompanyDto.Account/Activity">Aktivitet</a> <a class="btn btn-primary float-end mx-2" href="/company/@_company.CompanyId/Activity">Aktivitet</a>
</div> </div>
</div> </div>
</div> </div>

View file

@ -29,38 +29,28 @@ public partial class CompanyView : IDisposable
{ {
[Inject] public ICompanyHttpRepository CompanyRepo { get; set; } [Inject] public ICompanyHttpRepository CompanyRepo { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public VirkRegistryService VirkRegistryService { get; set; } [Inject] public VatOwnerLookupService VatOwnerLookup { get; set; }
[Parameter] public string Account { get; set; } = ""; [Inject] public ILogger<CompanyView> Logger { get; set; }
[Parameter] public string CompanyId { get; set; } = ""; [Parameter] public string CompanyId { get; set; } = "";
private CompanyDto CompanyDto { get; set; } = new (); private CompanyDto _company { get; set; } = new ();
private VirkRegInfo VirkRegInfo { get; set; } = new(); private string _vatState { get; set; } = "the-dead";
private string VisitState { get; set; } = "the-ugly"; private bool _hasFolded { get; set; }
private bool ValidCvr { get; set; } = true;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
Interceptor.RegisterEvent(); Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent(); Interceptor.RegisterBeforeSendEvent();
if (!string.IsNullOrWhiteSpace(Account)) _company = await CompanyRepo.GetCompanyById(CompanyId);
if(_company.HasFolded == 1)
{ {
CompanyDto = await CompanyRepo.GetCompanyByAccount(Account); _vatState = "the-dead";
_hasFolded = true;
} }
else else
{ {
if (!string.IsNullOrWhiteSpace(CompanyId)) _vatState = VatUtils.ValidateFormat(_company.CountryCode, _company.VatNumber) ? "the-good" : "the-draw";
{
CompanyDto = await CompanyRepo.GetCompanyById(CompanyId);
}
} }
var theUgly = DateTime.Parse(CompanyDto.NextVisit);
var theBad = theUgly.AddDays(-14);
if (DateTime.Now <= theUgly)
{
VisitState = DateTime.Now >= theBad ? "the-bad" : "the-good";
}
ValidCvr = string.IsNullOrEmpty(CompanyDto.VatNumber) || CompanyDto.VatNumber.Length <= 8;
} }
/// <summary> /// <summary>
@ -68,6 +58,6 @@ public partial class CompanyView : IDisposable
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
Interceptor!.DisposeEvent(); Interceptor.DisposeEvent();
} }
} }

View file

@ -15,7 +15,7 @@
// //
*@ *@
@page "/company/{account}/activity" @page "/company/{companyId}/activity"
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Adviser")] @attribute [Authorize(Roles = "Adviser")]
@using Wonky.Client.Components @using Wonky.Client.Components
@ -102,6 +102,9 @@
<div class="row mb-2"> <div class="row mb-2">
<div class="col">
<a class="btn btn-primary" href="/company/@_company.CompanyId"></a>
</div>
<div class="col"> <div class="col">
<button type="submit" class="btn btn-success" disabled="@_poFormInvalid">Tilbud</button> <button type="submit" class="btn btn-success" disabled="@_poFormInvalid">Tilbud</button>
<button type="submit" class="btn btn-success" disabled="@_poFormInvalid">Bestilling</button> <button type="submit" class="btn btn-success" disabled="@_poFormInvalid">Bestilling</button>

View file

@ -33,7 +33,7 @@ public partial class PurchaseOrderCreate : IDisposable
{ {
private List<SalesItemDto> SalesItemList { get; set; } = new(); private List<SalesItemDto> SalesItemList { get; set; } = new();
private PurchaseOrder _purchaseOrder = new (); private PurchaseOrder _purchaseOrder = new ();
private CompanyDto _companyDto = new(); private CompanyDto _company = new();
private EditContext _editContext; private EditContext _editContext;
private bool _poFormInvalid = true; private bool _poFormInvalid = true;
private MetaData? MetaData { get; set; } = new(); private MetaData? MetaData { get; set; } = new();
@ -46,7 +46,7 @@ public partial class PurchaseOrderCreate : IDisposable
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public ILocalStorageService StorageService { get; set; } [Inject] public ILocalStorageService StorageService { get; set; }
[Inject] public ISalesItemHttpRepository SalesItemRepo { get; set; } [Inject] public ISalesItemHttpRepository SalesItemRepo { get; set; }
[Parameter] public string Account { get; set; } [Parameter] public string CompanyId { get; set; }
public List<CrmSalesLines> Lines { get; set; } = new(); public List<CrmSalesLines> Lines { get; set; } = new();
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
@ -55,9 +55,7 @@ public partial class PurchaseOrderCreate : IDisposable
Interceptor.RegisterBeforeSendEvent(); Interceptor.RegisterBeforeSendEvent();
var ux = await StorageService.GetItemAsync<UserInfoDto>("_ux"); var ux = await StorageService.GetItemAsync<UserInfoDto>("_ux");
_company = await CompanyRepo.GetCompanyById(CompanyId);
_companyDto = await CompanyRepo.GetCompanyByAccount(Account);
_editContext = new EditContext(_purchaseOrder); _editContext = new EditContext(_purchaseOrder);
_editContext.OnFieldChanged += HandleFieldChanged; _editContext.OnFieldChanged += HandleFieldChanged;
@ -66,30 +64,30 @@ public partial class PurchaseOrderCreate : IDisposable
// permanent identifications // permanent identifications
_purchaseOrder.SalesRep = ux.Adviser; _purchaseOrder.SalesRep = ux.Adviser;
_purchaseOrder.Account = Account; _purchaseOrder.Account = _company.Account;
_purchaseOrder.VatNumber = _companyDto.VatNumber; _purchaseOrder.VatNumber = _company.VatNumber;
_purchaseOrder.EMail = _companyDto.Email; _purchaseOrder.EMail = _company.Email;
_purchaseOrder.Phone = _companyDto.Phone; _purchaseOrder.Phone = _company.Phone;
_purchaseOrder.OurRef = ux.FullName.Split(" ")[0]; _purchaseOrder.OurRef = ux.FullName.Split(" ")[0];
_purchaseOrder.Name = _companyDto.Name; _purchaseOrder.Name = _company.Name;
_purchaseOrder.Address = _companyDto.Address1; _purchaseOrder.Address = _company.Address1;
_purchaseOrder.Address2 = _companyDto.Address2; _purchaseOrder.Address2 = _company.Address2;
_purchaseOrder.ZipCode = _companyDto.ZipCode; _purchaseOrder.ZipCode = _company.ZipCode;
_purchaseOrder.City = _companyDto.City; _purchaseOrder.City = _company.City;
_purchaseOrder.DlvName = _companyDto.Name; _purchaseOrder.DlvName = _company.Name;
_purchaseOrder.DlvAddress1 = _companyDto.Address1; _purchaseOrder.DlvAddress1 = _company.Address1;
_purchaseOrder.DlvAddress2 = _companyDto.Address2; _purchaseOrder.DlvAddress2 = _company.Address2;
_purchaseOrder.DlvZipCode = _companyDto.ZipCode; _purchaseOrder.DlvZipCode = _company.ZipCode;
_purchaseOrder.DlvCity = _companyDto.City; _purchaseOrder.DlvCity = _company.City;
} }
private async Task CreateActivity() private async Task CreateActivity()
{ {
await StorageService.SetItemAsync(Account, _purchaseOrder); await StorageService.SetItemAsync(CompanyId, _purchaseOrder);
ToastService.ShowSuccess($"Aktivitet oprettet."); ToastService.ShowSuccess($"Aktivitet oprettet.");
} }

View file

@ -57,7 +57,7 @@ builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, AuthStateProvider>(); builder.Services.AddScoped<AuthenticationStateProvider, AuthStateProvider>();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>(); builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
builder.Services.AddScoped<RefreshTokenService>(); builder.Services.AddScoped<RefreshTokenService>();
builder.Services.AddScoped<VirkRegistryService>(); builder.Services.AddScoped<VatOwnerLookupService>();
builder.Services.AddScoped<UserPreferenceService>(); builder.Services.AddScoped<UserPreferenceService>();

View file

@ -22,14 +22,14 @@ using Wonky.Entity.Requests;
namespace Wonky.Client.Services; namespace Wonky.Client.Services;
public class VirkRegistryService public class VatOwnerLookupService
{ {
private readonly JsonSerializerOptions _options = new() { PropertyNameCaseInsensitive = true }; private readonly JsonSerializerOptions _options = new() { PropertyNameCaseInsensitive = true };
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly IOptions<ApiConfig> _apiConfig; private readonly IOptions<ApiConfig> _apiConfig;
private readonly List<VirkRegInfo> _noData = new() { new VirkRegInfo { Name = "INGEN DATA" } }; private readonly List<VirkRegInfo> _noData = new() { new VirkRegInfo { Name = "INGEN DATA" } };
public VirkRegistryService(HttpClient client, IOptions<ApiConfig> apiConfig) public VatOwnerLookupService(HttpClient client, IOptions<ApiConfig> apiConfig)
{ {
_client = client; _client = client;
_apiConfig = apiConfig; _apiConfig = apiConfig;

View file

@ -14,6 +14,9 @@
min-width: 24px; min-width: 24px;
min-height: 24px; min-height: 24px;
} }
.the-dead {
background-color: black;
}
.the-draw { .the-draw {
background-color: purple; background-color: purple;
} }

View file

@ -21,10 +21,10 @@ namespace Wonky.Entity.DTO;
public class CompanyDto public class CompanyDto
{ {
[Required(ErrorMessage = "Navn skal udyldes")] public string Name { get; set; } = ""; [Required(ErrorMessage = "Navn skal udyldes")] public string Name { get; set; }
[Required(ErrorMessage = "Postnummer skal udfyldes")] public string ZipCode { get; set; } = ""; [Required(ErrorMessage = "Postnummer skal udfyldes")] public string ZipCode { get; set; }
[Required(ErrorMessage = "Bynavn skal udfyldes")] public string City { get; set; } = ""; [Required(ErrorMessage = "Bynavn skal udfyldes")] public string City { get; set; }
public string VatNumber { get; set; } = ""; [Required(ErrorMessage = "ORG/VAT/CVR er ikke et gyldigt nummer")] public string VatNumber { get; set; }
public string CompanyId { get; set; } = ""; public string CompanyId { get; set; } = "";
public string SalesRepId { get; set; } = ""; public string SalesRepId { get; set; } = "";
public string BcId { get; set; } = ""; public string BcId { get; set; } = "";