Files
TaskHybrid/salesbook.Shared/Components/SingleElements/Modal/ContactForm.razor
2025-08-06 12:31:52 +02:00

564 lines
22 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
@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>
@if (VtbTipi.IsNullOrEmpty())
{
<span class="warning-text">Nessun tipo cliente trovato</span>
}
else
{
<MudSelectExtended FullWidth="true" ReadOnly="@(IsView || VtbTipi.IsNullOrEmpty())" T="string?" Variant="Variant.Text" @bind-Value="ContactModel.CodVtip" @bind-Value:after="OnAfterChangeValue" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var tipo in VtbTipi)
{
<MudSelectItemExtended Class="custom-item-select" Value="@tipo.CodVtip">@($"{tipo.CodVtip} - {tipo.Descrizione}")</MudSelectItemExtended>
}
</MudSelectExtended>
}
</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>
<MudSelectExtended FullWidth="true" T="string?" Variant="Variant.Text" NoWrap="true"
@bind-Value="ContactModel.CodVage" @bind-Value:after="OnAfterChangeValue"
Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var user in Users)
{
<MudSelectItemExtended Class="custom-item-select" Value="@user.UserCode">@($"{user.UserCode} - {user.FullName}")</MudSelectItemExtended>
}
</MudSelectExtended>
</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>
}
<div class="container-button">
@if (IsNew)
{
<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>
}
else
{
@if (NetworkService.IsNetworkAvailable() && !ContactModel.IsContact)
{
<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.IsNetworkAvailable();
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; }
protected override async Task OnInitializedAsync()
{
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
await 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 async Task LoadData()
{
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.GetTable<AnagClie>(x => x.PartIva.Equals(pIva))).LastOrDefault();
if (clie == null)
{
var pros = (await ManageData.GetTable<PtbPros>(x => x.PartIva.Equals(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()
{
}
}