finalize endpoint split - v.0.47.1 - api v.2.22.325.1408

This commit is contained in:
Frede Hundewadt 2022-11-21 15:22:28 +01:00
parent 341e254071
commit abfa4ab132
17 changed files with 444 additions and 414 deletions

View file

@ -15,27 +15,26 @@
//
*@
<EditForm EditContext="_editContext" OnValidSubmit="SubmitForm">
<EditForm EditContext="_editContext">
<DataAnnotationsValidator/>
<div class="row">
<label for="streetName" class="col-md-2 col-form-label">Adresse</label>
<div class="col-md-4">
<InputText id="streetName" class="form-control" placeholder="Rudolfgårdsvej"
@bind-Value="@Address.StreetName"/>
<div class="row mb-2">
<label for="streetName" class="col-sm-1 col-form-label-sm">Vejnavn</label>
<div class="col-md-3">
<InputText id="streetName" class="form-control" @bind-Value="@Address.StreetName"/>
<ValidationMessage For="@(() => Address.StreetName)"/>
</div>
<div class="col-md-2">
<InputText id="houseNumber" class="form-control" placeholder="9"
@bind-Value="Address.HouseNumber"/>
<label for="houseNumber" class="col-sm-1 col-form-label-sm">Hus Nr.</label>
<div class="col-sm-2">
<InputText id="houseNumber" class="form-control" @bind-Value="Address.HouseNumber"/>
<ValidationMessage For="@(() => Address.HouseNumber)"/>
</div>
<div class="col-md-2">
<InputText id="zipCode" class="form-control" placeholder="8260"
@bind-Value="Address.ZipCode"/>
<label for="zipCode" class="col-sm-1 col-form-label-sm">Post Nr.</label>
<div class="col-sm-2">
<InputText id="zipCode" class="form-control" @bind-Value="Address.ZipCode"/>
<ValidationMessage For="@(() => Address.ZipCode)"/>
</div>
<div class="col-md-2">
<button class="btn btn-primary" type="submit">HENT</button>
<div class="col-sm-2 text-end">
<button class="btn btn-primary" type="button" @onclick="SubmitForm">HENT</button>
</div>
</div>
</EditForm>

View file

@ -19,31 +19,20 @@ using Microsoft.AspNetCore.Components.Forms;
using Wonky.Client.Models;
namespace Wonky.Client.Components;
public partial class VatAddressInputComponent : IDisposable
public partial class VatAddressInputComponent
{
[Parameter] public VatAddress Address { get; set; }
private EditContext _editContext { get; set; }
private bool _formInvalid = true;
private VatAddress _address { get; set; }
[Parameter] public EventCallback<VatAddress> OnValidSubmit { get; set; }
protected override void OnInitialized()
protected override void OnParametersSet()
{
_editContext = new EditContext(Address);
_editContext.OnFieldChanged += HandleFieldChanged;
_address = Address;
_editContext = new EditContext(_address);
}
private async Task SubmitForm()
{
await OnValidSubmit.InvokeAsync(Address);
}
private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
_formInvalid = !_editContext.Validate();
StateHasChanged();
}
public void Dispose()
{
_editContext.OnFieldChanged -= HandleFieldChanged;
}
}

View file

@ -15,17 +15,16 @@
//
*@
<EditForm EditContext="_editName" OnValidSubmit="SubmitForm">
<EditForm Model="_companyName">
<DataAnnotationsValidator/>
<div class="row mb-1">
<label for="companyName" class="col-md-2 col-form-label">Navn</label>
<div class="col-md-8">
<InputText id="companyName" class="form-control" placeholder="Innotec Danmark A/S"
@bind-Value="@CompanyName"/>
<ValidationMessage For="@(() => CompanyName)"/>
<div class="row mb-2">
<label for="companyName" class="col-sm-1 col-form-label-sm">Navn</label>
<div class="col-sm-9">
<InputText id="companyName" class="form-control" @bind-Value="@_companyName"/>
<ValidationMessage For="@(() => _companyName)"/>
</div>
<div class="col-md-2">
<button class="btn btn-primary" type="submit" disabled="@_formInvalid">HENT</button>
<div class="col-sm-2 text-end">
<button class="btn btn-primary" type="button" @onclick="SubmitForm">HENT</button>
</div>
</div>
</EditForm>

View file

@ -19,29 +19,18 @@ using Microsoft.AspNetCore.Components.Forms;
using Wonky.Client.Models;
namespace Wonky.Client.Components;
public partial class VatCompanyNameInputComponent : IDisposable
public partial class VatCompanyNameInputComponent
{
[Parameter] public string CompanyName { get; set; } = "";
private EditContext _editName { get; set; }
private bool _formInvalid = true;
[Required(ErrorMessage = "Navn kan ikke være tomt")] private string _companyName { get; set; } = "";
[Parameter] public EventCallback<string> OnValidSubmit { get; set; }
protected override void OnInitialized()
protected override void OnParametersSet()
{
_editName = new EditContext(CompanyName);
_editName.OnFieldChanged += HandleFieldChanged;
_companyName = CompanyName;
}
private async Task SubmitForm()
{
await OnValidSubmit.InvokeAsync(CompanyName);
}
private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
_formInvalid = !_editName.Validate();
StateHasChanged();
}
public void Dispose()
{
_editName!.OnFieldChanged -= HandleFieldChanged;
}
}

View file

@ -15,18 +15,17 @@
//
*@
<EditForm EditContext="_editVatNumber" OnValidSubmit="SubmitForm">
<EditForm Model="_vatNumber">
<DataAnnotationsValidator/>
<div class="row">
<div class="col-md-2 col-form-label">CVR/ORG</div>
<div class="col-md-4">
<InputText id="vatNumber" class="form-control" placeholder="26991765"
@bind-Value="@VatNumber"/>
<ValidationMessage For="@(() => VatNumber)"/>
<div class="row mb-2">
<div class="col-sm-1 col-form-label-sm">CVR/ORG</div>
<div class="col-sm-3">
<InputText id="vatNumber" class="form-control" @bind-Value="@_vatNumber"/>
<ValidationMessage For="@(() => _vatNumber)"/>
</div>
<div class="col-md-4"></div>
<div class="col-md-2">
<button class="btn btn-primary" type="submit" disabled="@_formInvalid">HENT</button>
<div class="col-sm-6"></div>
<div class="col-sm-2 text-end">
<button class="btn btn-primary" type="button" @onclick="SubmitForm">HENT</button>
</div>
</div>
</EditForm>

View file

@ -19,29 +19,20 @@ using Microsoft.AspNetCore.Components.Forms;
using Wonky.Client.Models;
namespace Wonky.Client.Components;
public partial class VatNumberInputComponent : IDisposable
public partial class VatNumberInputComponent
{
[Parameter] public string VatNumber { get; set; } = "";
private EditContext _editVatNumber { get; set; }
private bool _formInvalid = true;
[Required(ErrorMessage = "Moms Nummer skal angives")][MinLength(8, ErrorMessage = "Mindst 8 tegn")] private string _vatNumber { get; set; } = "";
[Parameter] public EventCallback<string> OnValidSubmit { get; set; }
protected override void OnInitialized()
protected override void OnParametersSet()
{
_editVatNumber = new EditContext(VatNumber);
_editVatNumber.OnFieldChanged += HandleFieldChanged;
_vatNumber = VatNumber;
}
private async Task SubmitForm()
{
await OnValidSubmit.InvokeAsync(VatNumber);
}
private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
_formInvalid = !_editVatNumber!.Validate();
StateHasChanged();
}
public void Dispose()
{
_editVatNumber!.OnFieldChanged -= HandleFieldChanged;
}
}

View file

@ -21,12 +21,49 @@ namespace Wonky.Client.HttpInterfaces;
public interface ICrmCompanyHttpRepository
{
/// <summary>
/// Get a list of CRM entities
/// </summary>
/// <param name="pagingParameters"></param>
/// <returns>A paged response defined by pagingParameters</returns>
Task<PagingResponse<CompanyDto>> GetCompanies(CompanyPagingParams pagingParameters);
/// <summary>
/// Get CRM entity by Id
/// </summary>
/// <param name="companyId"></param>
/// <returns>A CRM Company entity</returns>
Task<CompanyDto> GetCompanyById(string companyId);
/// <summary>
/// Create new CRM entity
/// </summary>
/// <param name="model"></param>
/// <returns>The Id of the entity</returns>
Task<string> CreateCompany(CompanyDto model);
Task<bool> UpdateCompany(string companyId, CompanyDto model);
/// <summary>
/// Delete the CRM entity
/// </summary>
/// <param name="companyId"></param>
/// <returns>true/false to define success</returns>
Task<bool> DeleteCompany(string companyId);
/// <summary>
/// Update CRM entity properties
/// </summary>
/// <param name="companyId"></param>
/// <param name="model"></param>
/// <returns>A CRM Company entity</returns>
Task<CompanyDto> UpdateCrmData(string companyId, CompanyDto model);
/// <summary>
/// Update ERP entity properties
/// </summary>
/// <param name="companyId"></param>
/// <param name="model"></param>
/// <returns>A CRM Company entity</returns>
Task<CompanyDto> UpdateErpData(string companyId, CompanyDto model);
/// <summary>
/// Update Entity Vat Number
/// </summary>
/// <param name="companyId"></param>
/// <param name="vatNumber"></param>
/// <returns>A CRM Company entity</returns>
Task<CompanyDto> UpdateCompanyVat(string companyId, string vatNumber);
}

View file

@ -104,19 +104,6 @@ public class CrmCompanyHttpRepository : ICrmCompanyHttpRepository
var result = JsonSerializer.Deserialize<CompanyDto>(content, _options);
return result.CompanyId;
}
/// <summary>
/// Update company card
/// </summary>
/// <param name="companyId"></param>
/// <param name="model"></param>
/// <returns></returns>
public async Task<bool> UpdateCompany(string companyId, CompanyDto model)
{
var response = await _client.PutAsJsonAsync($"{_conf.CrmCustomers}/{companyId}", model);
var content = await response.Content.ReadAsStringAsync();
return response.IsSuccessStatusCode;
}
/// <summary>
/// Delete company
@ -137,7 +124,16 @@ public class CrmCompanyHttpRepository : ICrmCompanyHttpRepository
/// <returns></returns>
public async Task<CompanyDto> UpdateCrmData(string companyId, CompanyDto model)
{
var response = await _client.PutAsJsonAsync($"{_conf.CrmCustomers}/{companyId}/crmData", model, _options);
var updateModel = new UpdateCrmDto
{
Interval = model.Interval,
Note = model.Note,
CrmNotes = model.CrmNotes,
IsHidden = model.IsHidden,
LastVisit = model.LastVisit,
NextVisit = model.NextVisit
};
var response = await _client.PutAsJsonAsync($"{_conf.CrmCustomers}/{companyId}/crmData", updateModel, _options);
var content = await response.Content.ReadAsStringAsync();
return string.IsNullOrWhiteSpace(content) ? new CompanyDto() : JsonSerializer.Deserialize<CompanyDto>(content);
}
@ -150,7 +146,20 @@ public class CrmCompanyHttpRepository : ICrmCompanyHttpRepository
/// <returns></returns>
public async Task<CompanyDto> UpdateErpData(string companyId, CompanyDto model)
{
var response = await _client.PutAsJsonAsync($"{_conf.CrmCustomers}/{companyId}/erpData", model, _options);
var updateModel = new UpdateErpDto
{
Address1 = model.Address1,
Address2 = model.Address2,
Attention = model.Attention,
City = model.City,
Email = model.Email,
Mobile = model.Mobile,
Name = model.Name,
Phone = model.Phone,
ZipCode = model.ZipCode
};
var response = await _client.PutAsJsonAsync($"{_conf.CrmCustomers}/{companyId}/erpData", updateModel, _options);
var content = await response.Content.ReadAsStringAsync();
return string.IsNullOrWhiteSpace(content) ? new CompanyDto() : JsonSerializer.Deserialize<CompanyDto>(content);
}
@ -171,5 +180,4 @@ public class CrmCompanyHttpRepository : ICrmCompanyHttpRepository
var content = await response.Content.ReadAsStringAsync();
return string.IsNullOrWhiteSpace(content) ? new CompanyDto() : JsonSerializer.Deserialize<CompanyDto>(content);
}
}

View file

@ -213,10 +213,13 @@ public partial class CrmActivityNewPage : IDisposable
/// </summary>
private async Task CreateActivity()
{
// disable submit button to avoid multiple clicks
PoFormInvalid = true;
if (string.IsNullOrWhiteSpace(Activity.Address1))
{
Toast.ShowError("Kunde adresse er ufuldstændig.");
PoFormInvalid = false;
return;
}
@ -224,27 +227,24 @@ public partial class CrmActivityNewPage : IDisposable
{
if (DraftStateProvider.Draft.Items.Count == 0)
{
Toast.ShowError("Ved bestilling skal der angives et eller flere varenumre.");
Toast.ShowError("Ved bestilling skal der være en eller flere linjer i kladden.");
PoFormInvalid = false;
return;
}
if (string.IsNullOrWhiteSpace(Activity.Phone))
if (Company.Account is "NY" or "" && string.IsNullOrWhiteSpace(Activity.Phone))
{
Toast.ShowError("Ved bestilling til ny kunde skal telefon nummer angives.");
PoFormInvalid = false;
return;
}
}
PoFormInvalid = true;
// reset selected item
SelectedItem = new SalesItemView();
// check if phone number need to be updated
if (OldPhone != Activity.Phone)
{
Company.Phone = Activity.Phone;
// update company phone record
Logger.LogDebug("CrmNewActivityPage => \n New Phone Number \n {}", Activity.Phone);
await CompanyRepo.UpdateCompany(Company.CompanyId, Company);
await CompanyRepo.UpdateErpData(Company.CompanyId, Company);
}
// begin assembling activity
Activity.ActivityDate = $"{SelectedDate:yyyy-MM-dd}";
@ -284,10 +284,10 @@ public partial class CrmActivityNewPage : IDisposable
// show result message
if (result.IsSuccess)
{
Toast.ShowSuccess($"{result.Message}", "RESULTAT");
await DraftStateProvider.DeleteDraftAsync();
Toast.ShowSuccess($"{result.Message}",
DraftStateProvider.Draft.Items.Count == 0 ? "Besøg er oprettet" : "Bestilling er oprettet");
await DeleteDraft();
Navigator.NavigateTo($"/companies");
return;
}
PoFormInvalid = false;

View file

@ -32,238 +32,202 @@
<div class="alert bg-dark text-white">
<h3>@Company.Name</h3>
</div>
<EditForm EditContext="ErpContext" OnValidSubmit="SubmitUpdate">
<DataAnnotationsValidator/>
<div class="row mb-1">
<label for="name" class="col-sm-1 col-form-label-sm">Navn</label>
<div class="col-sm-5">
<InputText id="name" class="form-control" @bind-Value="Company.Name"/>
<ValidationMessage For="@(() => Company.Name)"></ValidationMessage>
</div>
<label for="attention" class="col-sm-1 col-form-label-sm">Att.</label>
<div class="col-sm-3">
<InputText id="attention" class="form-control" @bind-Value="Company.Attention"/>
<ValidationMessage For="@(() => Company.Attention)"></ValidationMessage>
</div>
// erp context
<EditForm EditContext="ErpContext">
<DataAnnotationsValidator/>
<div class="row g-3">
@* Company Name *@
<label for="name" class="col-sm-1 col-form-label-sm">Navn</label>
<div class="col-sm-5">
<InputText id="name" class="form-control" @bind-Value="Company.Name"/>
<ValidationMessage For="@(() => Company.Name)"></ValidationMessage>
</div>
@* Company Attention *@
<label for="attention" class="col-sm-1 col-form-label-sm">Att.</label>
<div class="col-sm-5">
<InputText id="attention" class="form-control" @bind-Value="Company.Attention"/>
<ValidationMessage For="@(() => Company.Attention)"></ValidationMessage>
</div>
@* Address 1 *@
<label for="address1" class="col-sm-1 col-form-label-sm">Adresse</label>
<div class="col-sm-5">
<InputText id="address1" class="form-control" @bind-Value="Company.Address1"/>
<ValidationMessage For="@(() => Company.Address1)"></ValidationMessage>
</div>
@* Address 2 *@
<label for="address2" class="col-sm-1 col-form-label-sm">Adresse</label>
<div class="col-sm-5">
<InputText id="address2" class="form-control" @bind-Value="Company.Address2"/>
<ValidationMessage For="@(() => Company.Address2)"></ValidationMessage>
</div>
@* Post Code *@
<label for="zipCode" class="col-sm-1 col-form-label-sm">PostNr</label>
<div class="col-sm-2">
<InputText id="zipCode" class="form-control" @bind-Value="Company.ZipCode"/>
<ValidationMessage For="@(() => Company.ZipCode)"></ValidationMessage>
</div>
@* City Name *@
<label for="city" class="col-sm-1 col-form-label-sm">Bynavn</label>
<div class="col-sm-8">
<InputText id="city" class="form-control" @bind-Value="Company.City"/>
<ValidationMessage For="@(() => Company.City)"></ValidationMessage>
</div>
@* Phone *@
<label for="phone" class="col-sm-1 col-form-label-sm">Telefon</label>
<div class="col-sm-5">
<InputText id="phone" class="form-control" @bind-Value="Company.Phone"/>
<ValidationMessage For="@(() => Company.Phone)"></ValidationMessage>
</div>
@* Mobile *@
<label for="mobile" class="col-sm-1 col-form-label-sm">Mobil</label>
<div class="col-sm-5">
<InputText id="mobile" class="form-control" @bind-Value="Company.Mobile"/>
<ValidationMessage For="@(() => Company.Mobile)"></ValidationMessage>
</div>
@* Email *@
<label for="email" class="col-sm-1 col-form-label-sm">Epost</label>
<div class="col-sm-7">
<InputText id="email" class="form-control" @bind-Value="Company.Email"/>
<ValidationMessage For="@(() => Company.Email)"></ValidationMessage>
</div>
<div class="col-sm-2 text-end">
<button type="button" class="btn btn-primary" disabled="@(Company.HasFolded == 0)" onclick="@ForceActivity">Aktiver besøg</button>
</div>
<div class="col-sm-2 text-end">
<button type="button" class="btn btn-primary" onclick="@UpdateErpData" disabled="@(Working)">Gem ERP data</button>
</div>
<div class="row mb-1">
<label for="address1" class="col-sm-1 col-form-label-sm">Adresse</label>
<div class="col-sm-5">
<InputText id="address1" class="form-control" @bind-Value="Company.Address1"/>
<ValidationMessage For="@(() => Company.Address1)"></ValidationMessage>
</div>
<label for="address2" class="col-sm-1 col-form-label-sm">Adresse</label>
<div class="col-sm-3">
<InputText id="address2" class="form-control" @bind-Value="Company.Address2"/>
<ValidationMessage For="@(() => Company.Address2)"></ValidationMessage>
@* account and vat number*@
<label for="account" class="col-sm-1 col-form-label-sm">Konto</label>
<div class="col-sm-2">
<input id="account" type="text" class="form-control" readonly value="@Company.Account"/>
</div>
<label for="vatNumber" class="col-sm-1 col-form-label-sm">Moms Nr</label>
<div class="col-sm-4">
<div class="input-group">
<span class="input-group-text">
<DisplayStateComponent StateClass="@VatState"/>
</span>
<InputText id="vatNumber" class="form-control" @bind-Value="Company.VatNumber"/>
<ValidationMessage For="@(() => Company.VatNumber)"></ValidationMessage>
</div>
</div>
<div class="row mb-1">
<label for="zipCode" class="col-sm-1 col-form-label-sm">PostNr</label>
<div class="col-sm-2">
<InputText id="zipCode" class="form-control" @bind-Value="Company.ZipCode"/>
<ValidationMessage For="@(() => Company.ZipCode)"></ValidationMessage>
</div>
<label for="city" class="col-sm-1 col-form-label-sm">Bynavn</label>
<div class="col-sm-6">
<InputText id="city" class="form-control" @bind-Value="Company.City"/>
<ValidationMessage For="@(() => Company.City)"></ValidationMessage>
</div>
<div class="col-sm-2 text-end">
<button type="button" class="btn btn-primary" @onclick="CallVatLookupModal">CVR opslag</button>
</div>
<div class="row mb-1">
<label for="phone" class="col-sm-1 col-form-label-sm">Telefon</label>
<div class="col-sm-4">
<InputText id="phone" class="form-control" @bind-Value="Company.Phone"/>
<ValidationMessage For="@(() => Company.Phone)"></ValidationMessage>
</div>
<label for="mobile" class="col-sm-1 col-form-label-sm">Mobil</label>
<div class="col-sm-4">
<InputText id="mobile" class="form-control" @bind-Value="Company.Mobile"/>
<ValidationMessage For="@(() => Company.Mobile)"></ValidationMessage>
</div>
<div class="col-sm-2 text-end">
<button type="button" class="btn btn-primary" @onclick="UpdateVatNumber">Gem Moms Nr</button>
</div>
</div>
<div class="row mb-1">
<label for="email" class="col-sm-1 col-form-label-sm">Epost</label>
<div class="col-sm-9">
<InputText id="email" class="form-control" @bind-Value="Company.Email"/>
<ValidationMessage For="@(() => Company.Email)"></ValidationMessage>
</div>
<hr class="mb-3"/>
@* activity buttons *@
<div class="row mt-3 mb-2">
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@Company.CompanyId/invoices">Faktura</a>
</div>
<hr/>
@* vat context *@
<div class="row mb-1">
<label for="vatNumber" class="col-sm-1 col-form-label-sm">MomsNr</label>
<div class="col-sm-4">
<div class="input-group">
<span class="input-group-text">
<DisplayStateComponent StateClass="@VatState"/>
</span>
<InputText id="vatNumber" class="form-control" @bind-Value="Company.VatNumber"/>
<ValidationMessage For="@(() => Company.VatNumber)"></ValidationMessage>
</div>
</div>
<label for="account" class="col-sm-1 col-form-label-sm">Konto</label>
<div class="col-sm-4">
<input id="account" type="text" class="form-control" readonly value="@Company.Account"/>
</div>
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@Company.CompanyId/activities">Besøg</a>
</div>
@* erp context *@
<div class="row mb-1">
<label for="note" class="col-sm-1 col-form-label-sm">OBS</label>
<div class="col-sm-9">
@if (string.IsNullOrWhiteSpace(Company.Note))
{
<InputText name="note" id="note" class="form-control" @bind-Value="Company.Note"/>
}
else
{
<InputText name="note" id="note" class="form-control bg-warning text-black" @bind-Value="Company.Note"/>
}
<ValidationMessage For="@(() => Company.Note)"></ValidationMessage>
</div>
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@Company.CompanyId/h/i">Produkter</a>
</div>
<div class="row mb-1">
<label for="nextVisit" class="col-sm-1 col-form-label-sm">Næste besøg</label>
<div class="col-sm-3">
<div class="input-group">
<span class="input-group-text">
<DisplayStateComponent StateClass="@VisitState"/>
</span>
<InputDate id="nextVisit" class="form-control" @bind-Value="@(NextVisit)"/>
</div>
</div>
<label for="lastVisit" class="col-sm-1 col-form-label-sm">Sidse besøg</label>
<div class="col-sm-3">
<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">
<InputNumber id="interval" class="form-control" @bind-Value="Company.Interval"/>
<ValidationMessage For="@(() => Company.Interval)"></ValidationMessage>
</div>
<div class="col">
<ActivityButton ActionLink="@ActionLink"
ButtonText="Besøg"
ButtonType="primary"
Enabled="@EnableActivity">
</ActivityButton>
</div>
</div>
<div class="row mb-1">
<label for="contacts" class="col-sm-1 col-form-label-sm">Kontakt</label>
<div id="contacts" class="col-sm-6">
<div class="list-group">
<div class="list-group-item list-group-item-action" @onclick="() => OpenContact(DefaultContact)">
<div class="row">
<div class="col-sm-4">Stilling</div>
<div class="col-sm-4">Navn</div>
<div class="col-sm-3">Direkte</div>
<div class="col-sm-1 text-end">
<i class="bi-plus-circle"></i>
</div>
<hr class="mb-3"/>
@* crm context - OBS note *@
<div class="row mb-1">
<label for="note" class="col-sm-1 col-form-label-sm">OBS</label>
<div class="col-sm-8">
@if (string.IsNullOrWhiteSpace(Company.Note))
{
<InputText name="note" id="note" class="form-control" @bind-Value="Company.Note"/>
}
else
{
<InputText name="note" id="note" class="form-control bg-warning text-black" @bind-Value="Company.Note"/>
}
<ValidationMessage For="@(() => Company.Note)"></ValidationMessage>
</div>
@* Save CRM data button *@
<div class="col-sm-3 text-end">
<button type="button" class="btn btn-primary" @onclick="UpdateCrmData">Gem CRM data</button>
</div>
</div>
@* crm context - contacts *@
<div class="row mb-1">
<label for="contacts" class="col-sm-1 col-form-label-sm">Kontakt</label>
<div id="contacts" class="col-sm-6">
<div class="list-group">
<div class="list-group-item list-group-item-action" @onclick="() => OpenContact(DefaultContact)">
<div class="row">
<div class="col-sm-4">Stilling</div>
<div class="col-sm-4">Navn</div>
<div class="col-sm-3">Direkte</div>
<div class="col-sm-1 text-end">
<i class="bi-plus-circle"></i>
</div>
</div>
@if (Contacts.Any())
</div>
@if (Contacts.Any())
{
@foreach (var contact in Contacts)
{
@foreach (var contact in Contacts)
{
<div class="list-group-item list-group-item-action" @onclick="() => OpenContact(contact)">
<div class="row">
<div class="col-sm-4">@contact.JobTitle</div>
<div class="col-sm-4">@contact.FirstName @contact.LastName</div>
<div class="col-sm-3">@contact.PhoneDirect</div>
<div class="col-sm-1 text-end">
<i class="bi-pencil"></i>
</div>
<div class="list-group-item list-group-item-action" @onclick="() => OpenContact(contact)">
<div class="row">
<div class="col-sm-4">@contact.JobTitle</div>
<div class="col-sm-4">@contact.FirstName @contact.LastName</div>
<div class="col-sm-3">@contact.PhoneDirect</div>
<div class="col-sm-1 text-end">
<i class="bi-pencil"></i>
</div>
</div>
}
</div>
}
</div>
</div>
<label for="crmNotes" class="col-sm-1 col-form-label-sm">Noter</label>
<div class="col-sm-4">
<InputTextArea id="crmNotes" class="form-control" style="height: 100%;" @bind-Value="Company.CrmNotes"/>
</div>
</div>
<div class="row mt-3 mb-2">
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@Company.CompanyId/invoices">Faktura</a>
</div>
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@Company.CompanyId/activities">Besøg</a>
</div>
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@Company.CompanyId/h/i">Produkter</a>
</div>
<div class="col d-block">
<button type="submit" class="btn btn-light border-dark d-block" disabled="@(Working)">Gem</button>
@if (Company.HasFolded == 1)
{
<button type="button" class="btn btn-warning" onclick="@ForceActivity">Aktiver besøg</button>
}
</div>
<div class="col">
<ActivityButton ActionLink="@ActionLink"
ButtonText="Besøg"
ButtonType="primary"
Enabled="@EnableActivity">
</ActivityButton>
</div>
<label for="crmNotes" class="col-sm-1 col-form-label-sm">Noter</label>
<div class="col-sm-4">
<InputTextArea id="crmNotes" class="form-control" style="height: 100%;" @bind-Value="Company.CrmNotes"/>
</div>
</div>
@* crm context - dates and interval *@
<div class="row mb-1">
<label for="nextVisit" class="col-sm-1 col-form-label-sm">Næste besøg</label>
<div class="col-sm-3">
<div class="input-group">
<span class="input-group-text">
<DisplayStateComponent StateClass="@VisitState"/>
</span>
<InputDate id="nextVisit" class="form-control" @bind-Value="@(NextVisit)"/>
</div>
</div>
<label for="lastVisit" class="col-sm-1 col-form-label-sm">Sidse besøg</label>
<div class="col-sm-3">
<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">
<InputNumber id="interval" class="form-control" @bind-Value="Company.Interval"/>
<ValidationMessage For="@(() => Company.Interval)"></ValidationMessage>
</div>
</div>
</EditForm>
@if (Dk)
{
<div class="alert alert-light border-dark">
<h3>CVR adresse kontrol</h3>
<div class="row mt-2">
<VatAddressInputComponent Address="CompanyVatAddress" OnValidSubmit="GetInfoFromAddress"/>
@if (VatInfos.Any())
{
<table class="table">
<thead>
<tr>
<th scope="col">CVR ORG</th>
<th scope="col">Navn</th>
<th scope="col">Status</th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@foreach (var info in VatInfos)
{
<tr>
<td class="align-middle">@info.VatNumber</td>
<td class="align-middle">@info.Name</td>
<td class="align-middle">@info.States[^1].State</td>
<td class="align-middle">
<button class="btn btn-primary" @onclick="@(() => SelectCompany(info.VatNumber, true))">OVERFØR</button>
</td>
<td class="align-middle">
<button class="btn btn-primary" @onclick="@(() => SelectCompany(info.VatNumber, false))">CVR/VAT</button>
</td>
</tr>
}
</tbody>
</table>
}
</div>
</div>
}
}
else
{
<LoaderThreeDots/>
}
<VatLookupDkModal VatAddress="CompanyVatAddress" OnSelectedCompany="OnSelectedCompany" @ref="VatLookupPopup"/>
<ContactModal ParamContact="@SelectedContact" CompanyName="@Company.Name" @ref="ContactPopup" OnSaveClicked="SaveContact" OnDeleteClicked="DeleteContact"/>
<VatLookupDkModal VatAddress="CompanyVatAddress" EntityName="@Company.Name" VatNumber="@Company.VatNumber"
@ref="VatLookupPopup" OnSelectedCompany="OnSelectedCompanyCallback" />
<ContactModal ParamContact="@SelectedContact" CompanyName="@Company.Name"
@ref="ContactPopup" OnSaveClicked="SaveContact" OnDeleteClicked="DeleteContact"/>

View file

@ -51,7 +51,6 @@ public partial class CrmCompanyViewPage : IDisposable
private readonly JsonSerializerOptions _options = new () { PropertyNameCaseInsensitive = true };
private CompanyDto Company { get; set; } = new();
private EditContext ErpContext { get; set; }
private EditContext CrmContext { get; set; }
private List<VirkRegInfo> VatInfos { get; set; } = new();
private VirkRegInfo CompanyRegInfo { get; set; } = new();
private DateTime LastVisit { get; set; }
@ -61,15 +60,15 @@ public partial class CrmCompanyViewPage : IDisposable
private bool ValidVat;
private bool HasFolded;
private bool FormInvalid = true;
private string CurrentOrgVat;
private string CountryCode = "dk";
private string VisitState = "the-ugly";
private int EnableActivity = 1;
private bool HideButtons = false;
private string ActionLink = "";
private bool Working;
private string BtnUpdateText = "Opdater";
private bool Dk { get; set; } = true;
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 HideButtons { get; set; }
private string ActionLink { get; set; } = "";
private bool Working { get; set; }
private string BtnUpdateText { get; set; } = "Opdater";
private bool countryIsDk { get; set; } = true;
private int IsDirty { get; set; }
private int VatUpdated { get; set; }
private bool Loading { get; set; } = true;
@ -84,20 +83,20 @@ public partial class CrmCompanyViewPage : IDisposable
DefaultContact = new ContactDto { CompanyId = CompanyId, ContactId = "", FirstName = ""};
var ux = await Storage.GetItemAsync<UserInfoView>("_xu");
CountryCode = ux.CountryCode;
Dk = ux.CountryCode.ToLower() == "dk";
countryIsDk = ux.CountryCode.ToLower() == "dk";
Company = await CompanyRepo.GetCompanyById(CompanyId);
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
// Company = await _companyRepo.GetCompanyById(CompanyId);
CurrentOrgVat = Company.VatNumber;
CurrentVat = Company.VatNumber;
Company.CountryCode = ux.CountryCode.ToLower();
// internal flag
EnableActivity = Company.ValidVat;
// override if canvas which has account property as empty string or "NY"
if (Company.Account == "NY" || string.IsNullOrWhiteSpace(Company.Account))
EnableActivity = 1;
if (Dk)
if (countryIsDk)
CompanyVatAddress = PrepareVatAddress(Company);
if (Company.Interval == 0)
@ -116,25 +115,23 @@ public partial class CrmCompanyViewPage : IDisposable
ActionLink = $"/companies/{CompanyId}/activities/new"; // used when drawing visit button
if(Company.HasFolded == 1)
{
// this is only used if user has selected to show all customers
// this is only used if user has selected to show closed companies
HasFolded = true;
VatState = "the-dead";
VisitState = "the-dead";
}
else
{
// simple format validation if CRM indicates invalid vatNumber
if (Company.ValidVat == 0 && !string.IsNullOrWhiteSpace(Company.VatNumber))
Company.ValidVat = VatUtils.ValidateFormat(Company.CountryCode, Company.VatNumber) ? 1 : 0;
// flags
ValidVat = Company.ValidVat == 1; // flag set if company has a valid vatNumber
// valid vat enum
Company.ValidVat = VatUtils.ValidateFormat(Company.CountryCode, Company.VatNumber) ? 1 : 0;
// valid vat flag
ValidVat = Company.ValidVat == 1; // true/false flag set if company has a valid vatNumber
// vat state css class
VatState = Company.ValidVat == 1 ? "the-good" : "no-vat"; // assign css class
}
ErpContext = new EditContext(Company);
ErpContext.OnFieldChanged += HandleFieldChanged;
ErpContext.OnValidationStateChanged += ValidationChanged;
await SyncCompanyHistory();
Contacts = await ContactRepo.GetContacts(CompanyId);
}
@ -195,8 +192,6 @@ public partial class CrmCompanyViewPage : IDisposable
ValidVat = true;
EnableActivity = 1;
}
if(VatUpdated == 0 && e.FieldIdentifier.FieldName != "Note" && e.FieldIdentifier.FieldName != "CrmNotes")
IsDirty = 1;
StateHasChanged();
}
@ -215,66 +210,60 @@ public partial class CrmCompanyViewPage : IDisposable
ErpContext.OnValidationStateChanged += ValidationChanged;
}
private async Task<bool> UpdateCrmData()
/// <summary>
/// Update CRM data (
/// </summary>
/// <returns>true/false</returns>
private async Task UpdateCrmData()
{
Toaster.ShowInfo("Vent venligst ...", "Opdaterer CRM data");
Working = true;
Company.LastVisit = $"{LastVisit:yyyy-MM-dd}";
Company.NextVisit = $"{NextVisit:yyyy-MM-dd}";
Company.IsHidden = 0;
Company = await CompanyRepo.UpdateCompanyVat(CompanyId, Company.VatNumber);
StateHasChanged();
return true;
var result = await CompanyRepo.UpdateCrmData(CompanyId, Company);
if (!string.IsNullOrWhiteSpace(result.Name))
{
Company = result;
}
}
private async Task<bool> UpdateErpData()
/// <summary>
/// Update ERP data
/// </summary>
/// <returns></returns>
private async Task UpdateErpData()
{
Working = true;
Toaster.ShowInfo("Vent venligst ...", "Opdaterer Erp data");
Company = await CompanyRepo.UpdateCompanyVat(CompanyId, Company.VatNumber);
StateHasChanged();
Working = false;
return true;
var result = await CompanyRepo.UpdateErpData(CompanyId, Company);
if (!string.IsNullOrWhiteSpace(result.Name))
{
Company = result;
}
}
private async Task<bool> UpdateVatNumber()
/// <summary>
/// Update Vat Number
/// </summary>
/// <returns>true/false</returns>
private async Task UpdateVatNumber()
{
// simple format validation if CRM indicates invalid vatNumber
if (!VatUtils.ValidateFormat(Company.CountryCode, Company.VatNumber))
{
Toaster.ShowError($"CVR/VAT/ORG nummer mangler eller er ugyldig.");
StateHasChanged();
return false;
Toaster.ShowError($"Moms Nummer ugyldigt");
}
Working = true;
Toaster.ShowInfo("Vent venligst ...", "Opdaterer Moms Nr");
Company = await CompanyRepo.UpdateCompanyVat(CompanyId, Company.VatNumber);
StateHasChanged();
Working = false;
return true;
}
private async Task SubmitUpdate()
{
if (!await UpdateCrmData())
var result = await CompanyRepo.UpdateCompanyVat(CompanyId, Company.VatNumber);
if (!string.IsNullOrWhiteSpace(result.Name))
{
Toaster.ShowError("CRM data opdatering fejlede.");
return;
}
if (!await UpdateErpData())
{
Toaster.ShowError("ERP data opdatering fejlede.");
Company = result;
}
}
/// <summary>
/// Get vat info from known address
/// </summary>
/// <param name="address"></param>
private async Task GetInfoFromAddress(VatAddress address)
{
Toaster.ShowInfo("Vent for adresse info ...");
@ -291,6 +280,11 @@ public partial class CrmCompanyViewPage : IDisposable
}
}
/// <summary>
/// Prepare vat address from company model
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
private static VatAddress PrepareVatAddress(CompanyDto model)
{
var digits = "1234567890".ToCharArray();
@ -320,13 +314,21 @@ public partial class CrmCompanyViewPage : IDisposable
return new VatAddress();
}
/// <summary>
/// Show Vat Lookup modal
/// </summary>
private void CallVatLookupModal()
{
VatLookupPopup.Show();
}
private void OnSelectedCompany(VirkRegInfo regInfo)
/// <summary>
/// Modal callback to update company properties
/// </summary>
/// <param name="regInfo"></param>
private void OnSelectedCompanyCallback(VirkRegInfo regInfo)
{
// this can be removed in favor of the new data returned from updating the VatNumber
ValidVat = regInfo.States[0].State.ToLower() == "normal";
Company.HasFolded = ValidVat ? 1 : 0;
EnableActivity = ValidVat ? 1 : 0;
@ -340,18 +342,26 @@ public partial class CrmCompanyViewPage : IDisposable
Company.Address2 = CompanyRegInfo.CoName;
Company.ZipCode = CompanyRegInfo.ZipCode;
Company.City = CompanyRegInfo.City;
IsDirty = 1;
Toaster.ShowInfo("Husk at gemme firma data", "Påmindelse");
}
else
{
Company.VatNumber = CompanyRegInfo.VatNumber;
Company = CompanyRepo.UpdateCompanyVat(CompanyId, Company.VatNumber).Result;
}
// Updated properties are returned from API
Company = CompanyRepo.UpdateCompanyVat(CompanyId, Company.VatNumber).Result;
StateHasChanged();
}
/// <summary>
/// Select company and update properties - if applicable (syncAll)
/// To be deprecated in favor of a modal window to do lookup and selection
/// </summary>
/// <param name="vatNumber">Vat Number</param>
/// <param name="syncAll">true/false to update base properties as well as vat number</param>
private void SelectCompany(string vatNumber, bool syncAll)
{
// this can be reomved in f
CompanyRegInfo = (from x in VatInfos where x.VatNumber == vatNumber select x).First();
ValidVat = CompanyRegInfo.States[0].State.ToLower() == "normal";
Company.HasFolded = ValidVat ? 1 : 0;
@ -366,18 +376,22 @@ public partial class CrmCompanyViewPage : IDisposable
Company.Address2 = CompanyRegInfo.CoName;
Company.ZipCode = CompanyRegInfo.ZipCode;
Company.City = CompanyRegInfo.City;
IsDirty = 1;
Toaster.ShowInfo("Husk at gemme firma data", "Påmindelse");
}
else
{
Company.VatNumber = CompanyRegInfo.VatNumber;
Company = CompanyRepo.UpdateCompanyVat(CompanyId, Company.VatNumber).Result;
}
// Updated properties are returned from API
Company = CompanyRepo.UpdateCompanyVat(CompanyId, Company.VatNumber).Result;
// empty list
VatInfos = new List<VirkRegInfo>();
StateHasChanged();
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Interceptor.DisposeEvent();

View file

@ -55,14 +55,11 @@ public partial class ContactModal : IDisposable
if (string.IsNullOrWhiteSpace(Contact.ContactId))
DisableDelete = true;
}
protected override void OnInitialized()
{
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
}
private void DeleteContact()
{
OnDeleteClicked.InvokeAsync(Contact.ContactId);

View file

@ -24,10 +24,10 @@
<button type="button" class="btn-close" @onclick="Hide" data-bs-dismiss="modal" aria-label="Luk"></button>
</div>
<div class="modal-body">
<VatNumberInputComponent OnValidSubmit="GetInfoFromVat"/>
<VatCompanyNameInputComponent OnValidSubmit="GetInfoFromName"/>
<VatAddressInputComponent Address="VatAddress" OnValidSubmit="GetInfoFromAddress"/>
@if (_vInfos.Any())
<VatNumberInputComponent VatNumber="@_vatNumber" OnValidSubmit="GetInfoFromVat"/>
<VatCompanyNameInputComponent CompanyName="@_entityName" OnValidSubmit="GetInfoFromName"/>
<VatAddressInputComponent Address="@_vatAddress" OnValidSubmit="GetInfoFromAddress"/>
@if (VatInfos.Any())
{
<table class="table table-sm table-striped">
<thead>
@ -40,7 +40,7 @@
</tr>
</thead>
<tbody>
@foreach (var info in _vInfos)
@foreach (var info in VatInfos)
{
<tr>
<td class="align-middle">@info.VatNumber</td>

View file

@ -31,54 +31,66 @@ public partial class VatLookupDkModal
private string _modalDisplay = "";
private bool _showBackdrop;
[Parameter] public VatAddress VatAddress { get; set; } = new();
[Parameter] public string EntityName { get; set; } = "";
[Parameter] public string VatNumber { get; set; } = "";
[Parameter] public EventCallback<VirkRegInfo> OnSelectedCompany { get; set; }
[Inject] public VatInfoLookupService _vatService { get; set; }
[Inject] public IToastService _toast { get; set; }
private VirkRegInfo _virkRegInfo { get; set; } = new();
private List<VirkRegInfo> _vInfos { get; set; } = new();
[Inject] public VatInfoLookupService VatService { get; set; }
[Inject] public IToastService Toaster { get; set; }
private VirkRegInfo CompanyRegInfo { get; set; } = new();
private List<VirkRegInfo> VatInfos { get; set; } = new();
private VatAddress _vatAddress { get; set; } = new();
private string _vatNumber { get; set; } = "";
private string _entityName { get; set; } = "";
protected override void OnParametersSet()
{
_vatAddress = VatAddress;
_entityName = EntityName;
_vatNumber = VatNumber;
}
private async Task SelectCompany(string vatNumber, bool syncAll)
{
_virkRegInfo = _vInfos.First(x => x.VatNumber == vatNumber);
_virkRegInfo.SyncAll = syncAll;
await OnSelectedCompany.InvokeAsync(_virkRegInfo);
CompanyRegInfo = VatInfos.First(x => x.VatNumber == vatNumber);
CompanyRegInfo.SyncAll = syncAll;
await OnSelectedCompany.InvokeAsync(CompanyRegInfo);
}
private async Task GetInfoFromAddress(VatAddress address)
{
_toast.ShowInfo("Vent for adresse info ...");
_vInfos = await _vatService.QueryVirkRegistry(
Toaster.ShowInfo("Vent for adresse info ...");
VatInfos = await VatService.QueryVirkRegistry(
new VirkParams
{
StreetName = address.StreetName,
HouseNumber = address.HouseNumber,
ZipCode = address.ZipCode
});
if (!_vInfos.Any())
if (!VatInfos.Any())
{
_toast.ShowWarning($"Ingen data fundet ...");
Toaster.ShowWarning($"Ingen data fundet ...");
}
}
private async Task GetInfoFromVat(string vatNumber)
{
_toast.ShowInfo("Vent for firma info ...");
_vInfos = await _vatService
Toaster.ShowInfo("Vent for firma info ...");
VatInfos = await VatService
.QueryVirkRegistry(new VirkParams {VatNumber = vatNumber});
if (!_vInfos.Any())
if (!VatInfos.Any())
{
_toast.ShowError($"Firma med CVR '{vatNumber}' findes ikke.");
Toaster.ShowError($"Firma med CVR '{vatNumber}' findes ikke.");
}
}
private async Task GetInfoFromName(string entityName)
{
_toast.ShowInfo("Vent for firma info ...");
_vInfos = await _vatService
Toaster.ShowInfo("Vent for firma info ...");
VatInfos = await VatService
.QueryVirkRegistry(new VirkParams {EntityName = entityName});
if (!_vInfos.Any())
if (!VatInfos.Any())
{
_toast.ShowError($"Firma med navn '{entityName}' findes ikke.");
Toaster.ShowError($"Firma med navn '{entityName}' findes ikke.");
}
}
@ -91,6 +103,9 @@ public partial class VatLookupDkModal
private void Hide()
{
VatAddress = new VatAddress();
CompanyRegInfo = new VirkRegInfo();
VatInfos = new List<VirkRegInfo>();
_modalDisplay = "none;";
_showBackdrop = false;
StateHasChanged();

View file

@ -1,13 +1,13 @@
{
"appInfo": {
"name": "Wonky Client",
"version": "0.46.1",
"version": "0.47.1",
"rc": true,
"sandBox": false,
"image": "grumpy-coder.png"
},
"apiConfig": {
"innoBaseUrl": "https://dev.innotec.dk",
"innoBaseUrl": "https://zeta.innotec.dk",
"glsTrackUrl": "https://www.gls-group.eu/276-I-PORTAL-WEB/content/GLS/DK01/DA/5004.htm?txtAction=71000&txtRefNo=",
"glsId": "",
"serviceVirk": "api/v2/services/virk",

View file

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace Wonky.Entity.DTO;
public class UpdateCrmDto
{
public string CrmNotes { get; set; } = "";
public int Interval { get; set; }
public int IsHidden { get; set; }
[MaxLength(10)] public string LastVisit { get; set; } = "";
[MaxLength(10)] public string NextVisit { get; set; } = "";
[MaxLength(100)] public string Note { get; set; } = "";
}

View file

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Wonky.Entity.DTO;
public class UpdateErpDto
{
[MaxLength(100)] public string Address1 { get; set; } = "";
[MaxLength(50)] public string Address2 { get; set; } = "";
[MaxLength(100)] public string Attention { get; set; } = "";
[MaxLength(30)] public string City { get; set; } = "";
[MaxLength(80)] public string Email { get; set; } = "";
[MaxLength(30)] public string Mobile { get; set; } = "";
[MaxLength(100)] public string Name { get; set; } = "";
[MaxLength(30)] public string Phone { get; set; } = "";
[MaxLength(20)] public string ZipCode { get; set; } = "";
}