Leandro Hernan Rojas d5722495ae
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 5m16s
Add Update API UI Customers CRUD
2025-04-14 17:50:25 -03:00

421 lines
18 KiB
Plaintext

@page "/sales/customerform"
@page "/sales/customerform/{CustomerId:int}"
@using System.ComponentModel.DataAnnotations
@using phronCare.UIBlazor.Pages.Shared.Modals
@using phronCare.UIBlazor.Services.Sales
@inject IModalService Modal
@inject IToastService toastService
@inject HttpClient _httpClient
@inject NavigationManager Navigation
@inject AuthenticationStateProvider authenticationStateProvider
@inject AccountTypeService accountTypeService
@inject TaxConditionService taxConditionService
<div class="card" style="zoom:80%">
<div class="card-header d-flex justify-content-center align-items-center">
<h3 class="card-title m-0">Información del cliente</h3>
</div>
<div class="card-body">
<EditForm Model="@customer" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="row">
<div class="col-md-6">
<label for="Name">Nombre / Razón Social:</label>
<InputText id="Name" @bind-Value="customer.Name" class="form-control" />
</div>
<div class="col-md-6">
<label for="BusinessName">Sucursal / Nombre Comercial:</label>
<InputText id="BusinessName" @bind-Value="customer.BusinessName" class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="AccounttypesId">Tipo de Cuenta:</label>
<InputSelect id="AccounttypesId" @bind-Value="customer.AccounttypesId" class="form-control">
<option value="">-- Seleccionar --</option>
@foreach (var type in accountTypes)
{
<option value="@type.Id">@type.Name</option>
}
</InputSelect>
</div>
<div class="col-md-6">
<label for="TaxConditionId">Condición Fiscal:</label>
<InputSelect id="TaxConditionId" @bind-Value="customer.TaxConditionId" class="form-control">
<option value="">-- Seleccionar --</option>
@foreach (var tax in taxConditions)
{
<option value="@tax.Id">@tax.Description</option>
}
</InputSelect>
</div>
</div>
<div class="row">
<div class="col-md-4">
<label for="HasCreditAccount">¿Cuenta Corriente?</label><br />
<InputCheckbox id="HasCreditAccount" @bind-Value="customer.HasCreditAccount" />
</div>
<div class="col-md-4">
<label for="CreditLimit">Límite de Crédito:</label>
<InputNumber id="CreditLimit" @bind-Value="customer.CreditLimit" class="form-control" />
</div>
<div class="col-md-4">
<label for="Active">Activo:</label><br />
<InputCheckbox id="Active" @bind-Value="customer.Active" />
</div>
</div>
<hr />
<h5>Documentos</h5>
<div class="row align-items-end">
<div class="col-sm-4">
<label for="TipoDocumento">Tipo:</label>
<InputSelect id="TipoDocumento" class="form-control" @bind-Value="documentFormModel.DocumenttypesId">
<option value="">Seleccione</option>
@foreach (var tipo in documentTypes)
{
<option value="@tipo.Id">@tipo.Name</option>
}
</InputSelect>
</div>
<div class="col-sm-4">
<label for="NumeroDocumento">Número:</label>
<InputText id="NumeroDocumento" class="form-control" @bind-Value="documentFormModel.DocumentNumber" />
</div>
<div class="col-sm-2">
<button type="button" class="btn btn-success" @onclick="AddCustomerDocument">Agregar</button>
</div>
</div>
<br />
@if (customer.PhSCustomerDocuments?.Any() == true)
{
<table class="table table-sm table-bordered">
<thead>
<tr>
<th>Tipo</th>
<th>Número</th>
<th style="width:50px;"></th>
</tr>
</thead>
<tbody>
@foreach (var doc in customer.PhSCustomerDocuments)
{
<tr>
<td>@documentTypes.FirstOrDefault(t => t.Id == doc.DocumenttypesId)?.Name</td>
<td>@doc.DocumentNumber</td>
<td>
<button type="button" class="btn btn-sm btn-danger" @onclick="() => RemoveCustomerDocument(doc)">🗑</button>
</td>
</tr>
}
</tbody>
</table>
}
<hr />
<h5>Direcciones</h5>
<div class="row mb-3">
<div class="col-md-6">
<input class="form-control" placeholder="Nombre de sucursal" @bind="editingAddress.BusinessName" />
</div>
<div class="col-md-6">
<input class="form-control" placeholder="Dirección línea 1" @bind="editingAddress.Streetaddress1" />
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<input class="form-control" placeholder="Dirección línea 2" @bind="editingAddress.Streetaddress2" />
</div>
<div class="col-md-6">
<input class="form-control" placeholder="Ciudad" @bind="editingAddress.City" />
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<select class="form-control" value="@editingAddress.Country" @onchange="OnCountryChanged">
<option value="">Seleccionar país</option>
@foreach (var country in countries)
{
<option value="@country">@country</option>
}
</select>
</div>
<div class="col-md-6">
@if (editingAddress.Country == "Argentina" && provincesByCountry.TryGetValue("Argentina", out var provincias))
{
<select class="form-control" @bind="editingAddress.Stateprovince">
<option value="">Seleccionar provincia</option>
@foreach (var provincia in provincias)
{
<option value="@provincia">@provincia</option>
}
</select>
}
else
{
<input class="form-control" placeholder="Estado/Provincia" @bind="editingAddress.Stateprovince" />
}
</div>
</div>
<div class="row mb-3">
<div class="col-md-4">
<input class="form-control" placeholder="Código Postal" @bind="editingAddress.Postalcode" />
</div>
<div class="col-md-4">
<input class="form-control" placeholder="Teléfono" @bind="editingAddress.Phonenumber" />
</div>
<div class="col-md-4">
<input class="form-control" placeholder="Email" @bind="editingAddress.Email" />
</div>
</div>
<div class="row mb-3">
<div class="col-md-12">
<textarea class="form-control" placeholder="Notas" @bind="editingAddress.Notes"></textarea>
</div>
</div>
<div class="mb-3">
<button type="button" class="btn btn-sm btn-success" @onclick="AddOrUpdateAddress">
@((editingIndex == -1) ? "Agregar dirección" : "Actualizar dirección")
</button>
@if (editingIndex != -1)
{
<button type="button" class="btn btn-sm btn-secondary ms-2" @onclick="CancelAddressEdit">Cancelar</button>
}
</div>
@if (customer.PhSCustomerAddresses.Any())
{
<table class="table table-bordered">
<thead>
<tr>
<th>Sucursal</th>
<th>Dirección</th>
<th>Ciudad</th>
<th>Provincia</th>
<th>País</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var address in customer.PhSCustomerAddresses.Select((value, index) => new { value, index }))
{
<tr>
<td>@address.value.BusinessName</td>
<td>@address.value.Streetaddress1</td>
<td>@address.value.City</td>
<td>@address.value.Stateprovince</td>
<td>@address.value.Country</td>
<td>
<button type="button" class="btn btn-sm btn-primary me-1" @onclick="() => EditAddress(address.index)">Editar</button>
<button type="button" class="btn btn-sm btn-danger" @onclick="() => RemoveAddress(address.index)">Eliminar</button>
</td>
</tr>
}
</tbody>
</table>
}
</EditForm>
</div>
<div class="card-footer">
<div class="row">
<div class="col">
<div class="mt-4">
<button class="btn btn-primary" type="button" @onclick="HandleValidSubmit" disabled="@isSaving">
@(isSaving ? "Guardando..." : "Guardar Cliente")
</button>
</div>
</div>
</div>
</div>
</div>
@code {
[Parameter]
public int? CustomerId { get; set; }
private ECustomer customer { get; set; } = new();
private List<EAccountType> accountTypes = new();
private List<ETaxCondition> taxConditions = new();
private List<EDocumentType> documentTypes = new();
private ECustomerDocument documentFormModel = new();
private bool isSaving = false;
private string returnUrl = "/sales/customers";
private List<string> countries = new() {
"Argentina", "Brasil", "Chile", "Uruguay", "Paraguay", "Estados Unidos", "Canadá",
"México", "Alemania", "Reino Unido", "Francia", "Italia", "España"
};
private Dictionary<string, List<string>> provincesByCountry = new()
{
{
"Argentina", new List<string>
{
"Buenos Aires", "CABA", "Catamarca", "Chaco", "Chubut", "Córdoba", "Corrientes",
"Entre Ríos", "Formosa", "Jujuy", "La Pampa", "La Rioja", "Mendoza", "Misiones",
"Neuquén", "Río Negro", "Salta", "San Juan", "San Luis", "Santa Cruz",
"Santa Fe", "Santiago del Estero", "Tierra del Fuego", "Tucumán"
}
}
};
private ECustomerAddress editingAddress = new();
private int editingIndex = -1;
private void OnCountryChanged(ChangeEventArgs e)
{
var selectedCountry = e.Value?.ToString();
editingAddress.Country = selectedCountry;
editingAddress.Stateprovince = string.Empty; // Resetear la provincia si cambia el país
}
private void AddOrUpdateAddress()
{
var context = new ValidationContext(editingAddress, null, null);
var results = new List<ValidationResult>();
if (!Validator.TryValidateObject(editingAddress, context, results, true))
{
// Mostrar errores en un toast o en pantalla
foreach (var error in results)
{
toastService.ShowError(error.ErrorMessage);
}
return;
}
if (editingIndex == -1)
{
// Agregar nueva dirección
customer.PhSCustomerAddresses.Add(editingAddress);
}
else
{
// Actualizar dirección existente
var existing = customer.PhSCustomerAddresses.ElementAt(editingIndex);
existing.BusinessName = editingAddress.BusinessName;
existing.Streetaddress1 = editingAddress.Streetaddress1;
existing.Streetaddress2 = editingAddress.Streetaddress2;
existing.City = editingAddress.City;
existing.Stateprovince = editingAddress.Stateprovince;
existing.Postalcode = editingAddress.Postalcode;
existing.Country = editingAddress.Country;
existing.Latitude = editingAddress.Latitude;
existing.Longitude = editingAddress.Longitude;
existing.Phonenumber = editingAddress.Phonenumber;
existing.Email = editingAddress.Email;
existing.Notes = editingAddress.Notes;
}
ResetAddressForm();
}
private void EditAddress(int index)
{
var addr = customer.PhSCustomerAddresses.ElementAt(index);
editingAddress = new ECustomerAddress
{
Id = addr.Id, // Incluí el Id como mencionamos antes, para no perder referencia
BusinessName = addr.BusinessName,
Streetaddress1 = addr.Streetaddress1,
Streetaddress2 = addr.Streetaddress2,
City = addr.City,
Stateprovince = addr.Stateprovince,
Postalcode = addr.Postalcode,
Country = addr.Country,
Latitude = addr.Latitude,
Longitude = addr.Longitude,
Phonenumber = addr.Phonenumber,
Email = addr.Email,
Notes = addr.Notes
};
editingIndex = index;
}
private void RemoveAddress(int index)
{
var itemToRemove = customer.PhSCustomerAddresses.ElementAt(index);
customer.PhSCustomerAddresses.Remove(itemToRemove);
ResetAddressForm();
}
private void CancelAddressEdit()
{
ResetAddressForm();
}
private void ResetAddressForm()
{
editingAddress = new();
editingIndex = -1;
}
protected override async Task OnInitializedAsync()
{
await LoadAccountTypes();
await LoadTaxConditions();
await LoadDocumentTypes();
if (CustomerId.HasValue)
{
// Cargar datos del cliente existente desde la API
customer = await _httpClient.GetFromJsonAsync<ECustomer>($"/api/Customer/{CustomerId.Value}") ?? new();
}
}
private async Task LoadAccountTypes()
{
accountTypes = await accountTypeService.GetAllAsync();
}
private async Task LoadTaxConditions()
{
taxConditions = await taxConditionService.GetAllAsync();
}
private async Task LoadDocumentTypes()
{
var result = await _httpClient.GetFromJsonAsync<List<EDocumentType>>("/api/DocumentType/GetAll");
documentTypes = result ?? new();
}
private void AddCustomerDocument()
{
if (!string.IsNullOrWhiteSpace(documentFormModel.DocumentNumber))
{
customer.PhSCustomerDocuments.Add(documentFormModel);
documentFormModel = new();
}
}
private void RemoveCustomerDocument(ECustomerDocument document)
{
customer.PhSCustomerDocuments.Remove(document);
}
private void RemoveCustomerAddress(ECustomerAddress address)
{
customer.PhSCustomerAddresses.Remove(address);
}
private async Task HandleValidSubmit()
{
var parameters = new ModalParameters();
parameters.Add("Message", "¿Desea guardar los cambios del cliente?");
var modal = Modal.Show<ConfirmModal>("Confirmacion", parameters);
var result = await modal.Result;
if (result.Cancelled)
return;
try
{
HttpResponseMessage response;
if (CustomerId.HasValue)
{
response = await _httpClient.PutAsJsonAsync("/api/Customer/Update", customer);
}
else
{
response = await _httpClient.PostAsJsonAsync("/api/Customer/Create", customer);
}
if (response.IsSuccessStatusCode)
{
toastService.ShowSuccess("Cliente guardado exitosamente");
Navigation.NavigateTo(returnUrl);
}
else
{
var error = await response.Content.ReadAsStringAsync();
toastService.ShowError($"Error: {error}");
}
}
catch (Exception ex)
{
toastService.ShowError($"Error: {ex.Message}");
}
}
private void Cancel()
{
Navigation.NavigateTo(returnUrl);
}
}