Form persone di riferimento e clienti

This commit is contained in:
2025-07-21 10:08:10 +02:00
parent b2064ad71e
commit 7bcb0581cc
14 changed files with 298 additions and 88 deletions

View File

@@ -23,6 +23,7 @@ public class LocalDbService
_connection.CreateTableAsync<StbActivityResult>(); _connection.CreateTableAsync<StbActivityResult>();
_connection.CreateTableAsync<StbActivityType>(); _connection.CreateTableAsync<StbActivityType>();
_connection.CreateTableAsync<StbUser>(); _connection.CreateTableAsync<StbUser>();
_connection.CreateTableAsync<VtbTipi>();
} }
public async Task ResetSettingsDb() public async Task ResetSettingsDb()
@@ -32,10 +33,12 @@ public class LocalDbService
await _connection.ExecuteAsync("DROP TABLE IF EXISTS stb_activity_result;"); await _connection.ExecuteAsync("DROP TABLE IF EXISTS stb_activity_result;");
await _connection.ExecuteAsync("DROP TABLE IF EXISTS stb_activity_type;"); await _connection.ExecuteAsync("DROP TABLE IF EXISTS stb_activity_type;");
await _connection.ExecuteAsync("DROP TABLE IF EXISTS stb_user;"); await _connection.ExecuteAsync("DROP TABLE IF EXISTS stb_user;");
await _connection.ExecuteAsync("DROP TABLE IF EXISTS vtb_tipi;");
await _connection.CreateTableAsync<StbActivityResult>(); await _connection.CreateTableAsync<StbActivityResult>();
await _connection.CreateTableAsync<StbActivityType>(); await _connection.CreateTableAsync<StbActivityType>();
await _connection.CreateTableAsync<StbUser>(); await _connection.CreateTableAsync<StbUser>();
await _connection.CreateTableAsync<VtbTipi>();
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -82,5 +82,8 @@ public class SyncDbService(IIntegryApiService integryApiService, LocalDbService
if (!settingsResponse.StbUsers.IsNullOrEmpty()) if (!settingsResponse.StbUsers.IsNullOrEmpty())
await localDb.InsertAll(settingsResponse.StbUsers!); await localDb.InsertAll(settingsResponse.StbUsers!);
if (!settingsResponse.VtbTipi.IsNullOrEmpty())
await localDb.InsertAll(settingsResponse.VtbTipi!);
} }
} }

View File

@@ -139,8 +139,13 @@
margin-bottom: 1rem; margin-bottom: 1rem;
box-shadow: var(--custom-box-shadow); box-shadow: var(--custom-box-shadow);
border-radius: 16px; border-radius: 16px;
max-height: 32vh;
overflow: auto;
scrollbar-width: none;
} }
.container-pers-rif::-webkit-scrollbar { display: none; }
.container-pers-rif .divider { .container-pers-rif .divider {
margin: 0 0 0 3.5rem; margin: 0 0 0 3.5rem;
width: unset; width: unset;

View File

@@ -1,6 +1,6 @@
@using salesbook.Shared.Core.Entity @using salesbook.Shared.Core.Entity
<div class="activity-card"> <div style="margin-top: 1rem;" class="activity-card">
<div class="activity-left-section"> <div class="activity-left-section">
<div class="activity-body-section"> <div class="activity-body-section">
<div class="title-section"> <div class="title-section">

View File

@@ -2,9 +2,11 @@
@using salesbook.Shared.Components.Layout @using salesbook.Shared.Components.Layout
@using salesbook.Shared.Core.Interface @using salesbook.Shared.Core.Interface
@using salesbook.Shared.Components.Layout.Overlay @using salesbook.Shared.Components.Layout.Overlay
@using salesbook.Shared.Core.Entity
@inject IManageDataService ManageData @inject IManageDataService ManageData
@inject INetworkService NetworkService @inject INetworkService NetworkService
@inject IIntegryApiService IntegryApiService @inject IIntegryApiService IntegryApiService
@inject IDialogService Dialog
<MudDialog Class="customDialog-form"> <MudDialog Class="customDialog-form">
<DialogContent> <DialogContent>
@@ -24,10 +26,14 @@
</div> </div>
<div class="input-card"> <div class="input-card">
<div class="form-container">
<span class="disable-full-width">P. IVA</span>
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Placeholder="Partita IVA"
Variant="Variant.Text" Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1" Lines="1"
@bind-Value="ContactModel.PartIva" @bind-Value="ContactModel.PartIva"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
@@ -35,6 +41,44 @@
OnDebounceIntervalElapsed="OnAfterChangeValue"/> OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div> </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>
<div class="input-card"> <div class="input-card">
<div class="form-container"> <div class="form-container">
<span class="disable-full-width">Indirizzo</span> <span class="disable-full-width">Indirizzo</span>
@@ -43,6 +87,8 @@
T="string?" T="string?"
Variant="Variant.Text" Variant="Variant.Text"
Lines="1" Lines="1"
FullWidth="true"
Class="customIcon-select"
@bind-Value="ContactModel.Indirizzo" @bind-Value="ContactModel.Indirizzo"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
DebounceInterval="500" DebounceInterval="500"
@@ -57,6 +103,8 @@
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Variant="Variant.Text" Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1" Lines="1"
@bind-Value="ContactModel.Cap" @bind-Value="ContactModel.Cap"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
@@ -72,6 +120,8 @@
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Variant="Variant.Text" Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1" Lines="1"
@bind-Value="ContactModel.Citta" @bind-Value="ContactModel.Citta"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
@@ -87,6 +137,8 @@
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Variant="Variant.Text" Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1" Lines="1"
@bind-Value="ContactModel.Prov" @bind-Value="ContactModel.Prov"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
@@ -102,6 +154,8 @@
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Variant="Variant.Text" Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1" Lines="1"
@bind-Value="ContactModel.Nazione" @bind-Value="ContactModel.Nazione"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
@@ -112,26 +166,69 @@
<div class="input-card"> <div class="input-card">
<div class="form-container"> <div class="form-container">
<span class="disable-full-width">Tipo cliente</span> <span class="disable-full-width">PEC</span>
@* @if (Commesse.IsNullOrEmpty()) <MudTextField ReadOnly="IsView"
{ T="string?"
<span class="warning-text">Nessuna commessa presente</span> Variant="Variant.Text"
} FullWidth="true"
else Class="customIcon-select"
{ Lines="1"
<MudSelectExtended FullWidth="true" ReadOnly="@(IsView || Commesse.IsNullOrEmpty())" T="string?" Variant="Variant.Text" @bind-Value="UserModel.CodJcom" @bind-Value:after="OnCommessaChanged" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code"> @bind-Value="ContactModel.EMailPec"
@foreach (var com in Commesse) @bind-Value:after="OnAfterChangeValue"
{ DebounceInterval="500"
<MudSelectItemExtended Class="custom-item-select" Value="@com.CodJcom">@($"{com.CodJcom} - {com.Descrizione}")</MudSelectItemExtended> OnDebounceIntervalElapsed="OnAfterChangeValue"/>
} </div>
</MudSelectExtended>
} *@ <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> </div>
@if (IsNew) @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"> <div class="container-button">
<MudButton Class="button-settings gray-icon" <MudButton Class="button-settings gray-icon"
FullWidth="true" FullWidth="true"
@@ -155,6 +252,9 @@
[Parameter] public ContactDTO? OriginalModel { get; set; } [Parameter] public ContactDTO? OriginalModel { get; set; }
private ContactDTO ContactModel { get; set; } = new(); private ContactDTO ContactModel { get; set; } = new();
private List<VtbTipi>? VtbTipi { get; set; }
private List<PersRifDTO>? PersRifList { get; set; }
private bool IsNew => OriginalModel is null; private bool IsNew => OriginalModel is null;
private bool IsView => !NetworkService.IsNetworkAvailable(); private bool IsView => !NetworkService.IsNetworkAvailable();
@@ -168,13 +268,25 @@
{ {
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter; Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
_ = LoadData(); await LoadData();
LabelSave = IsNew ? "Aggiungi" : null; LabelSave = IsNew ? "Aggiungi" : null;
} }
private async Task Save() private async Task Save()
{ {
VisibleOverlay = true;
StateHasChanged();
var requestDto = new CRMCreateContactRequestDTO
{
TipoAnag = ContactModel.IsContact ? "C" : "P",
Cliente = ContactModel,
PersRif = PersRifList
};
await IntegryApiService.SaveContact(requestDto);
SuccessAnimation = true; SuccessAnimation = true;
StateHasChanged(); StateHasChanged();
@@ -185,10 +297,18 @@
private async Task LoadData() private async Task LoadData()
{ {
if (!IsNew) if (IsNew)
{
ContactModel.IsContact = false;
}
else
{
ContactModel = OriginalModel!.Clone(); ContactModel = OriginalModel!.Clone();
} }
VtbTipi = await ManageData.GetTable<VtbTipi>();
}
private void OnAfterChangeValue() private void OnAfterChangeValue()
{ {
if (!IsNew) if (!IsNew)
@@ -197,8 +317,30 @@
} }
} }
private void NewPersRif() 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();
}
} }
}

View File

@@ -2,3 +2,8 @@
background: var(--mud-palette-background-gray) !important; background: var(--mud-palette-background-gray) !important;
box-shadow: unset; box-shadow: unset;
} }
.container-chip-persrif {
width: 100%;
margin-bottom: 1rem;
}

View File

@@ -8,13 +8,13 @@
<MudDialog Class="customDialog-form"> <MudDialog Class="customDialog-form">
<DialogContent> <DialogContent>
<HeaderLayout ShowProfile="false" Cancel="true" OnCancel="() => MudDialog.Cancel()" LabelSave="@LabelSave" OnSave="Save" Title="@(IsNew ? "Nuovo" : $"{PersRifModel.CodPersRif}")" /> <HeaderLayout ShowProfile="false" Cancel="true" OnCancel="() => MudDialog.Cancel()" LabelSave="@LabelSave" OnSave="Save" Title="@(IsNew ? "Nuovo" : "Persona di riferimento")" />
<div class="content"> <div class="content">
<div class="input-card"> <div class="input-card">
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Placeholder="Persona di riferimento" Placeholder="Cognome e Nome"
Variant="Variant.Text" Variant="Variant.Text"
Lines="1" Lines="1"
@bind-Value="PersRifModel.PersonaRif" @bind-Value="PersRifModel.PersonaRif"
@@ -42,6 +42,8 @@
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Variant="Variant.Text" Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1" Lines="1"
@bind-Value="PersRifModel.EMail" @bind-Value="PersRifModel.EMail"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
@@ -57,6 +59,8 @@
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Variant="Variant.Text" Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1" Lines="1"
@bind-Value="PersRifModel.Fax" @bind-Value="PersRifModel.Fax"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
@@ -72,6 +76,8 @@
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Variant="Variant.Text" Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1" Lines="1"
@bind-Value="PersRifModel.NumCellulare" @bind-Value="PersRifModel.NumCellulare"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
@@ -87,6 +93,8 @@
<MudTextField ReadOnly="IsView" <MudTextField ReadOnly="IsView"
T="string?" T="string?"
Variant="Variant.Text" Variant="Variant.Text"
FullWidth="true"
Class="customIcon-select"
Lines="1" Lines="1"
@bind-Value="PersRifModel.Telefono" @bind-Value="PersRifModel.Telefono"
@bind-Value:after="OnAfterChangeValue" @bind-Value:after="OnAfterChangeValue"
@@ -126,12 +134,15 @@
private async Task Save() private async Task Save()
{ {
VisibleOverlay = true;
StateHasChanged();
SuccessAnimation = true; SuccessAnimation = true;
StateHasChanged(); StateHasChanged();
await Task.Delay(1250); await Task.Delay(1250);
MudDialog.Close(); MudDialog.Close(PersRifModel);
} }
private async Task LoadData() private async Task LoadData()
@@ -147,9 +158,4 @@
LabelSave = !OriginalModel.Equals(PersRifModel) ? "Aggiorna" : null; LabelSave = !OriginalModel.Equals(PersRifModel) ? "Aggiorna" : null;
} }
} }
private void NewPersRif()
{
}
} }

View File

@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;
namespace salesbook.Shared.Core.Dto;
public class CRMCreateContactRequestDTO
{
[JsonPropertyName("codVdes")]
public string? CodVdes { get; set; }
[JsonPropertyName("tipoAnag")]
public string TipoAnag { get; set; }
[JsonPropertyName("cliente")]
public ContactDTO Cliente { get; set; }
[JsonPropertyName("persRif")]
public List<PersRifDTO>? PersRif { get; set; }
}

View File

@@ -28,6 +28,9 @@ public class PersRifDTO
[JsonPropertyName("telefono")] [JsonPropertyName("telefono")]
public string Telefono { get; set; } public string Telefono { get; set; }
[JsonIgnore]
public int TempId { get; set; }
public PersRifDTO Clone() public PersRifDTO Clone()
{ {
return (PersRifDTO)MemberwiseClone(); return (PersRifDTO)MemberwiseClone();

View File

@@ -13,4 +13,7 @@ public class SettingsResponseDTO
[JsonPropertyName("stbUsers")] [JsonPropertyName("stbUsers")]
public List<StbUser>? StbUsers { get; set; } public List<StbUser>? StbUsers { get; set; }
[JsonPropertyName("vtbTipi")]
public List<VtbTipi>? VtbTipi { get; set; }
} }

View File

@@ -0,0 +1,14 @@
using SQLite;
using System.Text.Json.Serialization;
namespace salesbook.Shared.Core.Entity;
[Table("vtb_tipi")]
public class VtbTipi
{
[PrimaryKey, Column("cod_vtip"), JsonPropertyName("codVtip")]
public string CodVtip { get; set; }
[Column("descrizione"), JsonPropertyName("descrizione")]
public string Descrizione { get; set; }
}

View File

@@ -14,4 +14,5 @@ public interface IIntegryApiService
Task DeleteActivity(string activityId); Task DeleteActivity(string activityId);
Task<List<StbActivity>?> SaveActivity(ActivityDTO activity); Task<List<StbActivity>?> SaveActivity(ActivityDTO activity);
Task SaveContact(CRMCreateContactRequestDTO request);
} }

View File

@@ -1,4 +1,5 @@
using IntegryApiClient.Core.Domain.Abstraction.Contracts.Account; using System.Xml;
using IntegryApiClient.Core.Domain.Abstraction.Contracts.Account;
using IntegryApiClient.Core.Domain.RestClient.Contacts; using IntegryApiClient.Core.Domain.RestClient.Contacts;
using salesbook.Shared.Core.Dto; using salesbook.Shared.Core.Dto;
using salesbook.Shared.Core.Entity; using salesbook.Shared.Core.Entity;
@@ -67,4 +68,7 @@ public class IntegryApiService(IIntegryApiRestClient integryApiRestClient, IUser
public Task<List<StbActivity>?> SaveActivity(ActivityDTO activity) => public Task<List<StbActivity>?> SaveActivity(ActivityDTO activity) =>
integryApiRestClient.AuthorizedPost<List<StbActivity>?>("crm/saveActivity", activity); integryApiRestClient.AuthorizedPost<List<StbActivity>?>("crm/saveActivity", activity);
public Task SaveContact(CRMCreateContactRequestDTO request) =>
integryApiRestClient.AuthorizedPost<object>("crm/createContact", request);
} }

View File

@@ -41,20 +41,23 @@
} }
.form-container > span { .form-container > span {
font-weight: 600; font-weight: 700;
width: 50%; width: 50%;
margin-right: .3rem; margin-right: .3rem;
} }
.form-container > .warning-text { .form-container > .warning-text {
font-weight: 500; font-weight: 700;
color: var(--mud-palette-gray-darker); color: var(--mud-palette-gray-darker);
width: unset; width: unset;
margin: 0; margin: 0;
} }
.form-container > .disable-full-width { width: unset !important; } .form-container > .disable-full-width {
width: unset !important;
white-space: nowrap;
}
.dateTime-picker { .dateTime-picker {
display: flex; display: flex;