/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2007 ComPiere, Inc. All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY, without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package ar.com.ergio.model;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import org.compiere.model.I_C_Order;
import org.compiere.model.I_C_OrderLine;
import org.compiere.model.I_M_InOutLine;
import org.compiere.model.MBPartner;
import org.compiere.model.MDocType;
import org.compiere.model.MInOut;
import org.compiere.model.MInOutLine;
import org.compiere.model.MInvoice;
import org.compiere.model.MInvoiceLine;
import org.compiere.model.MLocation;
import org.compiere.model.MOrder;
import org.compiere.model.MOrderLine;
import org.compiere.model.MPayment;
import org.compiere.model.MPriceList;
import org.compiere.model.MProduct;
import org.compiere.model.MRefList;
import org.compiere.model.MSysConfig;
import org.compiere.model.MTax;
import org.compiere.model.PO;
import org.compiere.pos.PosOrderModel;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Trx;
import static ar.com.ergio.model.LAR_TaxPayerType.*;
import ar.com.ergio.print.fiscal.FiscalPrinterDevice;
import ar.com.ergio.print.fiscal.FiscalPrinterListener;
import ar.com.ergio.print.fiscal.document.CreditNote;
import ar.com.ergio.print.fiscal.document.Customer;
import ar.com.ergio.print.fiscal.document.DebitNote;
import ar.com.ergio.print.fiscal.document.DiscountLine;
import ar.com.ergio.print.fiscal.document.Document;
import ar.com.ergio.print.fiscal.document.DocumentLine;
import ar.com.ergio.print.fiscal.document.Invoice;
import ar.com.ergio.print.fiscal.document.DNFH;
import ar.com.ergio.print.fiscal.document.NonFiscalDocument;
import ar.com.ergio.print.fiscal.document.Payment;
import ar.com.ergio.print.fiscal.document.PerceptionLine;
import ar.com.ergio.print.fiscal.exception.DocumentException;
import ar.com.ergio.print.fiscal.exception.FiscalPrinterIOException;
import ar.com.ergio.print.fiscal.exception.FiscalPrinterStatusError;
import ar.com.ergio.print.fiscal.msg.FiscalMessages;
import ar.com.ergio.util.LAR_Utils;
/**
* Impresión fiscal de documentos. Esta clase se encarga de mapear documentos de
* openXpertya a documentos aceptados por las impresoras fiscales.
*
* @author Franco Bonafine
* @contributor Emiliano Pereyra - http://www.ergio.com.ar
*
*/
public class FiscalDocumentPrint {
// Acciones
public enum Actions {
ACTION_PRINT_DOCUMENT,
ACTION_FISCAL_CLOSE,
ACTION_PRINT_DELIVERY_DOCUMENT,
ACTION_PRINT_SHIPMENT_DOCUMENT
}
/** Cantidad maxima de esperas cuando la impresora se encuentra en estado ocupado */
private static int MAX_BSY_SLEEP_COUNT = 12;
/** Período en milisegundos mediante el cual se chequea el estado de la impresora fiscal */
private static long BSY_SLEEP_TIME = 5000;
/** Logger del sistema */
private static CLogger log = CLogger.getCLogger(FiscalDocumentPrint.class);
/** Manejador de eventos de la impresora fiscal */
//private FiscalPrinterListener printerEventListener;
/** Manejador de eventos del estado del Controlador Fiscal de OXP */
private List<FiscalDocumentListener> documentPrintListeners;
/** Modelo asociado a la configuración de la impresora fiscal */
private final MFiscalPrinter fiscalPrinter;
/** Impresora fiscal con la que se imprime el documento */
private final FiscalPrinterDevice fiscalPrinterDevice;
/** Mapeo entre la categorías de IVA de LAR y las de
* las clases de documentos para impresoras fiscales */
private Map<String, Integer> taxPayerTypes;
/** Contexto de la aplicación */
protected Properties ctx = Env.getCtx();
/** Indica si se debe ignorar el estado de error de la impresora
* e intentar imprimir un documento estando en este estado */
private boolean ignoreErrorStatus = false;
/** Indica si se debe cancelar la espera de la impresora fiscal
* cuando se encuentra en estado BUSY */
private boolean cancelWaiting = false;
/** Tipo de documento a emitir por la impresora fiscal */
private String printerDocType = null;
/** Documento de OXP que se va a emitir por la impresora fiscal. */
private PO oxpDocument;
/** Transacción utilizada para la impresión de un documento */
private Trx trx;
/** Mensaje de error del impresor de documentos */
private String errorMsg;
/** Indica si se debe crear o no la transacción en caso de que
* no se asigne ninguna externamente */
private boolean createTrx = false;
/**
* Creates a fiscal document print
*
* @param LAR_Fiscal_Printer_ID
* fiscal printer configuration id
* @throws Exception
* if fiscal printer object cannot be instantiated
*/
// TODO - Review whether is more proper id or object instance for fiscal printer
public FiscalDocumentPrint(int LAR_Fiscal_Printer_ID) throws Exception
{
fiscalPrinter = new MFiscalPrinter(ctx, LAR_Fiscal_Printer_ID, null);
fiscalPrinterDevice = fiscalPrinter.getFiscalPrinter();
documentPrintListeners = new ArrayList<FiscalDocumentListener>();
}
/**
* Creates a fiscal document print with a printer listener
*
* @param LAR_Fiscal_Printer_ID
* fiscal printer configuration id
* @param fiscalPrinterListener
* listener for fiscal printer
* @throws Exception
* if fiscal printer object cannot be instantiated
*/
public FiscalDocumentPrint(int LAR_Fiscal_Printer_ID,
final FiscalPrinterListener fiscalPrinterListener) throws Exception
{
this(LAR_Fiscal_Printer_ID);
// this.printerEventListener = fpl;
setPrinterEventListener(fiscalPrinterListener);
}
/**
* Creates a fiscal document print with a printer and document listeners
*
* @param LAR_Fiscal_Printer_ID
* fiscal printer configuration id
* @param fiscalPrinterListener
* listener for fiscal printer
* @param fiscalDocumentListener
* listener for fiscal document
* @throws Exception
* if fiscal printer object cannot be instantiated
*/
public FiscalDocumentPrint(int LAR_Fiscal_Printer_ID,
final FiscalPrinterListener fiscalPrinterListener,
final FiscalDocumentListener fiscalDocumentListener) throws Exception
{
this(LAR_Fiscal_Printer_ID, fiscalPrinterListener);
addDocumentPrintListener(fiscalDocumentListener);
}
/**
* Ejecuta la acción (comando) parámetro con los argumentos específicos
* en la impresora parámetro.
* @param action acción a ejecutar
* @param args argumentos de la acción
* @return true si se ejecutó correctamente, false caso contrario.
*/
//MFiscalPrinter cFiscal = null;
private boolean execute(final Actions action, final Object[] args)
{
log.info("Executing action: " + action);
boolean error = false;
String newPrinterStatus = MFiscalPrinter.STATUS_IDLE;
String errorTitle = "";
String errorDesc = "";
// Se inicializa el indicador de fin de espera para el casos en que
// la impresora se encuentre en estado BUSY.
setCancelWaiting(false);
try {
// Se informa al manejador que se esta chequeando el status de
// la impresora.
fireActionStarted(FiscalDocumentListener.AC_CHECK_STATUS);
// Chequeo el estado de la impresora
if (!checkPrinterStatus(fiscalPrinter)) {
return false;
}
// Se informa al manejador que se esta intentando conectar con
// la impresora fiscal.
fireActionStarted(FiscalDocumentListener.AC_CONNECT_PRINTER);
// Se intenta conectar la impresora.
log.info("connecting to fiscal device");
getFiscalPrinter().connect();
// Ejecutar la acción correspondiente
doAction(action, args);
// Se libera la impresora fiscal.
setFiscalPrinterStatus(fiscalPrinter, MFiscalPrinter.STATUS_IDLE);
} catch (FiscalPrinterStatusError e) {
// Si la impresora retornó un estado de error se marca el controlador
// fiscal con estado de ERROR.
newPrinterStatus = MFiscalPrinter.STATUS_Error;
// Se asigna el mensaje de error.
errorTitle = "FiscalPrinterStatusError";
errorDesc = e.getDeviceErrorMsg();
error = true;
log.log(Level.SEVERE, e.getMessage() + ". " + errorDesc, e);
} catch (FiscalPrinterIOException e) {
// Se asigna el mensaje de error.
errorTitle = "FiscalPrinterIOError";
errorDesc = e.getMessage();
error = true;
log.log(Level.SEVERE, e.getFullMessage(), e);
} catch (DocumentException e) {
// Se asigna el mensaje de error.
errorTitle = "DocumentValidationError";
errorDesc = e.getMessage();
error = true;
log.log(Level.SEVERE, e.getMessage(), e);
} catch (IOException e) {
// Se asigna el mensaje de error.
errorTitle = "UnexpectedIOError";
errorDesc = e.getMessage();
error = true;
log.log(Level.SEVERE, e.getMessage(), e);
} catch (Exception e) {
// Se asigna el mensaje de error.
errorTitle = "PrintFiscalDocumentError";
errorDesc = e.getMessage();
error = true;
log.log(Level.SEVERE, e.getMessage(), e);
} finally {
try {
// Si hubo error...
if (error) {
// Se asigna el nuevo estado de la impresora.
setFiscalPrinterStatus(fiscalPrinter, newPrinterStatus);
// Se dispara el evento que informa del error ocurrido.
fireErrorOcurred(errorTitle, errorDesc);
// Se guarda el mensaje de error.
setErrorMsg("@" + errorTitle + "@ - @" + errorDesc + "@");
}
// Se desconecta la impresora en caso de que este conectada aún.
if (fiscalPrinterDevice != null && fiscalPrinterDevice.isConnected())
fiscalPrinterDevice.close();
} catch (IOException e) {
log.log(Level.SEVERE, e.getMessage(), e);
}
}
return !error;
} // execute
/**
* Ejecuto la acción correspondiente a partir de la acción parámetro
*
* @param action
* @param args
* @throws Exception
*/
private void doAction(final Actions action, final Object[] args) throws Exception
{
switch (action) {
case ACTION_PRINT_DOCUMENT:
doPrintDocument(args);
break;
case ACTION_FISCAL_CLOSE:
doFiscalClose(args);
break;
case ACTION_PRINT_DELIVERY_DOCUMENT:
doPrintDeliveryDocument(args);
break;
case ACTION_PRINT_SHIPMENT_DOCUMENT:
doPrintShipmentDocument(args);
break;
default:
throw new Exception(Msg.getMsg(ctx, "InvalidAction"));
}
}
// **************************************************
// ACCIONES (COMANDOS)
// **************************************************
// *************************
// IMPRESIÓN DE FACTURA
// *************************
/**
* Manda a imprimir un documento en una impresora fiscal.
*
* @param document
* <code>PO</code> que representa el documento a imprimir.
* @return <code>true</code> en caso de que el documento se haya emitido
* correctamente, <code>false</false> en caso contrario.
*/
// TODO - Review the parameter (i think that can be moved to contruction fase)
public boolean printDocument(final PO document)
{
// fireDocActionStatusChanged(new DocActionStatusEvent(this,
// DocActionStatusEvent.ST_FISCAL_PRINT_DOCUMENT, new Object[] { this }));
if (document == null) {
throw new IllegalArgumentException("Error: the document is null");
}
// Se valida que el documento tenga asignado el tipo de documento.
int C_DocType_ID = document.get_ValueAsInt("C_DocTypeTarget_ID");
if (C_DocType_ID == 0) {
throw new IllegalArgumentException("Error: the document has no type");
}
// Se obtiene el tipo de documento a emitir por la impresora.
MDocType docType = new MDocType(ctx, C_DocType_ID, null);
setPrinterDocType(docType.get_ValueAsString("FiscalDocument"));
// Se asigna el documento OXP.
setOxpDocument(document);
// Ejecutar la acción
boolean ok = execute(Actions.ACTION_PRINT_DOCUMENT, new Object[] { document });
// Se actualizan los datos del documento oxp.
// updateOxpDocument((MInvoice)document, !ok);
// reset documento oxp y tipo de doc de la impresora
// setOxpDocument(null);
setPrinterDocType(null);
return ok;
}
// // TODO - Review the parameters
// public boolean printDocument(final PO document, final Document documentPrintable, final MDocType docType, final MInvoice originalInvoice) {
// // Se valida que el tipo de documento exista
// if(docType == null) {
// throw new IllegalArgumentException("Error: No document type");
// }
//
// // Se obtiene el tipo de documento a emitir por la impresora.
// String fiscalDocument = (String) docType.get_Value("fiscaldocument");
// setPrinterDocType(fiscalDocument);
//
// // Se asigna el documento OXP.
// setOxpDocument(document);
//
// // Se obtiene el controlador fiscal para chequear el status
// //MFiscalPrinter cFiscal = MFiscalPrinter.getOfDocType(docType.get_ID());
//
// // Ejecutar la acción
// boolean ok = execute(Actions.ACTION_PRINT_DOCUMENT,
// new Object[] { document, documentPrintable, originalInvoice });
//
// // Se actualizan los datos del documento oxp.
// //updateOxpDocument((MInvoice)document, !ok);
//
// // reset documento oxp y tipo de doc de la impresora
// //setOxpDocument(null);
// setPrinterDocType(null);
// return ok;
// }
/**
* Realiza la impresión de la factura con los parámetros correspondientes.
*
* @param args
* @throws Exception
*/
// TODO - Review the parameters: object array with differents t
private void doPrintDocument(final Object[] args) throws Exception
{
// Argumentos
//MInvoice document = (MInvoice) args[0];
Document documentPrintable = null;
MInvoice originalInvoice = null;
// Factura imprimible por la impresora creada a partir del documento oxp
if (args.length > 1) {
documentPrintable = (Document) args[1];
}
// Documento oxp original configurada en el documento oxp, posiblemente
// necesario para notas de crédito
if (args.length > 2) {
originalInvoice = (MInvoice) args[2];
}
// Se manda a imprimir el documento según el tipo de documento
// de las impresoras fiscales asignado al tipo de documento de oxp
fireActionStarted(FiscalDocumentListener.AC_PRINT_DOCUMENT);
// Emisión de una factura.
if (getPrinterDocType().equals(LAR_MDocType.FISCALDOCUMENT_Invoice)) {
printInvoice(documentPrintable);
// Emisión de una nota de crédito.
} else if (getPrinterDocType().equals(LAR_MDocType.FISCALDOCUMENT_CreditNote)) {
printCreditNote(documentPrintable, originalInvoice);
// Emisión de una nota de débito.
} else if (getPrinterDocType().equals(LAR_MDocType.FISCALDOCUMENT_DebitNote)) {
printDebitNote(documentPrintable);
}
// Se dispara el evento de impresión finalizada.
fireDocumentPrintEndedOk();
// Se actualiza la secuencia del tipo de documento emitido.
//updateDocTypeSequence(document);
}
// *************************
// CIERRE FISCAL
// *************************
/**
* Envia el comando de cierre de jornada fiscal a la impresora.
* @param closeType Tipo de cierre
* @return verdadero en caso de exito, falso si hubo algún problema
*/
public boolean fiscalClose(final String closeType) {
// Ejecutar la acción
return execute(Actions.ACTION_FISCAL_CLOSE, new Object[]{closeType});
}
/**
* Realiza el cierre con los parámetros dados
* @param args arreglo de parámetros del procedimiento
* @throws Exception
*/
private void doFiscalClose(final Object[] args) throws Exception{
// Argumentos
String closeType = (String)args[0];
fireActionStarted(FiscalDocumentListener.AC_EXECUTING_ACTION);
// Cerrar la impresora fiscal
getFiscalPrinter().fiscalClose(closeType);
// Se dispara el evento de impresión finalizada.
fireActionEndedOk(Actions.ACTION_FISCAL_CLOSE);
}
// **************************************************************
// IMPRESION DE DOCUMENTO CON ARTICULOS PENDIENTES DE ENTREGA
// **************************************************************
/**
* Manda a imprimir un documento NO fiscal que en sus líneas contiene
* el detalle de cada artículo que tiene alguna cantidad pendiente de
* entrega dentro de un pedido del sistema.
* @param cFiscalID Impresora que emite el documento
* @param order Pedido del cual se obtienen los artículos pendientes de
* entrega
* @return <code>true</code> en caso de que el documento se haya emitido
* correctamente, <code>false</false> en caso contrario.
*/
public boolean printDeliveryDocument(final MOrder order) {
return execute(Actions.ACTION_PRINT_DELIVERY_DOCUMENT, new Object[] {order});
}
/**
* Imprime un remito en un documento no-fiscal homologado (DFNH) que en sus
* líneas contiene el detalle de cada artículo que tiene dicho remito.
*
* @param shipment remito a imprimir
* @return <code>true</code> en caso de que el documento se haya emitido
* correctamente, <code>false</false> en caso contrario.
*/
public boolean printShipmentDocument(final PO shipment)
{
return execute(Actions.ACTION_PRINT_SHIPMENT_DOCUMENT, new Object[] { shipment });
}
/**
* Realiza la impresión del documento no fiscal homologado
* con los artículos a entregar.
*
* @param args Arreglo con los argumentos requeridos por esta funcionalidad
* @throws Exception
*/
private void doPrintShipmentDocument(final Object[] args) throws Exception
{
final MInOut shipment = (MInOut) args[0];
// Informa el inicio de la impresión
fireActionStarted(FiscalDocumentListener.AC_PRINT_DOCUMENT);
// Crea el documento no fiscal y luego obtiene todas las líneas del pedido
final DNFH dnfh = createDNFH(shipment);
// Se asigna el documento OXP.
setOxpDocument(shipment);
// Manda a imprimir el documento en la impresora fiscal
getFiscalPrinter().printDocument(dnfh);
// Guarda la info devuelta por el controlador
saveShipmentData(shipment, dnfh);
// Se dispara el evento de impresión finalizada.
fireDocumentPrintEndedOk();
}
/**
* Realiza la impresión del documento no fiscal con los artículos a entregar.
* @param args Arreglo con los argumentos requeridos por esta funcionalidad
* @throws Exception
*/
private void doPrintDeliveryDocument(final Object[] args) throws Exception {
MOrder order = (MOrder) args[0];
// Informa el inicio de la impresión
fireActionStarted(FiscalDocumentListener.AC_PRINT_DOCUMENT);
// Crea el documento no fiscal y luego obtiene todas las líneas del pedido
NonFiscalDocument nonFiscalDocument = new NonFiscalDocument();
//MOrderLine[] orderLines = order.getLines(true);
MOrderLine[] orderLines = order.getLines();//TODO - revisar porque tenía arg true
String line = null;
for (MOrderLine orderLine : orderLines) {
// Por cada línea, si tiene artículos pendientes de entrega
//if (orderLine.hasNotDeliveredProducts()) {//TODO - reivsar porque AD no tiene hasNotDeliveredProducts
// Obtiene la cantidad que falta entregar
BigDecimal qtyToDeliver = orderLine.getQtyOrdered().subtract(orderLine.getQtyDelivered());
MProduct product = orderLine.getProduct();
// Crea la descripción que se mostrará en la línea del documento
line =
"[x" + qtyToDeliver + "] " + product.getValue() + " " + product.getName();
// Agrega la línea al documento no fiscal
nonFiscalDocument.addLine(line);
//}
}
// Manda a imprimir el documento en la impresora fiscal
getFiscalPrinter().printDocument(nonFiscalDocument);
// Se dispara el evento de impresión finalizada.
fireDocumentPrintEndedOk();
}
// **************************************************
/**
* @return Returns the fiscalPrinter.
*/
public FiscalPrinterDevice getFiscalPrinter() {
return fiscalPrinterDevice;
}
/**
* Crea un documento imprimible mediante un controlador fiscal a partir de
* la factura parámetro y del tipo de documento fiscal configurado.
*
* @param mInvoice
* factura oxp
* @param originalInvoice
* factura original oxp para notas de crédito
* @return documento imprimible fiscalmente creado a partir de los
* parámetros y del tipo de documento fiscal configurado
*/
public Document createDocument(final MInvoice mInvoice, final MInvoice originalInvoice){
Document document = null;
// Creación de una factura.
if(getPrinterDocType().equals(LAR_MDocType.FISCALDOCUMENT_Invoice)) {
document = createInvoice(mInvoice);
// Creación de una nota de crédito.
} else if(getPrinterDocType().equals(LAR_MDocType.FISCALDOCUMENT_CreditNote)) {
document = createCreditNote(mInvoice, originalInvoice);
// Creación de una nota de débito.
} else if(getPrinterDocType().equals(LAR_MDocType.FISCALDOCUMENT_DebitNote)) {
document = createDebitNote(mInvoice);
}
return document;
}
/**
* Crea una factura imprimible por el controlador fiscal a partir de la
* factura oxp parámetro
*
* @param mInvoice
* factura
* @return la factura imprimible creada
*/
public Invoice createInvoice(final MInvoice mInvoice){
Invoice invoice = new Invoice();
// Se asigna el cliente.
invoice.setCustomer(getCustomer(mInvoice.getC_BPartner_ID()));
// Se asigna la letra de la factura.
invoice.setLetter(LAR_Utils.getLetter(mInvoice));
// Se asigna el número de remito en caso de existir.
loadShipmentOrderNumbers(mInvoice, invoice); // @emmie
// Se agregan las líneas de la factura al documento.
loadDocumentLines(mInvoice, invoice);
// Agrega los pagos correspondientes de la factura partir de las imputaciones
loadInvoicePayments(invoice, mInvoice);
// Se asignan los descuentos de la factura
//loadDocumentDiscounts(invoice, mInvoice.getDiscounts());
return invoice;
}
/**
* Crea una nota de débito imprimible por un controlador fiscal a partir de
* una factura oxp parámetro
*
* @param mInvoice
* factura oxp
* @return nota de débito creada
*/
public DebitNote createDebitNote(final MInvoice mInvoice){
DebitNote debitNote = new DebitNote();
// Se asigna el cliente.
debitNote.setCustomer(getCustomer(mInvoice.getC_BPartner_ID()));
// Se asigna la letra de la nota de débito.
debitNote.setLetter(LAR_Utils.getLetter(mInvoice));
// TODO: Se asigna el número de remito en caso de existir.
// Se agregan las líneas de la nota de débito al documento.
loadDocumentLines(mInvoice, debitNote);
return debitNote;
}
/**
* Crea una nota de crédito imprimible por un controlador fiscal a partir de
* una factura oxp parámetro. La factura original parámetro permite obtener
* el nro de factura original.
*
* @param mInvoice
* factura oxp
* @param originalInvoice
* factura original, si es null y la factura oxp parámetro
* contiene seteado una factura original dentro del campo
* C_Invoice_Orig_ID se busca desde la BD.
* @return nota de crédito imprimible por un controlador fiscal
*/
public CreditNote createCreditNote(final MInvoice mInvoice, final MInvoice originalInvoice){
CreditNote creditNote = new CreditNote();
// Se asigna el cliente.
creditNote.setCustomer(getCustomer(mInvoice.getC_BPartner_ID()));
// Se asigna la letra de la nota de crédito.
creditNote.setLetter(LAR_Utils.getLetter(mInvoice));
// Se asigna el número de factura original.
String origInvoiceNumber = null;
MInvoice mOriginalInvoice = originalInvoice;
// Si la factura parámetro es null y la factura oxp parámetro contiene
// una factura original seteada entonces la busco
if (mOriginalInvoice == null && mInvoice.get_ValueAsInt("Source_Invoice_ID") != 0) {
mOriginalInvoice = new MInvoice(ctx, mInvoice.get_ValueAsInt("Source_Invoice_ID"), getTrxName());
}
// Si existe una factura original entonces obtengo el nro de factura
// original
if(mOriginalInvoice != null) {
origInvoiceNumber = mOriginalInvoice.getDocumentNo();
// Si no cumple con el formato de comprobantes fiscales se envia
// el documentNo como número de factura original.
if(origInvoiceNumber.length() == 13) {
// El formato es: PPPP-NNNNNNNN, Ej: 0001-00000023
origInvoiceNumber = origInvoiceNumber.substring(1,5) + "-" + origInvoiceNumber.substring(5,13);
}
}
creditNote.setOriginalDocumentNo(origInvoiceNumber);
// Se agregan las líneas de la nota de crédito al documento.
loadDocumentLines(mInvoice, creditNote);
return creditNote;
}
private DNFH createDNFH(final MInOut shipment)
{
final DNFH dnfh = new DNFH();
// Se asigna el cliente.
Customer customer = null;
final MBPartner bPartner = new MBPartner(Env.getCtx(), shipment.getC_BPartner_ID(), null);
if (bPartner != null)
{
customer = new Customer();
// Se asigna la categoría de iva del cliente.
LAR_TaxPayerType taxPayerType = LAR_TaxPayerType.getTaxPayerType(bPartner);
customer.setIvaResponsibility(traduceTaxPayerType(taxPayerType.getName()));
// Se asigna el nombre del cliente a partir del BPartner.
customer.setName(bPartner.getName());
// Se asigna el domicilio.
final MLocation location = MLocation.getBPLocation(Env.getCtx(), shipment.getC_BPartner_Location_ID(), getTrxName());
customer.setLocation(location.toString());
// Se identifica al cliente con el C.U.I.T. configurado en el Bpartner.
if (bPartner.getTaxID() != null && !bPartner.getTaxID().trim().equals(""))
{
customer.setIdentificationType(Customer.CUIT);
customer.setIdentificationNumber(bPartner.getTaxID());
}
}
dnfh.setCustomer(customer);
// Recupera el numero de copias a imprimir
int numberOfCopies = MSysConfig.getIntValue("LAR_Remitos_NumeroDeCopias", 0, Env.getAD_Client_ID(shipment.getCtx()));
dnfh.setNumberOfCopies(numberOfCopies);
// Cargar las lineas del remito
loadDNFHLines(shipment, dnfh);
return dnfh;
} // createDNFH
private void saveShipmentData(final MInOut shipment, final DNFH dnfh)
{
// Recupera el Nro de POS (tamaño 4)
final MDocType dt = new MDocType(shipment.getCtx(), shipment.getC_DocType_ID(), shipment.get_TrxName());
final String sql = "SELECT PosNumber FROM C_POS WHERE C_POS_ID=?";
int nroPOS = DB.getSQLValue(shipment.get_TrxName(), sql, dt.get_ValueAsInt("C_POS_ID"));
String pos = Integer.valueOf(nroPOS).toString();
pos = ("0000" + pos).substring(pos.length(), pos.length() + 4);
// Recupera el Nro de Remito generado (tamaño 8)
String documentNo = dnfh.getDocumentNo();
documentNo = ("00000000" + documentNo).substring(documentNo.length(), documentNo.length() + 8);
// Marca el remito como impreso en controlador y registra info recuperada
documentNo = "R" + pos + documentNo;
shipment.set_ValueOfColumn("FiscalReceiptNumber", dnfh.getDocumentNo());
shipment.set_ValueOfColumn("IsFiscalPrinted", true);
shipment.setDocumentNo(documentNo);
shipment.saveEx();
}
/**
* Impresión de una factura.
*
* @param document
* factura imprimible creada a partir del documento oxp, si es
* null se crea una nueva a partir del documento oxp que está
* configurado. Dentro de este método se realiza un casting del
* documento parámetro hacia {@link Invoice} por lo que debe ser
* una instancia de esa clase sino se producirá un error en
* tiempo de ejecución.
*/
// TODO - Redesign this method
private void printInvoice(final Document document) throws FiscalPrinterStatusError, FiscalPrinterIOException, Exception {
MInvoice mInvoice = (MInvoice)getOxpDocument();
// Se valida el documento OXP.
validateOxpDocument(mInvoice);
/*
* @emmie inicio - Impresion remito En caso de tratarse de una venta en
* ctacte, se imprime el remito asocido a la factura ANTES de imprimir
* la factura
*/
final MOrder order = new MOrder(ctx, mInvoice.getC_Order_ID(), getTrxName());
boolean printShipment = order.get_ValueAsBoolean("PrintShipment");
if (printShipment && mInvoice.getC_PaymentTerm_ID() == PosOrderModel.PAYMENTTERMS_Account)
{
// Se recupera el remito de la primera linea (se asume 1 remito)
int m_InOut_ID = mInvoice.getLines()[0].getM_InOutLine().getM_InOut_ID();
final MInOut shipment = new MInOut(ctx, m_InOut_ID, getTrxName());
// Crea el documento no-fiscal y luego obtiene todas las líneas del pedido
final DNFH dnfh = createDNFH(shipment);
// Se le indica al documento no-fiscal que no finalice la impresión
dnfh.setPrintEnded(false);
// Manda a imprimir el documento en la impresora fiscal
getFiscalPrinter().printDocument(dnfh);
// Guarda la info devuelta por el controlador
saveShipmentData(shipment, dnfh);
}
// @emmie fin - Impresion remito
// Se crea la factura imprimible en caso que no exista como parámetro
final Invoice printeableInvoice;
if (document != null) {
printeableInvoice = (Invoice) document;
} else {
printeableInvoice = createInvoice(mInvoice);
}
// Se manda a imprimir la factura a la impresora fiscal.
getFiscalPrinter().printDocument(printeableInvoice);
// Se actualizan los datos de la factura de oxp.
saveDocumentData(mInvoice, printeableInvoice);
}
/**
* Impresión de una nota de débito.
*
* @param document
* nota de débito imprimible por el controlador fiscal creada a
* partir del documento oxp configurado. Dentro de este método se
* realiza un casting del documento parámetro hacia
* {@link DebitNote}, por lo tanto debe ser una instancia de esa
* clase, sino se producirá un error.
*/
private void printDebitNote(final Document document) throws Exception {
MInvoice mInvoice = (MInvoice)getOxpDocument();
// Se valida el documento OXP.
validateOxpDocument(mInvoice);
// Se crea la nota de débito imprimible
DebitNote debitNote = document != null ? (DebitNote) document
: createDebitNote(mInvoice);
// Se manda a imprimir la nota de débito a la impresora fiscal.
getFiscalPrinter().printDocument(debitNote);
// Se actualizan los datos de la nota de debito de oxp.
saveDocumentData(mInvoice, debitNote);
}
/**
* Impresión de una nota de crédito.
*
* @param document
* nota de crédito imprimible por el controlador fiscal creada a
* partir del documento oxp configurado. Dentro de este método se
* realiza un casting del documento parámetro hacia
* {@link CreditNote}, por lo tanto debe ser una instancia de esa
* clase, sino se producirá un error.
* @param originalInvoice
* factura original del documento oxp configurado, si es null y
* el documento parámetro también, se verifica si el documento
* oxp configurado contiene una factura original, en ese caso la
* obtiene de la BD.
*/
private void printCreditNote(final Document document, final MInvoice originalInvoice) throws FiscalPrinterStatusError, FiscalPrinterIOException, Exception {
MInvoice mInvoice = (MInvoice)getOxpDocument();
// Se valida el documento OXP.
validateOxpDocument(mInvoice);
CreditNote creditNote = document != null ? (CreditNote) document
: createCreditNote(mInvoice, originalInvoice);
// Se manda a imprimir la nota de crédito a la impresora fiscal.
getFiscalPrinter().printDocument(creditNote);
// Se actualizan los datos de la nota de crédito de oxp.
saveDocumentData(mInvoice, creditNote);
}
/**
* Crea un <code>Customer</code> a partir de una entidad comercial.
* @param bPartnerID ID de la entidad comercial.
* @return el cliente correspondiente.
*/
public Customer getCustomer(int bPartnerID) {
//TODO - Review this implementation
MBPartner bPartner = new MBPartner(Env.getCtx(), bPartnerID, getTrxName());
Customer customer = new Customer();
if (bPartner != null) {
// Se asigna la categoría de iva del cliente.
LAR_TaxPayerType taxPayerType = LAR_TaxPayerType.getTaxPayerType(bPartner);
customer.setIvaResponsibility(traduceTaxPayerType(taxPayerType.getName()));
MInvoice mInvoice = (MInvoice) getOxpDocument();// TODO - Parametrize this method
// Se asigna el nombre del cliente a partir del BPartner.
customer.setName(bPartner.getName());
// Se asigna el domicilio.
MLocation location = MLocation.getBPLocation(Env.getCtx(), mInvoice.getC_BPartner_Location_ID(),
getTrxName());
customer.setLocation(location.toString());
// Se identifica al cliente con el C.U.I.T. configurado en el
// Bpartner.
if (bPartner.getTaxID() != null && !bPartner.getTaxID().trim().equals("")) {
customer.setIdentificationType(Customer.CUIT);
customer.setIdentificationNumber(bPartner.getTaxID());
}
}
return customer;
}
/**
* Retrieve shipment and sales order numbers, if they exists.
*
* @param mInvoice ADempiere invoice document
* @param document fiscal printer document
*/
private void loadShipmentOrderNumbers(final MInvoice mInvoice, final Invoice document)
{
final I_C_Order order = mInvoice.getC_Order();
if (order.getC_Order_ID() > 0)
document.addObservation(Msg.translate(ctx, "C_Order_ID") + ": " + order.getDocumentNo());
final MInvoiceLine[] iLines = mInvoice.getLines();
for (final MInvoiceLine line : iLines)
{
final I_M_InOutLine ioline = line.getM_InOutLine();
if (ioline.getM_InOutLine_ID() > 0)
{
String obs = ioline.getM_InOut().getDocumentNo();
if (!document.getObservations().contains(obs))
document.addObservation(obs);
}
}
}
/**
* Carga las lineas del remito en el documento no-fiscal
* homologado a imprimir
*
* @param shipment remito del cual se obtienen la lineas
* @param document documento no-fiscal a imprimir
*/
private void loadDNFHLines(final MInOut shipment, final Document document)
{
for (MInOutLine line : shipment.getLines())
{
final DocumentLine docLine = new DocumentLine();
docLine.setLineNumber(line.getLine());
docLine.setQuantity(line.getQtyEntered());
final MProduct product = MProduct.get(ctx, line.getM_Product_ID());
// Usar los campos Identificador para definir el contenido de la linea
if (fiscalPrinter.isOnPrintUseProductReference())
{
docLine.setDescription(genDescriptionFromIdentifiers(product));
}
// Usar alguna de las combinaciones CLAVE NOMBRE - NOMBRE CLAVE - NOMBRE - CLAVE
else
{
String description = " ";
String name = " ";
String value = " ";
// recuperar clave y nombre del articulo
if (product.getValue() != null && product.getValue().trim().isEmpty())
value = product.getValue().trim();
if (product.getName() != null && !product.getName().trim().isEmpty())
name = product.getName().trim();
// armar la descripción según la selección
if (MFiscalPrinter.ONPRINTPRODUCTFORMAT_Name.equals(fiscalPrinter.getOnPrintProductFormat()))
description = name;
if (MFiscalPrinter.ONPRINTPRODUCTFORMAT_Value.equals(fiscalPrinter.getOnPrintProductFormat()))
description = value;
if (MFiscalPrinter.ONPRINTPRODUCTFORMAT_NameValue.equals(fiscalPrinter.getOnPrintProductFormat()))
description = name + " " + value;
if (MFiscalPrinter.ONPRINTPRODUCTFORMAT_ValueName.equals(fiscalPrinter.getOnPrintProductFormat()))
description = value + " " + name;
docLine.setDescription(description);
// Se agrega la línea al documento.
document.addLine(docLine);
}
}
} // loadDNFHLines
/**
* Carga las líneas que se encuentran en el documento de ADempiere hacia
* el documento de impresoras fiscales.
* @param mInvoice Documento de ADempiere.
* @param document Documento de impresoras fiscales.
*/
private void loadDocumentLines(final MInvoice mInvoice, final Document document) {
// Se obtiene el indicador de si los precios contienen los impuestos incluido
boolean taxIncluded = MPriceList.get(ctx, mInvoice.getM_PriceList_ID(), getTrxName()).isTaxIncluded();
// Se obtiene el redondeo para precios de la moneda de la factura
//int scale = MCurrency.get(oxpDocument.getCtx(), oxpDocument.getC_Currency_ID()).getCostingPrecision();
MInvoiceLine[] lines = mInvoice.getLines();
BigDecimal totalLineAmt = BigDecimal.ZERO;
//String description = "";
for (int i = 0; i < lines.length; i++)
{
MInvoiceLine mLine = lines[i];
BigDecimal qtyEntered = mLine.getQtyEntered();
// @emmie - avoid "special" invoice lines (as shipments comments lines)
if (qtyEntered.compareTo(BigDecimal.ZERO) > 0)
{
DocumentLine docLine = new DocumentLine();
docLine.setLineNumber(mLine.getLine());
docLine.setDescription(manageLineDescription(docLine, mLine));
// TODO - Review discounts at invoice behavior
// Calcula el precio unitario de la línea.
// Aquí tenemos dos casos de línea: Con Bonificaciones o Sin
// Bonificaciones
// 1. Sin Bonificaciones
// El precio unitario es entonces simplemente el precio actual
// de la
// línea, es decir el PriceActual.
// if (!mLine.hasBonus()) {
BigDecimal unitPrice = mLine.getPriceActual();
totalLineAmt = totalLineAmt.add(unitPrice);
// } else {
// 2. Con Bonificaciones
// Aquí NO se puede utilizar el mLine.getPriceActual() ya que el
// mismo tiene contemplado las bonificaciones mientras que en la
// impresión del ticket, las bonificaciones se restan al final
// del mismo. De esta forma, el precio unitario para el ticket
// va a ser mayor que el PriceActual de la línea en caso de que
// la misma contenga bonificaciones.
// El cálculo a realizar es:
// (PriceList * Qty - LineDiscountAmt) / Qty
//
// unitPrice =
// (mLine.getPriceList().multiply(mLine.getQtyEntered())
// .subtract(mLine.getLineDiscountAmt())).divide(
// mLine.getQtyEntered(), scale, RoundingMode.HALF_UP);
// }
// LAR - Process discount for invoice
final I_C_OrderLine ol = mLine.getC_OrderLine();
final BigDecimal discountRate = ol.getDiscount();
if (discountRate.compareTo(BigDecimal.ZERO) > 0)
{
final BigDecimal originalAmt = BigDecimal.valueOf(100).multiply(unitPrice).divide(
BigDecimal.valueOf(100).subtract(discountRate), 2, BigDecimal.ROUND_FLOOR);
final DiscountLine discountLine = new DiscountLine("Dto aplicado",
originalAmt.subtract(unitPrice).multiply(mLine.getQtyEntered()), true, false, discountRate);
// Add discount to document line
docLine.setDiscount(discountLine);
unitPrice = originalAmt; // Set proper unitPrice
}
// LAR - Process discount for invoice
docLine.setUnitPrice(unitPrice);
docLine.setQuantity(mLine.getQtyEntered());
docLine.setPriceIncludeIva(taxIncluded);
// Se obtiene la tasa del IVA de la línea
// Se asume que el impuesto es siempre IVA, a futuro se verá
// que hacer si el producto tiene otro impuesto que no sea IVA.
MTax mTax = MTax.get(Env.getCtx(), mLine.getC_Tax_ID());
docLine.setIvaRate(mTax.getRate());
// Se agrega la línea al documento.
document.addLine(docLine);
}
}
// TODO - Improve this behavior
BigDecimal amt = mInvoice.get_Value("WithHoldingAmt") == null ? Env.ZERO :
((BigDecimal) mInvoice.get_Value("WithHoldingAmt")).negate(); // LAR perception are negative
if (amt.compareTo(BigDecimal.ZERO) > 0)
{
// TODO Corregir el calculo del porcentaje de percepción.
// BigDecimal rate = amt.divide(totalLineAmt, 2, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100));
// @mzuniga - Se agrega la provincia correspondiente a la Percepción en la descripción de la línea
String sql = "SELECT l.RegionName FROM AD_OrgInfo oi JOIN C_Location l ON oi.C_Location_ID = l.C_Location_ID WHERE oi.AD_Org_ID=?";
String prov = DB.getSQLValueString(mInvoice.get_TrxName(), sql, (Integer) mInvoice.get_Value("AD_Org_ID"));
String desc = "Perc. IIBB " + prov; //String.format("Percepci\u00f3n", rate);
// @mzuniga
PerceptionLine perceptionLine = new PerceptionLine(desc, amt, null);
document.setPerceptionLine(perceptionLine);
}
}
/**
* Carga los descuentos para un documento a imprimir.
* @param document Documento a imprimir
* @param discounts Lista de descuentos que se deben cargar.
*/
// private void loadDocumentDiscounts(Document document, List<MDocumentDiscount> discounts) {
// BigDecimal generalDiscountAmt = BigDecimal.ZERO;
// DiscountLine discountLine = null;
// for (MDocumentDiscount mDocumentDiscount : discounts) {
// // Solo se tienen en cuenta descuentos que sean Bonificación o a nivel de
// // Documento. Aquellos que son "Al Precio" ya se encuentran reflejados en
// // los precios de las líneas del documento, y no deben se impresos en el
// // ticket.
// if (MDocumentDiscount.CUMULATIVELEVEL_Line.equals(mDocumentDiscount
// .getCumulativeLevel())
// && !mDocumentDiscount.isBonusApplication()) {
// continue;
// }
//
// // Si es descuento general manual se suma el importe al total de descuento
// // de ese tipo. Este tipo de descuento es proporcional a todas las tasas de
// // impuestos con lo cual no es necesario discriminar los importes según la
// // tasa.
// if (mDocumentDiscount.isManualGeneralDiscountKind()) {
// generalDiscountAmt = generalDiscountAmt.add(mDocumentDiscount
// .getDiscountAmt().negate());
// } else {
// // Para el resto de bonificaciones, se crea una línea de descuento por cada
// // tasa de impuesto debido a que el controlador fiscal requiere saber para
// // cada importe descontado, a que tasa de impuesto afecta para registrar la
// // reducción o incremento del importe computado para la misma.
// for (MDocumentDiscount mDiscountByTax : mDocumentDiscount
// .getDiscountsByTax()) {
//
// // Crea la línea de descuento para la tasa
// discountLine = new DiscountLine(
// mDiscountByTax.getDescription(),
// mDiscountByTax.getDiscountAmt().negate(),
// true, // Los importes en DocumentDiscount incluyen siempre el impuesto
// mDiscountByTax.getTaxRate());
// // Agrega el descuento al documento.
// document.addDocumentDiscount(discountLine);
// }
// }
// }
//
// // Si hay descuentos manuales generales se asigna un descuento general
// // al documento.
// if (generalDiscountAmt.compareTo(BigDecimal.ZERO) != 0) {
// document.setGeneralDiscount(
// new DiscountLine(
// Msg.translate(Env.getCtx(), "FiscalTicketGeneralDiscount"),
// generalDiscountAmt,
// true // Incluye impuestos
// )
// );
// }
//
// }
/**
* Carga los pagos en la factura a emitir a partir de las imputaciones que
* tenga la factura en la BD.
*/
private void loadInvoicePayments(final Invoice invoice, final MInvoice mInvoice) {
BigDecimal totalPaidAmt = BigDecimal.ZERO;
final String othersDesc = Msg.translate(ctx, "FiscalTicketOthersPayment");
final String cashDesc = Msg.translate(ctx, "FiscalTicketCashPayment");
// Lista temporal de pagos creados a partir de los allocations
List<Payment> payments = new ArrayList<Payment>();
// Obtiene todas las imputaciones de la factura agrupadas por pago.
String sql =
//TODO - Review this, we may be needed for billing outside POS
"SELECT C_Payment_ID, " +
"C_CashLine_ID, " +
//"C_Invoice_Credit_ID, " +
"SUM(Amount + DiscountAmt + WriteoffAmt) AS PaidAmount " +
"FROM C_AllocationLine " +
"WHERE C_Invoice_ID = ? " +
///////////////////////////////////////////////////////////////////////////
" AND 1=2" + // <<<<<<<<<<<<<<<<<< (emmie) AVOID PROCESS THIS QUERY
///////////////////////////////////////////////////////////////////////////
"GROUP BY C_Payment_ID, C_CashLine_ID " + //, C_Invoice_Credit_ID " +
"ORDER BY PaidAmount ";
PreparedStatement pstmt = null;
ResultSet rs = null;
// Crea los pagos de Efectivo y Otros para acumular montos de sendos tipos.
Payment othersPayment = new Payment(BigDecimal.ZERO, othersDesc);
Payment cashPayment =new Payment(BigDecimal.ZERO, cashDesc);
try {
pstmt = DB.prepareStatement(sql, getTrxName());
pstmt.setInt(1, mInvoice.getC_Invoice_ID());
rs = pstmt.executeQuery();
int paymentID;
int cashLineID;
//int invoiceCreditID;
BigDecimal paidAmt = null;
String description = null;
// Pago que se crea en caso de que la imputación no entre en la clase
// Efectivo u Otros Pagos.
Payment payment = null;
while (rs.next()) {
// Obtiene los IDs de los documentos para determinar cual es el que
// se utilizó para el pago
paymentID = rs.getInt("C_Payment_ID");
cashLineID = rs.getInt("C_CashLine_ID");
//invoiceCreditID = rs.getInt("C_Invoice_Credit_ID");
paidAmt = rs.getBigDecimal("PaidAmount");
description = null;
payment = null;
// 1. Imputación con un C_Payment.
if (paymentID > 0) {
// Obtiene la descripción.
description = getInvoicePaymentDescription(new MPayment(ctx, paymentID, getTrxName()));
// 2. Imputación con Línea de Caja
} else if (cashLineID > 0) {
// Todas las imputaciones con líneas de caja se suman al pago
// global en Efectivo para imprimir una única línea que diga
// "Efectivo".
cashPayment.setAmount(cashPayment.getAmount().add(paidAmt));
// 3. Imputación con Factura de Crédito (NC)
// } else if (invoiceCreditID > 0) {
// // Obtiene la descripción.
// description = getInvoicePaymentDescription(new MInvoice(
// ctx, invoiceCreditID, getTrxName()));
}
// Si es un tipo que entra dentro de "Otros Pagos", se suma el importe
// al payment de "Otros Pagos".
if (othersDesc.equals(description)) {
othersPayment.setAmount(othersPayment.getAmount().add(paidAmt));
// Caso Contrario (Tarjeta, Cheque, Transferencia, NC, etc), se crea el pago
// con la descripción.
} else if (description != null) {
payment = new Payment(paidAmt, description);
}
// Si se creó un nuevo pago se agrega a la lista ordenada de pagos
// según el mayor importe.
if (payment != null) {
payments.add(payment);
}
totalPaidAmt = totalPaidAmt.add(paidAmt);
} // while
} catch (SQLException e) {
log.log(Level.SEVERE, "Error getting invoice allocations", e);
} finally {
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
} catch (Exception e) {}
}
// Si el total pagado es cero implica que la factura no contiene imputaciones
// de pagos realizados. En ese caso, se agrega un único pago por el total de
// la factura y en la descripción se pone el PaymentRule de la factura.
if (totalPaidAmt.compareTo(BigDecimal.ZERO) == 0) {
// Se crea un pago con el total de la factura y el medio de pago en
// la descripción.
String paymentRule = mInvoice.getPaymentRule();
String paymentMedium = MRefList.getListName(ctx,
MInvoice.PAYMENTRULE_AD_Reference_ID, paymentRule);
invoice.addPayment(new Payment(mInvoice.getGrandTotal(),
paymentMedium));
// Si hay pagos, se cargan a los pagos de la factura a emitir.
} else {
int paymentQty = 0;
// Primero el efectivo
if (cashPayment.getAmount().compareTo(BigDecimal.ZERO) > 0) {
invoice.addPayment(cashPayment);
paymentQty++;
}
// Si hay "otros pagos" se suma el contador pero se agrega al final
// de la lista de modo que la impresión quede en esa ubicación.
if (othersPayment.getAmount().compareTo(BigDecimal.ZERO) > 0) {
paymentQty++;
}
int allowedCount = getFiscalPrinter().getAllowedPaymentQty();
for (Iterator<Payment> paymentsIter = payments.iterator(); paymentsIter.hasNext();) {
Payment payment = paymentsIter.next();
if (paymentQty == allowedCount - 1) {
if (!paymentsIter.hasNext()
|| othersPayment.getAmount().compareTo(
BigDecimal.ZERO) > 0) {
invoice.addPayment(payment);
} else {
othersPayment.setAmount(othersPayment.getAmount().add(
payment.getAmount()));
}
} else if (paymentQty < allowedCount) {
invoice.addPayment(payment);
} else {
othersPayment.setAmount(othersPayment.getAmount().add(
payment.getAmount()));
}
paymentQty++;
}
if (othersPayment.getAmount().compareTo(BigDecimal.ZERO) > 0) {
invoice.addPayment(othersPayment);
}
}
}
/**
* Devuelve la descripción a imprimir para un pago según un MPayment.
*/
private String getInvoicePaymentDescription(final MPayment mPayment) {
//Properties ctx = mPayment.getCtx();
String description = null;
// - Tarjeta de Crédito: NombreTarjeta NroCupon.
// Ej: VISA 1248
if (MPayment.TENDERTYPE_CreditCard.equals(mPayment.getTenderType())) {
description = MRefList.getListName(ctx,
MPayment.CREDITCARDTYPE_AD_Reference_ID,
mPayment.getCreditCardType()); // + " " + mPayment.getCouponNumber();
// - Cheque: Cheque NumeroCheque
// Ej: Cheque 00032456
} else if (MPayment.TENDERTYPE_Check.equals(mPayment.getTenderType())) {
description = Msg.translate(ctx, "FiscalTicketCheckPayment") + " "
+ mPayment.getCheckNo();
// - Transferencia: Transf NroDeTransferencia
// Ej: Transf 893276662
} else if (MPayment.TENDERTYPE_DirectDeposit.equals(mPayment.getTenderType())) {
description = Msg.translate(ctx, "FiscalTicketTransferPayment")
+ " " + mPayment.getCheckNo(); // En CheckNo se guarda el nro de transferencia actualmente.
// Otros tipos: Otros pagos
} else {
description = Msg.translate(ctx, "FiscalTicketOthersPayment");
}
return description;
}
/**
* Devuelve la descripción a imprimir para un pago según un MInvoice.
*/
// private String getInvoicePaymentDescription(MInvoice mInvoice) {
// String description = null;
// // - Nota de Crédito: NC NroNotaCrédito
// // Ej: NC A000100004567
// if (LAR_MDocType.DOCBASETYPE_ARCreditMemo.equals(MDocType.get(ctx,
// mInvoice.getC_DocType_ID()).getDocBaseType())) {
// description = Msg.translate(ctx, "FiscalTicketCreditNotePayment")
// + " " + mInvoice.getDocumentNo();
// // - Resto de facturas (FP, Retenciones, etc): se toman como "Otros Pagos"
// } else {
// description = Msg.translate(ctx, "FiscalTicketOthersPayment");
// }
//
// return description;
// }
private void saveDocumentData(final MInvoice mInvoice, final Document printeableDocument) {
String dtType = printeableDocument.getDocumentType();
/////////////////////////////////////////////////////////////////
// -- Numero del comprobante emitido --
//Solo para facturas, notas de crédito y débito.
if (dtType.equals(Document.DT_INVOICE) || dtType.equals(Document.DT_CREDIT_NOTE)
|| dtType.equals(Document.DT_DEBIT_NOTE)) {
// TODO - Review receipt fiscal number implementation
// int receiptNo =
// Integer.parseInt(printeableDocument.getDocumentNo());
String printedFiscalReceiptNumber = printeableDocument.getDocumentNo();
// Solo se actualiza el documento de oxp en caso de que el
// número de comprobante emitido por la impresora fiscal
// sead distinto al que le había asignado oxp.
String fiscalReceiptNumber = mInvoice.get_ValueAsString("FiscalReceiptNumber");
if (printedFiscalReceiptNumber != fiscalReceiptNumber) {
mInvoice.set_ValueOfColumn("FiscalReceiptNumber", printedFiscalReceiptNumber);
// Se modifica el número de documento de OXP acorde al número de
// comprobante fiscal.
// String documentNo = CalloutInvoiceExt.GenerarNumeroDeDocumento(
// oxpDocument.getPuntoDeVenta(), receiptNo, oxpDocument.getLetra(), oxpDocument.isSOTrx());
// oxpDocument.setDocumentNo(documentNo);
}
/////////////////////////////////////////////////////////////////
// -- Numero del CAI --
// Solo para facturas y notas de débito, siempre y cuando
// la impresora haya seteado alguno.
if (dtType.equals(Document.DT_INVOICE) || dtType.equals(Document.DT_DEBIT_NOTE)) {
Invoice invoiceOrDN = (Invoice) printeableDocument;
if (invoiceOrDN.hasCAINumber()) {
// Se asigna el número del CAI.
mInvoice.set_ValueOfColumn("CAI", invoiceOrDN.getCAINumber());
// Se asigna la fecha del CAI como la fecha actual.
mInvoice.set_ValueOfColumn("CAIDate", new Timestamp(System.currentTimeMillis()));
}
}
/////////////////////////////////////////////////////////////////
// -- Documento Impreso por Impresora fiscal --
// Se marca el documento como impreso fiscalmente para que no pueda
// volver a imprimirse.
mInvoice.set_ValueOfColumn("IsFiscalPrinted", true);
// Se guardan los cambios realizados.
if (canSaveDocument()) {
mInvoice.save();
}
}
}
/**
* Creates and caches tax payer types
*
* @return maps of tax categories
*/
protected Map<String, Integer> getTaxPayerTypes() {
if(taxPayerTypes == null) {
taxPayerTypes = new HashMap<String,Integer>();
taxPayerTypes.put(CONSUMIDOR_FINAL.getName(), Customer.CONSUMIDOR_FINAL);
taxPayerTypes.put(RESPONSABLE_INSCRIPTO.getName(), Customer.RESPONSABLE_INSCRIPTO);
taxPayerTypes.put(EXENTO.getName(), Customer.EXENTO);
taxPayerTypes.put(RESPONSABLE_MONOTRIBUTO.getName(), Customer.RESPONSABLE_MONOTRIBUTO);
taxPayerTypes.put(NO_CATEGORIZADO.getName(), Customer.NO_CATEGORIZADO);
}
return taxPayerTypes;
}
/**
* Realiza la conversión entre el entero que representa a la categoría de
* IVA de openXpertya y el entero en las clases de documentos de las
* impresoras fiscales.
*
* @param taxPayerType
* Valor de la responsabilidad frente a IVA.
* @return El entero que representa la responsabilidad frente al IVA.
*/
protected int traduceTaxPayerType(final String taxPayerType) {
Integer result = getTaxPayerTypes().get(taxPayerType);
if(result == null) {
result = Customer.NO_CATEGORIZADO;
}
return result;
}
/**
* @return Returns the printerEventListener.
*/
// public FiscalPrinterListener getPrinterEventListener() {
// return printerEventListener;
// }
/**
* @param printerEventListener The printerEventListener to set.
*/
public void setPrinterEventListener(final FiscalPrinterListener printerEventListener) {
//this.printerEventListener = printerEventListener;
log.info("Set printer event listener. Fiscal device: " + getFiscalPrinter());
if(getFiscalPrinter() != null)
getFiscalPrinter().setEventListener(printerEventListener);
}
private void fireStatusReported(final MFiscalPrinter cFiscal, final String status) {
for (FiscalDocumentListener fdpl : getDocumentPrintListeners()) {
fdpl.statusReported(this, cFiscal, status);
}
}
private void fireStatusReported(final MFiscalPrinter cFiscal) {
fireStatusReported(cFiscal, cFiscal.getStatus());
}
private void fireActionStarted(int action) {
for (FiscalDocumentListener fdpl : getDocumentPrintListeners()) {
fdpl.actionStarted(this, action);
}
}
protected void fireErrorOcurred(final String errorTitle, final String errorDesc) {
for (FiscalDocumentListener fdpl : getDocumentPrintListeners()) {
fdpl.errorOcurred(
this,
Msg.parseTranslation(ctx,errorTitle),
Msg.parseTranslation(ctx,errorDesc));
}
}
protected void fireDocumentPrintEndedOk() {
for (FiscalDocumentListener fdpl : getDocumentPrintListeners()) {
fdpl.documentPrintEndedOk(this);
}
}
private void fireActionEndedOk(final Actions action){
for (FiscalDocumentListener fdpl : getDocumentPrintListeners()) {
fdpl.actionEnded(true, action);
}
}
/**
* @return Returns the documentPrintListeners.
*/
public List<FiscalDocumentListener> getDocumentPrintListeners() {
return documentPrintListeners;
}
private boolean checkPrinterStatus(final MFiscalPrinter cFiscal) throws Exception {
int bsyCount = 0;
// Si la impresora se encuentra en estado de error se dispara el evento
// que informa dicha situación.
if(cFiscal.getStatus().equals(MFiscalPrinter.STATUS_Error)) {
fireStatusReported(cFiscal);
// Dependiendo de si hay que ignorar o no el estado de error
// se continua con la impresión. (Esto se utiliza para evitar
// los casos en que la impresora quede marcada como error en
// la BD pero el dispositivo ya no contenga mas este error.
if(isIgnoreErrorStatus())
// Por ello se setea la impresora como Lista y se intenta
// continuar con la impresión.
setFiscalPrinterStatus(cFiscal, MFiscalPrinter.STATUS_IDLE);
else
// Si no se pueden ignorar estados de error, entonces
// no es posible continuar con la impresión.
return false;
}
// Mientras el status sea BUSY, espera 5 segundos y vuelve a chequear.
while(cFiscal.getStatus().equals(MFiscalPrinter.STATUS_Busy) && !isCancelWaiting()) {
fireStatusReported(cFiscal);
Thread.sleep(BSY_SLEEP_TIME);
bsyCount++;
cFiscal.load((String)null);
if(bsyCount == MAX_BSY_SLEEP_COUNT)
throw new IOException(Msg.translate(ctx,"FiscalPrinterBusyTimeoutError"));
}
// Si fue cancelada la operacion de espera entonces se retorna, indicando
// que el estado no es correcto.
if(isCancelWaiting()) {
log.fine("Fiscal printer wait canceled");
return false;
}
fireStatusReported(cFiscal, MFiscalPrinter.STATUS_IDLE);
// Se asigna el status de la impresora, el usuario que realiza la operación
// y la fecha de operación.
cFiscal.setStatus(MFiscalPrinter.STATUS_Busy);
//cFiscal.setUsedBy_ID(Env.getAD_User_ID(ctx)); TODO - Revisar
cFiscal.setOperation_Date(new Timestamp(System.currentTimeMillis()));
// No se usa trx dado que los cambios deben ser visibles
// inmediatamente por otros usuarios.
cFiscal.save();
return true;
}
private void setFiscalPrinterStatus(final MFiscalPrinter cFiscal, String status) {
if(cFiscal != null) {
cFiscal.setStatus(status);
cFiscal.save();
}
}
/**
* @return Returns the ignoreErrorStatus.
*/
public boolean isIgnoreErrorStatus() {
return ignoreErrorStatus;
}
/**
* @param ignoreErrorStatus The ignoreErrorStatus to set.
*/
public void setIgnoreErrorStatus(boolean ignoreErrorStatus) {
this.ignoreErrorStatus = ignoreErrorStatus;
}
/**
* @return Returns the cancelWaiting.
*/
public boolean isCancelWaiting() {
return cancelWaiting;
}
/**
* @param cancelWaiting The cancelWaiting to set.
*/
public void setCancelWaiting(boolean cancelWaiting) {
this.cancelWaiting = cancelWaiting;
}
private void validateOxpDocument(final MInvoice mInvoice) throws Exception {
// Validar si la factura ya fue impresa.
if(mInvoice.get_ValueAsBoolean("IsFiscalPrinted")) {
log.severe("The invoice was already printed with a fiscal printer.");
throw new Exception(Msg.translate(ctx,"FiscalAlreadyPrintedError"));
}
}
// TODO - Determine if this behavior is needed
// private void updateOxpDocument(final MInvoice mInvoice, boolean error) {
// if(error) {
// //TODO: verificar.
// //mInvoice.setDocStatus(MInvoice.DOCSTATUS_InProgress);
// //mInvoice.save();
// }
// }
/* TODO - This method should not touch sequence, but an invoice proper field
private void updateDocTypeSequence(final MInvoice mInvoice) {
// Se actualiza la secuencia del tipo de documento del documento
// emitido recientemento por la impresora fiscal.
int lastDocumentNo = new Integer(getFiscalPrinter().getLastDocumentNo());
MDocType docType = MDocType.get(ctx, mInvoice.getC_DocTypeTarget_ID());
// Se obtiene la secuencia del tipo de documento...
if(docType.getDocNoSequence_ID() != 0) {
MSequence seq = new MSequence(ctx, docType.getDocNoSequence_ID(), getTrxName());
String currentNext = String.valueOf(seq.getCurrentNext());
NumberFormat format = NumberFormat.getNumberInstance();
format.setMinimumIntegerDigits(8);
format.setMaximumIntegerDigits(8);
format.setGroupingUsed(false);
// Se obtiene el número siguiente de documento según el ultimo comprobante
// emitido por la impresora fiscal.
String newCurrentNext = currentNext.substring(0,1) + format.format(lastDocumentNo + 1);
// Se actualiza la secuencia solo si el número de comprobante siguiente es distinto al
// que ya tenía la secuencia.
if(!currentNext.equals(newCurrentNext)) {
seq.setCurrentNext(Integer.parseInt(newCurrentNext));
seq.save();
}
}
}*/
/**
* Agrega un manejador de impresió de documentos al cual se le reportan
* los estados de la impresión.
* @param fdpl <code>FiscalDocumentListener</code> manejador de eventos.
*/
public void addDocumentPrintListener(final FiscalDocumentListener fdpl) {
log.info("Set document print listener. Document listener: " + getDocumentPrintListeners());
if(!getDocumentPrintListeners().contains(fdpl)) {
getDocumentPrintListeners().add(fdpl);
fdpl.setFiscalDocumentPrint(this);
}
}
/**
* Elimina un manejador de eventos de la colección.
* @param fdpl Manejador de eventos a eliminar.
*/
public void removeDocumentPrintListener(final FiscalDocumentListener fdpl) {
getDocumentPrintListeners().remove(fdpl);
}
/**
* @return Returns the printerDocType.
*/
public String getPrinterDocType() {
return printerDocType;
}
/**
* @param printerDocType The printerDocType to set.
*/
protected void setPrinterDocType(String printerDocType) {
this.printerDocType = printerDocType;
}
/**
* @return Returns the oxpDocument.
*/
public PO getOxpDocument() {
return oxpDocument;
}
/**
* @param oxpDocument The oxpDocument to set.
*/
protected void setOxpDocument(PO oxpDocument) {
this.oxpDocument = oxpDocument;
}
/**
* @return Returns the trx.
*/
public Trx getTrx() {
return trx;
}
/**
* @param trx The trx to set.
*/
public void setTrx(Trx trx) {
this.trx = trx;
}
/**
* @return El nombre de la transacción actual.
*/
protected String getTrxName() {
if(getTrx() == null)
return null;
return getTrx().getTrxName();
}
/**
* @return Returns the errorMsg.
*/
public String getErrorMsg() {
return errorMsg;
}
/**
* @param errorMsg The errorMsg to set.
*/
protected void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
/**
* @return Devuelve los mensajes de la impresora fiscal actualmente instanciada.
* Si no se ha instanciado una impresora fiscal devuelve <code>null</code>.
*/
public FiscalMessages getFiscalMessages() {
FiscalMessages fiscalMessages = null;
if (getFiscalPrinter() != null) {
fiscalMessages = getFiscalPrinter().getMessages();
}
return fiscalMessages;
}
/**
* @return Intenta reimprimir el documento asociado con este impresor, el
* cual se intentó imprimir previamente y se produjo algún error.
*/
public boolean reprintDocument() {
if (getOxpDocument() == null) {
throw new IllegalStateException("Document must be not null");
}
cancelWaiting = false;
errorMsg = null;
printerDocType = null;
//fiscalPrinter = null;
if (getOxpDocument() instanceof MInOut)
return printShipmentDocument(getOxpDocument());
return printDocument(getOxpDocument());
}
/**
* @return Indica si se debe crear o no la transacción en caso de que no se
* asigne ninguna externamente
*/
public boolean isCreateTrx() {
return createTrx;
}
/**
* Setea el valor indica si se debe crear o no la transacción en caso de que
* no se asigne ninguna externamente
*
* @param useTrx
* el valor de useTrx a asignar
*/
public void setCreateTrx(boolean createTrx) {
this.createTrx = createTrx;
}
/**
* @return true si se debe guardar el documento oxp, false caso contrario
*/
protected boolean canSaveDocument(){
return true;
}
/**
* @return el String a imprimir en cada linea del ticket
*/
private String manageLineDescription(DocumentLine docLine, MInvoiceLine mLine)
{
MProduct aProduct = null;
String value = "";
String name = "";
// Cargar la descripcion con la información de la línea
String description =
(mLine.getM_Product_ID() != 0 || mLine.getC_Charge_ID() != 0?
mLine.getName():mLine.getDescription());
// Si es un artículo se setea según la configuración indicada en la impresora fiscal
if (mLine.getM_Product_ID() > 0)
{
// recuperar el articulo
aProduct = MProduct.get(ctx, mLine.getM_Product_ID());
// vacio la descripcion, ya que no utilizaré la de la linea, sino la info del artículo
description = " ";
/* Usar los campos Identificador para definir el contenido de la linea */
if (fiscalPrinter.isOnPrintUseProductReference())
{
return genDescriptionFromIdentifiers(aProduct);
}
/* Usar alguna de las combinaciones CLAVE NOMBRE - NOMBRE CLAVE - NOMBRE - CLAVE */
else
{
// recuperar clave y nombre del articulo
if (aProduct.getValue() != null && !aProduct.getValue().trim().isEmpty())
value = aProduct.getValue().trim();
if (aProduct.getName() != null && !aProduct.getName().trim().isEmpty())
name = aProduct.getName().trim();
// armar la descripción según la selección
if (MFiscalPrinter.ONPRINTPRODUCTFORMAT_Name.equals(fiscalPrinter.getOnPrintProductFormat()))
description = name;
if (MFiscalPrinter.ONPRINTPRODUCTFORMAT_Value.equals(fiscalPrinter.getOnPrintProductFormat()))
description = value;
if (MFiscalPrinter.ONPRINTPRODUCTFORMAT_NameValue.equals(fiscalPrinter.getOnPrintProductFormat()))
description = name + " " + value;
if (MFiscalPrinter.ONPRINTPRODUCTFORMAT_ValueName.equals(fiscalPrinter.getOnPrintProductFormat()))
description = value + " " + name;
}
}
return description;
}
public static String genDescriptionFromIdentifiers(final MProduct mProduct)
{
// By default, indenfiers columns of product are search key (value) and name.
// Use this as description
// TODO - improve this behavior (if needed)
return mProduct.getValue() + " " + mProduct.getName();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder("FiscalDocumentPrint[");
sb.append("fiscalPrinter=").append(fiscalPrinter);
sb.append("fiscalDevice=").append(fiscalPrinterDevice);
return sb.toString();
}
}