opure / openbravoposru

Automatically exported from code.google.com/p/openbravoposru
0 stars 0 forks source link

Ответ от фискального регистратора Аура-01ФР-KZ(протокол Атол) #188

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Решила полностью разобраться с кассой, 
потому что там были глюки по некоторым 
причинам, например, если бумагу зажевало 
или бумага кончилась, или произошла ошибка 
при регистрации и т.д. А распечатать 
фискальный чек заново - это проблема, можно 
распечатать копию, но бывают разные 
клиенты и некоторых может не устроит 
нефискальный чек. 
Для этого мне нужно было обработать ответы 
от фискальника после каждой команды. Для 
начала, реализовала ответ в Logger, т.е. в 
функции readLine() класса DeviceAuraFRComm.java:
{
int ans = 0;
        synchronized (this) {

            if (!m_aLines.isEmpty()) {
                return m_aLines.poll();
            }

                try {
                    wait(T5);
                } catch (InterruptedException e) {
                }

            if (m_aLines.isEmpty()) {
                throw new TicketPrinterException("Timeout");
            } else {
                byte[] bs = m_aLines.peek();
                ByteArrayOutputStream l = new ByteArrayOutputStream();
                try {
                    l.write(bs);
                } catch (IOException ex) {
                    Logger.getLogger(DeviceAuraFRComm.class.getName()).log(Level.SEVERE, null, ex);
                }

                if(l.toByteArray().length >= 2){
              Logger.getLogger(DeviceAuraFRComm.class.getName()).log(Level.INFO, "{0} {1}", new Object[]{l.toByteArray()[2], m_FR.ReadErrorMessage(l.toByteArray()[2])});
                      ans = l.toByteArray()[2];

                }

                m_aLines.poll();
            }
        }
        return ans; // ответ фискальника
}
и изменила код функции sendMessage(), добавила 
переменную, которую отправляем дальше (int 
sendMessage() {.... return ans;}).
Все типы void в функциях переделала в int;
Создала static переменные 
status = 0;//состояние чека 
s = 0; //ответ фискальника
которые обнуляются при connectDevice() и 
disconnectDevice().
Например:
public void sendTextMessage(String sText) throws TicketPrinterException {
        if(status == 0){
           s = sendMessage(m_FR.TextMessage(sText), 1);
        }
         if(s != 0 && status != 1){
            status = 1;
            JOptionPane.showMessageDialog(null, m_FR.ReadErrorMessage((byte) s));
        }
    }
А при регистрации и регистрации возврата 
еще добавила "Аннулирование чека" в AuraFR.java:

    private static final byte REVOCATION = (byte) 0x59;
    ...
    public byte[] TicketRevocation() {
        counter = 1;
        ByteArrayOutputStream lineout = new ByteArrayOutputStream();
        lineout.write(STX);
        for (int i = 0; i < PASS.length; i++) lineout.write(PASS[i]);
        lineout.write(REVOCATION);
        lineout.write(ETX);
        lineout.write(calcCheckSumCRC(lineout.toByteArray()));
        return lineout.toByteArray();
    }
    для того, чтоб при ошибке регистрации аннулировать чек и заново напечатать, это как раз в тех случаях, когда бумагу зажевало или бумага кончилась.

     @Override
    public int sendRegistrationLine(int iFlag, double dProductPrice, double dSaleUnits, int iProductSection) throws TicketPrinterException {
        if(status == 0){
            s = sendMessage(m_FR.RegistrationLine(iFlag, dProductPrice, dSaleUnits, iProductSection), 1);
        }
        if(s != 0 && status != 1){
            status = 1;
            int i = sendMessage(m_FR.TicketRevocation(), 1);
            {
                JOptionPane.showMessageDialog(null, m_FR.ReadErrorMessage((byte) s)+AppLocal.getIntString("message.ticketrevocation")/*Чек аннулирован, при необходимости, распечатайте заново*/);
            }
            }
        return s;
    }
    @Override
    public int sendRefundLine(int iFlag, double dProductPrice, double dSaleUnits) throws TicketPrinterException {
        if(status == 0){
            s = sendMessage(m_FR.RefundLine(iFlag, dProductPrice, dSaleUnits), 1);
        }
        if(s != 0 && status != 1){
            status = 1;
            int i = sendMessage(m_FR.TicketRevocation(), 1);
            {
                JOptionPane.showMessageDialog(null, m_FR.ReadErrorMessage((byte) s)+AppLocal.getIntString("message.ticketrevocation")/*Чек аннулирован, при необходимости, распечатайте заново*/);
            }

        }
        return s;
    }

И еще у меня частенько бывали проблемы с 
печатью первого чека за смену, иногда не 
открывалась смена, и чек зависал. Для этого 
реализовала функцию, которая считает 
регистр, и в зависимости от ответа 
открывает смену:
    в AuraFR.java:

    private static final byte READ_REGISTER = (byte) 0x91;
    private static final byte OPEN = (byte) 0x9A; //Открыть смену

    ...

    public byte[] ReadRegister(int iRegister, int iParameter1, int iParameter2) {
        ByteArrayOutputStream lineout = new ByteArrayOutputStream();
        lineout.write(STX);
        for (int i = 0; i < PASS.length; i++) lineout.write(PASS[i]);
        lineout.write(READ_REGISTER);
        byte[] MSG = new byte[1];
        String hex = Integer.toHexString(iRegister);
        MSG = hexStringToByteArray(hex);
        lineout.write(MSG[0]);
        lineout.write(iParameter1);
        lineout.write(iParameter2);
        byte[] bData = new byte[lineout.size()];
        bData = lineout.toByteArray();
        lineout.reset();
        for (int i = 0; i < 4; i++) lineout.write(bData[i]);;
        for (int i = 4; i < bData.length; i++) {
            if (bData[i] == ETX || bData[i] == DLE) {
                lineout.write(DLE);
            }
            lineout.write(bData[i]);
        }
        lineout.write(ETX);
        lineout.write(calcCheckSumCRC(lineout.toByteArray()));
        return lineout.toByteArray();
    }

    ...

    public byte[] OpenMessage(int iFlag, String iText) {
        ByteArrayOutputStream lineout = new ByteArrayOutputStream();
        lineout.write(STX);
        for (int i = 0; i < PASS.length; i++) lineout.write(PASS[i]);
        lineout.write(OPEN);
        lineout.write(iFlag);
        lineout.write(ETX);
        lineout.write(calcCheckSumCRC(lineout.toByteArray()));
        return lineout.toByteArray();
    }

    а в DeviceAuraFRComm.java:

     @Override
    public int sendOpenTicket(int iFlag, int iTypeTicket) throws TicketPrinterException {
        if(status == 0) {
            int a = isOpen(m_FR.ReadRegister(18, 0, 0), 1);
            if(a == 0){
                s = sendMessage(m_FR.OpenMessage(0, ""), 1);
                if(s != 0 && status != 1){
                    status = 1;
                    JOptionPane.showMessageDialog(null, m_FR.ReadErrorMessage((byte) s));
                }
                s = sendMessage(m_FR.OpenTicket(iFlag, iTypeTicket), 1);
                if(s != 0 && status != 1){
                    status = 1;
                    JOptionPane.showMessageDialog(null, m_FR.ReadErrorMessage((byte) s));
                }
            }
            if(a == 1){
                s = sendMessage(m_FR.OpenTicket(iFlag, iTypeTicket), 1);
                if(s != 0 && status != 1){
                    status = 1;
                    JOptionPane.showMessageDialog(null, m_FR.ReadErrorMessage((byte) s));
                }
            }
        }
        return s;
    }

    ...

    private int isOpen(byte[] bMessage, int iGetAnswer) throws TicketPrinterException {
        int r = 0;
        writeLine(ENQ);
        System.out.println("ReadLine: ENQ " + readLine().length);
        writeLine(bMessage);
        System.out.println("ReadLine: bMessage " + readLine().length);
        writeLine(EOT);
        if (iGetAnswer == 1) {
            System.out.println("ReadLine: EOT " + readLine().length);
            writeLine(ACK);
            r = readACKIsOpen();
            System.out.println("ReadLine: ACK " + r);
           writeLine(ACK);
            System.out.println("ReadLine: ACK " + readLine().length);
        }
        return r;
    }

    private int readACKIsOpen() throws TicketPrinterException {
        int ans = 0;
        synchronized (this) {

            if (!m_aLines.isEmpty()) {
                m_aLines.poll();
            }

                try {
                    wait(T5);
                } catch (InterruptedException e) {
                }

            if (m_aLines.isEmpty()) {
                throw new TicketPrinterException("Timeout");
            } else {
                byte[] bs = m_aLines.peek();
                ByteArrayOutputStream l = new ByteArrayOutputStream();
                try {
                    l.write(bs);
                } catch (IOException ex) {
                    Logger.getLogger(DeviceAuraFRComm.class.getName()).log(Level.SEVERE, null, ex);
                }
                if(l.toByteArray().length >= 3){
                  Logger.getLogger(DeviceAuraFRComm.class.getName()).log(Level.INFO, "{0} {1}", new Object[]{l.toByteArray()[2], m_FR.ReadErrorMessage(l.toByteArray()[2])});
                  ans = l.toByteArray()[3];
                  }
                m_aLines.poll();
            }
        }
        return ans;
    }

    последние две функции почти что копия функции sendMessage(...) и readLine(...), но пока пусть будет так.

И наконец, все эти ответы нужно прочитать в 
TicketParser.java:

private static int ack = 0;

 class TicketException extends SAXException { //custom class for throwing

         private int type = -1;
         public TicketException(int t){
            type = t;
         }
         @Override
         public String getMessage (){
             return Integer.toString(type);
         }

     }

     ...
     public void endElement(String uri, String localName, String qName) throws SAXException {

        switch (m_iOutputType) {
        ...

        case OUTPUT_FISCAL:
            ...
            else if ("line".equals(qName)) {
                ack = m_printer.getFiscalPrinter().printLine(text.toString(), m_dValue1, m_dValue2, attribute3);
                if(ack != 0){
                    throw new TicketException(2);
                }
                text = null;               
            } 
            ...
            else if ("total".equals(qName)) {
                ack = m_printer.getFiscalPrinter().printTotal(text.toString(), m_dValue1, type);
                if(ack != 0){
                    throw new TicketException(1);
                }
                text = null;
                type = null;
            }
            break;

            ...
        }
    }

    public int printTicket(String sIn) throws TicketPrinterException {
        return printTicket(new StringReader(sIn));
    }

    public int printTicket(Reader in) throws TicketPrinterException  {
        try {
            ...
        } ...
        catch (SAXException eSAX) {
            if (eSAX instanceof TicketException) {
                return Integer.parseInt(eSAX.getMessage());
            } else {
                throw new TicketPrinterException(LocalRes.getIntString("exception.xmlfile") , eSAX);
            }
        } 
        ...
        return 0;
    }    

И последнее, получив ответ от фискальника, 
можно манипулировать чеком:

int i = 
m_TTP2.printTicket(script.eval(m_dlSystem.getResourceAsXML("Printer.Ticket")).to
String());
                    if(i == 0){
                       dlSales.getOrdersStatusUpdate(orderid, "6").exec(); //в нашей реализации программа немного отличается 
                    } else if(i == 1){
                       ...
                    } else if(i == 2){
                        ...
                    }

Кассовый аппарат один из самых важных и 
нужных оборудований в торговле, и думаю что 
нужно разработать максимально правильную 
реализацию функциональностей для 
правильной работы.

Original issue reported on code.google.com by m.makazh...@gmail.com on 26 Oct 2011 at 9:48

GoogleCodeExporter commented 9 years ago
Майя, отлично! Я добавил Вас в коммитеры 
проекта, прошу свои изменения сбросьте в 
репозитарий, это в будущем и мне и Вам будет 
удобнее, пароль к репозитарию смотрите на 
странице http://code.google.com/p/openbravoposru/source/checkout. 
Если будут ещё вопросы напишите мне лично 
на svininykh@gmail.com или по скайпу svininykh.

Original comment by svinin...@gmail.com on 26 Oct 2011 at 10:11

GoogleCodeExporter commented 9 years ago
Майя, мне кажется полностью решил 
поставленный вами вопрос, полностью 
переписав вывод и обработку методов в 
драйвере Аура(Атол) с выводом кода ошибки и 
текста на экран. Прошу посмотреть и 
протестировать r70ce6cc9c262 

Изменения кардинальные, главное все 
команды ФР выведены в отдельные классы, 
которые собраны в пакете 
com.openbravo.pos.printer.aurafr.command, такой подход хорошо 
себя зарекомендовал при работе с 
протоколом Штрих-М. Пока не работают 
появившиеся в Issue 214 новые методы printCashIn(double 
dSumm) и printCashOut(double dSumm), там мне нужно 
разобраться с считыванием регистров, но 
основная цепочка 
продажа->возврат->X-отчёт->Z-отчёт работают 
нормально. Жду ваших комментариев и 
пожеланий.

Original comment by svinin...@gmail.com on 25 Jun 2012 at 11:56

GoogleCodeExporter commented 9 years ago
Майя, извините, поспешил. В r745fc613d602 сделал 
решение на правильно указанную ошибку с 
первой регистрацией после закрытия смены, 
добавил команды на проверку регистра 
открытия и на само открытие смены. Сейчас 
проверил 10 раз весь цикл полностью, вроде 
всё работает без сбоев.

В r745fc613d602 также заработали команды 
внесения и выплаты наличности.

Original comment by svinin...@gmail.com on 26 Jun 2012 at 1:16