Implentata scansione barcode e lista articoli nel form scheda
This commit is contained in:
@@ -8,6 +8,7 @@ public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(op
|
||||
{
|
||||
public DbSet<Ispezione> Ispezioni => Set<Ispezione>();
|
||||
public DbSet<Scheda> Schede => Set<Scheda>();
|
||||
public DbSet<SchedaArticolo> SchedaArticoli => Set<SchedaArticolo>();
|
||||
|
||||
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.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();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
|
||||
public Task<Ispezione?> GetIspezioneAsync(string codMdep, DateOnly data, string rilevatore) =>
|
||||
db.Ispezioni
|
||||
.Include(x => x.Schede)
|
||||
.ThenInclude(s => s.Articoli)
|
||||
.FirstOrDefaultAsync(x =>
|
||||
x.CodMdep == codMdep &&
|
||||
x.Data == data &&
|
||||
@@ -17,6 +18,7 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
|
||||
public Task<List<Ispezione>> GetAllIspezioniWithSchedeAsync() =>
|
||||
db.Ispezioni
|
||||
.Include(x => x.Schede)
|
||||
.ThenInclude(s => s.Articoli)
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(x => x.Data)
|
||||
.ToListAsync();
|
||||
@@ -65,7 +67,7 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancella l'ispezione e tutte le schede collegate.
|
||||
/// Cancella l'ispezione e tutte le schede collegate (e relativi articoli via cascade).
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
// assicura che il parent esista
|
||||
await GetOrCreateIspezioneAsync(codMdep, data, rilevatore);
|
||||
|
||||
scheda.CodMdep = codMdep;
|
||||
scheda.Data = data;
|
||||
scheda.Rilevatore = rilevatore;
|
||||
|
||||
if (scheda.Articoli is { Count: > 0 })
|
||||
{
|
||||
foreach (var a in scheda.Articoli)
|
||||
{
|
||||
a.SchedaId = scheda.Id;
|
||||
}
|
||||
}
|
||||
|
||||
db.Schede.Add(scheda);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
@@ -99,6 +108,7 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
|
||||
public Task<List<Scheda>> GetAllSchedeOfIspezioneAsync(string codMdep, DateOnly data, string rilevatore) =>
|
||||
db.Schede
|
||||
.AsNoTracking()
|
||||
.Include(s => s.Articoli)
|
||||
.Where(x => x.CodMdep == codMdep &&
|
||||
x.Data == data &&
|
||||
x.Rilevatore == rilevatore)
|
||||
@@ -107,11 +117,13 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
|
||||
public Task<Scheda?> GetSchedaAsync(int schedaId) =>
|
||||
db.Schede
|
||||
.AsNoTracking()
|
||||
.Include(s => s.Articoli)
|
||||
.FirstOrDefaultAsync(x => x.Id == schedaId);
|
||||
|
||||
public Task<Scheda?> GetSchedaWithIspezioneAsync(int schedaId) =>
|
||||
db.Schede
|
||||
.Include(x => x.Ispezione)
|
||||
.Include(x => x.Articoli)
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Id == schedaId);
|
||||
|
||||
@@ -128,11 +140,56 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
|
||||
|
||||
public async Task<bool> UpdateSchedaAsync(Scheda scheda)
|
||||
{
|
||||
var exists = await db.Schede.AnyAsync(x => x.Id == scheda.Id);
|
||||
if (!exists)
|
||||
var existing = await db.Schede
|
||||
.Include(s => s.Articoli)
|
||||
.FirstOrDefaultAsync(s => s.Id == scheda.Id);
|
||||
|
||||
if (existing is null)
|
||||
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();
|
||||
return true;
|
||||
}
|
||||
|
||||
148
SteUp.Data/Migrations/20260225104013_AddListArticoli.Designer.cs
generated
Normal file
148
SteUp.Data/Migrations/20260225104013_AddListArticoli.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
48
SteUp.Data/Migrations/20260225104013_AddListArticoli.cs
Normal file
48
SteUp.Data/Migrations/20260225104013_AddListArticoli.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,33 @@ namespace SteUp.Data.Migrations
|
||||
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")
|
||||
@@ -92,10 +119,26 @@ namespace SteUp.Data.Migrations
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
13
SteUp.Shared/Components/Layout/Spinner/LoaderLayout.razor
Normal file
13
SteUp.Shared/Components/Layout/Spinner/LoaderLayout.razor
Normal 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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
@using SteUp.Shared.Components.Layout
|
||||
@using SteUp.Shared.Components.Layout.Overlay
|
||||
@using SteUp.Shared.Components.Layout.Spinner
|
||||
@using SteUp.Shared.Components.SingleElements.Card.ModalForm
|
||||
@using SteUp.Shared.Components.SingleElements.MessageBox
|
||||
@using SteUp.Shared.Core.Dto
|
||||
@@ -14,6 +15,7 @@
|
||||
@inject IIntegryApiService IntegryApiService
|
||||
@inject IAttachedService AttachedService
|
||||
@inject IIspezioniService IspezioniService
|
||||
@inject IIntegrySteupService IntegrySteupService
|
||||
@inject OnScannerService OnScannerService
|
||||
|
||||
<MudDialog Class="customDialog-form">
|
||||
@@ -27,7 +29,7 @@
|
||||
<CardFormModal Title="Reparto" Loading="SteupDataService.Reparti.IsNullOrEmpty()">
|
||||
<MudSelectExtended ReadOnly="IsView" T="JtbFasiDto?" Variant="Variant.Text"
|
||||
@bind-Value="Scheda.Reparto" ToStringFunc="@(x => x?.Descrizione)"
|
||||
@bind-Value:after="OnAfterChangeValue" Required="true"
|
||||
@bind-Value:after="OnAfterChangeReparto" Required="true"
|
||||
RequiredError="Reparto obbligatorio">
|
||||
@foreach (var fasi in SteupDataService.Reparti)
|
||||
{
|
||||
@@ -103,8 +105,27 @@
|
||||
<div class="input-manual-barcode">
|
||||
<MudTextField FullWidth="true" ReadOnly="IsView" T="string?" Variant="Variant.Text"
|
||||
@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>
|
||||
@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>
|
||||
|
||||
@if (!IsView)
|
||||
@@ -114,7 +135,7 @@
|
||||
FullWidth="true"
|
||||
StartIcon="@Icons.Material.Rounded.Balance"
|
||||
Size="Size.Medium"
|
||||
OnClick="@OpenAddAttached"
|
||||
OnClick="@OpenSelectArt"
|
||||
Variant="Variant.Outlined">
|
||||
Consulta articoli
|
||||
</MudButton>
|
||||
@@ -161,6 +182,7 @@
|
||||
</MudDialog>
|
||||
|
||||
<SpinnerOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="SuccessAnimation"/>
|
||||
<LoaderLayout Visible="OnLoading" Text="@TextLoading"/>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
|
||||
@@ -171,11 +193,13 @@
|
||||
|
||||
private bool IsView => !NetworkService.ConnectionAvailable;
|
||||
|
||||
private string ManualBarcode { get; set; }
|
||||
private string? ManualBarcode { get; set; }
|
||||
|
||||
//Overlay
|
||||
private bool VisibleOverlay { get; set; }
|
||||
private bool SuccessAnimation { get; set; }
|
||||
private bool OnLoading { get; set; }
|
||||
private string? TextLoading { get; set; }
|
||||
|
||||
private bool FileLoading { get; set; }
|
||||
|
||||
@@ -189,7 +213,9 @@
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
OnScannerService.OnNewScanSuccessful += OnNewScanSuccessful;
|
||||
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
|
||||
|
||||
OnScannerService.OnNewScanSuccessful += HandleNewScanSuccessful;
|
||||
OnScannerService.OnErrorScan += OnErrorScan;
|
||||
|
||||
_originalScheda = Scheda.Clone();
|
||||
@@ -238,6 +264,7 @@
|
||||
StateHasChanged();
|
||||
|
||||
await Task.Delay(1250);
|
||||
DisposeMessage();
|
||||
MudDialog.Close(Scheda);
|
||||
}
|
||||
|
||||
@@ -305,12 +332,19 @@
|
||||
if (await CheckSavePreAction())
|
||||
{
|
||||
await AttachedService.CleanTempStorageAsync();
|
||||
DisposeMessage();
|
||||
MudDialog.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
#region Form
|
||||
|
||||
private void OnAfterChangeReparto()
|
||||
{
|
||||
Scheda.ActivityTypeId = null;
|
||||
OnAfterChangeValue();
|
||||
}
|
||||
|
||||
private void OnAfterChangeValue()
|
||||
{
|
||||
RecalcDirty();
|
||||
@@ -374,7 +408,7 @@
|
||||
var result = await ModalHelper.OpenAddAttached(Dialog);
|
||||
if (result is not { Canceled: false, Data: List<AttachedDto> attachedList }) return;
|
||||
|
||||
VisibleOverlay = true;
|
||||
OnLoading = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await Task.Yield();
|
||||
|
||||
@@ -408,12 +442,77 @@
|
||||
|
||||
await InvokeAsync(() =>
|
||||
{
|
||||
VisibleOverlay = false;
|
||||
OnLoading = false;
|
||||
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)
|
||||
{
|
||||
if (AttachedList is null || index < 0 || index >= AttachedList.Count) return;
|
||||
@@ -455,16 +554,89 @@
|
||||
|
||||
#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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
margin-top: .5rem;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.scroll-attached {
|
||||
@@ -36,3 +38,12 @@
|
||||
.input-card {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.art-list {
|
||||
max-height: 150px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.art-list ::deep p {
|
||||
white-space: normal;
|
||||
}
|
||||
@@ -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")}";
|
||||
|
||||
}
|
||||
18
SteUp.Shared/Core/Dto/ArticoliInGrigliaDto.cs
Normal file
18
SteUp.Shared/Core/Dto/ArticoliInGrigliaDto.cs
Normal 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;
|
||||
}
|
||||
15
SteUp.Shared/Core/Dto/RetrieveGrigliaPluRequestDto.cs
Normal file
15
SteUp.Shared/Core/Dto/RetrieveGrigliaPluRequestDto.cs
Normal 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; }
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public class Scheda : EntityBase<Scheda>
|
||||
public string Rilevatore { get; set; } = string.Empty;
|
||||
public Ispezione? Ispezione { get; set; }
|
||||
|
||||
public List<SchedaArticolo> Articoli { get; set; } = [];
|
||||
public List<string>? ImageNames { get; set; }
|
||||
|
||||
public string? DescrizioneReparto { get; set; }
|
||||
|
||||
24
SteUp.Shared/Core/Entities/SchedaArticolo.cs
Normal file
24
SteUp.Shared/Core/Entities/SchedaArticolo.cs
Normal 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;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using MudBlazor;
|
||||
using SteUp.Shared.Components.SingleElements.Modal;
|
||||
using SteUp.Shared.Components.SingleElements.Modal.ExceptionModal;
|
||||
using SteUp.Shared.Core.Dto;
|
||||
using SteUp.Shared.Core.Entities;
|
||||
|
||||
@@ -86,4 +87,48 @@ public abstract class ModalHelper
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,6 @@ public interface IIntegrySteupService
|
||||
Task<List<PuntoVenditaDto>> RetrievePuntiVendita();
|
||||
Task<List<JtbFasiDto>> RetrieveReparti();
|
||||
Task<List<ActivityTypeDto>> RetrieveActivityType();
|
||||
Task<List<ArticoliInGrigliaDto>?> RetrieveGrigliaPlu(RetrieveGrigliaPluRequestDto request);
|
||||
Task<ArticoliInGrigliaDto?> RetrieveArtFromBarcode(string barcode);
|
||||
}
|
||||
@@ -19,5 +19,17 @@ public class IntegrySteupService(IIntegryApiRestClient integryApiRestClient) : I
|
||||
public Task<List<ActivityTypeDto>> 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
|
||||
}
|
||||
@@ -25,6 +25,7 @@ html, body {
|
||||
|
||||
html ::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
html ::-webkit-scrollbar-thumb {
|
||||
|
||||
Reference in New Issue
Block a user