Implentata scansione barcode e lista articoli nel form scheda

This commit is contained in:
2026-02-26 15:46:49 +01:00
parent a5e27bcf06
commit e027d8e5cf
20 changed files with 770 additions and 19 deletions

View File

@@ -8,6 +8,7 @@ public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(op
{ {
public DbSet<Ispezione> Ispezioni => Set<Ispezione>(); public DbSet<Ispezione> Ispezioni => Set<Ispezione>();
public DbSet<Scheda> Schede => Set<Scheda>(); public DbSet<Scheda> Schede => Set<Scheda>();
public DbSet<SchedaArticolo> SchedaArticoli => Set<SchedaArticolo>();
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
@@ -31,5 +32,15 @@ public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(op
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null), v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions?)null) ?? new List<string>() v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions?)null) ?? new List<string>()
); );
modelBuilder.Entity<SchedaArticolo>()
.HasOne(a => a.Scheda)
.WithMany(s => s.Articoli)
.HasForeignKey(a => a.SchedaId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<SchedaArticolo>()
.HasIndex(a => new { a.SchedaId, a.Barcode })
.IsUnique();
} }
} }

View File

@@ -9,6 +9,7 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
public Task<Ispezione?> GetIspezioneAsync(string codMdep, DateOnly data, string rilevatore) => public Task<Ispezione?> GetIspezioneAsync(string codMdep, DateOnly data, string rilevatore) =>
db.Ispezioni db.Ispezioni
.Include(x => x.Schede) .Include(x => x.Schede)
.ThenInclude(s => s.Articoli)
.FirstOrDefaultAsync(x => .FirstOrDefaultAsync(x =>
x.CodMdep == codMdep && x.CodMdep == codMdep &&
x.Data == data && x.Data == data &&
@@ -17,6 +18,7 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
public Task<List<Ispezione>> GetAllIspezioniWithSchedeAsync() => public Task<List<Ispezione>> GetAllIspezioniWithSchedeAsync() =>
db.Ispezioni db.Ispezioni
.Include(x => x.Schede) .Include(x => x.Schede)
.ThenInclude(s => s.Articoli)
.AsNoTracking() .AsNoTracking()
.OrderByDescending(x => x.Data) .OrderByDescending(x => x.Data)
.ToListAsync(); .ToListAsync();
@@ -65,7 +67,7 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
} }
/// <summary> /// <summary>
/// Cancella l'ispezione e tutte le schede collegate. /// Cancella l'ispezione e tutte le schede collegate (e relativi articoli via cascade).
/// </summary> /// </summary>
public async Task<bool> DeleteIspezioneAsync(string codMdep, DateOnly data, string rilevatore) public async Task<bool> DeleteIspezioneAsync(string codMdep, DateOnly data, string rilevatore)
{ {
@@ -85,13 +87,20 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
public async Task AddSchedaAsync(string codMdep, DateOnly data, string rilevatore, Scheda scheda) public async Task AddSchedaAsync(string codMdep, DateOnly data, string rilevatore, Scheda scheda)
{ {
// assicura che il parent esista
await GetOrCreateIspezioneAsync(codMdep, data, rilevatore); await GetOrCreateIspezioneAsync(codMdep, data, rilevatore);
scheda.CodMdep = codMdep; scheda.CodMdep = codMdep;
scheda.Data = data; scheda.Data = data;
scheda.Rilevatore = rilevatore; scheda.Rilevatore = rilevatore;
if (scheda.Articoli is { Count: > 0 })
{
foreach (var a in scheda.Articoli)
{
a.SchedaId = scheda.Id;
}
}
db.Schede.Add(scheda); db.Schede.Add(scheda);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
} }
@@ -99,6 +108,7 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
public Task<List<Scheda>> GetAllSchedeOfIspezioneAsync(string codMdep, DateOnly data, string rilevatore) => public Task<List<Scheda>> GetAllSchedeOfIspezioneAsync(string codMdep, DateOnly data, string rilevatore) =>
db.Schede db.Schede
.AsNoTracking() .AsNoTracking()
.Include(s => s.Articoli)
.Where(x => x.CodMdep == codMdep && .Where(x => x.CodMdep == codMdep &&
x.Data == data && x.Data == data &&
x.Rilevatore == rilevatore) x.Rilevatore == rilevatore)
@@ -107,11 +117,13 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
public Task<Scheda?> GetSchedaAsync(int schedaId) => public Task<Scheda?> GetSchedaAsync(int schedaId) =>
db.Schede db.Schede
.AsNoTracking() .AsNoTracking()
.Include(s => s.Articoli)
.FirstOrDefaultAsync(x => x.Id == schedaId); .FirstOrDefaultAsync(x => x.Id == schedaId);
public Task<Scheda?> GetSchedaWithIspezioneAsync(int schedaId) => public Task<Scheda?> GetSchedaWithIspezioneAsync(int schedaId) =>
db.Schede db.Schede
.Include(x => x.Ispezione) .Include(x => x.Ispezione)
.Include(x => x.Articoli)
.AsNoTracking() .AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == schedaId); .FirstOrDefaultAsync(x => x.Id == schedaId);
@@ -128,11 +140,56 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
public async Task<bool> UpdateSchedaAsync(Scheda scheda) public async Task<bool> UpdateSchedaAsync(Scheda scheda)
{ {
var exists = await db.Schede.AnyAsync(x => x.Id == scheda.Id); var existing = await db.Schede
if (!exists) .Include(s => s.Articoli)
.FirstOrDefaultAsync(s => s.Id == scheda.Id);
if (existing is null)
return false; return false;
db.Schede.Update(scheda); db.Entry(existing).CurrentValues.SetValues(scheda);
var incoming = scheda.Articoli;
foreach (var toRemove in existing.Articoli
.Where(ea => incoming.All(ia => ia.Id != ea.Id))
.ToList())
{
existing.Articoli.Remove(toRemove);
}
foreach (var ia in incoming)
{
if (ia.Id == 0)
{
existing.Articoli.Add(new SchedaArticolo
{
Barcode = ia.Barcode,
Descrizione = ia.Descrizione,
SchedaId = existing.Id
});
}
else
{
var ea = existing.Articoli.FirstOrDefault(x => x.Id == ia.Id);
if (ea is null)
{
existing.Articoli.Add(new SchedaArticolo
{
Id = ia.Id,
Barcode = ia.Barcode,
Descrizione = ia.Descrizione,
SchedaId = existing.Id
});
}
else
{
ea.Barcode = ia.Barcode;
ea.Descrizione = ia.Descrizione;
}
}
}
await db.SaveChangesAsync(); await db.SaveChangesAsync();
return true; return true;
} }

View File

@@ -0,0 +1,148 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SteUp.Data.LocalDb;
#nullable disable
namespace SteUp.Data.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260225104013_AddListArticoli")]
partial class AddListArticoli
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.3");
modelBuilder.Entity("SteUp.Shared.Core.Entities.Ispezione", b =>
{
b.Property<string>("CodMdep")
.HasColumnType("TEXT");
b.Property<DateOnly>("Data")
.HasColumnType("TEXT");
b.Property<string>("Rilevatore")
.HasColumnType("TEXT");
b.Property<int>("Stato")
.HasColumnType("INTEGER");
b.HasKey("CodMdep", "Data", "Rilevatore");
b.ToTable("Ispezioni");
});
modelBuilder.Entity("SteUp.Shared.Core.Entities.Scheda", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ActivityTypeId")
.HasColumnType("TEXT");
b.Property<string>("CodJfas")
.HasColumnType("TEXT");
b.Property<string>("CodMdep")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateOnly>("Data")
.HasColumnType("TEXT");
b.Property<string>("DescrizioneReparto")
.HasColumnType("TEXT");
b.Property<string>("ImageNames")
.HasColumnType("TEXT");
b.Property<string>("Note")
.HasColumnType("TEXT");
b.Property<string>("Responsabile")
.HasColumnType("TEXT");
b.Property<string>("Rilevatore")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Scadenza")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CodMdep", "Data", "Rilevatore");
b.ToTable("Schede");
});
modelBuilder.Entity("SteUp.Shared.Core.Entities.SchedaArticolo", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Barcode")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("Descrizione")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<int>("SchedaId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SchedaId", "Barcode")
.IsUnique();
b.ToTable("SchedaArticoli");
});
modelBuilder.Entity("SteUp.Shared.Core.Entities.Scheda", b =>
{
b.HasOne("SteUp.Shared.Core.Entities.Ispezione", "Ispezione")
.WithMany("Schede")
.HasForeignKey("CodMdep", "Data", "Rilevatore")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Ispezione");
});
modelBuilder.Entity("SteUp.Shared.Core.Entities.SchedaArticolo", b =>
{
b.HasOne("SteUp.Shared.Core.Entities.Scheda", "Scheda")
.WithMany("Articoli")
.HasForeignKey("SchedaId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Scheda");
});
modelBuilder.Entity("SteUp.Shared.Core.Entities.Ispezione", b =>
{
b.Navigation("Schede");
});
modelBuilder.Entity("SteUp.Shared.Core.Entities.Scheda", b =>
{
b.Navigation("Articoli");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,48 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace SteUp.Data.Migrations
{
/// <inheritdoc />
public partial class AddListArticoli : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "SchedaArticoli",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
SchedaId = table.Column<int>(type: "INTEGER", nullable: false),
Barcode = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false),
Descrizione = table.Column<string>(type: "TEXT", maxLength: 256, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SchedaArticoli", x => x.Id);
table.ForeignKey(
name: "FK_SchedaArticoli_Schede_SchedaId",
column: x => x.SchedaId,
principalTable: "Schede",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_SchedaArticoli_SchedaId_Barcode",
table: "SchedaArticoli",
columns: new[] { "SchedaId", "Barcode" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "SchedaArticoli");
}
}
}

View File

@@ -81,6 +81,33 @@ namespace SteUp.Data.Migrations
b.ToTable("Schede"); b.ToTable("Schede");
}); });
modelBuilder.Entity("SteUp.Shared.Core.Entities.SchedaArticolo", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Barcode")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("Descrizione")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<int>("SchedaId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SchedaId", "Barcode")
.IsUnique();
b.ToTable("SchedaArticoli");
});
modelBuilder.Entity("SteUp.Shared.Core.Entities.Scheda", b => modelBuilder.Entity("SteUp.Shared.Core.Entities.Scheda", b =>
{ {
b.HasOne("SteUp.Shared.Core.Entities.Ispezione", "Ispezione") b.HasOne("SteUp.Shared.Core.Entities.Ispezione", "Ispezione")
@@ -92,10 +119,26 @@ namespace SteUp.Data.Migrations
b.Navigation("Ispezione"); b.Navigation("Ispezione");
}); });
modelBuilder.Entity("SteUp.Shared.Core.Entities.SchedaArticolo", b =>
{
b.HasOne("SteUp.Shared.Core.Entities.Scheda", "Scheda")
.WithMany("Articoli")
.HasForeignKey("SchedaId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Scheda");
});
modelBuilder.Entity("SteUp.Shared.Core.Entities.Ispezione", b => modelBuilder.Entity("SteUp.Shared.Core.Entities.Ispezione", b =>
{ {
b.Navigation("Schede"); b.Navigation("Schede");
}); });
modelBuilder.Entity("SteUp.Shared.Core.Entities.Scheda", b =>
{
b.Navigation("Articoli");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@@ -0,0 +1,13 @@
<MudOverlay Visible="Visible" DarkBackground="false">
<div class="overlay-container">
<span>@(Text ?? "Caricamento")</span>
<MudProgressLinear Color="Color.Primary" Rounded="true" Size="Size.Medium" Indeterminate="true" />
</div>
</MudOverlay>
@code
{
[Parameter] public bool Visible { get; set; }
[Parameter] public string? Text { get; set; }
}

View File

@@ -0,0 +1,18 @@
.overlay-container {
background: var(--mud-palette-background);
width: 20rem;
height: 6rem;
padding: 0 1rem;
display: flex;
gap: .5rem;
flex-direction: column;
justify-content: center;
border-radius: 20px;
box-shadow: var(--custom-box-shadow);
}
.overlay-container > span {
text-align: center;
font-size: medium;
font-weight: 600;
}

View File

@@ -0,0 +1,23 @@
<MudDialog OnBackdropClick="Cancel">
<DialogContent>
<div class="exception-header mb-2">
<i class="ri-emotion-unhappy-line"></i>
<span>Ops</span>
</div>
<div class="text">
<code>@ErrorMessage</code>
</div>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Text" OnClick="@Cancel" Size="Size.Small" Color="Color.Primary">
Chiudi
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter] public string ErrorMessage { get; set; } = string.Empty;
private void Cancel() => MudDialog.Cancel();
}

View File

@@ -0,0 +1,30 @@
.exception-header {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.exception-header > i {
font-size: 3rem;
line-height: normal;
color: var(--bs-danger);
}
.exception-header > span {
font-size: x-large;
font-weight: 700;
}
.text {
font-size: medium;
font-weight: 500;
display: flex;
text-align: center;
}
code {
width: 100%;
height: auto;
color: var(--bs-gray-dark);
}

View File

@@ -1,5 +1,6 @@
@using SteUp.Shared.Components.Layout @using SteUp.Shared.Components.Layout
@using SteUp.Shared.Components.Layout.Overlay @using SteUp.Shared.Components.Layout.Overlay
@using SteUp.Shared.Components.Layout.Spinner
@using SteUp.Shared.Components.SingleElements.Card.ModalForm @using SteUp.Shared.Components.SingleElements.Card.ModalForm
@using SteUp.Shared.Components.SingleElements.MessageBox @using SteUp.Shared.Components.SingleElements.MessageBox
@using SteUp.Shared.Core.Dto @using SteUp.Shared.Core.Dto
@@ -14,6 +15,7 @@
@inject IIntegryApiService IntegryApiService @inject IIntegryApiService IntegryApiService
@inject IAttachedService AttachedService @inject IAttachedService AttachedService
@inject IIspezioniService IspezioniService @inject IIspezioniService IspezioniService
@inject IIntegrySteupService IntegrySteupService
@inject OnScannerService OnScannerService @inject OnScannerService OnScannerService
<MudDialog Class="customDialog-form"> <MudDialog Class="customDialog-form">
@@ -27,7 +29,7 @@
<CardFormModal Title="Reparto" Loading="SteupDataService.Reparti.IsNullOrEmpty()"> <CardFormModal Title="Reparto" Loading="SteupDataService.Reparti.IsNullOrEmpty()">
<MudSelectExtended ReadOnly="IsView" T="JtbFasiDto?" Variant="Variant.Text" <MudSelectExtended ReadOnly="IsView" T="JtbFasiDto?" Variant="Variant.Text"
@bind-Value="Scheda.Reparto" ToStringFunc="@(x => x?.Descrizione)" @bind-Value="Scheda.Reparto" ToStringFunc="@(x => x?.Descrizione)"
@bind-Value:after="OnAfterChangeValue" Required="true" @bind-Value:after="OnAfterChangeReparto" Required="true"
RequiredError="Reparto obbligatorio"> RequiredError="Reparto obbligatorio">
@foreach (var fasi in SteupDataService.Reparti) @foreach (var fasi in SteupDataService.Reparti)
{ {
@@ -103,8 +105,27 @@
<div class="input-manual-barcode"> <div class="input-manual-barcode">
<MudTextField FullWidth="true" ReadOnly="IsView" T="string?" Variant="Variant.Text" <MudTextField FullWidth="true" ReadOnly="IsView" T="string?" Variant="Variant.Text"
@bind-Value="ManualBarcode" Placeholder="Digita manualmente il codice"/> @bind-Value="ManualBarcode" Placeholder="Digita manualmente il codice"/>
<MudIconButton Color="Color.Primary" Size="Size.Small" Icon="@Icons.Material.Rounded.Send" /> <MudIconButton Color="Color.Primary" OnClick="@OnManualBarcodeSet"
Size="Size.Small" Icon="@Icons.Material.Rounded.Send"/>
</div> </div>
@if (!Scheda.Articoli.IsNullOrEmpty())
{
<div class="art-list">
@foreach (var articolo in Scheda.Articoli)
{
<MudChip T="string" OnClose="@(() => RemoveArt(articolo.Barcode))" style="height: auto;">
<MudStack Direction="Column" Spacing="0" class="py-1">
<MudText Typo="Typo.subtitle2" Style="line-height: 1.1; font-weight: 700;">
@articolo.Descrizione
</MudText>
<MudText Typo="Typo.body2" Style="line-height: 1.5;">
Barcode: @articolo.Barcode
</MudText>
</MudStack>
</MudChip>
}
</div>
}
</CardFormModal> </CardFormModal>
@if (!IsView) @if (!IsView)
@@ -114,7 +135,7 @@
FullWidth="true" FullWidth="true"
StartIcon="@Icons.Material.Rounded.Balance" StartIcon="@Icons.Material.Rounded.Balance"
Size="Size.Medium" Size="Size.Medium"
OnClick="@OpenAddAttached" OnClick="@OpenSelectArt"
Variant="Variant.Outlined"> Variant="Variant.Outlined">
Consulta articoli Consulta articoli
</MudButton> </MudButton>
@@ -161,6 +182,7 @@
</MudDialog> </MudDialog>
<SpinnerOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="SuccessAnimation"/> <SpinnerOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="SuccessAnimation"/>
<LoaderLayout Visible="OnLoading" Text="@TextLoading"/>
@code { @code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!; [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
@@ -171,11 +193,13 @@
private bool IsView => !NetworkService.ConnectionAvailable; private bool IsView => !NetworkService.ConnectionAvailable;
private string ManualBarcode { get; set; } private string? ManualBarcode { get; set; }
//Overlay //Overlay
private bool VisibleOverlay { get; set; } private bool VisibleOverlay { get; set; }
private bool SuccessAnimation { get; set; } private bool SuccessAnimation { get; set; }
private bool OnLoading { get; set; }
private string? TextLoading { get; set; }
private bool FileLoading { get; set; } private bool FileLoading { get; set; }
@@ -189,7 +213,9 @@
protected override void OnInitialized() protected override void OnInitialized()
{ {
OnScannerService.OnNewScanSuccessful += OnNewScanSuccessful; Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
OnScannerService.OnNewScanSuccessful += HandleNewScanSuccessful;
OnScannerService.OnErrorScan += OnErrorScan; OnScannerService.OnErrorScan += OnErrorScan;
_originalScheda = Scheda.Clone(); _originalScheda = Scheda.Clone();
@@ -238,6 +264,7 @@
StateHasChanged(); StateHasChanged();
await Task.Delay(1250); await Task.Delay(1250);
DisposeMessage();
MudDialog.Close(Scheda); MudDialog.Close(Scheda);
} }
@@ -305,12 +332,19 @@
if (await CheckSavePreAction()) if (await CheckSavePreAction())
{ {
await AttachedService.CleanTempStorageAsync(); await AttachedService.CleanTempStorageAsync();
DisposeMessage();
MudDialog.Cancel(); MudDialog.Cancel();
} }
} }
#region Form #region Form
private void OnAfterChangeReparto()
{
Scheda.ActivityTypeId = null;
OnAfterChangeValue();
}
private void OnAfterChangeValue() private void OnAfterChangeValue()
{ {
RecalcDirty(); RecalcDirty();
@@ -374,7 +408,7 @@
var result = await ModalHelper.OpenAddAttached(Dialog); var result = await ModalHelper.OpenAddAttached(Dialog);
if (result is not { Canceled: false, Data: List<AttachedDto> attachedList }) return; if (result is not { Canceled: false, Data: List<AttachedDto> attachedList }) return;
VisibleOverlay = true; OnLoading = true;
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
await Task.Yield(); await Task.Yield();
@@ -408,12 +442,77 @@
await InvokeAsync(() => await InvokeAsync(() =>
{ {
VisibleOverlay = false; OnLoading = false;
StateHasChanged(); StateHasChanged();
}); });
}); });
} }
private void OpenSelectArt()
{
if (Scheda.CodJfas.IsNullOrEmpty())
{
Snackbar.Clear();
Snackbar.Add("Selezionare prima il reparto", Severity.Error);
return;
}
OnLoading = true;
TextLoading = "Download articoli in griglia";
StateHasChanged();
_ = Task.Run(async () =>
{
List<ArticoliInGrigliaDto>? articoli;
try
{
articoli = await IntegrySteupService.RetrieveGrigliaPlu(
new RetrieveGrigliaPluRequestDto
{
CodMdep = CodMdep,
ActivityTypeId = Scheda.ActivityTypeId,
CodJfas = Scheda.CodJfas
}
);
}
catch (Exception e)
{
await InvokeAsync(() =>
{
OnLoading = false;
TextLoading = null;
StateHasChanged();
OnError(e.Message);
});
return;
}
var modal = await ModalHelper.OpenSelectArt(Dialog, articoli);
await InvokeAsync(() =>
{
OnLoading = false;
TextLoading = null;
StateHasChanged();
});
if (modal is { Canceled: false, Data: List<ArticoliInGrigliaDto> articoliSelezionati })
{
Scheda.Articoli.AddRange(articoliSelezionati.ConvertAll(x => new SchedaArticolo
{
Barcode = x.Barcode,
Descrizione = x.Descrizione
})
);
await InvokeAsync(StateHasChanged);
}
});
}
private void OnRemoveAttached(int index) private void OnRemoveAttached(int index)
{ {
if (AttachedList is null || index < 0 || index >= AttachedList.Count) return; if (AttachedList is null || index < 0 || index >= AttachedList.Count) return;
@@ -455,16 +554,89 @@
#region Scanner #region Scanner
public static void OnNewScanSuccessful(string? value) private void OnManualBarcodeSet()
{ {
//To be implemented if (ManualBarcode == null) return;
HandleNewScanSuccessful(ManualBarcode);
ManualBarcode = null;
} }
private static void OnErrorScan(string? value) private async void HandleNewScanSuccessful(string? value)
{ {
//To be implemented try
{
if (value is null) return;
if (Scheda.Articoli.Any(x => x.Barcode.Equals(value))) return;
await InvokeAsync(() =>
{
OnLoading = true;
StateHasChanged();
});
var art = await IntegrySteupService.RetrieveArtFromBarcode(value);
await InvokeAsync(() =>
{
OnLoading = false;
StateHasChanged();
if (art != null)
{
RecalcDirty(true);
Scheda.Articoli.Add(new SchedaArticolo
{
Barcode = art.Barcode,
Descrizione = art.Descrizione
});
StateHasChanged();
}
else
{
OnError("Nessun articolo trovato");
}
});
}
catch (Exception e)
{
await InvokeAsync(() =>
{
OnLoading = false;
StateHasChanged();
});
OnError(e.Message);
}
} }
private void OnErrorScan(string? value) => OnError(value);
#endregion #endregion
private void OnError(string? errorMessage)
{
if (errorMessage == null) return;
_ = ModalHelper.ShowError(Dialog, errorMessage);
}
private void RemoveArt(string barcode)
{
var index = Scheda.Articoli.FindIndex(x => x.Barcode.Equals(barcode));
if (index < 0) return;
RecalcDirty(true);
Scheda.Articoli.RemoveAt(index);
StateHasChanged();
}
private void DisposeMessage()
{
OnScannerService.OnNewScanSuccessful -= HandleNewScanSuccessful;
OnScannerService.OnErrorScan -= OnErrorScan;
}
} }

View File

@@ -11,6 +11,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 1.5rem; gap: 1.5rem;
margin-top: .5rem;
margin-bottom: .5rem;
} }
.scroll-attached { .scroll-attached {
@@ -36,3 +38,12 @@
.input-card { .input-card {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
.art-list {
max-height: 150px;
overflow: auto;
}
.art-list ::deep p {
white-space: normal;
}

View File

@@ -0,0 +1,59 @@
@using SteUp.Shared.Components.Layout
@using SteUp.Shared.Core.Dto
<MudDialog OnBackdropClick="Cancel">
<DialogContent>
<HeaderLayout SmallHeader="true" Cancel="true" OnCancel="@(() => MudDialog.Cancel())"
Title="Seleziona articoli"/>
@if (!Articoli.IsNullOrEmpty())
{
<MudSelectExtended MultiSelection="true" ItemCollection="Articoli" SearchBox="true"
SearchBoxAutoFocus="true" @bind-SelectedValues="ArticoliSelected"
SearchFunc="SearchFunc" T="ArticoliInGrigliaDto" Virtualize="true"
Label="Articoli" AnchorOrigin="Origin.BottomCenter" Variant="Variant.Outlined"
SearchBoxPlaceholder="Descrizione articolo" SearchBoxClearable="true"
MultiSelectionTextFunc="MultiSelectionTextFunc" ToStringFunc="ToStringFunc"/>
}
else
{
<div class="spinner-container" style="height: unset !important; margin-bottom: 1rem;">
<MudIcon Size="Size.Large" Color="Color.Error" Icon="@Icons.Material.Rounded.Close"/>
<MudText>Nessun articolo trovato</MudText>
</div>
}
</DialogContent>
<DialogActions>
@if (!Articoli.IsNullOrEmpty())
{
<MudButton Variant="Variant.Filled" OnClick="@Submit" Size="Size.Small" Color="Color.Primary">
Seleziona
</MudButton>
}
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter] public List<ArticoliInGrigliaDto>? Articoli { get; set; }
private IEnumerable<ArticoliInGrigliaDto>? ArticoliSelected { get; set; }
private void Cancel() => MudDialog.Cancel();
private void Submit() =>
MudDialog.Close(DialogResult.Ok(ArticoliSelected?.ToList()));
private static bool SearchFunc(ArticoliInGrigliaDto? obj, string? searchString)
{
if (searchString.IsNullOrEmpty() || obj == null) return true;
return obj.Descrizione.ContainsIgnoreCase(searchString!);
}
private static string? ToStringFunc(ArticoliInGrigliaDto? arg) => arg?.Descrizione;
private static string MultiSelectionTextFunc(List<ArticoliInGrigliaDto?> arg) =>
$"{arg.Count} selezionat{(arg.Count == 1 ? "o" : "i")}";
}

View File

@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;
namespace SteUp.Shared.Core.Dto;
public class ArticoliInGrigliaDto
{
[JsonPropertyName("codMart")]
public string CodMart { get; set; } = string.Empty;
[JsonPropertyName("descrizione")]
public string Descrizione { get; set; } = string.Empty;
[JsonPropertyName("barcode")]
public string Barcode { get; set; } = string.Empty;
[JsonPropertyName("codMsgr")]
public string CodMsgr { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace SteUp.Shared.Core.Dto;
public class RetrieveGrigliaPluRequestDto
{
[JsonPropertyName("codMdep")]
public string? CodMdep { get; set; }
[JsonPropertyName("codJfas")]
public string? CodJfas { get; set; }
[JsonPropertyName("activityTypeId")]
public string? ActivityTypeId { get; set; }
}

View File

@@ -17,6 +17,7 @@ public class Scheda : EntityBase<Scheda>
public string Rilevatore { get; set; } = string.Empty; public string Rilevatore { get; set; } = string.Empty;
public Ispezione? Ispezione { get; set; } public Ispezione? Ispezione { get; set; }
public List<SchedaArticolo> Articoli { get; set; } = [];
public List<string>? ImageNames { get; set; } public List<string>? ImageNames { get; set; }
public string? DescrizioneReparto { get; set; } public string? DescrizioneReparto { get; set; }

View File

@@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace SteUp.Shared.Core.Entities;
public class SchedaArticolo
{
[Key]
public int Id { get; set; }
[Required]
public int SchedaId { get; set; }
[ForeignKey(nameof(SchedaId))]
public Scheda Scheda { get; set; } = null!;
[Required]
[MaxLength(64)]
public string Barcode { get; set; } = string.Empty;
[Required]
[MaxLength(256)]
public string Descrizione { get; set; } = string.Empty;
}

View File

@@ -1,5 +1,6 @@
using MudBlazor; using MudBlazor;
using SteUp.Shared.Components.SingleElements.Modal; using SteUp.Shared.Components.SingleElements.Modal;
using SteUp.Shared.Components.SingleElements.Modal.ExceptionModal;
using SteUp.Shared.Core.Dto; using SteUp.Shared.Core.Dto;
using SteUp.Shared.Core.Entities; using SteUp.Shared.Core.Entities;
@@ -86,4 +87,48 @@ public abstract class ModalHelper
return await modal.Result; return await modal.Result;
} }
public static async Task<DialogResult?> OpenSelectArt(IDialogService dialog, List<ArticoliInGrigliaDto>? articoli)
{
var modal = await dialog.ShowAsync<ModalSelectArt>(
"ModalSelectArt",
new DialogParameters<ModalSelectArt>
{
{ x => x.Articoli, articoli }
},
new DialogOptions
{
FullScreen = false,
CloseButton = false,
NoHeader = true,
BackdropClick = true,
FullWidth = true,
MaxWidth = MaxWidth.ExtraLarge
}
);
return await modal.Result;
}
public static async Task ShowError(IDialogService dialog, string message)
{
var modal = await dialog.ShowAsync<ModalError>(
"ModalError",
new DialogParameters<ModalError>
{
{ x => x.ErrorMessage, message }
},
new DialogOptions
{
FullScreen = false,
CloseButton = false,
NoHeader = true,
BackdropClick = true,
FullWidth = true,
MaxWidth = MaxWidth.ExtraLarge
}
);
await modal.Result;
}
} }

View File

@@ -8,4 +8,6 @@ public interface IIntegrySteupService
Task<List<PuntoVenditaDto>> RetrievePuntiVendita(); Task<List<PuntoVenditaDto>> RetrievePuntiVendita();
Task<List<JtbFasiDto>> RetrieveReparti(); Task<List<JtbFasiDto>> RetrieveReparti();
Task<List<ActivityTypeDto>> RetrieveActivityType(); Task<List<ActivityTypeDto>> RetrieveActivityType();
Task<List<ArticoliInGrigliaDto>?> RetrieveGrigliaPlu(RetrieveGrigliaPluRequestDto request);
Task<ArticoliInGrigliaDto?> RetrieveArtFromBarcode(string barcode);
} }

View File

@@ -19,5 +19,17 @@ public class IntegrySteupService(IIntegryApiRestClient integryApiRestClient) : I
public Task<List<ActivityTypeDto>> RetrieveActivityType() => public Task<List<ActivityTypeDto>> RetrieveActivityType() =>
integryApiRestClient.AuthorizedGet<List<ActivityTypeDto>>($"{BaseRequest}/retrieveActivityType")!; integryApiRestClient.AuthorizedGet<List<ActivityTypeDto>>($"{BaseRequest}/retrieveActivityType")!;
public Task<List<ArticoliInGrigliaDto>?> RetrieveGrigliaPlu(RetrieveGrigliaPluRequestDto request) =>
integryApiRestClient.AuthorizedPost<List<ArticoliInGrigliaDto>?>($"{BaseRequest}/retrieveGrigliaPlu", request);
public Task<ArticoliInGrigliaDto?> RetrieveArtFromBarcode(string barcode) =>
integryApiRestClient.AuthorizedGet<ArticoliInGrigliaDto?>(
$"{BaseRequest}/retrieveArtFromBarcode",
new Dictionary<string, object>
{
{ "barcode", barcode }
}
);
#endregion #endregion
} }

View File

@@ -25,6 +25,7 @@ html, body {
html ::-webkit-scrollbar { html ::-webkit-scrollbar {
width: 3px; width: 3px;
height: 3px;
} }
html ::-webkit-scrollbar-thumb { html ::-webkit-scrollbar-thumb {