Merge pull request 'feat(eos): create sales document persistence model' (#56) from feature/leandro/48-deliverynote-excel-export-ui into master
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 4m24s

Reviewed-on: http://saludlab.com.ar:3000/leandro/phronCare/pulls/56
This commit is contained in:
Leandro Hernan Rojas 2026-04-30 19:23:29 +00:00
commit e75145ec65
18 changed files with 1411 additions and 309 deletions

View File

@ -1,23 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Domain\Domain.csproj" />
</ItemGroup>
</Project>

View File

@ -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<System.DateTime> FechaCreacion { get; set; }
public string AsignadoAUsuarioId { get; set; } = string.Empty;
public Nullable<System.DateTime> 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<bool> EsSolicitudCliente { get; set; }
public string AdjuntoArchivo { get; set; } = string.Empty;
}
}

View File

@ -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<System.DateTime> 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;
}
}

View File

@ -1,8 +0,0 @@
namespace Data.Entities
{
public partial class Tickets_GetSummary_Result
{
public string Estado { get; set; } = string.Empty;
public Nullable<int> Cantidad { get; set; }
}
}

View File

@ -1,13 +0,0 @@
using Domain.Entities;
namespace Data.Interfaces
{
public interface ITicketRepository
{
IEnumerable<ETicket> GetAll();
ETicket GetById(Guid ticketId);
IEnumerable<ETickets_GetSummary> GetSummary();
IEnumerable<ETicket_Dashboard> GetTicketDashboard(string Estado, string Orden);
void InsertTicket(ETicket ticket);
}
}

View File

@ -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<OperationsHubContext> 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<PhOH_Tickets> Tickets { get; set; }
public DbSet<Tickets_Dashboard_Result> TicketsDashboardResults { get; set; }
public DbSet<Tickets_GetSummary_Result> TicketsSummaryResults { get; set; }
public async Task<List<Tickets_Dashboard_Result>> 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<List<Tickets_GetSummary_Result>> 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<PhOH_Tickets>().ToTable("PhOH_Tickets");
// Marcar los DbSet de resultados como entidades de solo lectura
modelBuilder.Entity<Tickets_Dashboard_Result>().HasNoKey();
modelBuilder.Entity<Tickets_GetSummary_Result>().HasNoKey();
}
}
}

View File

@ -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<ETicket> GetByIdAsync(Guid ticketId)
{
try
{
var ticket = await _dbConnection.Tickets
.FirstOrDefaultAsync(t => t.TicketId == ticketId);
if (ticket == null) return new ETicket();
var eTicket = MapEntity<PhOH_Tickets, ETicket>(ticket);
return eTicket;
}
catch (Exception ex)
{
throw new Exception($"{MethodBase.GetCurrentMethod()?.Name} Message: {ex.Message}", ex);
}
}
public async Task<IEnumerable<ETicket>> GetAllAsync()
{
var tickets = await _dbConnection.Tickets.ToListAsync();
return tickets.Select(ticket => MapEntity<PhOH_Tickets, ETicket>(ticket));
}
public async Task InsertTicketAsync(ETicket ticket)
{
try
{
ticket.TicketId = Guid.NewGuid();
var dataTicket = MapEntity<ETicket, PhOH_Tickets>(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<ETickets_GetSummary> GetSummary()
//{
// var summary_Results = _dbConnection.Tickets_GetSummary();
// return summary_Results.Select(item =>
// {
// var eSummary = Activator.CreateInstance<ETickets_GetSummary>();
// 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<ETicket_Dashboard> GetTicketDashboard(string Estado, string Orden)
//{
// var ticketDashboard_Results = _dbConnection.Tickets_Dashboard(Estado, Orden);
// return (ticketDashboard_Results.Select(item =>
// {
// var eTicketDashboard = Activator.CreateInstance<ETicket_Dashboard>();
// 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, TDestination>(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<ETicket> GetAll()
{
throw new NotImplementedException();
}
public void InsertTicket(ETicket ticket)
{
throw new NotImplementedException();
}
public IEnumerable<ETickets_GetSummary> GetSummary()
{
throw new NotImplementedException();
}
public IEnumerable<ETicket_Dashboard> GetTicketDashboard(string Estado, string Orden)
{
throw new NotImplementedException();
}
#endregion
}
}

View File

@ -33,5 +33,9 @@ public partial class PhSCustomer
public virtual ICollection<PhSPatient> PhSPatients { get; set; } = new List<PhSPatient>(); public virtual ICollection<PhSPatient> PhSPatients { get; set; } = new List<PhSPatient>();
public virtual ICollection<PhSSalesDocument> PhSSalesDocumentBillToCustomers { get; set; } = new List<PhSSalesDocument>();
public virtual ICollection<PhSSalesDocument> PhSSalesDocumentCustomers { get; set; } = new List<PhSSalesDocument>();
public virtual PhOhTaxCondition? TaxCondition { get; set; } public virtual PhOhTaxCondition? TaxCondition { get; set; }
} }

View File

@ -44,4 +44,6 @@ public partial class PhSFormSeries
public virtual PhSFormType Formtype { get; set; } = null!; public virtual PhSFormType Formtype { get; set; } = null!;
public virtual PhSFormSeriesNextNumber? PhSFormSeriesNextNumber { get; set; } public virtual PhSFormSeriesNextNumber? PhSFormSeriesNextNumber { get; set; }
public virtual ICollection<PhSSalesDocument> PhSSalesDocuments { get; set; } = new List<PhSSalesDocument>();
} }

View File

@ -28,4 +28,6 @@ public partial class PhSProduct
public virtual PhSProductCategory? Category { get; set; } public virtual PhSProductCategory? Category { get; set; }
public virtual ICollection<PhSQuoteDetail> PhSQuoteDetails { get; set; } = new List<PhSQuoteDetail>(); public virtual ICollection<PhSQuoteDetail> PhSQuoteDetails { get; set; } = new List<PhSQuoteDetail>();
public virtual ICollection<PhSSalesDocumentDetail> PhSSalesDocumentDetails { get; set; } = new List<PhSSalesDocumentDetail>();
} }

View File

@ -70,6 +70,8 @@ public partial class PhSQuoteDetail
public virtual ICollection<PhSDeliveryNoteDetail> PhSDeliveryNoteDetails { get; set; } = new List<PhSDeliveryNoteDetail>(); public virtual ICollection<PhSDeliveryNoteDetail> PhSDeliveryNoteDetails { get; set; } = new List<PhSDeliveryNoteDetail>();
public virtual ICollection<PhSSalesDocumentDetail> PhSSalesDocumentDetails { get; set; } = new List<PhSSalesDocumentDetail>();
public virtual PhSProduct Product { get; set; } = null!; public virtual PhSProduct Product { get; set; } = null!;
public virtual PhSQuoteHeader Quoteheader { get; set; } = null!; public virtual PhSQuoteHeader Quoteheader { get; set; } = null!;

View File

@ -139,4 +139,6 @@ public partial class PhSQuoteHeader
public virtual ICollection<PhSQuoteRole> PhSQuoteRoles { get; set; } = new List<PhSQuoteRole>(); public virtual ICollection<PhSQuoteRole> PhSQuoteRoles { get; set; } = new List<PhSQuoteRole>();
public virtual ICollection<PhSQuoteTaxis> PhSQuoteTaxes { get; set; } = new List<PhSQuoteTaxis>(); public virtual ICollection<PhSQuoteTaxis> PhSQuoteTaxes { get; set; } = new List<PhSQuoteTaxis>();
public virtual ICollection<PhSSalesDocument> PhSSalesDocuments { get; set; } = new List<PhSSalesDocument>();
} }

View File

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
namespace Models.Models;
/// <summary>
/// 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.
/// </summary>
public partial class PhSSalesDocument
{
/// <summary>
/// Identificador interno del documento comercial.
/// </summary>
public int Id { get; set; }
/// <summary>
/// Talonario/serie interna existente en PhronCare. Reutiliza PhS_FormSeries para numeracion interna.
/// </summary>
public int? FormseriesId { get; set; }
/// <summary>
/// Numero secuencial interno asignado al emitir internamente el documento. No corresponde al numero fiscal ARCA.
/// </summary>
public int? InternalSequenceNumber { get; set; }
/// <summary>
/// Numero visible interno del documento, formado desde la serie/talonario interno. Puede diferir del numero fiscal.
/// </summary>
public string? InternalDocumentNumber { get; set; }
/// <summary>
/// Tipo comercial interno del documento. Ejemplos: Invoice, DebitNote, CreditNote, CreditInvoice, CreditDebitNote, CreditCreditNote.
/// </summary>
public int DocumentType { get; set; }
/// <summary>
/// Tipo de comprobante fiscal AFIP/ARCA previsto para autorizacion futura. Ejemplos: 1, 6, 11, 201, 202, 203.
/// </summary>
public int? FiscalVoucherType { get; set; }
/// <summary>
/// Letra fiscal prevista del comprobante: A, B, C u otras segun configuracion fiscal.
/// </summary>
public string? FiscalVoucherLetter { get; set; }
/// <summary>
/// Estado comercial interno. Ejemplos: Draft, Validated, Issued, Cancelled. Independiente del estado fiscal.
/// </summary>
public int Status { get; set; }
/// <summary>
/// Presupuesto origen opcional. Puede ser NULL para ventas manuales o de escritorio.
/// </summary>
public int? QuoteId { get; set; }
/// <summary>
/// Cliente origen de la operacion comercial.
/// </summary>
public int CustomerId { get; set; }
/// <summary>
/// Cliente al que se factura realmente. Permite escenarios obra social / particular u otros terceros pagadores.
/// </summary>
public int BillToCustomerId { get; set; }
/// <summary>
/// Fecha de emision interna del documento comercial.
/// </summary>
public DateTime? IssueDate { get; set; }
/// <summary>
/// Moneda del documento comercial.
/// </summary>
public string Currency { get; set; } = null!;
/// <summary>
/// Cotizacion utilizada para la moneda del documento.
/// </summary>
public decimal ExchangeRate { get; set; }
/// <summary>
/// Importe neto total del documento.
/// </summary>
public decimal NetAmount { get; set; }
/// <summary>
/// Importe total de impuestos del documento.
/// </summary>
public decimal TaxAmount { get; set; }
/// <summary>
/// Importe total del documento.
/// </summary>
public decimal TotalAmount { get; set; }
/// <summary>
/// Tipo de documento interno asociado opcional, por ejemplo remito, orden de compra o autorizacion. No representa CbtesAsoc fiscal.
/// </summary>
public string? AssociatedDocumentType { get; set; }
/// <summary>
/// Numero del documento interno asociado opcional.
/// </summary>
public string? AssociatedDocumentNumber { get; set; }
/// <summary>
/// Fecha del documento interno asociado opcional.
/// </summary>
public DateTime? AssociatedDocumentDate { get; set; }
/// <summary>
/// Observaciones comerciales del documento.
/// </summary>
public string? Observations { get; set; }
/// <summary>
/// Snapshot JSON con informacion extra contextual del documento.
/// </summary>
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<PhSSalesDocumentDetail> PhSSalesDocumentDetails { get; set; } = new List<PhSSalesDocumentDetail>();
public virtual PhSSalesFiscalDocument? PhSSalesFiscalDocument { get; set; }
public virtual ICollection<PhSSalesFiscalDocumentAssociation> PhSSalesFiscalDocumentAssociations { get; set; } = new List<PhSSalesFiscalDocumentAssociation>();
public virtual PhSQuoteHeader? Quote { get; set; }
}

View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
namespace Models.Models;
/// <summary>
/// Detalles valorizados de documentos comerciales de venta.
/// </summary>
public partial class PhSSalesDocumentDetail
{
public int Id { get; set; }
/// <summary>
/// Documento comercial al que pertenece el detalle.
/// </summary>
public int SalesdocumentId { get; set; }
/// <summary>
/// Numero de linea dentro del documento.
/// </summary>
public int LineNumber { get; set; }
/// <summary>
/// Origen logico del item: Manual, QuoteDetail, Adjustment u otro valor definido por Domain/Core.
/// </summary>
public string OriginType { get; set; } = null!;
/// <summary>
/// Identificador generico del origen cuando aplique.
/// </summary>
public int? OriginId { get; set; }
/// <summary>
/// Detalle del presupuesto aprobado que origina la linea, cuando exista. Puede ser NULL en ventas manuales.
/// </summary>
public int? QuoteDetailId { get; set; }
/// <summary>
/// Producto asociado a la linea, si aplica.
/// </summary>
public int? ProductId { get; set; }
/// <summary>
/// Descripcion visible de la linea facturada.
/// </summary>
public string Description { get; set; } = null!;
/// <summary>
/// Cantidad facturada.
/// </summary>
public decimal Quantity { get; set; }
/// <summary>
/// Precio unitario autorizado o de referencia proveniente del origen comercial.
/// </summary>
public decimal? AuthorizedUnitPrice { get; set; }
/// <summary>
/// Importe autorizado o de referencia proveniente del origen comercial.
/// </summary>
public decimal? AuthorizedAmount { get; set; }
/// <summary>
/// Porcentaje facturado sobre el origen. Permite facturacion parcial obra social / particular.
/// </summary>
public decimal? BilledPercentage { get; set; }
/// <summary>
/// Precio unitario efectivo de la linea del documento.
/// </summary>
public decimal UnitPrice { get; set; }
/// <summary>
/// Importe neto de la linea.
/// </summary>
public decimal NetAmount { get; set; }
/// <summary>
/// Importe de impuestos de la linea.
/// </summary>
public decimal TaxAmount { get; set; }
/// <summary>
/// Importe total de la linea.
/// </summary>
public decimal TotalAmount { get; set; }
/// <summary>
/// Snapshot JSON del origen de la linea para trazabilidad historica.
/// </summary>
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!;
}

View File

@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
namespace Models.Models;
/// <summary>
/// Documento fiscal asociado a un documento comercial de venta. Guarda estado, CAE, numeracion fiscal, request/response e idempotencia ARCA.
/// </summary>
public partial class PhSSalesFiscalDocument
{
public int Id { get; set; }
/// <summary>
/// Documento comercial interno vinculado al documento fiscal.
/// </summary>
public int SalesdocumentId { get; set; }
/// <summary>
/// Estado fiscal independiente del estado comercial. Ejemplos: None, Pending, Authorized, Rejected, Error, PendingReconciliation.
/// </summary>
public int FiscalStatus { get; set; }
/// <summary>
/// Ambiente fiscal usado para autorizacion: homologacion, produccion u otro valor definido por configuracion.
/// </summary>
public string Environment { get; set; } = null!;
/// <summary>
/// Punto de venta fiscal ARCA/AFIP.
/// </summary>
public short? PointOfSale { get; set; }
/// <summary>
/// Tipo de comprobante fiscal ARCA/AFIP utilizado en FECAESolicitar.
/// </summary>
public int? VoucherType { get; set; }
/// <summary>
/// Letra fiscal del comprobante autorizado o a autorizar.
/// </summary>
public string? VoucherLetter { get; set; }
/// <summary>
/// Numero fiscal del comprobante asignado para ARCA. Se mantiene separado del numero interno.
/// </summary>
public int? VoucherNumber { get; set; }
/// <summary>
/// Codigo de autorizacion electronico obtenido desde ARCA/AFIP.
/// </summary>
public string? Cae { get; set; }
/// <summary>
/// Fecha de vencimiento del CAE.
/// </summary>
public DateTime? CaeExpirationDate { get; set; }
/// <summary>
/// Huella de idempotencia fiscal para evitar duplicacion de solicitudes ante ARCA.
/// </summary>
public string? RequestFingerprint { get; set; }
/// <summary>
/// Indica si el resultado fiscal es final y no debe volver a mutar salvo procesos controlados de auditoria.
/// </summary>
public bool IsFinal { get; set; }
/// <summary>
/// Payload JSON enviado a ARCA/AFIP.
/// </summary>
public string? ArcaRequestPayloadJson { get; set; }
/// <summary>
/// Payload JSON recibido desde ARCA/AFIP.
/// </summary>
public string? ArcaResponsePayloadJson { get; set; }
/// <summary>
/// Errores devueltos por ARCA/AFIP serializados como JSON.
/// </summary>
public string? ErrorsJson { get; set; }
/// <summary>
/// Eventos devueltos por ARCA/AFIP serializados como JSON.
/// </summary>
public string? EventsJson { get; set; }
/// <summary>
/// Observaciones devueltas por ARCA/AFIP serializadas como JSON.
/// </summary>
public string? ObservationsJson { get; set; }
/// <summary>
/// Fecha/hora UTC del intento de autorizacion fiscal.
/// </summary>
public DateTime? AttemptedAtUtc { get; set; }
/// <summary>
/// Fecha/hora UTC de finalizacion del flujo fiscal.
/// </summary>
public DateTime? CompletedAtUtc { get; set; }
/// <summary>
/// Indica que el documento fiscal fue resuelto mediante reconciliacion posterior a timeout o resultado ambiguo.
/// </summary>
public bool ReconciledAfterTimeout { get; set; }
public DateTime Createdat { get; set; }
public DateTime? Modifiedat { get; set; }
public virtual ICollection<PhSSalesFiscalDocumentAssociation> PhSSalesFiscalDocumentAssociations { get; set; } = new List<PhSSalesFiscalDocumentAssociation>();
public virtual PhSSalesDocument Salesdocument { get; set; } = null!;
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
namespace Models.Models;
/// <summary>
/// Comprobantes fiscales asociados enviados como CbtesAsoc. Aplica a notas de credito, notas de debito, NCE/NDE y casos FCE donde corresponda.
/// </summary>
public partial class PhSSalesFiscalDocumentAssociation
{
public int Id { get; set; }
/// <summary>
/// Documento fiscal que contiene esta asociacion.
/// </summary>
public int SalesfiscaldocumentId { get; set; }
/// <summary>
/// Documento comercial interno asociado, si existe dentro de PhronCare.
/// </summary>
public int? AssociatedSalesdocumentId { get; set; }
/// <summary>
/// Tipo fiscal ARCA/AFIP del comprobante asociado.
/// </summary>
public int AssociatedVoucherType { get; set; }
/// <summary>
/// Punto de venta fiscal del comprobante asociado.
/// </summary>
public short AssociatedPointOfSale { get; set; }
/// <summary>
/// Numero fiscal del comprobante asociado.
/// </summary>
public int AssociatedVoucherNumber { get; set; }
/// <summary>
/// CUIT emisor del comprobante asociado, cuando sea requerido por ARCA.
/// </summary>
public string? AssociatedVoucherCuit { get; set; }
/// <summary>
/// Fecha del comprobante fiscal asociado.
/// </summary>
public DateTime? AssociatedVoucherDate { get; set; }
public DateTime Createdat { get; set; }
public virtual PhSSalesDocument? AssociatedSalesdocument { get; set; }
public virtual PhSSalesFiscalDocument Salesfiscaldocument { get; set; } = null!;
}

View File

@ -97,6 +97,14 @@ public partial class PhronCareOperationsHubContext : DbContext
public virtual DbSet<PhSQuoteTaxis> PhSQuoteTaxes { get; set; } public virtual DbSet<PhSQuoteTaxis> PhSQuoteTaxes { get; set; }
public virtual DbSet<PhSSalesDocument> PhSSalesDocuments { get; set; }
public virtual DbSet<PhSSalesDocumentDetail> PhSSalesDocumentDetails { get; set; }
public virtual DbSet<PhSSalesFiscalDocument> PhSSalesFiscalDocuments { get; set; }
public virtual DbSet<PhSSalesFiscalDocumentAssociation> PhSSalesFiscalDocumentAssociations { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
modelBuilder.UseCollation("Modern_Spanish_CI_AS"); modelBuilder.UseCollation("Modern_Spanish_CI_AS");
@ -1862,6 +1870,369 @@ public partial class PhronCareOperationsHubContext : DbContext
.HasConstraintName("FK_PhS_QuoteTaxes_QuoteHeaders"); .HasConstraintName("FK_PhS_QuoteTaxes_QuoteHeaders");
}); });
modelBuilder.Entity<PhSSalesDocument>(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<PhSSalesDocumentDetail>(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<PhSSalesFiscalDocument>(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<PhSSalesFiscalDocument>(d => d.SalesdocumentId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_PhS_SalesFiscalDocuments_SalesDocuments");
});
modelBuilder.Entity<PhSSalesFiscalDocumentAssociation>(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); OnModelCreatingPartial(modelBuilder);
} }

View File

@ -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';