From 4ee6880e924fe76995711231807f80db00c6417d Mon Sep 17 00:00:00 2001 From: leandro Date: Thu, 30 Apr 2026 16:20:51 -0300 Subject: [PATCH] feat(eos): create sales document persistence model closes #55 --- Data/Data.csproj | 23 - Data/Entities/PhOH_Tickets.cs | 30 - Data/Entities/Tickets_Dashboard_Result.cs | 17 - Data/Entities/Tickets_GetSummary_Result.cs | 8 - Data/Interfaces/ITicketRepository.cs | 13 - Data/Models/OperationsHubContext.cs | 87 --- Data/Repositories/TicketRepository.cs | 131 ---- Models/Models/PhSCustomer.cs | 4 + Models/Models/PhSFormSeries.cs | 2 + Models/Models/PhSProduct.cs | 2 + Models/Models/PhSQuoteDetail.cs | 2 + Models/Models/PhSQuoteHeader.cs | 2 + Models/Models/PhSSalesDocument.cs | 138 ++++ Models/Models/PhSSalesDocumentDetail.cs | 102 +++ Models/Models/PhSSalesFiscalDocument.cs | 115 ++++ .../PhSSalesFiscalDocumentAssociation.cs | 53 ++ .../Models/PhronCareOperationsHubContext.cs | 371 +++++++++++ ...sales_document_model_with_descriptions.sql | 620 ++++++++++++++++++ 18 files changed, 1411 insertions(+), 309 deletions(-) delete mode 100644 Data/Data.csproj delete mode 100644 Data/Entities/PhOH_Tickets.cs delete mode 100644 Data/Entities/Tickets_Dashboard_Result.cs delete mode 100644 Data/Entities/Tickets_GetSummary_Result.cs delete mode 100644 Data/Interfaces/ITicketRepository.cs delete mode 100644 Data/Models/OperationsHubContext.cs delete mode 100644 Data/Repositories/TicketRepository.cs create mode 100644 Models/Models/PhSSalesDocument.cs create mode 100644 Models/Models/PhSSalesDocumentDetail.cs create mode 100644 Models/Models/PhSSalesFiscalDocument.cs create mode 100644 Models/Models/PhSSalesFiscalDocumentAssociation.cs create mode 100644 docs/sql/55_sales_document_model_with_descriptions.sql 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'; +