General Refactor 1
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 5m30s

This commit is contained in:
Leandro Hernan Rojas 2025-05-08 15:46:04 -03:00
parent 001ba4146a
commit 605447e18a
16 changed files with 217 additions and 288 deletions

View File

@ -6,122 +6,125 @@ using Models.Interfaces;
using System.Reflection; using System.Reflection;
using Transversal.Services; using Transversal.Services;
public class PatientService : IPatientDom namespace Core.Services
{ {
#region Declaraciones y Constructor public class PatientService : IPatientDom
private readonly IPhSPatientRepository _repository;
public PatientService(IPhSPatientRepository patientRepository)
{ {
_repository = patientRepository ?? throw new ArgumentNullException(nameof(patientRepository)); #region Declaraciones y Constructor
} private readonly IPhSPatientRepository _repository;
#endregion public PatientService(IPhSPatientRepository patientRepository)
#region Métodos de servicio
public async Task<PagedResult<EPatient>> GetAllAsync(int page = 1, int pageSize = 50)
{
try
{ {
return await _repository.GetAllAsync(page, pageSize); _repository = patientRepository ?? throw new ArgumentNullException(nameof(patientRepository));
} }
catch (Exception ex) #endregion
#region Métodos de servicio
public async Task<PagedResult<EPatient>> GetAllAsync(int page = 1, int pageSize = 50)
{ {
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod"; try
throw new Exception($"{methodName} Message: {ex.Message}", ex);
}
}
public async Task<EPatient?> GetByIdAsync(int id)
{
try
{
return await _repository.GetByIdAsync(id);
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
throw new Exception($"{methodName} Message: {ex.Message}", ex);
}
}
public async Task<EPatient> CreateAsync(EPatient entity)
{
if (entity is null)
throw new ArgumentNullException(nameof(entity), "El paciente no puede ser nulo.");
if (string.IsNullOrWhiteSpace(entity.Firstname) || string.IsNullOrWhiteSpace(entity.Lastname))
throw new ArgumentException("Debe completar el nombre y apellido del paciente.");
if (string.IsNullOrWhiteSpace(entity.DocumentNumber))
throw new ArgumentException("Debe completar el número de documento del paciente.");
return await _repository.CreateAsync(entity);
}
public async Task<bool> UpdateAsync(EPatient entity)
{
if (entity is null)
throw new ArgumentNullException(nameof(entity), "El paciente no puede ser nulo.");
if (string.IsNullOrWhiteSpace(entity.Firstname) || string.IsNullOrWhiteSpace(entity.Lastname))
throw new ArgumentException("Debe completar el nombre y apellido del paciente.");
if (string.IsNullOrWhiteSpace(entity.DocumentNumber))
throw new ArgumentException("Debe completar el número de documento del paciente.");
return await _repository.UpdateAsync(entity);
}
public async Task<PagedResult<EPatient>> SearchAsync(string? name, string? document, int page = 1, int pageSize = 50)
{
try
{
return await _repository.SearchAsync(name, document, page, pageSize);
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
throw new Exception($"{methodName} Message: {ex.Message}", ex);
}
}
public Task<bool> DeleteAsync(int id)
{
// Implementar según políticas del sistema (soft delete, etc.)
throw new NotImplementedException();
}
public async Task<byte[]> ExportFilteredPatientsToExcelAsync(PatientSearchParams searchParams)
{
try
{
var searchResult = await SearchAsync(
searchParams.Name,
searchParams.Document,
searchParams.Page,
searchParams.PageSize
);
if (searchResult?.Items is null || !searchResult.Items.Any())
{ {
throw new Exception("No se encontraron pacientes para exportar."); return await _repository.GetAllAsync(page, pageSize);
} }
catch (Exception ex)
var stream = new XLSXExportBase();
var patientData = searchResult.Items.Select(p => new
{ {
p.Id, var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
p.Firstname, throw new Exception($"{methodName} Message: {ex.Message}", ex);
p.Lastname, }
p.DocumentNumber,
p.AffiliateNumber,
p.Email,
p.Phone,
p.Birthdate,
p.Gender
}).ToList();
var excelFile = stream.ExportExcel(patientData);
return excelFile;
} }
catch (Exception ex) public async Task<EPatient?> GetByIdAsync(int id)
{ {
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod"; try
throw new Exception($"{methodName} - Error al exportar pacientes: {ex.Message}", ex); {
return await _repository.GetByIdAsync(id);
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
throw new Exception($"{methodName} Message: {ex.Message}", ex);
}
} }
public async Task<EPatient> CreateAsync(EPatient entity)
{
if (entity is null)
throw new ArgumentNullException(nameof(entity), "El paciente no puede ser nulo.");
if (string.IsNullOrWhiteSpace(entity.Firstname) || string.IsNullOrWhiteSpace(entity.Lastname))
throw new ArgumentException("Debe completar el nombre y apellido del paciente.");
if (string.IsNullOrWhiteSpace(entity.DocumentNumber))
throw new ArgumentException("Debe completar el número de documento del paciente.");
return await _repository.CreateAsync(entity);
}
public async Task<bool> UpdateAsync(EPatient entity)
{
if (entity is null)
throw new ArgumentNullException(nameof(entity), "El paciente no puede ser nulo.");
if (string.IsNullOrWhiteSpace(entity.Firstname) || string.IsNullOrWhiteSpace(entity.Lastname))
throw new ArgumentException("Debe completar el nombre y apellido del paciente.");
if (string.IsNullOrWhiteSpace(entity.DocumentNumber))
throw new ArgumentException("Debe completar el número de documento del paciente.");
return await _repository.UpdateAsync(entity);
}
public async Task<PagedResult<EPatient>> SearchAsync(string? name, string? document, int page = 1, int pageSize = 50)
{
try
{
return await _repository.SearchAsync(name, document, page, pageSize);
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
throw new Exception($"{methodName} Message: {ex.Message}", ex);
}
}
public Task<bool> DeleteAsync(int id)
{
// Implementar según políticas del sistema (soft delete, etc.)
throw new NotImplementedException();
}
public async Task<byte[]> ExportFilteredPatientsToExcelAsync(PatientSearchParams searchParams)
{
try
{
var searchResult = await SearchAsync(
searchParams.Name,
searchParams.Document,
searchParams.Page,
searchParams.PageSize
);
if (searchResult?.Items is null || !searchResult.Items.Any())
{
throw new Exception("No se encontraron pacientes para exportar.");
}
var stream = new XLSXExportBase();
var patientData = searchResult.Items.Select(p => new
{
p.Id,
p.Firstname,
p.Lastname,
p.DocumentNumber,
p.AffiliateNumber,
p.Email,
p.Phone,
p.Birthdate,
p.Gender
}).ToList();
var excelFile = stream.ExportExcel(patientData);
return excelFile;
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
throw new Exception($"{methodName} - Error al exportar pacientes: {ex.Message}", ex);
}
}
#endregion
} }
#endregion }
}

View File

@ -5,7 +5,7 @@ using Models.Interfaces;
using System.Reflection; using System.Reflection;
using Transversal.Services; using Transversal.Services;
namespace PhronCare.Core.Services.Sales namespace Core.Services
{ {
public class QuoteService( public class QuoteService(
IPhSQuoteHeaderRepository quoteHeaderRepository, IPhSQuoteHeaderRepository quoteHeaderRepository,

View File

@ -2,7 +2,7 @@
using Models.Interfaces; using Models.Interfaces;
using Models.Models; using Models.Models;
namespace PhronCare.Core.Data.Repositories.Sales namespace Models.Repositories
{ {
public class PhSFormSeriesRepository(PhronCareOperationsHubContext context) : IPhSFormSeriesRepository public class PhSFormSeriesRepository(PhronCareOperationsHubContext context) : IPhSFormSeriesRepository
{ {

View File

@ -6,7 +6,7 @@ using Models.Helpers;
using Models.Interfaces; using Models.Interfaces;
using Models.Models; using Models.Models;
namespace Infrastructure.Repositories.Patients namespace Models.Repositories
{ {
public class PhSPatientRepository(PhronCareOperationsHubContext context, ILogger<PhSPatientRepository> logger) : IPhSPatientRepository public class PhSPatientRepository(PhronCareOperationsHubContext context, ILogger<PhSPatientRepository> logger) : IPhSPatientRepository
{ {

View File

@ -6,7 +6,7 @@ using Models.Helpers;
using Models.Interfaces; using Models.Interfaces;
using Models.Models; using Models.Models;
namespace PhronCare.Core.Data.Repositories.Sales namespace Models.Repositories
{ {
public class PhSPeopleRepository(PhronCareOperationsHubContext context) : IPhSPeopleRepository public class PhSPeopleRepository(PhronCareOperationsHubContext context) : IPhSPeopleRepository
{ {

View File

@ -5,7 +5,7 @@ using Models.Helpers;
using Models.Interfaces; using Models.Interfaces;
using Models.Models; using Models.Models;
namespace PhronCare.Core.Data.Repositories.Sales namespace Models.Repositories
{ {
public class PhSQuoteDetailRepository(PhronCareOperationsHubContext context) : IPhSQuoteDetailRepository public class PhSQuoteDetailRepository(PhronCareOperationsHubContext context) : IPhSQuoteDetailRepository
{ {

View File

@ -5,7 +5,7 @@ using Models.Models;
using Domain.Entities; using Domain.Entities;
using Domain.Generics; using Domain.Generics;
namespace PhronCare.Core.Data.Repositories.Sales namespace Models.Repositories
{ {
public class PhSQuoteHeaderRepository(PhronCareOperationsHubContext context) : IPhSQuoteHeaderRepository public class PhSQuoteHeaderRepository(PhronCareOperationsHubContext context) : IPhSQuoteHeaderRepository
{ {

View File

@ -5,7 +5,7 @@ using Models.Helpers;
using Models.Interfaces; using Models.Interfaces;
using Models.Models; using Models.Models;
namespace PhronCare.Core.Data.Repositories.Sales namespace Models.Repositories
{ {
public class PhSQuoteRoleRepository(PhronCareOperationsHubContext context) : IPhSQuoteRoleRepository public class PhSQuoteRoleRepository(PhronCareOperationsHubContext context) : IPhSQuoteRoleRepository
{ {

View File

@ -11,14 +11,10 @@ using Services.Interfaces;
using Services.Services; using Services.Services;
using Services.Models; using Services.Models;
using System.Text; using System.Text;
using Infrastructure.Repositories.Patients;
using PhronCare.Core.Data.Repositories.Sales;
using PhronCare.Core.Services.Sales;
using phronCare.API.Models.Security;
using phronCare.API.Models; using phronCare.API.Models;
using Core.Interfaces; using Core.Interfaces;
using Core.Services; using Core.Services;
using System.Net.Http.Headers; using phronCare.API.Models.Security;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -37,7 +33,6 @@ builder.Services.AddDbContext<PhronCareOperationsHubContext>(options =>
#region Repositorios y Servicios #region Repositorios y Servicios
RepositorysAndServices(builder); RepositorysAndServices(builder);
#endregion #endregion
#region Require Confirmed Email #region Require Confirmed Email
@ -55,16 +50,7 @@ builder.Services.Configure<DataProtectionTokenProviderOptions>(opts =>
builder.Services.AddSingleton<TwoFactorAuthenticator>(); builder.Services.AddSingleton<TwoFactorAuthenticator>();
#endregion #endregion
//#region Security Identity EF Configuration
//builder.Services.AddIdentity<IdentityUser, IdentityRole>()
// .AddEntityFrameworkStores<phronCareDbContext>()
// .AddDefaultTokenProviders();
//builder.Services.Configure<DataProtectionTokenProviderOptions>( opts => opts.TokenLifespan=TimeSpan.FromHours(10));
//builder.Services.AddSingleton<TwoFactorAuthenticator>();
//#endregion
#region Authentication Service #region Authentication Service
builder.Services.AddAuthentication(options => builder.Services.AddAuthentication(options =>
{ {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
@ -142,16 +128,8 @@ builder.Services.AddSwaggerGen(option =>
#endregion #endregion
#region CORS sin cambios #region CORS sin cambios
// builder.Services.AddCors(p => p.AddPolicy("CORS", builder =>
// {
// builder
// .AllowAnyOrigin()
// .AllowAnyMethod()
// .AllowAnyHeader();
// }));
builder.Services.AddCors(options => builder.Services.AddCors(options =>
{ {
options.AddPolicy("CORS", policy => options.AddPolicy("CORS", policy =>
{ {
/* /*
@ -182,8 +160,8 @@ var app = builder.Build();
//if (app.Environment.IsDevelopment()) //if (app.Environment.IsDevelopment())
//{ //{
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();
//} //}
app.UseCors("CORS"); app.UseCors("CORS");
@ -246,20 +224,18 @@ static void RepositorysAndServices(WebApplicationBuilder builder)
builder.Services.AddScoped<IPhSQuoteHeaderRepository, PhSQuoteHeaderRepository>(); builder.Services.AddScoped<IPhSQuoteHeaderRepository, PhSQuoteHeaderRepository>();
builder.Services.AddScoped<IPhSQuoteRepository, PhSQuoteRepository>(); builder.Services.AddScoped<IPhSQuoteRepository, PhSQuoteRepository>();
//builder.Services.AddScoped<IPhSQuoteRoleRepository, PhSQuoteRoleRepository>();
builder.Services.AddScoped<IPhSFormSeriesRepository, PhSFormSeriesRepository>(); builder.Services.AddScoped<IPhSFormSeriesRepository, PhSFormSeriesRepository>();
// Registrar el service de lookup // Registrar el service de lookup
builder.Services.AddScoped<ILookUpDom, LookupService>(); builder.Services.AddScoped<ILookUpDom, LookupService>();
builder.Services.AddScoped<IPhSLookUpRepository, PhSLookUpRepository>(); builder.Services.AddScoped<IPhSLookUpRepository, PhSLookUpRepository>();
// 2) Repositorio de histórico de cotizaciones // Repositorio de histórico de cotizaciones
builder.Services.AddScoped<IPhOHExchangeRateHistory, PhOHExchangeRateHistory>(); builder.Services.AddScoped<IPhOHExchangeRateHistory, PhOHExchangeRateHistory>();
// 3) Dominio/servicio con HttpClient para BCRA // Dominio/servicio con HttpClient para BCRA
builder.Services.AddHttpClient<IExchangeRateDom, ExchangeRateDom>(client => builder.Services.AddHttpClient<IExchangeRateDom, ExchangeRateDom>(client =>
{ {
client.BaseAddress = new Uri("https://api.bcra.gob.ar/"); client.BaseAddress = new Uri("https://api.bcra.gob.ar/");
}); });

View File

@ -1,5 +1,5 @@
@page "/sales/patients" @page "/sales/patients"
@using phronCare.UIBlazor.Services.Sales @using Services.Sales
@using Domain.Entities @using Domain.Entities
@using Domain.Generics @using Domain.Generics
@using Domain.SearchParams @using Domain.SearchParams

View File

@ -2,19 +2,19 @@
@using System.Globalization; @using System.Globalization;
@using System.Net.Http.Json @using System.Net.Http.Json
@using Blazored.Typeahead @using Blazored.Typeahead
@using Pages.Sales.Modals
@using Services.Lookups @using Services.Lookups
@using phronCare.UIBlazor.Pages.Sales.Modals @using Services.Integrations
@using phronCare.UIBlazor.Services.Integrations @using Services.Sales.Quotes
@using phronCare.UIBlazor.Services.Sales.Quotes
@using Blazored.Toast.Services @using Blazored.Toast.Services
@using Blazored.Toast.Configuration @using Blazored.Toast.Configuration
@inject ISalesLookupService SalesLookupService
@inject IQuoteService QuoteService
@inject IToastService toastService
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject IModalService Modal @inject ISalesLookupService SalesLookupService
@inject IExchangeRateService ExchangeRateService @inject IExchangeRateService ExchangeRateService
@inject IQuoteService QuoteService
@inject IToastService toastService
@inject IModalService Modal
<EditForm Model="_quoteModel" > <EditForm Model="_quoteModel" >
<div class="container mt-4" style="zoom:0.8;"> <div class="container mt-4" style="zoom:0.8;">
@ -147,7 +147,6 @@
<InputText class="form-control" @bind-Value="_quoteModel.Observations" /> <InputText class="form-control" @bind-Value="_quoteModel.Observations" />
</div> </div>
</div> </div>
<!-- Productos Cotizados --> <!-- Productos Cotizados -->
<hr /> <hr />
<div class="d-flex justify-content-between align-items-center mb-2"> <div class="d-flex justify-content-between align-items-center mb-2">
@ -199,7 +198,6 @@
<td> <td>
<button class="btn btn-sm btn-danger" @onclick="() => RemoveDetail(item)">🗑</button> <button class="btn btn-sm btn-danger" @onclick="() => RemoveDetail(item)">🗑</button>
</td> </td>
</tr> </tr>
} }
} }
@ -212,9 +210,8 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- Totales + Ajustes --> <!-- Totales + Ajustes -->
<div class="row justify-content-end mt-3"> <div class="row justify-content-end mt-3" >
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label d-flex justify-content-between align-items-center"> <label class="form-label d-flex justify-content-between align-items-center">
Ajustes comerciales Ajustes comerciales
@ -240,8 +237,6 @@
{ {
<p class="text-muted">Sin ajustes.</p> <p class="text-muted">Sin ajustes.</p>
} }
</div> </div>
<!-- Impuestos --> <!-- Impuestos -->
<div class="col-md-4"> <div class="col-md-4">
@ -304,7 +299,6 @@
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
<div class="card-footer text-end"> <div class="card-footer text-end">
<button type="button" class="btn btn-primary" @onclick="HandleValidSubmit">Guardar</button> <button type="button" class="btn btn-primary" @onclick="HandleValidSubmit">Guardar</button>
@ -316,22 +310,12 @@
@code { @code {
private EQuoteHeader _quoteModel = new(); private EQuoteHeader _quoteModel = new();
private ELookUpItem? _selectedCustomer; private ELookUpItem? _selectedCustomer, _selectedProfessional, _selectedInstitution, _selectedPatient, _selectedPerson;
private ELookUpItem? _selectedProfessional;
private ELookUpItem? _selectedInstitution;
private ELookUpItem? _selectedPatient;
private ELookUpItem? _selectedPerson;
private List<ELookUpItem> _businessUnits = new(); private List<ELookUpItem> _businessUnits = new();
private decimal _netAmount = 0;
private decimal _taxAmount = 0;
private decimal _grandTotal = 0;
private EExchangeRateHistory? YesterdayRate; private EExchangeRateHistory? YesterdayRate;
private Task OnValueChanged<T>(EQuoteDetail item, T value, Action<EQuoteDetail, T> setter) private decimal _netAmount = 0, _taxAmount = 0, _grandTotal = 0;
{
setter(item, value); public const int QuoteSeriesId = 1; // Serie de comprobante para presupuestos (talonario Q).
RecalculateTotals();
return Task.CompletedTask;
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -392,35 +376,6 @@
// _quoteModel.ProfessionalId = nuevo.Id; // _quoteModel.ProfessionalId = nuevo.Id;
} }
} }
private Task OnCustomerSelected(ELookUpItem item)
=> SetLookupSelection(item, sel => _selectedCustomer = sel, id => _quoteModel.CustomerId = id);
private Task OnPersonSelected(ELookUpItem item)
=> SetLookupSelection(item, sel => _selectedPerson = sel, id => _quoteModel.PeopleId = id);
// private Task OnProfessionalSelected(ELookUpItem item)
// => SetLookupSelection(item, sel => _selectedProfessional = sel);
// private Task OnInstitutionSelected(ELookUpItem item)
// => SetLookupSelection(item, sel => _selectedInstitution = sel);
// private Task OnPatientSelected(ELookUpItem item)
// => SetLookupSelection(item, sel => _selectedPatient = sel);
private Task SetLookupSelection(ELookUpItem? item, Action<ELookUpItem?> setSelected, Action<int>? setModelId = null)
{
setSelected(item);
if (item != null && setModelId != null) setModelId(item.Id);
return Task.CompletedTask;
}
private void OnDetailChanged(EQuoteDetail item) { }
private void RemoveDetail(EQuoteDetail item)
=> _quoteModel.PhSQuoteDetails.Remove(item);
private void RecalculateTotals()
{
_netAmount = _quoteModel.PhSQuoteDetails.Sum(d => d.Quantity * d.Unitprice);
_taxAmount = _quoteModel.PhSQuoteTaxes.Sum(t => t.Taxamount);
_grandTotal = _netAmount + _taxAmount;
_quoteModel.Netamount = _netAmount;
_quoteModel.Total = _grandTotal;
}
private async Task OpenAdjustmentModal() private async Task OpenAdjustmentModal()
{ {
var modal = Modal.Show<QuoteAdjustmentQuickAddModal>("Agregar Ajuste", new ModalOptions { HideHeader = true }); var modal = Modal.Show<QuoteAdjustmentQuickAddModal>("Agregar Ajuste", new ModalOptions { HideHeader = true });
@ -435,23 +390,28 @@
}); });
} }
} }
private async Task HandleValidSubmit()
private void RemoveAdjustment(EQuoteAdjustment adj)
{ {
_quoteModel.PhSQuoteAdjustments.Remove(adj); var result = await QuoteService.CreateFullQuoteAsync(_quoteModel, QuoteSeriesId);
if (!result.Success)
{
toastService.ShowError(result.ErrorMessage);
return;
}
ToastParameters _parameters = new ToastParameters();
_parameters.Add(nameof(PhLinkToast.Comprobante), result.QuoteNumber);
_parameters.Add(nameof(PhLinkToast.Style), "success");
toastService.ShowToast<PhLinkToast>(_parameters);
} }
private async Task AddNewTax() private async Task AddNewTax()
{ {
var parameters = new ModalParameters(); var parameters = new ModalParameters();
parameters.Add(nameof(QuoteTaxQuickAddModal.NetAmount), _netAmount); parameters.Add(nameof(QuoteTaxQuickAddModal.NetAmount), _netAmount);
var options = new ModalOptions var options = new ModalOptions
{ {
HideHeader = true, HideHeader = true,
Size = ModalSize.Small Size = ModalSize.Small
}; };
var modal = Modal.Show<QuoteTaxQuickAddModal>("", parameters, options); var modal = Modal.Show<QuoteTaxQuickAddModal>("", parameters, options);
var result = await modal.Result; var result = await modal.Result;
@ -461,49 +421,61 @@
RecalculateTotals(); RecalculateTotals();
} }
} }
private Task OnCustomerSelected(ELookUpItem item)
=> SetLookupSelection(item, sel => _selectedCustomer = sel, id => _quoteModel.CustomerId = id);
private Task OnPersonSelected(ELookUpItem item)
=> SetLookupSelection(item, sel => _selectedPerson = sel, id => _quoteModel.PeopleId = id);
private Task OnProfessionalSelected(ELookUpItem item)
{
_selectedProfessional = item;
AddOrUpdateRole("PhS_Professionals", item.Id, "Medico");
return Task.CompletedTask;
}
private Task OnInstitutionSelected(ELookUpItem item)
{
_selectedInstitution = item;
AddOrUpdateRole("PhS_Institutions", item.Id, "Hospital");
return Task.CompletedTask;
}
private Task OnPatientSelected(ELookUpItem item)
{
_selectedPatient = item;
AddOrUpdateRole("PhS_Patients", item.Id, "Paciente");
return Task.CompletedTask;
}
private Task OnValueChanged<T>(EQuoteDetail item, T value, Action<EQuoteDetail, T> setter)
{
setter(item, value);
RecalculateTotals();
return Task.CompletedTask;
}
private Task SetLookupSelection(ELookUpItem? item, Action<ELookUpItem?> setSelected, Action<int>? setModelId = null)
{
setSelected(item);
if (item != null && setModelId != null) setModelId(item.Id);
return Task.CompletedTask;
}
private void OnDetailChanged(EQuoteDetail item) { }
private void RemoveDetail(EQuoteDetail item)
=> _quoteModel.PhSQuoteDetails.Remove(item);
private void RemoveAdjustment(EQuoteAdjustment adj)
{
_quoteModel.PhSQuoteAdjustments.Remove(adj);
}
private void RemoveTax(EQuoteTax tax) private void RemoveTax(EQuoteTax tax)
{ {
_quoteModel.PhSQuoteTaxes.Remove(tax); _quoteModel.PhSQuoteTaxes.Remove(tax);
RecalculateTotals(); RecalculateTotals();
} }
private void RecalculateTotals()
private async Task Cancel()
{ {
_netAmount = _quoteModel.PhSQuoteDetails.Sum(d => d.Quantity * d.Unitprice);
// 2) Añades los parámetros que luego el componente tomará _taxAmount = _quoteModel.PhSQuoteTaxes.Sum(t => t.Taxamount);
// settings.Parameters.Add(nameof(CustomActionToast.Url), url); _grandTotal = _netAmount + _taxAmount;
ToastParameters _toastParameters; _quoteModel.Netamount = _netAmount;
_quoteModel.Total = _grandTotal;
_toastParameters = new ToastParameters();
_toastParameters.Add(nameof(PhLinkToast.Comprobante), "FAC A0001-00000009");
_toastParameters.Add(nameof(PhLinkToast.Style), "purple");
toastService.ShowToast<PhLinkToast>(_toastParameters);
} }
private async Task HandleValidSubmit()
{
// Si necesitas validar algo extra antes de llamar al servicio, hazlo aquí
int selectedSeriesId = 1/* obtenlo de donde corresponda, p.ej. de un dropdown */;
var result = await QuoteService.CreateFullQuoteAsync(_quoteModel, selectedSeriesId);
if (!result.Success)
{
toastService.ShowError(result.ErrorMessage);
return;
}
ToastParameters _toastParameters;
_toastParameters = new ToastParameters();
_toastParameters.Add(nameof(PhLinkToast.Comprobante), result.QuoteNumber);
_toastParameters.Add(nameof(PhLinkToast.Style), "success");
toastService.ShowToast<PhLinkToast>(_toastParameters);
}
/// <summary>
/// Agrega o actualiza un rol dentro de _quoteModel.PhSQuoteRoles
/// </summary>
private void AddOrUpdateRole(string entityType, int entityId, string roleName) private void AddOrUpdateRole(string entityType, int entityId, string roleName)
{ {
var existing = _quoteModel.PhSQuoteRoles var existing = _quoteModel.PhSQuoteRoles
@ -516,33 +488,19 @@
else else
{ {
_quoteModel.PhSQuoteRoles.Add(new EQuoteRole _quoteModel.PhSQuoteRoles.Add(new EQuoteRole
{ {
// QuoteheaderId lo llenará EF en el servidor Entitytype = entityType,
Entitytype = entityType, EntityId = entityId,
EntityId = entityId, Role = roleName
Role = roleName });
});
} }
} }
private Task OnProfessionalSelected(ELookUpItem item) private void Cancel()
{ {
_selectedProfessional = item; ToastParameters _parameters = new ToastParameters();
AddOrUpdateRole("PhS_Professionals", item.Id, "Medico"); _parameters.Add(nameof(PhLinkToast.Comprobante), "Q-00000009");
return Task.CompletedTask; _parameters.Add(nameof(PhLinkToast.Style), "lime");
}
private Task OnInstitutionSelected(ELookUpItem item) toastService.ShowToast<PhLinkToast>(_parameters);
{
_selectedInstitution = item;
AddOrUpdateRole("PhS_Institutions", item.Id, "Hospital");
return Task.CompletedTask;
} }
private Task OnPatientSelected(ELookUpItem item)
{
_selectedPatient = item;
AddOrUpdateRole("PhS_Patients", item.Id, "Paciente");
return Task.CompletedTask;
}
} }

View File

@ -4,14 +4,14 @@ using phronCare.UIBlazor.Services.Sales;
using phronCare.UIBlazor.Services.Lookups; using phronCare.UIBlazor.Services.Lookups;
using phronCare.UIBlazor.Services.Tickets; using phronCare.UIBlazor.Services.Tickets;
using phronCare.UIBlazor.Services.Authorization; using phronCare.UIBlazor.Services.Authorization;
using phronCare.UIBlazor.Services.Sales.Quotes;
using phronCare.UIBlazor.Services.Integrations;
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Blazored.Modal; using Blazored.Modal;
using Blazored.Toast; using Blazored.Toast;
using phronCare.UIBlazor.Services.Sales.Quotes;
using phronCare.UIBlazor.Services.Integrations;
var builder = WebAssemblyHostBuilder.CreateDefault(args); var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app"); builder.RootComponents.Add<App>("#app");
@ -26,14 +26,6 @@ builder.Services.AddAuthorizationCore();
#endregion #endregion
#region Load Configuration #region Load Configuration
//var apiBaseUrl = Environment.GetEnvironmentVariable("API_BASE_URL")
// ?? builder.Configuration.GetSection("BaseAd").Value;
//if (!string.IsNullOrEmpty(apiBaseUrl) && Uri.TryCreate(apiBaseUrl, UriKind.Absolute, out var validUri))
//{
// builder.Services.AddScoped(sp => new HttpClient { BaseAddress = validUri });
//}
//Console.WriteLine($" valor apiBaseUrl: {apiBaseUrl}");
var config = builder.Configuration.GetSection("BaseAd").Value; var config = builder.Configuration.GetSection("BaseAd").Value;
if (config != null) if (config != null)
{ {
@ -63,7 +55,6 @@ static void InjectDependencies(WebAssemblyHostBuilder builder)
builder.Services.AddScoped<CustomerService>(); builder.Services.AddScoped<CustomerService>();
builder.Services.AddScoped<TaxConditionService>(); builder.Services.AddScoped<TaxConditionService>();
builder.Services.AddScoped<AccountTypeService>(); builder.Services.AddScoped<AccountTypeService>();
builder.Services.AddScoped<PatientService>();
builder.Services.AddScoped<DocumentTypeService>(); builder.Services.AddScoped<DocumentTypeService>();
builder.Services.AddScoped<InstitutionService>(); builder.Services.AddScoped<InstitutionService>();
builder.Services.AddScoped<ProductService>(); builder.Services.AddScoped<ProductService>();
@ -72,5 +63,5 @@ static void InjectDependencies(WebAssemblyHostBuilder builder)
builder.Services.AddScoped<PeopleService>(); builder.Services.AddScoped<PeopleService>();
builder.Services.AddScoped<ProfessionalSpecialtyService>(); builder.Services.AddScoped<ProfessionalSpecialtyService>();
builder.Services.AddScoped<ProductCategoryService>(); builder.Services.AddScoped<ProductCategoryService>();
builder.Services.AddScoped<PatientService>();
} }

View File

@ -1,11 +1,12 @@
using Domain.Entities; using Domain.Entities;
using Domain.Generics; using Domain.Generics;
using Domain.SearchParams; using Domain.SearchParams;
using Microsoft.JSInterop;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Reflection;
using System.Text.Json; using System.Text.Json;
using System.Text; using System.Text;
using Microsoft.JSInterop;
using System.Reflection;
namespace phronCare.UIBlazor.Services.Sales namespace phronCare.UIBlazor.Services.Sales
{ {

View File

@ -1,7 +1,7 @@
using System.Net.Http.Json; using System.Net.Http.Json;
using Domain.Entities; using Domain.Entities;
namespace phronCare.UIBlazor.Services.People namespace phronCare.UIBlazor.Services.Sales
{ {
public class PeopleGroupService public class PeopleGroupService
{ {

View File

@ -1,8 +1,8 @@
@using Microsoft.AspNetCore.Components @using Microsoft.AspNetCore.Components
@inject NavigationManager Navigation @inject NavigationManager Navigation
<div class="toast-body text-white p-3 rounded shadow-lg" <div class="toast-body text-white p-3 rounded shadow-sm"
style="background-color:@GetBackgroundColor();"> style="background-color:@GetBackgroundColor(); border:2px solid white;">
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<i class="fas fa-link fa-sm me-2"></i> <i class="fas fa-link fa-sm me-2"></i>
<span class="fw-bold small me-3">Documento</span> <span class="fw-bold small me-3">Documento</span>