Files
TaskHybrid/salesbook.Shared/Components/SingleElements/Modal/ContactForm.razor
2025-09-18 09:40:31 +02:00

658 lines
25 KiB
Plaintext

@using salesbook.Shared.Core.Dto
@using salesbook.Shared.Components.Layout
@using salesbook.Shared.Core.Interface
@using salesbook.Shared.Components.Layout.Overlay
@using salesbook.Shared.Core.Entity
@using salesbook.Shared.Components.SingleElements.BottomSheet
@using salesbook.Shared.Core.Dto.Contact
@using salesbook.Shared.Core.Interface.IntegryApi
@using salesbook.Shared.Core.Interface.System.Network
@inject IManageDataService ManageData
@inject INetworkService NetworkService
@inject IIntegryApiService IntegryApiService
@inject IDialogService Dialog
<MudDialog Class="customDialog-form">
<DialogContent>
<HeaderLayout ShowProfile="false" Cancel="true" OnCancel="() => MudDialog.Cancel()" LabelSave="@LabelSave" OnSave="Save" Title="@(IsNew ? "Nuovo" : $"{ContactModel.CodContact}")"/>
<div class="content">
<div class="input-card">
<MudTextField ReadOnly="IsView"
T="string?"
Placeholder="Azienda"
Variant="Variant.Text"
Lines="1"
@bind-Value="ContactModel.RagSoc"
@bind-Value:after="OnAfterChangeValue"
DebounceInterval="500"
OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">Nazione</span>
@if (Nazioni.IsNullOrEmpty())
{
<span class="warning-text">Nessuna nazione trovata</span>
}
else
{
<MudSelectExtended FullWidth="true" ReadOnly="@(IsView || Nazioni.IsNullOrEmpty())" T="string?"
Variant="Variant.Text" @bind-Value="ContactModel.Nazione" @bind-Value:after="OnAfterChangeValue"
Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var nazione in Nazioni)
{
<MudSelectItemExtended Class="custom-item-select" Value="@nazione.CodNazioneAlpha2">@($"{nazione.Descrizione}")</MudSelectItemExtended>
}
</MudSelectExtended>
}
</div>
</div>
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">P. IVA</span>
<MudTextField ReadOnly="IsView"
T="string?"
Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1"
@bind-Value="ContactModel.PartIva"
@bind-Value:after="OnAfterChangePIva"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Cod. Fiscale</span>
<MudTextField ReadOnly="IsView"
T="string?"
Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1"
@bind-Value="ContactModel.CodFisc"
@bind-Value:after="OnAfterChangeValue"
DebounceInterval="500"
OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
</div>
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">Tipo cliente</span>
<MudAutocomplete Disabled="VtbTipi.IsNullOrEmpty()" ReadOnly="IsView"
T="VtbTipi"
@bind-Value="SelectedType"
@bind-Value:after="OnTypeSelectedAfter"
SearchFunc="SearchTypeAsync"
ToStringFunc="@(u => u == null ? string.Empty : $"{u.CodVtip} - {u.Descrizione}")"
Clearable="true"
ShowProgressIndicator="true"
DebounceInterval="300"
MaxItems="50"
Class="customIcon-select"
AdornmentIcon="@Icons.Material.Filled.Code" />
</div>
</div>
@if (!IsView)
{
<div class="container-button mb-3">
<MudButton Class="button-settings blue-icon"
FullWidth="true"
StartIcon="@Icons.Material.Rounded.Search"
Size="Size.Medium"
OnClick="() => OpenSearchAddress = !OpenSearchAddress"
Variant="Variant.Outlined">
Cerca indirizzo
</MudButton>
</div>
}
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">Indirizzo</span>
<MudTextField ReadOnly="IsView"
T="string?"
Variant="Variant.Text"
Lines="1"
FullWidth="true"
Class="customIcon-select"
@bind-Value="ContactModel.Indirizzo"
@bind-Value:after="OnAfterChangeValue"
DebounceInterval="500"
OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">CAP</span>
<MudTextField ReadOnly="IsView"
T="string?"
Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1"
@bind-Value="ContactModel.Cap"
@bind-Value:after="OnAfterChangeValue"
DebounceInterval="500"
OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Città</span>
<MudTextField ReadOnly="IsView"
T="string?"
Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1"
@bind-Value="ContactModel.Citta"
@bind-Value:after="OnAfterChangeValue"
DebounceInterval="500"
OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Provincia</span>
<MudTextField ReadOnly="IsView"
T="string?"
Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
MaxLength="2"
Lines="1"
@bind-Value="ContactModel.Prov"
@bind-Value:after="OnAfterChangeValue"
DebounceInterval="500"
OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
</div>
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">PEC</span>
<MudTextField ReadOnly="IsView"
T="string?"
Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1"
@bind-Value="ContactModel.EMailPec"
@bind-Value:after="OnAfterChangeValue"
DebounceInterval="500"
OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">E-Mail</span>
<MudTextField ReadOnly="IsView"
T="string?"
Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1"
@bind-Value="ContactModel.EMail"
@bind-Value:after="OnAfterChangeValue"
DebounceInterval="500"
OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Telefono</span>
<MudTextField ReadOnly="IsView"
T="string?"
Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1"
@bind-Value="ContactModel.Telefono"
@bind-Value:after="OnAfterChangeValue"
DebounceInterval="500"
OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
</div>
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">Agente</span>
<MudAutocomplete Disabled="Users.IsNullOrEmpty()" ReadOnly="IsView"
T="StbUser"
@bind-Value="SelectedUser"
@bind-Value:after="OnUserSelectedAfter"
SearchFunc="SearchUtentiAsync"
ToStringFunc="@(u => u == null ? string.Empty : u.FullName)"
Clearable="true"
ShowProgressIndicator="true"
DebounceInterval="300"
MaxItems="50"
Class="customIcon-select"
AdornmentIcon="@Icons.Material.Filled.Code" />
</div>
</div>
@if (IsNew)
{
<div class="container-chip-persrif">
@if (!PersRifList.IsNullOrEmpty())
{
foreach (var item in PersRifList!.Select((p, index) => new { p, index }))
{
<MudChip T="string" Color="Color.Default" OnClick="() => OpenPersRifForm(item.index, item.p)" OnClose="() => OnRemovePersRif(item.index)">
@item.p.PersonaRif
</MudChip>
}
}
</div>
}
@if (IsNew)
{
<div class="container-button">
<MudButton Class="button-settings gray-icon"
FullWidth="true"
StartIcon="@Icons.Material.Filled.PersonAddAlt1"
Size="Size.Medium"
OnClick="NewPersRif"
Variant="Variant.Outlined">
Persona di riferimento
</MudButton>
</div>
}
else
{
@if (NetworkService.ConnectionAvailable && !ContactModel.IsContact)
{
<div class="container-button">
<MudButton Class="button-settings blue-icon"
FullWidth="true"
StartIcon="@Icons.Material.Rounded.Sync"
Size="Size.Medium"
OnClick="ConvertProspectToContact"
Variant="Variant.Outlined">
Converti in cliente
</MudButton>
</div>
}
}
</div>
<MudMessageBox MarkupMessage="new MarkupString(VatMessage)" @ref="CheckVat" Class="c-messageBox" Title="Verifica partita iva" CancelText="@(VatAlreadyRegistered ? "" : "Annulla")">
<YesButton>
@if (VatAlreadyRegistered)
{
<MudButton Size="Size.Small" Variant="Variant.Filled" Color="Color.Error"
StartIcon="@Icons.Material.Rounded.Close">
Chiudi
</MudButton>
}
else
{
<MudButton Size="Size.Small" Variant="Variant.Filled" Color="Color.Primary"
StartIcon="@Icons.Material.Rounded.Check">
Aggiungi
</MudButton>
}
</YesButton>
</MudMessageBox>
</DialogContent>
</MudDialog>
<SaveOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="SuccessAnimation"/>
<SearchAddress Region="@ContactModel.Nazione" @bind-IsSheetVisible="OpenSearchAddress" @bind-Indirizzo="Address" @bind-Indirizzo:after="OnAddressSet"/>
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; }
[Parameter] public ContactDTO? OriginalModel { get; set; }
private ContactDTO ContactModel { get; set; } = new();
private List<VtbTipi>? VtbTipi { get; set; }
private List<PersRifDTO>? PersRifList { get; set; }
private List<Nazioni>? Nazioni { get; set; }
private List<StbUser> Users { get; set; } = [];
private bool IsNew => OriginalModel is null;
private bool IsView => !NetworkService.ConnectionAvailable;
private string? LabelSave { get; set; }
//Overlay for save
private bool VisibleOverlay { get; set; }
private bool SuccessAnimation { get; set; }
//MessageBox
private MudMessageBox CheckVat { get; set; }
private string VatMessage { get; set; } = "";
private bool VatAlreadyRegistered { get; set; }
//BottomSeath
private bool OpenSearchAddress { get; set; }
private IndirizzoDTO Address { get; set; }
//Agente
private StbUser? SelectedUser { get; set; }
//Type
private VtbTipi? SelectedType { get; set; }
protected override async Task OnInitializedAsync()
{
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
_ = LoadData();
LabelSave = IsNew ? "Aggiungi" : null;
}
private async Task Save()
{
VisibleOverlay = true;
StateHasChanged();
var requestDto = new CRMCreateContactRequestDTO
{
TipoAnag = ContactModel.IsContact ? "C" : "P",
Cliente = ContactModel,
PersRif = PersRifList,
CodVage = ContactModel.CodVage
};
var response = await IntegryApiService.SaveContact(requestDto);
switch (response)
{
case null:
VisibleOverlay = false;
StateHasChanged();
return;
case {AnagClie: null, PtbPros: not null}:
await ManageData.InsertOrUpdate(response.PtbPros);
await ManageData.InsertOrUpdate(response.PtbProsRif);
break;
case { AnagClie: not null, PtbPros: null }:
await ManageData.InsertOrUpdate(response.AnagClie);
await ManageData.InsertOrUpdate(response.VtbCliePersRif);
break;
default:
VisibleOverlay = false;
StateHasChanged();
return;
}
SuccessAnimation = true;
StateHasChanged();
await Task.Delay(1250);
MudDialog.Close(response);
}
private Task LoadData()
{
return Task.Run(async () =>
{
if (IsNew)
{
var loggedUser = (await ManageData.GetTable<StbUser>(x => x.UserName.Equals(UserSession.User.Username))).Last();
ContactModel.IsContact = false;
ContactModel.Nazione = "IT";
ContactModel.CodVage = loggedUser.UserCode;
}
else
{
ContactModel = OriginalModel!.Clone();
}
Users = await ManageData.GetTable<StbUser>(x => x.KeyGroup == 5);
Nazioni = await ManageData.GetTable<Nazioni>();
VtbTipi = await ManageData.GetTable<VtbTipi>();
});
}
private void OnAfterChangeValue()
{
if (!IsNew)
{
LabelSave = !OriginalModel.Equals(ContactModel) ? "Aggiorna" : null;
}
}
private Task NewPersRif() => OpenPersRifForm(null, null);
private async Task OpenPersRifForm(int? index, PersRifDTO? persRif)
{
var result = await ModalHelpers.OpenPersRifForm(Dialog, persRif);
if (result is { Canceled: false, Data: not null } && result.Data.GetType() == typeof(PersRifDTO))
{
if (index != null)
OnRemovePersRif(index.Value);
PersRifList ??= [];
PersRifList.Add((PersRifDTO)result.Data);
}
}
private void OnRemovePersRif(int index)
{
if (PersRifList is null || index < 0 || index >= PersRifList.Count)
return;
PersRifList.RemoveAt(index);
StateHasChanged();
}
private async Task OnAfterChangePIva()
{
CheckVatResponseDTO? response = null;
var error = false;
VisibleOverlay = true;
StateHasChanged();
var nazione = Nazioni?.Find(x =>
x.CodNazioneAlpha2.Equals(ContactModel.Nazione) ||
x.CodNazioneIso.Equals(ContactModel.Nazione) ||
x.Nazione.Equals(ContactModel.Nazione)
);
if (nazione == null)
return;
var pIva = ContactModel.PartIva.Trim();
var clie = (await ManageData.GetClienti(new WhereCondContact {PartIva = pIva})).LastOrDefault();
if (clie == null)
{
var pros = (await ManageData.GetProspect(new WhereCondContact {PartIva = pIva})).LastOrDefault();
if (pros == null)
{
if (nazione.ChkPartIva)
{
if (!IsView)
{
try
{
response = await IntegryApiService.CheckVat(new CheckVatRequestDTO
{
CountryCode = nazione.CodNazioneAlpha2,
VatNumber = pIva
});
VatMessage = $"La p.Iva (<b>{pIva}</b>) corrisponde a:<br><br><b>{response.RagSoc}</b><br><b>{response.CompleteAddress}</b><br><br>Si desidera aggiungere questi dati nel form?";
VatAlreadyRegistered = false;
}
catch (Exception e)
{
Snackbar.Clear();
Snackbar.Add(e.Message, Severity.Error);
error = true;
}
}
else
{
Snackbar.Clear();
Snackbar.Add("La ricerca della partita iva non è al momento disponibile", Severity.Warning);
error = true;
}
}
else
{
Snackbar.Clear();
Snackbar.Add("La ricerca della partita iva non è abilitata per questa nazione", Severity.Warning);
error = true;
}
}
else
{
VatMessage = $"Prospect (<b>{pros.CodPpro}</b>) già censito con la p.iva: <b>{pIva} - {pros.RagSoc}</b>";
VatAlreadyRegistered = true;
}
}
else
{
VatMessage = $"Cliente (<b>{clie.CodAnag}</b>) già censito con la p.iva: <b>{pIva} - {clie.RagSoc}</b>";
VatAlreadyRegistered = true;
}
VisibleOverlay = false;
StateHasChanged();
if (!error)
{
var result = await CheckVat.ShowAsync();
if (result is true && response != null)
{
ContactModel.RagSoc = response.RagSoc;
ContactModel.Indirizzo = response.Indirizzo;
ContactModel.Cap = response.Cap;
ContactModel.Citta = response.Citta;
ContactModel.Prov = response.Prov;
}
}
OnAfterChangeValue();
}
private void OnAddressSet()
{
ContactModel.Citta = Address.Citta;
ContactModel.Indirizzo = Address.Indirizzo;
ContactModel.Prov = Address.Prov;
ContactModel.Cap = Address.Cap;
}
private async Task ConvertProspectToContact()
{
VisibleOverlay = true;
StateHasChanged();
var response = await IntegryApiService.TransferProspect(new CRMTransferProspectRequestDTO
{
CodPpro = ContactModel.CodContact
});
await ManageData.DeleteProspect(ContactModel.CodContact);
if (response.AnagClie != null)
await ManageData.InsertOrUpdate(response.AnagClie);
if (response.VtbCliePersRif != null)
await ManageData.InsertOrUpdate(response.VtbCliePersRif);
if (response.VtbDest != null)
await ManageData.InsertOrUpdate(response.VtbDest);
SuccessAnimation = true;
StateHasChanged();
await Task.Delay(1250);
MudDialog.Close(response.AnagClie);
}
private async Task OnUserSelectedAfter()
{
ContactModel.CodVage = SelectedUser?.UserCode;
OnAfterChangeValue();
}
private Task<IEnumerable<StbUser>> SearchUtentiAsync(string value, CancellationToken token)
{
IEnumerable<StbUser> list;
if (string.IsNullOrWhiteSpace(value))
{
list = Users.OrderBy(u => u.FullName).Take(50);
}
else
{
list = Users
.Where(x => x.UserName.Contains(value, StringComparison.OrdinalIgnoreCase)
|| x.FullName.Contains(value, StringComparison.OrdinalIgnoreCase))
.OrderBy(u => u.FullName)
.Take(50);
}
return Task.FromResult(token.IsCancellationRequested ? [] : list);
}
private async Task OnTypeSelectedAfter()
{
ContactModel.CodVtip = SelectedType?.CodVtip;
OnAfterChangeValue();
}
private Task<IEnumerable<VtbTipi>> SearchTypeAsync(string value, CancellationToken token)
{
IEnumerable<VtbTipi> list = [];
if (VtbTipi == null) return Task.FromResult(token.IsCancellationRequested ? [] : list);
if (string.IsNullOrWhiteSpace(value))
{
list = VtbTipi.OrderBy(u => u.CodVtip);
}
else
{
list = VtbTipi
.Where(x => x.CodVtip.Contains(value, StringComparison.OrdinalIgnoreCase)
|| x.Descrizione.Contains(value, StringComparison.OrdinalIgnoreCase))
.OrderBy(u => u.CodVtip)
.Take(50);
}
return Task.FromResult(token.IsCancellationRequested ? [] : list);
}
}