added vatNumber validation order - necessary for Sweden

This commit is contained in:
Frede Hundewadt 2023-01-20 06:54:15 +01:00
parent 4591cf156a
commit e170d00f0d
4 changed files with 82 additions and 51 deletions

View file

@ -51,7 +51,7 @@ public class VatUtils
/// </summary> /// </summary>
/// <param name="vatNumber"></param> /// <param name="vatNumber"></param>
/// <returns></returns> /// <returns></returns>
private static string SanitizeVatNumber(string vatNumber) public static string SanitizeVatNumber(string vatNumber)
{ {
if (string.IsNullOrWhiteSpace(vatNumber)) if (string.IsNullOrWhiteSpace(vatNumber))
return ""; return "";

View file

@ -54,9 +54,9 @@ else
<EditForm EditContext="ActivityContext"> <EditForm EditContext="ActivityContext">
<DataAnnotationsValidator/> <DataAnnotationsValidator/>
<div class="row mb-1"> <div class="row mb-1 g-2">
<label for="activityType" class="col-md-2 col-form-label">Ordre Type</label> <label for="activityType" class="col-sm-2 col-form-label-sm">Ordre Type</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputSelect id="activityType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityTypeEnum"> <InputSelect id="activityType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityTypeEnum">
<option value="">&rarr; TAG MIG &larr;</option> <option value="">&rarr; TAG MIG &larr;</option>
<option value="onSite">Besøg</option> <option value="onSite">Besøg</option>
@ -65,8 +65,8 @@ else
<ValidationMessage For="@(() => Activity.ActivityTypeEnum)"></ValidationMessage> <ValidationMessage For="@(() => Activity.ActivityTypeEnum)"></ValidationMessage>
</div> </div>
<label for="statusType" class="col-md-2 col-form-label">Status</label> <label for="statusType" class="col-sm-2 col-form-label-sm">Status</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputSelect id="statusType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityStatusEnum"> <InputSelect id="statusType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityStatusEnum">
<option value="noSale">Ingen salg</option> <option value="noSale">Ingen salg</option>
@if (!string.IsNullOrEmpty(Activity.VatNumber) && !string.IsNullOrWhiteSpace(Activity.Address1) && Company.HasFolded == 0) @if (!string.IsNullOrEmpty(Activity.VatNumber) && !string.IsNullOrWhiteSpace(Activity.Address1) && Company.HasFolded == 0)
@ -99,64 +99,75 @@ else
</div> </div>
} }
</div> </div>
</div>
<div class="row mb-1"> <label for="demo" class="col-sm-2 col-form-label-sm">Demo</label>
<label for="demo" class="col-md-2 col-form-label">Demo</label> <div class="col-sm-4">
<div class="col-md-4">
<InputText id="demo" class="form-control" @bind-Value="Activity.Demo"/> <InputText id="demo" class="form-control" @bind-Value="Activity.Demo"/>
<ValidationMessage For="@(() => Activity.Demo)"></ValidationMessage> <ValidationMessage For="@(() => Activity.Demo)"></ValidationMessage>
</div> </div>
<label for="email" class="col-md-2 col-form-label">Epost</label> <label for="email" class="col-sm-2 col-form-label-sm">Epost</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputText id="email" class="form-control" @bind-Value="Activity.Email"/> <InputText id="email" class="form-control" @bind-Value="Activity.Email"/>
<ValidationMessage For="@(() => Activity.Email)"></ValidationMessage> <ValidationMessage For="@(() => Activity.Email)"></ValidationMessage>
</div> </div>
</div>
<div class="row mb-1"> <label for="referenceNumber" class="col-sm-2 col-form-label-sm">Rekvisition</label>
<label for="referenceNumber" class="col-md-2 col-form-label">Rekvisition</label> <div class="col-sm-4">
<div class="col-md-4">
<InputText id="referenceNumber" class="form-control" @bind-Value="Activity.ReferenceNumber"/> <InputText id="referenceNumber" class="form-control" @bind-Value="Activity.ReferenceNumber"/>
<ValidationMessage For="@(() => Activity.ReferenceNumber)"></ValidationMessage> <ValidationMessage For="@(() => Activity.ReferenceNumber)"></ValidationMessage>
</div> </div>
<label for="yourRef" class="col-md-2 col-form-label">Indkøber</label> <label for="yourRef" class="col-sm-2 col-form-label-sm">Indkøber</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputText id="yourRef" class="form-control" @bind-Value="Activity.YourRef"/> <InputText id="yourRef" class="form-control" @bind-Value="Activity.YourRef"/>
<ValidationMessage For="@(() => Activity.YourRef)"></ValidationMessage> <ValidationMessage For="@(() => Activity.YourRef)"></ValidationMessage>
</div> </div>
</div>
<div class="row mb-1"> <label for="orderMessage" class="col-sm-2 col-form-label-sm">Note /Kontor</label>
<label for="orderMessage" class="col-md-2 col-form-label">Note /Kontor</label> <div class="col-sm-4">
<div class="col-md-4">
<InputTextArea id="orderMessage" class="form-control" @bind-Value="Activity.OrderMessage"/> <InputTextArea id="orderMessage" class="form-control" @bind-Value="Activity.OrderMessage"/>
<ValidationMessage For="@(() => Activity.OrderMessage)"></ValidationMessage> <ValidationMessage For="@(() => Activity.OrderMessage)"></ValidationMessage>
</div> </div>
<label for="crmNote" class="col-md-2 col-form-label">Note /Selv</label> <label for="crmNote" class="col-sm-2 col-form-label-sm">Note /Selv</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputTextArea id="crmNote" class="form-control" @bind-Value="Activity.CrmNote"/> <InputTextArea id="crmNote" class="form-control" @bind-Value="Activity.CrmNote"/>
<ValidationMessage For="@(() => Activity.CrmNote)"></ValidationMessage> <ValidationMessage For="@(() => Activity.CrmNote)"></ValidationMessage>
</div> </div>
</div>
<div class="row mb-2"> <label for="attention" class="col-sm-2 col-form-label-sm">Att.</label>
<label for="attention" class="col-md-2 col-form-label">Att.</label> <div class="col-sm-4">
<div class="col-md-4">
<InputText id="attention" class="form-control" @bind-Value="Activity.Attention"/> <InputText id="attention" class="form-control" @bind-Value="Activity.Attention"/>
<ValidationMessage For="@(() => Activity.Attention)"></ValidationMessage> <ValidationMessage For="@(() => Activity.Attention)"></ValidationMessage>
</div> </div>
<label for="phone" class="col-md-2 col-form-label">Tlf.</label> <label for="phone" class="col-sm-2 col-form-label-sm">Tlf.</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputText id="phone" class="form-control" @bind-Value="Activity.Phone"/> <InputText id="phone" class="form-control" @bind-Value="Activity.Phone"/>
<ValidationMessage For="@(() => Activity.Phone)"></ValidationMessage> <ValidationMessage For="@(() => Activity.Phone)"></ValidationMessage>
</div> </div>
<label for="vatNumber" class="col-sm-2 col-form-label-sm">Org.nr.</label>
<div class="col-sm-4">
<InputText hidden="vatNumber" class="form-control" @bind-Value="Activity.VatNumber" />
<ValidationMessage For="@(() => Activity.VatNumber)" />
</div>
</div> </div>
<div class="row">
<div class="col-sm-4">
<button class="btn btn-danger">Faktura</button>
</div>
<div class="cols-sm-3">
<button class="btn btn-warning">Tidl. besøg</button>
</div>
<div class="col-sm-4">
<button class="btn btn-success">Produkter</button>
</div>
</div>
<div id="this-draft" style="@(Activity.ActivityStatusEnum is "order" or "quote" ? "display: block" : "display:none")"> <div id="this-draft" style="@(Activity.ActivityStatusEnum is "order" or "quote" ? "display: block" : "display:none")">
@* Draft lines in draft -----------------------------------------------------*@ @* Draft lines in draft -----------------------------------------------------*@
<div class="row"> <div class="row">
@ -280,31 +291,31 @@ else
aria-labelledby="deliveryHeader" data-bs-parent="#crmActivity"> aria-labelledby="deliveryHeader" data-bs-parent="#crmActivity">
<div class="accordion-body"> <div class="accordion-body">
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvName" class="col-md-2 col-form-label">Lev. Navn</label> <label for="dlvName" class="col-sm-2 col-form-label-sm">Lev. Navn</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvName" class="form-control" @bind-Value="Activity.DlvName"/> <InputText id="dlvName" class="form-control" @bind-Value="Activity.DlvName"/>
</div> </div>
</div> </div>
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvAddress1" class="col-md-2 col-form-label">Lev. Adresse</label> <label for="dlvAddress1" class="col-sm-2 col-form-label-sm">Lev. Adresse</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvAddress1" class="form-control" @bind-Value="Activity.DlvAddress1"/> <InputText id="dlvAddress1" class="form-control" @bind-Value="Activity.DlvAddress1"/>
</div> </div>
</div> </div>
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvAddress2" class="col-md-2 col-form-label">Lev. Adresse</label> <label for="dlvAddress2" class="col-sm-2 col-form-label-sm">Lev. Adresse</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvAddress2" class="form-control" @bind-Value="Activity.DlvAddress2"/> <InputText id="dlvAddress2" class="form-control" @bind-Value="Activity.DlvAddress2"/>
</div> </div>
</div> </div>
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvZipCode" class="col-md-2 col-form-label">Lev. Postnr</label> <label for="dlvZipCode" class="col-sm-2 col-form-label-sm">Lev. Postnr</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvZipCode" class="form-control" @bind-Value="Activity.DlvZipCode"/> <InputText id="dlvZipCode" class="form-control" @bind-Value="Activity.DlvZipCode"/>
</div> </div>
</div> </div>
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvCity" class="col-md-2 col-form-label">Lev. Bynavn</label> <label for="dlvCity" class="col-sm-2 col-form-label-sm">Lev. Bynavn</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvCity" class="form-control" @bind-Value="Activity.DlvCity"/> <InputText id="dlvCity" class="form-control" @bind-Value="Activity.DlvCity"/>
</div> </div>

View file

@ -39,7 +39,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
[Inject] private ILogger<AdvisorActivityCreatePage> Logger { get; set; } [Inject] private ILogger<AdvisorActivityCreatePage> Logger { get; set; }
[Inject] private HttpInterceptorService Interceptor { get; set; } [Inject] private HttpInterceptorService Interceptor { get; set; }
[Inject] private UserProfileService Profiles { get; set; } [Inject] private UserProfileService Profiles { get; set; }
[Inject] private IToastService Toast { get; set; } [Inject] private IToastService Toaster { get; set; }
[Inject] private NavigationManager Navigator { get; set; } [Inject] private NavigationManager Navigator { get; set; }
[Inject] private ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] private ICountryCatalogRepository Catalog { get; set; } [Inject] private ICountryCatalogRepository Catalog { get; set; }
@ -153,6 +153,10 @@ public partial class AdvisorActivityCreatePage : IDisposable
} }
Working = false; Working = false;
//StateHasChanged(); //StateHasChanged();
if (Company.CountryCode.ToLower() == "se" && VatUtils.SanitizeVatNumber(Activity.VatNumber).Length < 10)
{
Toaster.ShowWarning("Organisationsnummer skal opdateres.");
}
} }
private async Task CallConfirmProductCheckModel() private async Task CallConfirmProductCheckModel()
@ -181,17 +185,17 @@ public partial class AdvisorActivityCreatePage : IDisposable
{ {
Working = true; Working = true;
// pop a message // pop a message
Toast.ShowError("Produkt gennemgang mangler. Vent mens produkt oversigt indlæses. Gå ikke væk fra siden!", "Produkt check ..."); Toaster.ShowError("Produkt gennemgang mangler. Vent mens produkt oversigt indlæses. Gå ikke væk fra siden!", "Produkt check ...");
// product inventory has not been updated // product inventory has not been updated
// send rpc call to sync ERP to CRM // send rpc call to sync ERP to CRM
Toast.ShowInfo("Vent mens data synkroniseres ...", "ERP til CRM ..."); Toaster.ShowInfo("Vent mens data synkroniseres ...", "ERP til CRM ...");
var ts = await CustomerHistory.InvoiceErpToCrmRpc(CompanyId, Company.HistorySync); var ts = await CustomerHistory.InvoiceErpToCrmRpc(CompanyId, Company.HistorySync);
while (string.IsNullOrWhiteSpace(ts)) while (string.IsNullOrWhiteSpace(ts))
await Task.Delay(500); await Task.Delay(500);
// save pDate // save pDate
await Storage.SetItemAsync($"{CompanyId}-pDate", ts); await Storage.SetItemAsync($"{CompanyId}-pDate", ts);
// request products from backend // request products from backend
Toast.ShowInfo("Vent mens produkt oversigt hentes", "CRM produkt liste"); Toaster.ShowInfo("Vent mens produkt oversigt hentes", "CRM produkt liste");
CheckList = await CustomerHistory.FetchInventory(CompanyId); CheckList = await CustomerHistory.FetchInventory(CompanyId);
if(CheckList.Any()) if(CheckList.Any())
@ -211,6 +215,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
// Show CheckList modal // Show CheckList modal
ConfirmProductCheckModal.Show(); ConfirmProductCheckModal.Show();
} }
private async Task ConfirmProductCheckCallback() private async Task ConfirmProductCheckCallback()
{ {
ConfirmProductCheckModal.Hide(); ConfirmProductCheckModal.Hide();
@ -297,26 +302,35 @@ public partial class AdvisorActivityCreatePage : IDisposable
// avoid duplication // avoid duplication
if (Working) if (Working)
return; return;
// validate customer address1 - this is a required input // validate customer address1
// - this is a required input
if (string.IsNullOrWhiteSpace(Activity.Address1)) if (string.IsNullOrWhiteSpace(Activity.Address1))
{ {
Toast.ShowError("Kunde adresse er ufuldstændig."); Toaster.ShowError("Kunde adresse er ufuldstændig.");
return; return;
} }
// // validate org number
// - this is a required input
// - must validate according to country rules.
if (!VatUtils.ValidateFormat(Company.CountryCode, Activity.VatNumber))
{
Toaster.ShowError("Firma registreringsnummer er ikke korrekt.");
return;
}
// validate input according to status
switch (Activity.ActivityStatusEnum) switch (Activity.ActivityStatusEnum)
{ {
// don't accept order with no lines // don't accept order with no lines
case "order" when DraftProvider.Draft.Items.Count == 0: case "order" when DraftProvider.Draft.Items.Count == 0:
Toast.ShowError("Ved bestilling skal der være en eller flere linjer i kladden."); Toaster.ShowError("Ved bestilling skal der være en eller flere linjer i kladden.");
return; return;
// phone number is required if first time customer // phone number is required if first time customer
case "order" when Company.Account is "NY" or "" && string.IsNullOrWhiteSpace(Activity.Phone): case "order" when Company.Account is "NY" or "" && string.IsNullOrWhiteSpace(Activity.Phone):
Toast.ShowError("Ved bestilling til ny kunde skal telefon nummer angives."); Toaster.ShowError("Ved bestilling til ny kunde skal telefon nummer angives.");
return; return;
// verify email address is a valid address // verify email address is a valid address
case "quote" when !Utils.IsValidEmail(Activity.Email): case "quote" when !Utils.IsValidEmail(Activity.Email):
Toast.ShowError("Ved tilbud skal en gyldig email adresse angives."); Toaster.ShowError("Ved tilbud skal en gyldig email adresse angives.");
return; return;
} }
// raise working flag // raise working flag
@ -371,7 +385,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
// show result message // show result message
if (result.IsSuccess) if (result.IsSuccess)
{ {
Toast.ShowSuccess($"{result.Message}", Toaster.ShowSuccess($"{result.Message}",
DraftProvider.Draft.Items.Count == 0 ? "Besøg er oprettet" : "Bestilling/Tilbud er oprettet"); DraftProvider.Draft.Items.Count == 0 ? "Besøg er oprettet" : "Bestilling/Tilbud er oprettet");
await DeleteDraft(); await DeleteDraft();
Navigator.NavigateTo($"/advisor/customers"); Navigator.NavigateTo($"/advisor/customers");
@ -380,7 +394,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
// lower working flag // lower working flag
Working = false; Working = false;
// show error message // show error message
Toast.ShowError(result.Message, "ORDRE FEJL"); Toaster.ShowError(result.Message, "ORDRE FEJL");
} }
/// <summary> /// <summary>
@ -481,7 +495,13 @@ public partial class AdvisorActivityCreatePage : IDisposable
{ {
if (string.IsNullOrEmpty(Activity.ActivityTypeEnum) && !ReportClosed) if (string.IsNullOrEmpty(Activity.ActivityTypeEnum) && !ReportClosed)
{ {
Toast.ShowWarning("Aktivitet type kan ikke være tom"); Toaster.ShowWarning("Aktivitet type kan ikke være tom");
PoFormInvalid = true;
return;
}
if (Company.CountryCode.ToLower() == "se" && VatUtils.SanitizeVatNumber(Activity.VatNumber).Length < 10)
{
PoFormInvalid = true; PoFormInvalid = true;
return; return;
} }

View file

@ -1,13 +1,13 @@
{ {
"appInfo": { "appInfo": {
"name": "Wonky Online", "name": "Wonky Online",
"version": "0.103.xxx", "version": "0.103.1",
"rc": true, "rc": true,
"sandBox": false, "sandBox": false,
"image": "grumpy-coder.png" "image": "grumpy-coder.png"
}, },
"apiConfig": { "apiConfig": {
"baseUrl": "https://dev.innotec.dk", "baseUrl": "https://zeta.innotec.dk",
"catalog": "api/v2/catalog/country", "catalog": "api/v2/catalog/country",
"crmCustomers": "api/v2/crm/companies", "crmCustomers": "api/v2/crm/companies",
"crmInventoryExt": "history/inventory", "crmInventoryExt": "history/inventory",