El post que buscas se encuentra eliminado, pero este también te puede interesar

Imprimir factura desde C# [Nivel Avanzado]

Anuncios

Hola gente, cómo andan?
El otro día se me presentó un trabajito:
Me habían dicho que necesitaban imprimir facturas desde el programa, y yo les dije que no tenía ni idea y que no sé si lo iba a poder hacer, recién estaba empezando. El tema es que al final se los terminé haciendo.

Lo hice de 2 formas diferentes, la correcta y la incorrecta.

La incorrecta consistió en tener un modelo de factura vacía como imágen, incrustarla en el programa y llenar de labeles todas las posiciones, entonces completabas campos y se iban llenando... al final tocabas aceptar y gracias a una función que se llama DrawToBitmap se dibujaba todo el picturebox con la imagen factura y los labeles escritos encima. Un asco, encima en su PC no tomaba la captura.

La forma correcta o la más factible se las paso a explicar. Vamos a crear un proyecto windowsForms con el nombre que deseen, yo le pongo imprimirfactura:
Imprimir factura desde C# [Nivel Avanzado]

Ahora vamos a crear unos campos para llenar los datos, vamos a arrastrar desde el cuadro de herramientas 4 textBoxes, 4 labeles, 2 botones y un dataGridView:
Programacion

Ahora cambiamos el texto de los labeles a Cantidad, Descripción, Precio unitario, e importe. Por otro lado cambiamos los nombres de los textBoxes a textBoxCantidad, textBoxDescripcion, textBoxPrecioUnitario y textBoxImporte. Cambiamos el nombre del DataGridView a DGV, y el texto y nombre de los botones 1 y 2 Agregar, btnAgregar, Imprimir, btnImprimir respectivamente y finalmente dejamos en ReadOnly el DGV seleccionandolo, tocando la flechita que sale arriba a su derecha y destildando todo:
impresora

Listo. Ya tenemos el diseño.
Esta primera parte va a funcionar de la siguiente manera:
-Se llenan los campos
-Se presiona agregar
-Se comprueba que no haya campos vacíos con la función camposCompletos() : Boolean
-Se comprueba que sean dátos válidos con la función datosValidos() : Boolean
-Se añade como registro o fila al DGV
-Se vacían los campos con la función vaciarCampos() : void

Así que procedemos tocando F7 para ir al código y creamos la primer función privada Booleana camposCompletos():
Visual Studio

Nos va a mostrar un errorcito porque la función tiene que devolver un Boolean (true o false) y todavía no pusimos ningún return.
Dentro del método lo que tenemos que hacer es algo parecido a lo que hicimos en el aporte de conectar C# con Sql que pueden ver desde mi perfil, comprobar que ningún textBox sea equivalente a "" o String.Empty.
Así que dentro de la función ponemos lo siguiente:

if(textBoxCantidad.Text.Equals( "" ) || textBoxDescripcion.Text.Equals("" ) ||
textBoxPrecioUnitario.Text.Equals("" ) || textBoxImporte.Text.Equals("" ))
return false ;

else
return true ;

(Cuando dentro de los bloques if sólo hay una línea de código, no hace falta poner las llaves, lo mismo para los else y else if)

Quedaría así:
imprimir

Ahora podemos hacerlo de varias formas, se me vienen a la cabeza unas... 3.
Una es crear un evento keyPress para esos 2 textBox y controlar si la tecla presionada es un dígito. Si no es un dígito, no lo escribe. Pero no me gusta mucho.
Otra es un try-catch para controlar formatException que es la que vamos a usar pero paso a explicarles la 3ra que es la que más les puede servir en otros casos:

Lo que haría es tomar el .Text y analizar char por char controlando que IsDigit devuelva un true.
El ejemplo:

foreach (Char c in textBoxCantidad.Text)
if (!Char.IsDigit(c))
return false ;
Eso iría en un método que podría llamarse... datosValidos() : Boolean y sería llamada al tocar el botón agregar.

Se los muestro porque les puede servir, pero ahora vamos con la que necesitamos en este caso.
Usamos esta forma ya que el importe (que es la cantidad multiplicado por el precio unitario) se calcula sólo, no hay que llenarlo.

Lo que hacemos es volver al diseño para crear un evento text changed en el textBoxCantidad y en el textBoxPrecioUnitario, se hace así:
En el diseño, seleccionamos primero el textBoxCantidad y tocamos el "rayito" que está a la derecha, en las propiedades del control.
programar

Observen que ya el evento TextChanged esta seleccionado por defecto, supongo yo que es porque es el que más se suele utilizar. Hacemos doble-click en el espacio a la derecha de TextChanged y se generará el evento, de forma parecida al evento click de los botones:
factura

Ahora lo que hacemos es.. si textBoxCantidad.Text y textBoxPrecioUnitario.Text no están vacíos, textBoxImporte.Text va a ser igual a textBoxCantidad.Text * textBoxPrecioUnitario.Text.
Código:

if(!(textBoxCantidad.Text.Equals("" ) || textBoxPrecioUnitario.Text.Equals("" ))
textBoxImporte.Text = (Convert.ToInt32(textBoxCantidad.Text) * Convert.ToDouble(textBoxPrecioUnitario.Text)).ToString() ;

(Cambien el && por ||, mala mía.)

Quedaría así:
desarrollo de software

Ahora generamos el mismo evento en el textBoxPrecioUnitario y en el bloque generado ponemos el mismo código:
c sharp

Lo que pasará es que al escribir en los 2 textBox se calcule sólo el importe o precio final de ese producto (Y)

Ahora en ambos eventos rodeamos con un try y ponemos un catch de la siguiente manera:

try
{
//ACA VA LA MULTIPLICACION
}
catch (FormatException) { MessageBox.Show( "Porfavor, ingrese sólo números" , "Advertencia" ) ; }

Nos pasaría a quedar así:
imrpesion

Como ya no hay necesidad de escribir el importe, vamos al diseño y seteamos el textBoxImporte como enabled false:
Imprimir factura desde C# [Nivel Avanzado]


Perfecto. Ahora que ya hicimos los controles de datos lo que hacemos es ir al diseño y generar el evento click del btnAgregar:
Programacion

Ahora...
Si los campos están completos se agrega el registro a la DGV

dentro del btnAgregar_Click llamamos a una función añadirRegistro() que vamos a crear a continuación:
impresora

Ahora le tenemos que dar una forma o estructura inicial a la DGV, para eso creamos una private DataTable dt global y en el constructor le agregamos las columnas para estructurar el DGV:
Visual Studio

Ahora volvemos a la función añadirRegistro y dentro añadimos un registro a la dt para después pasársela al DGV:

dt.Rows.Add(textBoxCantidad.Text, textBoxDescripcion.Text, textBoxPrecioUnitario.Text,textBoxImporte.Text) ;
DGV.DataSource = dt ;
imprimir

Ahora ya funcionaría el traslado de los campos al DGV. Al presionar Agregar si todo está bien, se pasa la nueva fila y los campos deberían borrase para que quede más dinámico y rápido de usar, así que creamos una función private void vaciarCampos() con éste código dentro:

textBoxCantidad.Text = String.Empty;
textBoxDescripcion.Text = String.Empty;
textBoxPrecioUnitario.Text = String.Empty;
textBoxImporte.Text = String.Empty;
programar

y la llamamos desde btnAgregar_Click luego del añadirRegistro()
factura

Perfecto. Como la factura modelo que vamos a usar tiene 14 items, hay que controlar que el DGv no tenga más de 14 registros, por lo que en el btnAgregar_Click en el if, agregamos un && DGV.Rows.Count <14. Si supera los 14 muestra un mensaje:

else { MessageBox.Show("No se pueden facturar más productos en una misma factura","Aviso";}
desarrollo de software

Listo. La metodología que vamos a usar es dar uso a una función llamada DrawString que lo que hace es dibujar sobre una imagen un texto. Ya se dan una idea, no?

Primero, se bajan esta imagen:
c sharp

Perfecto. Ahora lo que tenemos que hacer es agregar la factura modelo al proyecto como recurso. Para hacerlo vamos a Proyecto >> Propiedades de ImprimirFactura
imrpesion

Imprimir factura desde C# [Nivel Avanzado]

Ahora a la izquierda vamos a donde dice Recursos
Programacion

Tocamos la flechita de Agregar Recurso y seleccionamos Archivo existente
impresora

y seleccionan la factura normalmente, cierren la pestaña y guarden.

Con esto ya tenemos incrustada la imagen en el proyecto

Vamos a generar el evento double-click del btnImprimir
Visual Studio

Lo que hacemos ahora es debajo de la declaración de la DataTable dt, delcaramos un private Image facturaModelo; y la cargamos en el constructor.
imprimir

Ahora para poder dibujar el String en la imagen (Los textBoxes.Text) lo que tenemos que tener son las coordenadas x e y de cantidad, desc, precio e importe de cada fila.
Al ser una factura simétrica con lo que a filas y columnas respecta, podemos trabajar en un ciclo repetitivo for sumando valores estáticos x e y.

Primero empezamos declarando las variables que necesitamos:
int[] x = {27,111,143,128};
int firstY = 32;
int y = 28;
Image facturaActual = facturaModelo;
Graphics g = Graphics.FromImage(facturaActual);
StringFormat formatter = new StringFormat();
formatter.LineAlignment = StringAlignment.Center;
formatter.Alignment = StringAlignment.Center;
Font font = new Font("Microsoft Sans Serif", 10, FontStyle.Regular);
SolidBrush brush = new SolidBrush(Color.Black);

El array x de tipo int se declara así porque las columnas no están a una distancia en común, el punto central de la columna cantidad se encuentra en x 27, la columna desc se encuentra a x 111, y así con las otras 2.

int firstY lo que hace es dar la altura de la primera fila, ya que verán que los items no comienzan desde arriba de todo, sino que hay un header nombrando las columnas.

int y 28 va a ser el valor que se va a repetir entre fila y fila, será multiplicado por i dentro del for luego del firstY.

Se iguala la factura actual a la modelo para luego ser llenada.

Se crea un Graphics g basándose en facturaActual que es lo que dibujará sobre ella.

Se le da un formato al string que será dibujado (Alineación).

Se crea una fuente.

Y por último un pincel para el dibujado o trazado del String en la imágen.

Listo. Ahora el for. A continuación lo paso a explicar, no se asusten (?

for (int i = 0; i < DGV.Rows.Count; i++)
{
for (int j = 0; j < 4; j++)
{
if(i==0)
g.DrawString(DGV.Rows.Cells[j].Value.ToString(), font, brush, new Point(x[j], firstY), formatter);
else
g.DrawString(DGV.Rows.Cells[j].Value.ToString(), font, brush, new Point(x[j], y), formatter);
}

y += 26;
}

Perfecto.
El primero for que tiene un índice i es el encargado de pasar de fila a fila.

El segundo for con índice j se encarga de las columnas.

dentro de ambos, se hace la comprobación de i == 0, para saber si es la primera fila y usar el firstY, así que es lo primero que hará al entrar por primera vez.

Luego se usa el método de g para Dibujar el String, y le estamos pasándo los siguientes parámetros:

El valor de la celda actual del DGV, la fuente, el pincel, las coordenadas de dibujo y finalmente el formato del String.

Cuando se termina de dibujar la fila entera, osea, antes de completar el ciclo del for i se suma 'y' en 26 para dar lugar a una nueva fila indicando las coordenadas de 'y'.


Listop, les quedaría así:
programar

Ahora lo que hice para testear yo es crear un nuevo Form (todo mediante código) para ver si está realmente funcionando (Oooobviamente lo hice en el transcurso del codeo de los fors para ver bien las coordenadas y demás ).

Form f2 = new Form();
f2.Show();
f2.Validate();
PictureBox pbox = new PictureBox();
pbox.Image = facturaActual;
pbox.SizeMode = PictureBoxSizeMode.AutoSize;
f2.Width = pbox.Width + 17;
f2.Height = pbox.Height + 37;
f2.Controls.Add(pbox);

Lo que se hace es crear un nuevo form llamado f2, mostrarlo, validarlo, crear un pictureBox, cargarle una imagen, hacer que el tamaño del pBox se adapte al tamaño de la imagen, y el tamaño del form que se adapte al tamaño del pBox más unos píxeles (Por el tema del encabezado del form, bordes y demás), y finalmente cargarlo al form.

El código lo ponen debajo de los for de dibujo, les queda así:
factura

Listop, pueden ir probándolo e.e Acá les dejo una muestra:
desarrollo de software


D:
Nos olvidábamos (me) del total!

Para incluír el total en la factura primero le indicamos las coordenadas declarándolas junto a las otras de la siguiente manera:
int totalX = 493;
int totalY = 435;

seguido de un int total = 0;

y vamos calculando el total justo debajo del y += 26;
total += Convert.ToInt32(DGV.Rows.Cells[3].Value.ToString());

y abajo de los for declaramos font como new font para agrandar su fuente (Así se ve mejor el total) y agregamos el último g.DrawString()

font = new Font("Microsoft Sans Serif", 14, FontStyle.Regular);
g.DrawString(total.ToString(), font, brush, new Point(totalX, totalY), formatter);

Quedaría todo así (Entro justito todo en la imágen)
c sharp

Listo, ahora sí:
imrpesion

Ahora lo que tenemos que hacer es poder imprimirla :3
Borramos el código para crear un nuevo form con la imágen ya que sólo es testeo (O bien lo meten en un método y no lo invocan, si lo quieren usar en otro momento, recomendado) y lo que debemos hacer a continuación es crear un evento nosotros por código, que es sencillo así que tranquilos.

Primero creamos un método Boolean imprimirFactura(Image facturaActual) y dentro ponemos lo siguiente:

try
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += (sender, e) => e.Graphics.DrawImage(facturaActual, 0, 0);
pd.Print();
return true;
}
catch (Exception)
{
MessageBox.Show("Error al imprimir la factura. Compruebe que la impresora esté conectada y encendida", "Advertencia";
return false;
}

Quedaría así, detectando este error:
Imprimir factura desde C# [Nivel Avanzado]

¿Por qué el error? Porque estamos usando partes de una librería que no tenemos en este proyecto, así que nos dirigimos arriba de todo y agregamos un using System.Drawing.Printing;
Programacion

Ya nos deja de marcar error.

¿Qué es lo que hace el método?
Buena pregunta. No sé. ah

Lo que hace es intentar (por estar en un bloque try-catch) crear un documento a imprimir, crear un evento con parámetros object sender y eventArgs e, y llamarlo automáticamente, si no me equivoco, pasándole unos parámetros, entre ellos la imagen a imprimir.
Imprime y si todo salió bien, devuelve un true. Sino, muestra el mensaje y devuelve false.

Así que... si tienen la impresora prendida con hojas y tinta, a probarlo !

Iniciamos:
impresora

Llenamos:
Visual Studio

e imprimimos !

imprimir

programar

factura

(Colegio técnico, mucho calitecno pero mi letra sigue siendo una m*erda jajaja)

Espero que les haya servido gente, cualquier duda/problema, no duden en comentar.
Si quieren el proyecto, me mandan MP


Este es un aporte hecho por mí en base a mis conocimientos y experiencias de desarrollo en C#.
Si quieren ver más sobre este contenido pueden pasarse por mi comunidad:


Desarrollo de Software



Otros aportes:


C# conectado a Sql Server [Nivel Medio]

Anuncios

29 comentarios - Imprimir factura desde C# [Nivel Avanzado]

Mokander
Hola, puedes subir el proyecto, para poderlo compilar y probarlo
Ludebe +1
Ahora te mando un mp
PrincessAlba
Hola, puedes subir el proyecto, para poderlo compilar y probarlo
Ludebe +1
mp enviado :3
RandallMed +1
Amigo puedes subir el proyecto!
Ludebe +1
Te lo paso por mp! Ya te lo envío.
skidra +1
holaaa esta chido este programa justo lo que necesito.. me lo podrias pasar porfis!! gracias!!
Ludebe +1
MP Enviado
CRISDANR
Hola, excelente post. Seria bueno que subieras el codigo, igual hay van tus +10 bien merecidos.
Tengo una duda, eso lo haces asi pq imprimes el formato de la factura, pero si la factura esta en un preimpreso?? como configurarias las margenes teniendo en cuenta que se imprime en diferentes impresoras y que el area de impresion de esas impresoras va a variar?? Como se haria una configuracion de margenes o algo asi??
Ludebe +2
Hola, gracias por los puntos
(El código me lo piden y lo mando por MP, está dicho arriba)
El área de impresión no varía dependiendo la impresora, según mi lógica.
Si lo que querés hacer es que sea algo así como "más dinámico", tendrías que calcular el largo de los String y acortarlos o cuando se supera cierta cantidad de carácteres poniendo un punto, por ejemplo. Creo que no es lo que me estás preguntando, pero de paso lo digo
CRISDANR +1
@Ludebe Hola, gracias por responder, el area de impresion si cambia segun la impresora, justo tengo un desarrolo donde sucede, si le das una margen de 0.5mm y el area no es suficiente se corren los datos. te encargo el codigo me gustaria revisarlo.
Ludebe +1
@CRISDANR supongo que surgen problemas cuando hay varias impresoras involucradas, para este caso habría que hacer el programa un poco más dinámico. Desconozco cómo pero con el problema bien planteado algo se puede hacer. Como siempre. Ya te lo envío
pokaluz_16
Me puedes pasar tu proyecto..?
Ludebe +1
Ya te lo paso
Raul15m +1
Muy bueno amigo!, me podrías pasar el proyecto por favor? Gracias!
robacorsa
Buenas me podrias pasar el proyecto? gracias
marcosfuentes940
hola me podrías pasar el proyecto, lo que pasa que me salio un error y quiero verificar.
Ludebe
Cómo no.
MP
BLaguna
Se ve muy bueno tu proyecto, apenas empiezo en esto de la programacion, y me gustaria que me dieras un consejo.

Aqui donde trabajo, se requieren varias aplicaciones, he echo en access y todo bien, pero requieren ya con una base mas potente.

ya instale MySql pero la interfaz para el usuario aun no.
elporn
Como tip, es mucho más fácil crear un reporte con el formato de factura y pasarle los parámetros así podrían darle mejor diseño, ajustar márgenes, incluir logotipo, o incluso permitir que el usuario cree sus propios formatos
buitle_may +1
Excelente Tuto!, Se que me sacara de dudas proximamente saludos!
elamigopato
hola man, me viene bien tu ejemplo me mandas el proyecto ? .. yo para pdf's uso NReco.PdfGenerator, y pues puedo combinar mi código con lo que tu hiciste.. saludos
Ludebe
Ya te mando mp
gusgio8
Interesante, puedes mandarme el proyecto? gracias
TakashiLpez
Amigo me lo podrías enviar necesito algo como lo que hiciste, excelente aporte! saludos.
ancheyta145
hoola, podrias enviarme el proyecto, por favor.
chemoral87
ssrs y en 5min tienes esa factura con colores, la puedes exportar a word, excel o pdf
navyivan
Eres novato vdd? de seguro tienes menos de 16 años, Eso no es una factura es una NOTA, no trae datos del emisor ni receptor y naaaada que ver con la facturacion electronica que se usa hoy en dia, Usaste Copy paste, cuando pudiste crear un metodo y mandarlo llamar desde los eventos key press y ponerlo en una sola linea, Lo del vaciado de campos lo pudiste haber hecho con un for each, No pusiste un validador de cantidades si alguien pone -999?, y pudiste haber utilizado CRYSTAL REPORTS que es mucho mas profesional.... Saludos
WCdth
Buen Post amigo, me podrías mandar el código por favor, te agradecería mucho, saludos!!
Ludebe
Enviado!
Sir_Stone
Hola, por fa me envias el codigo, esta excelente tu programa! estoy pendiente, saludos
Ludebe
enviado!
Gonzza_12
Muy buen post, Me podrias mandar el programa me seria de mucha ayuda para el proyecto que estoy trabajando. Gracias!!
Ludebe
Enviado!
DanielLp4
Excelente aporte, disculpa el abuso me podrias mandar a mi tambien el programa me ayudaria mucho a con un proyecto que tengo
Ludebe
Enviado!
Rubicela
Muy bueno... He andado loca buscando algo así... Podrías pasarme el programa por favor? De antemano muchas gracias
Ludebe
Eviadoo