diff --git a/Data/Data.csproj b/Data/Data.csproj
deleted file mode 100644
index b60bf86..0000000
--- a/Data/Data.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
-
diff --git a/Data/Entities/PhOH_Tickets.cs b/Data/Entities/PhOH_Tickets.cs
deleted file mode 100644
index 3a32f8d..0000000
--- a/Data/Entities/PhOH_Tickets.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Data.Entities
-{
- public partial class PhOH_Tickets
- {
- [Key]
- public System.Guid TicketId { get; set; }
- public string Titulo { get; set; } = string.Empty;
- public string Descripcion { get; set; } = string.Empty;
- public string Prioridad { get; set; } = string.Empty;
- public string Estado { get; set; } = string.Empty;
- public string CreadorUsuarioId { get; set; } = string.Empty;
- public Nullable FechaCreacion { get; set; }
- public string AsignadoAUsuarioId { get; set; } = string.Empty;
- public Nullable FechaEjecucion { get; set; }
- public string Categoria { get; set; } = string.Empty;
- public string Comentarios { get; set; } = string.Empty;
- public string Departamento { get; set; } = string.Empty;
- public string Impacto { get; set; } = string.Empty;
- public string Urgencia { get; set; } = string.Empty;
- public Nullable EsSolicitudCliente { get; set; }
- public string AdjuntoArchivo { get; set; } = string.Empty;
- }
-}
diff --git a/Data/Entities/Tickets_Dashboard_Result.cs b/Data/Entities/Tickets_Dashboard_Result.cs
deleted file mode 100644
index 99f42ba..0000000
--- a/Data/Entities/Tickets_Dashboard_Result.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Data.Entities
-{
- public partial class Tickets_Dashboard_Result
- {
- public System.Guid TicketId { get; set; }
- public string Titulo { get; set; }=string.Empty;
- public string Prioridad { get; set; } = string.Empty;
- public string Estado { get; set; } = string.Empty;
- public string CreadorUsuarioId { get; set; } = string.Empty;
- public Nullable FechaCreacion { get; set; }
- public string AsignadoAUsuarioId { get; set; } = string.Empty;
- public string Categoria { get; set; } =string.Empty;
- public string Departamento { get; set; } = string.Empty;
- public string Impacto { get; set; } = string.Empty;
- public string Urgencia { get; set; } = string.Empty;
- }
-}
diff --git a/Data/Entities/Tickets_GetSummary_Result.cs b/Data/Entities/Tickets_GetSummary_Result.cs
deleted file mode 100644
index 7e8fa68..0000000
--- a/Data/Entities/Tickets_GetSummary_Result.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Data.Entities
-{
- public partial class Tickets_GetSummary_Result
- {
- public string Estado { get; set; } = string.Empty;
- public Nullable Cantidad { get; set; }
- }
-}
diff --git a/Data/Interfaces/ITicketRepository.cs b/Data/Interfaces/ITicketRepository.cs
deleted file mode 100644
index 205b19f..0000000
--- a/Data/Interfaces/ITicketRepository.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Domain.Entities;
-
-namespace Data.Interfaces
-{
- public interface ITicketRepository
- {
- IEnumerable GetAll();
- ETicket GetById(Guid ticketId);
- IEnumerable GetSummary();
- IEnumerable GetTicketDashboard(string Estado, string Orden);
- void InsertTicket(ETicket ticket);
- }
-}
diff --git a/Data/Models/OperationsHubContext.cs b/Data/Models/OperationsHubContext.cs
deleted file mode 100644
index 0c217ca..0000000
--- a/Data/Models/OperationsHubContext.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using Data.Entities;
-using System.Configuration;
-using Microsoft.Data.SqlClient;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.IdentityModel.Protocols;
-using Microsoft.Extensions.Configuration;
-
-namespace Data.Models
-{
- public class OperationsHubContext : DbContext
- {
- // Constructor que permite la inyección de dependencias
- public OperationsHubContext(DbContextOptions options) : base(options)
- {
- }
-
- protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- {
- if (!optionsBuilder.IsConfigured)
- {
- // Leer la cadena de conexión desde app.config
- var connectionString = ConfigurationManager.ConnectionStrings["OperationsHub"]?.ConnectionString;
- if (connectionString == null)
- {
- throw new InvalidOperationException("No se encontró la cadena de conexión 'OperationsHub'.");
- }
- optionsBuilder.UseSqlServer(connectionString);
- // Prueba de conexión: Intento de apertura de conexión
- using (var connection = new SqlConnection(connectionString))
- {
- connection.Open(); // Lanza excepción si falla
- System.Diagnostics.Debug.WriteLine("Conexión a la base de datos exitosa.");
- connection.Close();
- }
- }
- }
- public bool TestConnection(string connectionString)
- {
- using (var connection = new SqlConnection(connectionString))
- {
- try
- {
- connection.Open();
- return true; // Conexión exitosa
- }
- catch (Exception ex)
- {
- // Manejar la excepción, tal vez loguearla
- Console.WriteLine($"Error al conectar: {ex.Message}");
- return false; // Conexión fallida
- }
- }
- }
-
- public DbSet Tickets { get; set; }
- public DbSet TicketsDashboardResults { get; set; }
- public DbSet TicketsSummaryResults { get; set; }
-
- public async Task> Tickets_DashboardAsync(string estadoParam, string ordenParam)
- {
- var estadoParamSql = new SqlParameter("@EstadoParam", estadoParam ?? (object)DBNull.Value);
- var ordenParamSql = new SqlParameter("@OrdenParam", ordenParam ?? (object)DBNull.Value);
-
- // Consulta usando FromSqlRaw sobre el DbSet correspondiente
- return await TicketsDashboardResults
- .FromSqlRaw("EXEC Tickets_Dashboard @EstadoParam, @OrdenParam", estadoParamSql, ordenParamSql)
- .ToListAsync();
- }
-
- public async Task> Tickets_GetSummaryAsync()
- {
- // Consulta usando FromSqlRaw sobre el DbSet correspondiente
- return await TicketsSummaryResults
- .FromSqlRaw("EXEC Tickets_GetSummary")
- .ToListAsync();
- }
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder.Entity().ToTable("PhOH_Tickets");
-
- // Marcar los DbSet de resultados como entidades de solo lectura
- modelBuilder.Entity().HasNoKey();
- modelBuilder.Entity().HasNoKey();
- }
- }
-}
\ No newline at end of file
diff --git a/Data/Repositories/TicketRepository.cs b/Data/Repositories/TicketRepository.cs
deleted file mode 100644
index 87263bb..0000000
--- a/Data/Repositories/TicketRepository.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using Data.Interfaces;
-using Domain.Entities;
-using Data.Entities;
-using Data.Models;
-using Microsoft.EntityFrameworkCore;
-using System.Reflection;
-
-namespace Data.Repositories
-{
- public class TicketRepository : ITicketRepository
- {
- #region Declaraciones y Constructor
- private readonly OperationsHubContext _dbConnection;
- public TicketRepository(OperationsHubContext dbConnection)
- {
- _dbConnection = dbConnection;
- }
- #endregion
- #region Metodos de clase
- public async Task GetByIdAsync(Guid ticketId)
- {
- try
- {
- var ticket = await _dbConnection.Tickets
- .FirstOrDefaultAsync(t => t.TicketId == ticketId);
-
- if (ticket == null) return new ETicket();
-
- var eTicket = MapEntity(ticket);
- return eTicket;
- }
- catch (Exception ex)
- {
- throw new Exception($"{MethodBase.GetCurrentMethod()?.Name} Message: {ex.Message}", ex);
- }
- }
- public async Task> GetAllAsync()
- {
- var tickets = await _dbConnection.Tickets.ToListAsync();
- return tickets.Select(ticket => MapEntity(ticket));
- }
-
- public async Task InsertTicketAsync(ETicket ticket)
- {
- try
- {
- ticket.TicketId = Guid.NewGuid();
- var dataTicket = MapEntity(ticket);
-
- await _dbConnection.Tickets.AddAsync(dataTicket);
- await _dbConnection.SaveChangesAsync();
- }
- catch (DbUpdateException ex)
- {
- throw new Exception($"{MethodBase.GetCurrentMethod()?.Name} Message: {ex.InnerException?.Message}", ex);
- }
- catch (Exception ex)
- {
- throw new Exception($"{MethodBase.GetCurrentMethod()?.Name} Message: {ex.Message}", ex);
- }
- }
- //public IEnumerable GetSummary()
- //{
- // var summary_Results = _dbConnection.Tickets_GetSummary();
-
- // return summary_Results.Select(item =>
- // {
- // var eSummary = Activator.CreateInstance();
- // foreach (var propertyInfo in typeof(Tickets_GetSummary_Result).GetProperties())
- // {
- // var value = propertyInfo.GetValue(item);
- // typeof(ETickets_GetSummary).GetProperty(propertyInfo.Name)?.SetValue(eSummary, value);
- // }
- // return eSummary;
- // });
- //}
- //public IEnumerable GetTicketDashboard(string Estado, string Orden)
- //{
- // var ticketDashboard_Results = _dbConnection.Tickets_Dashboard(Estado, Orden);
-
- // return (ticketDashboard_Results.Select(item =>
- // {
- // var eTicketDashboard = Activator.CreateInstance();
- // foreach (var propertyInfo in typeof(Tickets_Dashboard_Result).GetProperties())
- // {
- // var value = propertyInfo.GetValue(item);
- // typeof(ETicket_Dashboard).GetProperty(propertyInfo.Name)?.SetValue(eTicketDashboard, value);
- // }
- // return eTicketDashboard;
- // }));
- //}
- #endregion
- #region Métodos Auxiliares
- private TDestination MapEntity(TSource source) where TDestination : new()
- {
- var destination = new TDestination();
- foreach (var propertyInfo in typeof(TSource).GetProperties())
- {
- var value = propertyInfo.GetValue(source);
- typeof(TDestination).GetProperty(propertyInfo.Name)?.SetValue(destination, value);
- }
- return destination;
- }
-
- public ETicket GetById(Guid ticketId)
- {
- throw new NotImplementedException();
- }
-
- public IEnumerable GetAll()
- {
- throw new NotImplementedException();
- }
-
- public void InsertTicket(ETicket ticket)
- {
- throw new NotImplementedException();
- }
-
- public IEnumerable GetSummary()
- {
- throw new NotImplementedException();
- }
-
- public IEnumerable GetTicketDashboard(string Estado, string Orden)
- {
- throw new NotImplementedException();
- }
- #endregion
- }
-}
diff --git a/Models/Models/PhSCustomer.cs b/Models/Models/PhSCustomer.cs
index b2da33d..55e587a 100644
--- a/Models/Models/PhSCustomer.cs
+++ b/Models/Models/PhSCustomer.cs
@@ -33,5 +33,9 @@ public partial class PhSCustomer
public virtual ICollection PhSPatients { get; set; } = new List();
+ public virtual ICollection PhSSalesDocumentBillToCustomers { get; set; } = new List();
+
+ public virtual ICollection PhSSalesDocumentCustomers { get; set; } = new List();
+
public virtual PhOhTaxCondition? TaxCondition { get; set; }
}
diff --git a/Models/Models/PhSFormSeries.cs b/Models/Models/PhSFormSeries.cs
index 6283025..d73d96b 100644
--- a/Models/Models/PhSFormSeries.cs
+++ b/Models/Models/PhSFormSeries.cs
@@ -44,4 +44,6 @@ public partial class PhSFormSeries
public virtual PhSFormType Formtype { get; set; } = null!;
public virtual PhSFormSeriesNextNumber? PhSFormSeriesNextNumber { get; set; }
+
+ public virtual ICollection PhSSalesDocuments { get; set; } = new List();
}
diff --git a/Models/Models/PhSProduct.cs b/Models/Models/PhSProduct.cs
index 68dd941..05c00e4 100644
--- a/Models/Models/PhSProduct.cs
+++ b/Models/Models/PhSProduct.cs
@@ -28,4 +28,6 @@ public partial class PhSProduct
public virtual PhSProductCategory? Category { get; set; }
public virtual ICollection PhSQuoteDetails { get; set; } = new List();
+
+ public virtual ICollection PhSSalesDocumentDetails { get; set; } = new List();
}
diff --git a/Models/Models/PhSQuoteDetail.cs b/Models/Models/PhSQuoteDetail.cs
index 0fb915b..7dd77bf 100644
--- a/Models/Models/PhSQuoteDetail.cs
+++ b/Models/Models/PhSQuoteDetail.cs
@@ -70,6 +70,8 @@ public partial class PhSQuoteDetail
public virtual ICollection PhSDeliveryNoteDetails { get; set; } = new List();
+ public virtual ICollection PhSSalesDocumentDetails { get; set; } = new List();
+
public virtual PhSProduct Product { get; set; } = null!;
public virtual PhSQuoteHeader Quoteheader { get; set; } = null!;
diff --git a/Models/Models/PhSQuoteHeader.cs b/Models/Models/PhSQuoteHeader.cs
index 90a62c4..edda44a 100644
--- a/Models/Models/PhSQuoteHeader.cs
+++ b/Models/Models/PhSQuoteHeader.cs
@@ -139,4 +139,6 @@ public partial class PhSQuoteHeader
public virtual ICollection PhSQuoteRoles { get; set; } = new List();
public virtual ICollection PhSQuoteTaxes { get; set; } = new List();
+
+ public virtual ICollection PhSSalesDocuments { get; set; } = new List();
}
diff --git a/Models/Models/PhSSalesDocument.cs b/Models/Models/PhSSalesDocument.cs
new file mode 100644
index 0000000..2f3d5a6
--- /dev/null
+++ b/Models/Models/PhSSalesDocument.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Collections.Generic;
+
+namespace Models.Models;
+
+///
+/// Documentos comerciales internos de venta: facturas, notas de debito, notas de credito, FCE, NDE y NCE. Mantiene la emision interna separada de la autorizacion fiscal ARCA.
+///
+public partial class PhSSalesDocument
+{
+ ///
+ /// Identificador interno del documento comercial.
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// Talonario/serie interna existente en PhronCare. Reutiliza PhS_FormSeries para numeracion interna.
+ ///
+ public int? FormseriesId { get; set; }
+
+ ///
+ /// Numero secuencial interno asignado al emitir internamente el documento. No corresponde al numero fiscal ARCA.
+ ///
+ public int? InternalSequenceNumber { get; set; }
+
+ ///
+ /// Numero visible interno del documento, formado desde la serie/talonario interno. Puede diferir del numero fiscal.
+ ///
+ public string? InternalDocumentNumber { get; set; }
+
+ ///
+ /// Tipo comercial interno del documento. Ejemplos: Invoice, DebitNote, CreditNote, CreditInvoice, CreditDebitNote, CreditCreditNote.
+ ///
+ public int DocumentType { get; set; }
+
+ ///
+ /// Tipo de comprobante fiscal AFIP/ARCA previsto para autorizacion futura. Ejemplos: 1, 6, 11, 201, 202, 203.
+ ///
+ public int? FiscalVoucherType { get; set; }
+
+ ///
+ /// Letra fiscal prevista del comprobante: A, B, C u otras segun configuracion fiscal.
+ ///
+ public string? FiscalVoucherLetter { get; set; }
+
+ ///
+ /// Estado comercial interno. Ejemplos: Draft, Validated, Issued, Cancelled. Independiente del estado fiscal.
+ ///
+ public int Status { get; set; }
+
+ ///
+ /// Presupuesto origen opcional. Puede ser NULL para ventas manuales o de escritorio.
+ ///
+ public int? QuoteId { get; set; }
+
+ ///
+ /// Cliente origen de la operacion comercial.
+ ///
+ public int CustomerId { get; set; }
+
+ ///
+ /// Cliente al que se factura realmente. Permite escenarios obra social / particular u otros terceros pagadores.
+ ///
+ public int BillToCustomerId { get; set; }
+
+ ///
+ /// Fecha de emision interna del documento comercial.
+ ///
+ public DateTime? IssueDate { get; set; }
+
+ ///
+ /// Moneda del documento comercial.
+ ///
+ public string Currency { get; set; } = null!;
+
+ ///
+ /// Cotizacion utilizada para la moneda del documento.
+ ///
+ public decimal ExchangeRate { get; set; }
+
+ ///
+ /// Importe neto total del documento.
+ ///
+ public decimal NetAmount { get; set; }
+
+ ///
+ /// Importe total de impuestos del documento.
+ ///
+ public decimal TaxAmount { get; set; }
+
+ ///
+ /// Importe total del documento.
+ ///
+ public decimal TotalAmount { get; set; }
+
+ ///
+ /// Tipo de documento interno asociado opcional, por ejemplo remito, orden de compra o autorizacion. No representa CbtesAsoc fiscal.
+ ///
+ public string? AssociatedDocumentType { get; set; }
+
+ ///
+ /// Numero del documento interno asociado opcional.
+ ///
+ public string? AssociatedDocumentNumber { get; set; }
+
+ ///
+ /// Fecha del documento interno asociado opcional.
+ ///
+ public DateTime? AssociatedDocumentDate { get; set; }
+
+ ///
+ /// Observaciones comerciales del documento.
+ ///
+ public string? Observations { get; set; }
+
+ ///
+ /// Snapshot JSON con informacion extra contextual del documento.
+ ///
+ public string? ExtraInfoJson { get; set; }
+
+ public DateTime Createdat { get; set; }
+
+ public DateTime? Modifiedat { get; set; }
+
+ public virtual PhSCustomer BillToCustomer { get; set; } = null!;
+
+ public virtual PhSCustomer Customer { get; set; } = null!;
+
+ public virtual PhSFormSeries? Formseries { get; set; }
+
+ public virtual ICollection PhSSalesDocumentDetails { get; set; } = new List();
+
+ public virtual PhSSalesFiscalDocument? PhSSalesFiscalDocument { get; set; }
+
+ public virtual ICollection PhSSalesFiscalDocumentAssociations { get; set; } = new List();
+
+ public virtual PhSQuoteHeader? Quote { get; set; }
+}
diff --git a/Models/Models/PhSSalesDocumentDetail.cs b/Models/Models/PhSSalesDocumentDetail.cs
new file mode 100644
index 0000000..be7e604
--- /dev/null
+++ b/Models/Models/PhSSalesDocumentDetail.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+
+namespace Models.Models;
+
+///
+/// Detalles valorizados de documentos comerciales de venta.
+///
+public partial class PhSSalesDocumentDetail
+{
+ public int Id { get; set; }
+
+ ///
+ /// Documento comercial al que pertenece el detalle.
+ ///
+ public int SalesdocumentId { get; set; }
+
+ ///
+ /// Numero de linea dentro del documento.
+ ///
+ public int LineNumber { get; set; }
+
+ ///
+ /// Origen logico del item: Manual, QuoteDetail, Adjustment u otro valor definido por Domain/Core.
+ ///
+ public string OriginType { get; set; } = null!;
+
+ ///
+ /// Identificador generico del origen cuando aplique.
+ ///
+ public int? OriginId { get; set; }
+
+ ///
+ /// Detalle del presupuesto aprobado que origina la linea, cuando exista. Puede ser NULL en ventas manuales.
+ ///
+ public int? QuoteDetailId { get; set; }
+
+ ///
+ /// Producto asociado a la linea, si aplica.
+ ///
+ public int? ProductId { get; set; }
+
+ ///
+ /// Descripcion visible de la linea facturada.
+ ///
+ public string Description { get; set; } = null!;
+
+ ///
+ /// Cantidad facturada.
+ ///
+ public decimal Quantity { get; set; }
+
+ ///
+ /// Precio unitario autorizado o de referencia proveniente del origen comercial.
+ ///
+ public decimal? AuthorizedUnitPrice { get; set; }
+
+ ///
+ /// Importe autorizado o de referencia proveniente del origen comercial.
+ ///
+ public decimal? AuthorizedAmount { get; set; }
+
+ ///
+ /// Porcentaje facturado sobre el origen. Permite facturacion parcial obra social / particular.
+ ///
+ public decimal? BilledPercentage { get; set; }
+
+ ///
+ /// Precio unitario efectivo de la linea del documento.
+ ///
+ public decimal UnitPrice { get; set; }
+
+ ///
+ /// Importe neto de la linea.
+ ///
+ public decimal NetAmount { get; set; }
+
+ ///
+ /// Importe de impuestos de la linea.
+ ///
+ public decimal TaxAmount { get; set; }
+
+ ///
+ /// Importe total de la linea.
+ ///
+ public decimal TotalAmount { get; set; }
+
+ ///
+ /// Snapshot JSON del origen de la linea para trazabilidad historica.
+ ///
+ public string? OriginSnapshotJson { get; set; }
+
+ public DateTime Createdat { get; set; }
+
+ public DateTime? Modifiedat { get; set; }
+
+ public virtual PhSProduct? Product { get; set; }
+
+ public virtual PhSQuoteDetail? QuoteDetail { get; set; }
+
+ public virtual PhSSalesDocument Salesdocument { get; set; } = null!;
+}
diff --git a/Models/Models/PhSSalesFiscalDocument.cs b/Models/Models/PhSSalesFiscalDocument.cs
new file mode 100644
index 0000000..a0fc9cd
--- /dev/null
+++ b/Models/Models/PhSSalesFiscalDocument.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+
+namespace Models.Models;
+
+///
+/// Documento fiscal asociado a un documento comercial de venta. Guarda estado, CAE, numeracion fiscal, request/response e idempotencia ARCA.
+///
+public partial class PhSSalesFiscalDocument
+{
+ public int Id { get; set; }
+
+ ///
+ /// Documento comercial interno vinculado al documento fiscal.
+ ///
+ public int SalesdocumentId { get; set; }
+
+ ///
+ /// Estado fiscal independiente del estado comercial. Ejemplos: None, Pending, Authorized, Rejected, Error, PendingReconciliation.
+ ///
+ public int FiscalStatus { get; set; }
+
+ ///
+ /// Ambiente fiscal usado para autorizacion: homologacion, produccion u otro valor definido por configuracion.
+ ///
+ public string Environment { get; set; } = null!;
+
+ ///
+ /// Punto de venta fiscal ARCA/AFIP.
+ ///
+ public short? PointOfSale { get; set; }
+
+ ///
+ /// Tipo de comprobante fiscal ARCA/AFIP utilizado en FECAESolicitar.
+ ///
+ public int? VoucherType { get; set; }
+
+ ///
+ /// Letra fiscal del comprobante autorizado o a autorizar.
+ ///
+ public string? VoucherLetter { get; set; }
+
+ ///
+ /// Numero fiscal del comprobante asignado para ARCA. Se mantiene separado del numero interno.
+ ///
+ public int? VoucherNumber { get; set; }
+
+ ///
+ /// Codigo de autorizacion electronico obtenido desde ARCA/AFIP.
+ ///
+ public string? Cae { get; set; }
+
+ ///
+ /// Fecha de vencimiento del CAE.
+ ///
+ public DateTime? CaeExpirationDate { get; set; }
+
+ ///
+ /// Huella de idempotencia fiscal para evitar duplicacion de solicitudes ante ARCA.
+ ///
+ public string? RequestFingerprint { get; set; }
+
+ ///
+ /// Indica si el resultado fiscal es final y no debe volver a mutar salvo procesos controlados de auditoria.
+ ///
+ public bool IsFinal { get; set; }
+
+ ///
+ /// Payload JSON enviado a ARCA/AFIP.
+ ///
+ public string? ArcaRequestPayloadJson { get; set; }
+
+ ///
+ /// Payload JSON recibido desde ARCA/AFIP.
+ ///
+ public string? ArcaResponsePayloadJson { get; set; }
+
+ ///
+ /// Errores devueltos por ARCA/AFIP serializados como JSON.
+ ///
+ public string? ErrorsJson { get; set; }
+
+ ///
+ /// Eventos devueltos por ARCA/AFIP serializados como JSON.
+ ///
+ public string? EventsJson { get; set; }
+
+ ///
+ /// Observaciones devueltas por ARCA/AFIP serializadas como JSON.
+ ///
+ public string? ObservationsJson { get; set; }
+
+ ///
+ /// Fecha/hora UTC del intento de autorizacion fiscal.
+ ///
+ public DateTime? AttemptedAtUtc { get; set; }
+
+ ///
+ /// Fecha/hora UTC de finalizacion del flujo fiscal.
+ ///
+ public DateTime? CompletedAtUtc { get; set; }
+
+ ///
+ /// Indica que el documento fiscal fue resuelto mediante reconciliacion posterior a timeout o resultado ambiguo.
+ ///
+ public bool ReconciledAfterTimeout { get; set; }
+
+ public DateTime Createdat { get; set; }
+
+ public DateTime? Modifiedat { get; set; }
+
+ public virtual ICollection PhSSalesFiscalDocumentAssociations { get; set; } = new List();
+
+ public virtual PhSSalesDocument Salesdocument { get; set; } = null!;
+}
diff --git a/Models/Models/PhSSalesFiscalDocumentAssociation.cs b/Models/Models/PhSSalesFiscalDocumentAssociation.cs
new file mode 100644
index 0000000..9c7f52e
--- /dev/null
+++ b/Models/Models/PhSSalesFiscalDocumentAssociation.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+
+namespace Models.Models;
+
+///
+/// Comprobantes fiscales asociados enviados como CbtesAsoc. Aplica a notas de credito, notas de debito, NCE/NDE y casos FCE donde corresponda.
+///
+public partial class PhSSalesFiscalDocumentAssociation
+{
+ public int Id { get; set; }
+
+ ///
+ /// Documento fiscal que contiene esta asociacion.
+ ///
+ public int SalesfiscaldocumentId { get; set; }
+
+ ///
+ /// Documento comercial interno asociado, si existe dentro de PhronCare.
+ ///
+ public int? AssociatedSalesdocumentId { get; set; }
+
+ ///
+ /// Tipo fiscal ARCA/AFIP del comprobante asociado.
+ ///
+ public int AssociatedVoucherType { get; set; }
+
+ ///
+ /// Punto de venta fiscal del comprobante asociado.
+ ///
+ public short AssociatedPointOfSale { get; set; }
+
+ ///
+ /// Numero fiscal del comprobante asociado.
+ ///
+ public int AssociatedVoucherNumber { get; set; }
+
+ ///
+ /// CUIT emisor del comprobante asociado, cuando sea requerido por ARCA.
+ ///
+ public string? AssociatedVoucherCuit { get; set; }
+
+ ///
+ /// Fecha del comprobante fiscal asociado.
+ ///
+ public DateTime? AssociatedVoucherDate { get; set; }
+
+ public DateTime Createdat { get; set; }
+
+ public virtual PhSSalesDocument? AssociatedSalesdocument { get; set; }
+
+ public virtual PhSSalesFiscalDocument Salesfiscaldocument { get; set; } = null!;
+}
diff --git a/Models/Models/PhronCareOperationsHubContext.cs b/Models/Models/PhronCareOperationsHubContext.cs
index 9cab453..3817660 100644
--- a/Models/Models/PhronCareOperationsHubContext.cs
+++ b/Models/Models/PhronCareOperationsHubContext.cs
@@ -97,6 +97,14 @@ public partial class PhronCareOperationsHubContext : DbContext
public virtual DbSet PhSQuoteTaxes { get; set; }
+ public virtual DbSet PhSSalesDocuments { get; set; }
+
+ public virtual DbSet PhSSalesDocumentDetails { get; set; }
+
+ public virtual DbSet PhSSalesFiscalDocuments { get; set; }
+
+ public virtual DbSet PhSSalesFiscalDocumentAssociations { get; set; }
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseCollation("Modern_Spanish_CI_AS");
@@ -1862,6 +1870,369 @@ public partial class PhronCareOperationsHubContext : DbContext
.HasConstraintName("FK_PhS_QuoteTaxes_QuoteHeaders");
});
+ modelBuilder.Entity(entity =>
+ {
+ entity.ToTable("PhS_SalesDocuments", tb => tb.HasComment("Documentos comerciales internos de venta: facturas, notas de debito, notas de credito, FCE, NDE y NCE. Mantiene la emision interna separada de la autorizacion fiscal ARCA."));
+
+ entity.HasIndex(e => e.BillToCustomerId, "IX_PhS_SalesDocuments_BillToCustomer");
+
+ entity.HasIndex(e => e.CustomerId, "IX_PhS_SalesDocuments_Customer");
+
+ entity.HasIndex(e => e.FormseriesId, "IX_PhS_SalesDocuments_FormSeries");
+
+ entity.HasIndex(e => e.IssueDate, "IX_PhS_SalesDocuments_IssueDate");
+
+ entity.HasIndex(e => e.QuoteId, "IX_PhS_SalesDocuments_Quote");
+
+ entity.HasIndex(e => e.Status, "IX_PhS_SalesDocuments_Status");
+
+ entity.HasIndex(e => new { e.FormseriesId, e.InternalDocumentNumber }, "UX_PhS_SalesDocuments_InternalDocumentNumber")
+ .IsUnique()
+ .HasFilter("([formseries_id] IS NOT NULL AND [internal_document_number] IS NOT NULL)");
+
+ entity.HasIndex(e => new { e.FormseriesId, e.InternalSequenceNumber }, "UX_PhS_SalesDocuments_InternalNumber")
+ .IsUnique()
+ .HasFilter("([formseries_id] IS NOT NULL AND [internal_sequence_number] IS NOT NULL)");
+
+ entity.Property(e => e.Id)
+ .HasComment("Identificador interno del documento comercial.")
+ .HasColumnName("id");
+ entity.Property(e => e.AssociatedDocumentDate)
+ .HasComment("Fecha del documento interno asociado opcional.")
+ .HasColumnType("datetime")
+ .HasColumnName("associated_document_date");
+ entity.Property(e => e.AssociatedDocumentNumber)
+ .HasMaxLength(50)
+ .HasComment("Numero del documento interno asociado opcional.")
+ .HasColumnName("associated_document_number");
+ entity.Property(e => e.AssociatedDocumentType)
+ .HasMaxLength(50)
+ .HasComment("Tipo de documento interno asociado opcional, por ejemplo remito, orden de compra o autorizacion. No representa CbtesAsoc fiscal.")
+ .HasColumnName("associated_document_type");
+ entity.Property(e => e.BillToCustomerId)
+ .HasComment("Cliente al que se factura realmente. Permite escenarios obra social / particular u otros terceros pagadores.")
+ .HasColumnName("bill_to_customer_id");
+ entity.Property(e => e.Createdat)
+ .HasDefaultValueSql("(getdate())")
+ .HasColumnType("datetime")
+ .HasColumnName("createdat");
+ entity.Property(e => e.Currency)
+ .HasMaxLength(10)
+ .HasDefaultValue("ARS")
+ .HasComment("Moneda del documento comercial.")
+ .HasColumnName("currency");
+ entity.Property(e => e.CustomerId)
+ .HasComment("Cliente origen de la operacion comercial.")
+ .HasColumnName("customer_id");
+ entity.Property(e => e.DocumentType)
+ .HasComment("Tipo comercial interno del documento. Ejemplos: Invoice, DebitNote, CreditNote, CreditInvoice, CreditDebitNote, CreditCreditNote.")
+ .HasColumnName("document_type");
+ entity.Property(e => e.ExchangeRate)
+ .HasDefaultValue(1m)
+ .HasComment("Cotizacion utilizada para la moneda del documento.")
+ .HasColumnType("decimal(18, 6)")
+ .HasColumnName("exchange_rate");
+ entity.Property(e => e.ExtraInfoJson)
+ .HasComment("Snapshot JSON con informacion extra contextual del documento.")
+ .HasColumnName("extra_info_json");
+ entity.Property(e => e.FiscalVoucherLetter)
+ .HasMaxLength(5)
+ .HasComment("Letra fiscal prevista del comprobante: A, B, C u otras segun configuracion fiscal.")
+ .HasColumnName("fiscal_voucher_letter");
+ entity.Property(e => e.FiscalVoucherType)
+ .HasComment("Tipo de comprobante fiscal AFIP/ARCA previsto para autorizacion futura. Ejemplos: 1, 6, 11, 201, 202, 203.")
+ .HasColumnName("fiscal_voucher_type");
+ entity.Property(e => e.FormseriesId)
+ .HasComment("Talonario/serie interna existente en PhronCare. Reutiliza PhS_FormSeries para numeracion interna.")
+ .HasColumnName("formseries_id");
+ entity.Property(e => e.InternalDocumentNumber)
+ .HasMaxLength(50)
+ .HasComment("Numero visible interno del documento, formado desde la serie/talonario interno. Puede diferir del numero fiscal.")
+ .HasColumnName("internal_document_number");
+ entity.Property(e => e.InternalSequenceNumber)
+ .HasComment("Numero secuencial interno asignado al emitir internamente el documento. No corresponde al numero fiscal ARCA.")
+ .HasColumnName("internal_sequence_number");
+ entity.Property(e => e.IssueDate)
+ .HasComment("Fecha de emision interna del documento comercial.")
+ .HasColumnType("datetime")
+ .HasColumnName("issue_date");
+ entity.Property(e => e.Modifiedat)
+ .HasColumnType("datetime")
+ .HasColumnName("modifiedat");
+ entity.Property(e => e.NetAmount)
+ .HasComment("Importe neto total del documento.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("net_amount");
+ entity.Property(e => e.Observations)
+ .HasComment("Observaciones comerciales del documento.")
+ .HasColumnName("observations");
+ entity.Property(e => e.QuoteId)
+ .HasComment("Presupuesto origen opcional. Puede ser NULL para ventas manuales o de escritorio.")
+ .HasColumnName("quote_id");
+ entity.Property(e => e.Status)
+ .HasComment("Estado comercial interno. Ejemplos: Draft, Validated, Issued, Cancelled. Independiente del estado fiscal.")
+ .HasColumnName("status");
+ entity.Property(e => e.TaxAmount)
+ .HasComment("Importe total de impuestos del documento.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("tax_amount");
+ entity.Property(e => e.TotalAmount)
+ .HasComment("Importe total del documento.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("total_amount");
+
+ entity.HasOne(d => d.BillToCustomer).WithMany(p => p.PhSSalesDocumentBillToCustomers)
+ .HasForeignKey(d => d.BillToCustomerId)
+ .OnDelete(DeleteBehavior.ClientSetNull)
+ .HasConstraintName("FK_PhS_SalesDocuments_BillToCustomers");
+
+ entity.HasOne(d => d.Customer).WithMany(p => p.PhSSalesDocumentCustomers)
+ .HasForeignKey(d => d.CustomerId)
+ .OnDelete(DeleteBehavior.ClientSetNull)
+ .HasConstraintName("FK_PhS_SalesDocuments_Customers");
+
+ entity.HasOne(d => d.Formseries).WithMany(p => p.PhSSalesDocuments)
+ .HasForeignKey(d => d.FormseriesId)
+ .HasConstraintName("FK_PhS_SalesDocuments_FormSeries");
+
+ entity.HasOne(d => d.Quote).WithMany(p => p.PhSSalesDocuments)
+ .HasForeignKey(d => d.QuoteId)
+ .HasConstraintName("FK_PhS_SalesDocuments_QuoteHeaders");
+ });
+
+ modelBuilder.Entity(entity =>
+ {
+ entity.ToTable("PhS_SalesDocumentDetails", tb => tb.HasComment("Detalles valorizados de documentos comerciales de venta."));
+
+ entity.HasIndex(e => e.SalesdocumentId, "IX_PhS_SalesDocumentDetails_Document");
+
+ entity.HasIndex(e => e.QuoteDetailId, "IX_PhS_SalesDocumentDetails_QuoteDetail");
+
+ entity.HasIndex(e => new { e.SalesdocumentId, e.LineNumber }, "UX_PhS_SalesDocumentDetails_Line").IsUnique();
+
+ entity.Property(e => e.Id).HasColumnName("id");
+ entity.Property(e => e.AuthorizedAmount)
+ .HasComment("Importe autorizado o de referencia proveniente del origen comercial.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("authorized_amount");
+ entity.Property(e => e.AuthorizedUnitPrice)
+ .HasComment("Precio unitario autorizado o de referencia proveniente del origen comercial.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("authorized_unit_price");
+ entity.Property(e => e.BilledPercentage)
+ .HasComment("Porcentaje facturado sobre el origen. Permite facturacion parcial obra social / particular.")
+ .HasColumnType("decimal(9, 4)")
+ .HasColumnName("billed_percentage");
+ entity.Property(e => e.Createdat)
+ .HasDefaultValueSql("(getdate())")
+ .HasColumnType("datetime")
+ .HasColumnName("createdat");
+ entity.Property(e => e.Description)
+ .HasMaxLength(255)
+ .HasComment("Descripcion visible de la linea facturada.")
+ .HasColumnName("description");
+ entity.Property(e => e.LineNumber)
+ .HasComment("Numero de linea dentro del documento.")
+ .HasColumnName("line_number");
+ entity.Property(e => e.Modifiedat)
+ .HasColumnType("datetime")
+ .HasColumnName("modifiedat");
+ entity.Property(e => e.NetAmount)
+ .HasComment("Importe neto de la linea.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("net_amount");
+ entity.Property(e => e.OriginId)
+ .HasComment("Identificador generico del origen cuando aplique.")
+ .HasColumnName("origin_id");
+ entity.Property(e => e.OriginSnapshotJson)
+ .HasComment("Snapshot JSON del origen de la linea para trazabilidad historica.")
+ .HasColumnName("origin_snapshot_json");
+ entity.Property(e => e.OriginType)
+ .HasMaxLength(50)
+ .HasComment("Origen logico del item: Manual, QuoteDetail, Adjustment u otro valor definido por Domain/Core.")
+ .HasColumnName("origin_type");
+ entity.Property(e => e.ProductId)
+ .HasComment("Producto asociado a la linea, si aplica.")
+ .HasColumnName("product_id");
+ entity.Property(e => e.Quantity)
+ .HasComment("Cantidad facturada.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("quantity");
+ entity.Property(e => e.QuoteDetailId)
+ .HasComment("Detalle del presupuesto aprobado que origina la linea, cuando exista. Puede ser NULL en ventas manuales.")
+ .HasColumnName("quote_detail_id");
+ entity.Property(e => e.SalesdocumentId)
+ .HasComment("Documento comercial al que pertenece el detalle.")
+ .HasColumnName("salesdocument_id");
+ entity.Property(e => e.TaxAmount)
+ .HasComment("Importe de impuestos de la linea.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("tax_amount");
+ entity.Property(e => e.TotalAmount)
+ .HasComment("Importe total de la linea.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("total_amount");
+ entity.Property(e => e.UnitPrice)
+ .HasComment("Precio unitario efectivo de la linea del documento.")
+ .HasColumnType("decimal(18, 2)")
+ .HasColumnName("unit_price");
+
+ entity.HasOne(d => d.Product).WithMany(p => p.PhSSalesDocumentDetails)
+ .HasForeignKey(d => d.ProductId)
+ .HasConstraintName("FK_PhS_SalesDocumentDetails_Products");
+
+ entity.HasOne(d => d.QuoteDetail).WithMany(p => p.PhSSalesDocumentDetails)
+ .HasForeignKey(d => d.QuoteDetailId)
+ .HasConstraintName("FK_PhS_SalesDocumentDetails_QuoteDetails");
+
+ entity.HasOne(d => d.Salesdocument).WithMany(p => p.PhSSalesDocumentDetails)
+ .HasForeignKey(d => d.SalesdocumentId)
+ .OnDelete(DeleteBehavior.ClientSetNull)
+ .HasConstraintName("FK_PhS_SalesDocumentDetails_SalesDocuments");
+ });
+
+ modelBuilder.Entity(entity =>
+ {
+ entity.ToTable("PhS_SalesFiscalDocuments", tb => tb.HasComment("Documento fiscal asociado a un documento comercial de venta. Guarda estado, CAE, numeracion fiscal, request/response e idempotencia ARCA."));
+
+ entity.HasIndex(e => e.SalesdocumentId, "IX_PhS_SalesFiscalDocuments_Document");
+
+ entity.HasIndex(e => e.SalesdocumentId, "UX_PhS_SalesFiscalDocuments_Document").IsUnique();
+
+ entity.HasIndex(e => new { e.Environment, e.PointOfSale, e.VoucherType, e.VoucherNumber }, "UX_PhS_SalesFiscalDocuments_FiscalNumber")
+ .IsUnique()
+ .HasFilter("([point_of_sale] IS NOT NULL AND [voucher_type] IS NOT NULL AND [voucher_number] IS NOT NULL)");
+
+ entity.HasIndex(e => new { e.Environment, e.PointOfSale, e.VoucherType, e.RequestFingerprint }, "UX_PhS_SalesFiscalDocuments_Idempotency")
+ .IsUnique()
+ .HasFilter("([point_of_sale] IS NOT NULL AND [voucher_type] IS NOT NULL AND [request_fingerprint] IS NOT NULL)");
+
+ entity.Property(e => e.Id).HasColumnName("id");
+ entity.Property(e => e.ArcaRequestPayloadJson)
+ .HasComment("Payload JSON enviado a ARCA/AFIP.")
+ .HasColumnName("arca_request_payload_json");
+ entity.Property(e => e.ArcaResponsePayloadJson)
+ .HasComment("Payload JSON recibido desde ARCA/AFIP.")
+ .HasColumnName("arca_response_payload_json");
+ entity.Property(e => e.AttemptedAtUtc)
+ .HasComment("Fecha/hora UTC del intento de autorizacion fiscal.")
+ .HasColumnType("datetime")
+ .HasColumnName("attempted_at_utc");
+ entity.Property(e => e.Cae)
+ .HasMaxLength(20)
+ .HasComment("Codigo de autorizacion electronico obtenido desde ARCA/AFIP.")
+ .HasColumnName("cae");
+ entity.Property(e => e.CaeExpirationDate)
+ .HasComment("Fecha de vencimiento del CAE.")
+ .HasColumnType("datetime")
+ .HasColumnName("cae_expiration_date");
+ entity.Property(e => e.CompletedAtUtc)
+ .HasComment("Fecha/hora UTC de finalizacion del flujo fiscal.")
+ .HasColumnType("datetime")
+ .HasColumnName("completed_at_utc");
+ entity.Property(e => e.Createdat)
+ .HasDefaultValueSql("(getdate())")
+ .HasColumnType("datetime")
+ .HasColumnName("createdat");
+ entity.Property(e => e.Environment)
+ .HasMaxLength(20)
+ .HasComment("Ambiente fiscal usado para autorizacion: homologacion, produccion u otro valor definido por configuracion.")
+ .HasColumnName("environment");
+ entity.Property(e => e.ErrorsJson)
+ .HasComment("Errores devueltos por ARCA/AFIP serializados como JSON.")
+ .HasColumnName("errors_json");
+ entity.Property(e => e.EventsJson)
+ .HasComment("Eventos devueltos por ARCA/AFIP serializados como JSON.")
+ .HasColumnName("events_json");
+ entity.Property(e => e.FiscalStatus)
+ .HasComment("Estado fiscal independiente del estado comercial. Ejemplos: None, Pending, Authorized, Rejected, Error, PendingReconciliation.")
+ .HasColumnName("fiscal_status");
+ entity.Property(e => e.IsFinal)
+ .HasComment("Indica si el resultado fiscal es final y no debe volver a mutar salvo procesos controlados de auditoria.")
+ .HasColumnName("is_final");
+ entity.Property(e => e.Modifiedat)
+ .HasColumnType("datetime")
+ .HasColumnName("modifiedat");
+ entity.Property(e => e.ObservationsJson)
+ .HasComment("Observaciones devueltas por ARCA/AFIP serializadas como JSON.")
+ .HasColumnName("observations_json");
+ entity.Property(e => e.PointOfSale)
+ .HasComment("Punto de venta fiscal ARCA/AFIP.")
+ .HasColumnName("point_of_sale");
+ entity.Property(e => e.ReconciledAfterTimeout)
+ .HasComment("Indica que el documento fiscal fue resuelto mediante reconciliacion posterior a timeout o resultado ambiguo.")
+ .HasColumnName("reconciled_after_timeout");
+ entity.Property(e => e.RequestFingerprint)
+ .HasMaxLength(255)
+ .HasComment("Huella de idempotencia fiscal para evitar duplicacion de solicitudes ante ARCA.")
+ .HasColumnName("request_fingerprint");
+ entity.Property(e => e.SalesdocumentId)
+ .HasComment("Documento comercial interno vinculado al documento fiscal.")
+ .HasColumnName("salesdocument_id");
+ entity.Property(e => e.VoucherLetter)
+ .HasMaxLength(5)
+ .HasComment("Letra fiscal del comprobante autorizado o a autorizar.")
+ .HasColumnName("voucher_letter");
+ entity.Property(e => e.VoucherNumber)
+ .HasComment("Numero fiscal del comprobante asignado para ARCA. Se mantiene separado del numero interno.")
+ .HasColumnName("voucher_number");
+ entity.Property(e => e.VoucherType)
+ .HasComment("Tipo de comprobante fiscal ARCA/AFIP utilizado en FECAESolicitar.")
+ .HasColumnName("voucher_type");
+
+ entity.HasOne(d => d.Salesdocument).WithOne(p => p.PhSSalesFiscalDocument)
+ .HasForeignKey(d => d.SalesdocumentId)
+ .OnDelete(DeleteBehavior.ClientSetNull)
+ .HasConstraintName("FK_PhS_SalesFiscalDocuments_SalesDocuments");
+ });
+
+ modelBuilder.Entity(entity =>
+ {
+ entity.ToTable("PhS_SalesFiscalDocumentAssociations", tb => tb.HasComment("Comprobantes fiscales asociados enviados como CbtesAsoc. Aplica a notas de credito, notas de debito, NCE/NDE y casos FCE donde corresponda."));
+
+ entity.HasIndex(e => e.AssociatedSalesdocumentId, "IX_PhS_SalesFiscalDocumentAssociations_AssociatedDocument");
+
+ entity.HasIndex(e => e.SalesfiscaldocumentId, "IX_PhS_SalesFiscalDocumentAssociations_FiscalDocument");
+
+ entity.Property(e => e.Id).HasColumnName("id");
+ entity.Property(e => e.AssociatedPointOfSale)
+ .HasComment("Punto de venta fiscal del comprobante asociado.")
+ .HasColumnName("associated_point_of_sale");
+ entity.Property(e => e.AssociatedSalesdocumentId)
+ .HasComment("Documento comercial interno asociado, si existe dentro de PhronCare.")
+ .HasColumnName("associated_salesdocument_id");
+ entity.Property(e => e.AssociatedVoucherCuit)
+ .HasMaxLength(20)
+ .HasComment("CUIT emisor del comprobante asociado, cuando sea requerido por ARCA.")
+ .HasColumnName("associated_voucher_cuit");
+ entity.Property(e => e.AssociatedVoucherDate)
+ .HasComment("Fecha del comprobante fiscal asociado.")
+ .HasColumnType("datetime")
+ .HasColumnName("associated_voucher_date");
+ entity.Property(e => e.AssociatedVoucherNumber)
+ .HasComment("Numero fiscal del comprobante asociado.")
+ .HasColumnName("associated_voucher_number");
+ entity.Property(e => e.AssociatedVoucherType)
+ .HasComment("Tipo fiscal ARCA/AFIP del comprobante asociado.")
+ .HasColumnName("associated_voucher_type");
+ entity.Property(e => e.Createdat)
+ .HasDefaultValueSql("(getdate())")
+ .HasColumnType("datetime")
+ .HasColumnName("createdat");
+ entity.Property(e => e.SalesfiscaldocumentId)
+ .HasComment("Documento fiscal que contiene esta asociacion.")
+ .HasColumnName("salesfiscaldocument_id");
+
+ entity.HasOne(d => d.AssociatedSalesdocument).WithMany(p => p.PhSSalesFiscalDocumentAssociations)
+ .HasForeignKey(d => d.AssociatedSalesdocumentId)
+ .HasConstraintName("FK_PhS_SalesFiscalDocumentAssociations_SalesDocuments");
+
+ entity.HasOne(d => d.Salesfiscaldocument).WithMany(p => p.PhSSalesFiscalDocumentAssociations)
+ .HasForeignKey(d => d.SalesfiscaldocumentId)
+ .OnDelete(DeleteBehavior.ClientSetNull)
+ .HasConstraintName("FK_PhS_SalesFiscalDocumentAssociations_FiscalDocuments");
+ });
+
OnModelCreatingPartial(modelBuilder);
}
diff --git a/docs/sql/55_sales_document_model_with_descriptions.sql b/docs/sql/55_sales_document_model_with_descriptions.sql
new file mode 100644
index 0000000..b46571d
--- /dev/null
+++ b/docs/sql/55_sales_document_model_with_descriptions.sql
@@ -0,0 +1,620 @@
+-- ============================================================
+-- Story #55 - Sales Document persistence model
+-- ============================================================
+-- Objetivo:
+-- Crear la base de persistencia para comprobantes de venta:
+-- - Facturas internas
+-- - Notas de debito / credito
+-- - FCE / NDE / NCE futuras
+-- - Autorizacion fiscal ARCA futura
+--
+-- Decisiones:
+-- - No se crean nuevos talonarios.
+-- - Se reutiliza PhS_FormSeries / PhS_FormSeriesNextNumber.
+-- - La numeracion interna queda separada de la numeracion fiscal.
+-- - El documento comercial queda separado del documento fiscal ARCA.
+-- - El remito NO es obligatorio ni se modela como dependencia directa.
+-- - El vinculo principal con origen comercial se mantiene por quote_detail_id.
+-- ============================================================
+
+IF OBJECT_ID('dbo.PhS_SalesFiscalDocumentAssociations', 'U') IS NOT NULL
+ DROP TABLE dbo.PhS_SalesFiscalDocumentAssociations;
+
+IF OBJECT_ID('dbo.PhS_SalesFiscalDocuments', 'U') IS NOT NULL
+ DROP TABLE dbo.PhS_SalesFiscalDocuments;
+
+IF OBJECT_ID('dbo.PhS_SalesDocumentDetails', 'U') IS NOT NULL
+ DROP TABLE dbo.PhS_SalesDocumentDetails;
+
+IF OBJECT_ID('dbo.PhS_SalesDocuments', 'U') IS NOT NULL
+ DROP TABLE dbo.PhS_SalesDocuments;
+
+-- ============================================================
+-- Documento comercial interno
+-- ============================================================
+CREATE TABLE dbo.PhS_SalesDocuments
+(
+ id INT IDENTITY(1,1) NOT NULL,
+
+ formseries_id INT NULL,
+
+ internal_sequence_number INT NULL,
+ internal_document_number NVARCHAR(50) NULL,
+
+ document_type INT NOT NULL,
+
+ fiscal_voucher_type INT NULL,
+ fiscal_voucher_letter NVARCHAR(5) NULL,
+
+ status INT NOT NULL CONSTRAINT DF_PhS_SalesDocuments_status DEFAULT (0),
+
+ quote_id INT NULL,
+
+ customer_id INT NOT NULL,
+ bill_to_customer_id INT NOT NULL,
+
+ issue_date DATETIME NULL,
+
+ currency NVARCHAR(10) NOT NULL CONSTRAINT DF_PhS_SalesDocuments_currency DEFAULT ('ARS'),
+ exchange_rate DECIMAL(18,6) NOT NULL CONSTRAINT DF_PhS_SalesDocuments_exchange_rate DEFAULT (1),
+
+ net_amount DECIMAL(18,2) NOT NULL CONSTRAINT DF_PhS_SalesDocuments_net_amount DEFAULT (0),
+ tax_amount DECIMAL(18,2) NOT NULL CONSTRAINT DF_PhS_SalesDocuments_tax_amount DEFAULT (0),
+ total_amount DECIMAL(18,2) NOT NULL CONSTRAINT DF_PhS_SalesDocuments_total_amount DEFAULT (0),
+
+ associated_document_type NVARCHAR(50) NULL,
+ associated_document_number NVARCHAR(50) NULL,
+ associated_document_date DATETIME NULL,
+
+ observations NVARCHAR(MAX) NULL,
+ extra_info_json NVARCHAR(MAX) NULL,
+
+ createdat DATETIME NOT NULL CONSTRAINT DF_PhS_SalesDocuments_createdat DEFAULT (GETDATE()),
+ modifiedat DATETIME NULL,
+
+ CONSTRAINT PK_PhS_SalesDocuments PRIMARY KEY (id),
+
+ CONSTRAINT FK_PhS_SalesDocuments_FormSeries
+ FOREIGN KEY (formseries_id)
+ REFERENCES dbo.PhS_FormSeries(id),
+
+ CONSTRAINT FK_PhS_SalesDocuments_QuoteHeaders
+ FOREIGN KEY (quote_id)
+ REFERENCES dbo.PhS_QuoteHeaders(id),
+
+ CONSTRAINT FK_PhS_SalesDocuments_Customers
+ FOREIGN KEY (customer_id)
+ REFERENCES dbo.PhS_Customers(id),
+
+ CONSTRAINT FK_PhS_SalesDocuments_BillToCustomers
+ FOREIGN KEY (bill_to_customer_id)
+ REFERENCES dbo.PhS_Customers(id)
+);
+
+-- ============================================================
+-- Detalle comercial
+-- ============================================================
+CREATE TABLE dbo.PhS_SalesDocumentDetails
+(
+ id INT IDENTITY(1,1) NOT NULL,
+ salesdocument_id INT NOT NULL,
+
+ line_number INT NOT NULL,
+
+ origin_type NVARCHAR(50) NOT NULL,
+ origin_id INT NULL,
+
+ quote_detail_id INT NULL,
+
+ product_id INT NULL,
+ description NVARCHAR(255) NOT NULL,
+
+ quantity DECIMAL(18,2) NOT NULL,
+
+ authorized_unit_price DECIMAL(18,2) NULL,
+ authorized_amount DECIMAL(18,2) NULL,
+
+ billed_percentage DECIMAL(9,4) NULL,
+
+ unit_price DECIMAL(18,2) NOT NULL,
+ net_amount DECIMAL(18,2) NOT NULL,
+ tax_amount DECIMAL(18,2) NOT NULL,
+ total_amount DECIMAL(18,2) NOT NULL,
+
+ origin_snapshot_json NVARCHAR(MAX) NULL,
+
+ createdat DATETIME NOT NULL CONSTRAINT DF_PhS_SalesDocumentDetails_createdat DEFAULT (GETDATE()),
+ modifiedat DATETIME NULL,
+
+ CONSTRAINT PK_PhS_SalesDocumentDetails PRIMARY KEY (id),
+
+ CONSTRAINT FK_PhS_SalesDocumentDetails_SalesDocuments
+ FOREIGN KEY (salesdocument_id)
+ REFERENCES dbo.PhS_SalesDocuments(id),
+
+ CONSTRAINT FK_PhS_SalesDocumentDetails_QuoteDetails
+ FOREIGN KEY (quote_detail_id)
+ REFERENCES dbo.PhS_QuoteDetails(id),
+
+ CONSTRAINT FK_PhS_SalesDocumentDetails_Products
+ FOREIGN KEY (product_id)
+ REFERENCES dbo.PhS_Products(id)
+);
+
+-- ============================================================
+-- Documento fiscal ARCA / AFIP
+-- ============================================================
+CREATE TABLE dbo.PhS_SalesFiscalDocuments
+(
+ id INT IDENTITY(1,1) NOT NULL,
+ salesdocument_id INT NOT NULL,
+
+ fiscal_status INT NOT NULL CONSTRAINT DF_PhS_SalesFiscalDocuments_fiscal_status DEFAULT (0),
+
+ environment NVARCHAR(20) NOT NULL,
+
+ point_of_sale SMALLINT NULL,
+ voucher_type INT NULL,
+ voucher_letter NVARCHAR(5) NULL,
+ voucher_number INT NULL,
+
+ cae NVARCHAR(20) NULL,
+ cae_expiration_date DATETIME NULL,
+
+ request_fingerprint NVARCHAR(255) NULL,
+ is_final BIT NOT NULL CONSTRAINT DF_PhS_SalesFiscalDocuments_is_final DEFAULT (0),
+
+ arca_request_payload_json NVARCHAR(MAX) NULL,
+ arca_response_payload_json NVARCHAR(MAX) NULL,
+ errors_json NVARCHAR(MAX) NULL,
+ events_json NVARCHAR(MAX) NULL,
+ observations_json NVARCHAR(MAX) NULL,
+
+ attempted_at_utc DATETIME NULL,
+ completed_at_utc DATETIME NULL,
+ reconciled_after_timeout BIT NOT NULL CONSTRAINT DF_PhS_SalesFiscalDocuments_reconciled DEFAULT (0),
+
+ createdat DATETIME NOT NULL CONSTRAINT DF_PhS_SalesFiscalDocuments_createdat DEFAULT (GETDATE()),
+ modifiedat DATETIME NULL,
+
+ CONSTRAINT PK_PhS_SalesFiscalDocuments PRIMARY KEY (id),
+
+ CONSTRAINT FK_PhS_SalesFiscalDocuments_SalesDocuments
+ FOREIGN KEY (salesdocument_id)
+ REFERENCES dbo.PhS_SalesDocuments(id)
+);
+
+-- ============================================================
+-- Asociaciones fiscales reales: CbtesAsoc
+-- ============================================================
+CREATE TABLE dbo.PhS_SalesFiscalDocumentAssociations
+(
+ id INT IDENTITY(1,1) NOT NULL,
+ salesfiscaldocument_id INT NOT NULL,
+
+ associated_salesdocument_id INT NULL,
+
+ associated_voucher_type INT NOT NULL,
+ associated_point_of_sale SMALLINT NOT NULL,
+ associated_voucher_number INT NOT NULL,
+ associated_voucher_cuit NVARCHAR(20) NULL,
+ associated_voucher_date DATETIME NULL,
+
+ createdat DATETIME NOT NULL CONSTRAINT DF_PhS_SalesFiscalDocumentAssociations_createdat DEFAULT (GETDATE()),
+
+ CONSTRAINT PK_PhS_SalesFiscalDocumentAssociations PRIMARY KEY (id),
+
+ CONSTRAINT FK_PhS_SalesFiscalDocumentAssociations_FiscalDocuments
+ FOREIGN KEY (salesfiscaldocument_id)
+ REFERENCES dbo.PhS_SalesFiscalDocuments(id),
+
+ CONSTRAINT FK_PhS_SalesFiscalDocumentAssociations_SalesDocuments
+ FOREIGN KEY (associated_salesdocument_id)
+ REFERENCES dbo.PhS_SalesDocuments(id)
+);
+
+-- ============================================================
+-- Indices
+-- ============================================================
+
+CREATE INDEX IX_PhS_SalesDocuments_FormSeries
+ ON dbo.PhS_SalesDocuments(formseries_id);
+
+CREATE INDEX IX_PhS_SalesDocuments_Quote
+ ON dbo.PhS_SalesDocuments(quote_id);
+
+CREATE INDEX IX_PhS_SalesDocuments_Customer
+ ON dbo.PhS_SalesDocuments(customer_id);
+
+CREATE INDEX IX_PhS_SalesDocuments_BillToCustomer
+ ON dbo.PhS_SalesDocuments(bill_to_customer_id);
+
+CREATE INDEX IX_PhS_SalesDocuments_Status
+ ON dbo.PhS_SalesDocuments(status);
+
+CREATE INDEX IX_PhS_SalesDocuments_IssueDate
+ ON dbo.PhS_SalesDocuments(issue_date);
+
+CREATE UNIQUE INDEX UX_PhS_SalesDocuments_InternalNumber
+ ON dbo.PhS_SalesDocuments(formseries_id, internal_sequence_number)
+ WHERE formseries_id IS NOT NULL
+ AND internal_sequence_number IS NOT NULL;
+
+CREATE UNIQUE INDEX UX_PhS_SalesDocuments_InternalDocumentNumber
+ ON dbo.PhS_SalesDocuments(formseries_id, internal_document_number)
+ WHERE formseries_id IS NOT NULL
+ AND internal_document_number IS NOT NULL;
+
+CREATE INDEX IX_PhS_SalesDocumentDetails_Document
+ ON dbo.PhS_SalesDocumentDetails(salesdocument_id);
+
+CREATE UNIQUE INDEX UX_PhS_SalesDocumentDetails_Line
+ ON dbo.PhS_SalesDocumentDetails(salesdocument_id, line_number);
+
+CREATE INDEX IX_PhS_SalesDocumentDetails_QuoteDetail
+ ON dbo.PhS_SalesDocumentDetails(quote_detail_id);
+
+CREATE INDEX IX_PhS_SalesFiscalDocuments_Document
+ ON dbo.PhS_SalesFiscalDocuments(salesdocument_id);
+
+CREATE UNIQUE INDEX UX_PhS_SalesFiscalDocuments_Document
+ ON dbo.PhS_SalesFiscalDocuments(salesdocument_id);
+
+CREATE UNIQUE INDEX UX_PhS_SalesFiscalDocuments_FiscalNumber
+ ON dbo.PhS_SalesFiscalDocuments(environment, point_of_sale, voucher_type, voucher_number)
+ WHERE point_of_sale IS NOT NULL
+ AND voucher_type IS NOT NULL
+ AND voucher_number IS NOT NULL;
+
+CREATE UNIQUE INDEX UX_PhS_SalesFiscalDocuments_Idempotency
+ ON dbo.PhS_SalesFiscalDocuments(environment, point_of_sale, voucher_type, request_fingerprint)
+ WHERE point_of_sale IS NOT NULL
+ AND voucher_type IS NOT NULL
+ AND request_fingerprint IS NOT NULL;
+
+CREATE INDEX IX_PhS_SalesFiscalDocumentAssociations_FiscalDocument
+ ON dbo.PhS_SalesFiscalDocumentAssociations(salesfiscaldocument_id);
+
+CREATE INDEX IX_PhS_SalesFiscalDocumentAssociations_AssociatedDocument
+ ON dbo.PhS_SalesFiscalDocumentAssociations(associated_salesdocument_id);
+
+-- ============================================================
+-- Extended properties / MS_Description
+-- ============================================================
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Documentos comerciales internos de venta: facturas, notas de debito, notas de credito, FCE, NDE y NCE. Mantiene la emision interna separada de la autorizacion fiscal ARCA.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Identificador interno del documento comercial.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Talonario/serie interna existente en PhronCare. Reutiliza PhS_FormSeries para numeracion interna.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'formseries_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Numero secuencial interno asignado al emitir internamente el documento. No corresponde al numero fiscal ARCA.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'internal_sequence_number';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Numero visible interno del documento, formado desde la serie/talonario interno. Puede diferir del numero fiscal.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'internal_document_number';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Tipo comercial interno del documento. Ejemplos: Invoice, DebitNote, CreditNote, CreditInvoice, CreditDebitNote, CreditCreditNote.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'document_type';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Tipo de comprobante fiscal AFIP/ARCA previsto para autorizacion futura. Ejemplos: 1, 6, 11, 201, 202, 203.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'fiscal_voucher_type';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Letra fiscal prevista del comprobante: A, B, C u otras segun configuracion fiscal.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'fiscal_voucher_letter';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Estado comercial interno. Ejemplos: Draft, Validated, Issued, Cancelled. Independiente del estado fiscal.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'status';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Presupuesto origen opcional. Puede ser NULL para ventas manuales o de escritorio.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'quote_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Cliente origen de la operacion comercial.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'customer_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Cliente al que se factura realmente. Permite escenarios obra social / particular u otros terceros pagadores.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'bill_to_customer_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Fecha de emision interna del documento comercial.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'issue_date';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Moneda del documento comercial.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'currency';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Cotizacion utilizada para la moneda del documento.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'exchange_rate';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Importe neto total del documento.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'net_amount';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Importe total de impuestos del documento.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'tax_amount';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Importe total del documento.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'total_amount';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Tipo de documento interno asociado opcional, por ejemplo remito, orden de compra o autorizacion. No representa CbtesAsoc fiscal.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'associated_document_type';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Numero del documento interno asociado opcional.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'associated_document_number';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Fecha del documento interno asociado opcional.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'associated_document_date';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Observaciones comerciales del documento.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'observations';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Snapshot JSON con informacion extra contextual del documento.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocuments',
+ @level2type=N'COLUMN', @level2name=N'extra_info_json';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Detalles valorizados de documentos comerciales de venta.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Documento comercial al que pertenece el detalle.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'salesdocument_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Numero de linea dentro del documento.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'line_number';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Origen logico del item: Manual, QuoteDetail, Adjustment u otro valor definido por Domain/Core.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'origin_type';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Identificador generico del origen cuando aplique.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'origin_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Detalle del presupuesto aprobado que origina la linea, cuando exista. Puede ser NULL en ventas manuales.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'quote_detail_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Producto asociado a la linea, si aplica.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'product_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Descripcion visible de la linea facturada.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'description';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Cantidad facturada.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'quantity';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Precio unitario autorizado o de referencia proveniente del origen comercial.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'authorized_unit_price';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Importe autorizado o de referencia proveniente del origen comercial.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'authorized_amount';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Porcentaje facturado sobre el origen. Permite facturacion parcial obra social / particular.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'billed_percentage';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Precio unitario efectivo de la linea del documento.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'unit_price';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Importe neto de la linea.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'net_amount';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Importe de impuestos de la linea.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'tax_amount';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Importe total de la linea.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'total_amount';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Snapshot JSON del origen de la linea para trazabilidad historica.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesDocumentDetails',
+ @level2type=N'COLUMN', @level2name=N'origin_snapshot_json';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Documento fiscal asociado a un documento comercial de venta. Guarda estado, CAE, numeracion fiscal, request/response e idempotencia ARCA.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Documento comercial interno vinculado al documento fiscal.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'salesdocument_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Estado fiscal independiente del estado comercial. Ejemplos: None, Pending, Authorized, Rejected, Error, PendingReconciliation.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'fiscal_status';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Ambiente fiscal usado para autorizacion: homologacion, produccion u otro valor definido por configuracion.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'environment';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Punto de venta fiscal ARCA/AFIP.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'point_of_sale';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Tipo de comprobante fiscal ARCA/AFIP utilizado en FECAESolicitar.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'voucher_type';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Letra fiscal del comprobante autorizado o a autorizar.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'voucher_letter';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Numero fiscal del comprobante asignado para ARCA. Se mantiene separado del numero interno.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'voucher_number';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Codigo de autorizacion electronico obtenido desde ARCA/AFIP.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'cae';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Fecha de vencimiento del CAE.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'cae_expiration_date';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Huella de idempotencia fiscal para evitar duplicacion de solicitudes ante ARCA.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'request_fingerprint';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Indica si el resultado fiscal es final y no debe volver a mutar salvo procesos controlados de auditoria.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'is_final';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Payload JSON enviado a ARCA/AFIP.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'arca_request_payload_json';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Payload JSON recibido desde ARCA/AFIP.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'arca_response_payload_json';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Errores devueltos por ARCA/AFIP serializados como JSON.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'errors_json';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Eventos devueltos por ARCA/AFIP serializados como JSON.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'events_json';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Observaciones devueltas por ARCA/AFIP serializadas como JSON.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'observations_json';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Fecha/hora UTC del intento de autorizacion fiscal.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'attempted_at_utc';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Fecha/hora UTC de finalizacion del flujo fiscal.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'completed_at_utc';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Indica que el documento fiscal fue resuelto mediante reconciliacion posterior a timeout o resultado ambiguo.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocuments',
+ @level2type=N'COLUMN', @level2name=N'reconciled_after_timeout';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Comprobantes fiscales asociados enviados como CbtesAsoc. Aplica a notas de credito, notas de debito, NCE/NDE y casos FCE donde corresponda.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocumentAssociations';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Documento fiscal que contiene esta asociacion.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocumentAssociations',
+ @level2type=N'COLUMN', @level2name=N'salesfiscaldocument_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Documento comercial interno asociado, si existe dentro de PhronCare.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocumentAssociations',
+ @level2type=N'COLUMN', @level2name=N'associated_salesdocument_id';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Tipo fiscal ARCA/AFIP del comprobante asociado.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocumentAssociations',
+ @level2type=N'COLUMN', @level2name=N'associated_voucher_type';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Punto de venta fiscal del comprobante asociado.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocumentAssociations',
+ @level2type=N'COLUMN', @level2name=N'associated_point_of_sale';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Numero fiscal del comprobante asociado.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocumentAssociations',
+ @level2type=N'COLUMN', @level2name=N'associated_voucher_number';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'CUIT emisor del comprobante asociado, cuando sea requerido por ARCA.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocumentAssociations',
+ @level2type=N'COLUMN', @level2name=N'associated_voucher_cuit';
+
+EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Fecha del comprobante fiscal asociado.',
+ @level0type=N'SCHEMA', @level0name=N'dbo',
+ @level1type=N'TABLE', @level1name=N'PhS_SalesFiscalDocumentAssociations',
+ @level2type=N'COLUMN', @level2name=N'associated_voucher_date';
+