//--------------------------------------------------------------------------- #pragma hdrstop #include "PrinterOPOS.h" #include "OposFiscalPrinter_1_7_Lib_OCX.h" #include "ud_Module.h" #include #include #include #include //--------------------------------------------------------------------------- #pragma package(smart_init) //--------------------------------------------------------------------------- PrinterOPOS* printerOPOS; //--------------------------------------------------------------------------- char * kkm_err_msg_ru[] = { "OK", //0 "ФП ID ошибка", //1 FM ID Error "Ошибка записи в ФП ", //2 FM Rec Error "Flash Chip не может быть очищен", //3 Flash Chip cant clear "ФП не пуста", //4 FM not empty "Данные в ФП разрушенны", //5 FM Data destroyed "Реж принтер игнорир кмд", //6 Cmd ignor prn mode "Нет кода оператора", //7 Oper code is absent "Нет типа документа", //8 Doc type is absent "Сер. номер ККМ", //9 Ser num CR "Ошибка пароля", //10 Password error "Ошибка дата/время", //11 Data/Time error "Завод. рег. ранее", //12 Factory registration has already pass "Сер ном ККМ разрушен", //13 Ser num CR destroyed "Фискализация ранее", //14 Fiscalize has already pass "ККМ не фиск режим", //15 Not fisc mode CR "Фискал ККМ разрушен", //16 Fiscalize Data destroyed "Перерег ККМ исчерпан", //17 Rewrite is conclude "Перерег ККМ разрушен", //18 Rewrite is destroyed "Смена открыта", //19 Shift already open "См не была открыта", //20 Shift not opened "Много товара в чеке", //21 Many goods in receipt "СМ в ФП разрушенна", //22 EOD data in FM destroyed "Формат документа", //23 Document format error "Перезапись текста", //24 Text rewrite "Много пар-ров док", //25 Many parameters in document "Ош параметр док-та", //26 Document parameters error "Ош значения пар-ра", //27 Parameters value error "Несуществующ объект", //28 Object non-existent "Док не был завершен", //29 Document is not finished "Товар/опер не полно", //30 goods/operation description not enough "Контрол сумма пакета", //31 Check sum packet "Принтер не готов", //32 Prn not ready "Вложенные прерывания", //33 Nested interrupts "Байт подтв прием данных", //34 Check Sum Error "Переполненние в ФП", //35 Overflow if FM "Неизвестная комманда", //36 Unknown command "CRC ОЗУ", //37 CRC RAM "CRC ФП", //38 CRC FM "Порт данных неисправ", //39 Data port defective "Порядок рекв чека", //40 Receipts parameters sequence "0xy для обязат рекв", //41 0xy for required properties "Длинна запроса", //42 Length request "Разрядность числа", //43 number capacity "Переполнение", //44 Overflow "Нет числа", //45 Number is absent "Нет ответа", //46 Not response "Ошибка передачи", //47 Transmission error "Мат. переполнение", //48 Math overflow "Нет бумаги...", //49 Paper out... "ККМ не готова!!!", //50 CR not ready!!! "Сбой памяти!!", //51 Memory error!! "Принтер выключен", //52 Printer off "Недостаточно памяти", //53 Insufficient memory "Длинна номера меньше", //54 Lenght number less "Нулевая длинна номера", //55 Zero lenght number "Длинна пароля меньше допустимой", //56 Password lenght less permissible "Нулевая длинна пароля", //57 Zero lenght password "Недопустимая величина", //58 Impossible value "Превышен тайм-аут", //59 Time-out overdraw "", "Ош.ЭКЛЗ", "Недопустимое состояние ЭКЛЗ", "Данные учтены", "","","","","","",//69 "","","","","","","","","","",//79 "","","","","","","","","","",//89 "","","","","","","","","","",//99 "","","","","","","","","","",//109 "","","","","","","","","","",//119 "","","","","","","","","","",//129 "","","","","","","","","","",//139 "","","","","","","","","","",//149 "","","","","","","","","","",//159 "","","","","","","","","","",//169 "","","","","","","","","","",//179 "","","","","","","","","","",//189 "","","","","","","","","","",//199 "","","","","","","","","","",//209 "","","","","","","","","","",//219 "","","","","","","","","","",//229 "","","","","","","","","","",//239 "Команды нет",//240 Cmd not init "Ош. посылки команды",//241 Cmd write err "Команда не отослана ККМ-неготов(DSR=0)",//242 Prn not ready (DSR=0) "Команда отослана, но ответ что ККМ-неготов",//243 Cmd ok "Превышено время ожидания ответа",//244 Cmd resp timeout "Ошибка",//245 Prn error "Пришло больше данных, чем ожидали",//246 Cmd resp large "Пришло меньше данных, чем ожидали",//247 Cmd resp small "Пришли некие данные",//248 "Порт закрыт",//249 "Порт закрыт во время выполнения",//250 Cmd break "ККМ выключен или сломан",//251 "ККМ неготов",//252 "Ошибка в параметрах команды",//253 "Несуществующий номер команды",//254 "ККМ занят(предыдущая команда)!"//255 Previous cmd run }; //--------------------------------------------------------------------------- PrinterOPOS::PrinterOPOS(TComponent* AOwner) { Crashed=false; fp=new TOPOSFiscalPrinter(AOwner); fp->DeviceEnabled = False; FListGoods= new TList(); LoadSetup(); } //--------------------------------------------------------------------------- PrinterOPOS::~PrinterOPOS() { delete FListGoods; delete fp; } //--------------------------------------------------------------------------- void PrinterOPOS::LoadSetup() { TIniFile* ini=new TIniFile(ExtractFilePath(ParamStr(0))+"\\Setup.ini"); // cPort->port=ini->ReadString("Printer","Port",""); // cPort->BaudRate=ini->ReadInteger("Printer","BaudRate",0); CheckId=ini->ReadInteger("Printer","CheckCount",0); TestMode=ini->ReadBool("Printer","TestMode",true); HeadLine=ini->ReadString("Printer","HeadLine",""); delete ini; } //--------------------------------------------------------------------------- void PrinterOPOS::SaveSetup() { TIniFile* ini=new TIniFile(ExtractFilePath(ParamStr(0))+"\\Setup.ini"); // ini->WriteString("Printer","Port",cPort->port); // ini->WriteInteger("Printer","BaudRate",cPort->BaudRate); ini->WriteInteger("Printer","CheckCount",CheckId); ini->WriteBool("Printer","TestMode",CheckId); ini->WriteString("Printer","HeadLine",HeadLine); delete ini; } //--------------------------------------------------------------------------- bool PrinterOPOS::Start() //Соедениться по COM порту и проинициализировать принтер { if(fp->DeviceEnabled) return true; FState = fp->Open(L"MbsFiscalPrinter"); if(FState!=0) { saveLog(IntToStr(FState)+"(PrinterOPOS::Start)"+GetStatusDescription(FState)); return false; } FState = fp->ClaimDevice(1000); if(FState!=0) { saveLog(IntToStr(FState)+"(PrinterOPOS::Start)"+GetStatusDescription(FState)); return false; } fp->DeviceEnabled = True; return true; } //--------------------------------------------------------------------------- void PrinterOPOS::setHeader(TStrings* Text) { //if(fp->DayOpened){FState=19; return;} long err; for(int i=0;iCount;i++) { WideString str=Text->Strings[i]; err=fp->SetHeaderLine(i+1,str,false); if(err!=0) { saveLog(IntToStr(err)+"(PrinterOPOS::setHeader)"+GetStatusDescription(err)); break; } } err=fp->ResetPrinter(); if(err!=0) saveLog(IntToStr(err)+"(PrinterOPOS::setHeader)"+GetStatusDescription(err)); } //--------------------------------------------------------------------------- void PrinterOPOS::getHeader(TStrings* Text) { TIniFile* ini=new TIniFile(ExtractFilePath(ParamStr(0))+"\\Setup.ini"); Text->Add(ini->ReadString("Printer","HeadLine1","")); Text->Add(ini->ReadString("Printer","HeadLine2","")); Text->Add(ini->ReadString("Printer","HeadLine3","")); Text->Add(ini->ReadString("Printer","HeadLine4","")); Text->Add(ini->ReadString("Printer","HeadLine5","")); delete ini; for(int i=Text->Count-1;i>=0;i--) if(Text->Strings[i]=="")Text->Delete(i); else break; } //--------------------------------------------------------------------------- void PrinterOPOS::setTrailer(TStrings* Text) { //if(fp->DayOpened){FState=19; return;} long err; for(int i=0;iCount;i++) { WideString str=Text->Strings[i]; err=fp->SetTrailerLine(i+1,str,false); if(err!=0) { saveLog(IntToStr(err)+"(PrinterOPOS::setTrailer)"+GetStatusDescription(err)); break; } } err=fp->ResetPrinter(); if(err!=0) saveLog(IntToStr(err)+"(PrinterOPOS::setTrailer)"+GetStatusDescription(err)); } //--------------------------------------------------------------------------- void PrinterOPOS::getTrailer(TStrings* Text) { TIniFile* ini=new TIniFile(ExtractFilePath(ParamStr(0))+"\\Setup.ini"); Text->Add(ini->ReadString("Printer","TailLine1","")); Text->Add(ini->ReadString("Printer","TailLine2","")); Text->Add(ini->ReadString("Printer","TailLine3","")); Text->Add(ini->ReadString("Printer","TailLine4","")); Text->Add(ini->ReadString("Printer","TailLine5","")); delete ini; for(int i=Text->Count-1;i>=0;i--) if(Text->Strings[i]=="")Text->Delete(i); else break; } //--------------------------------------------------------------------------- void PrinterOPOS::addGoods(Goods* goods) { FListGoods->Add(goods); } //--------------------------------------------------------------------------- void PrinterOPOS::ClearGoods() { try { while(FListGoods->Count>0) { Goods* g=(Goods*)FListGoods->Items[0]; delete g; FListGoods->Delete(0); } FListGoods->Clear(); }catch(...) { Crashed=true; } } //--------------------------------------------------------------------------- int PrinterOPOS::getGoodsPrice() { int sum=0; for(int i=0;iCount;i++) sum+=((Goods*)FListGoods->Items[i])->price; return sum; } //------------------------------------------------------------------------------ int PrinterOPOS::getNextCheckId() { CheckId++; //Номер чека SaveSetup(); //сохраняем номер чека return CheckId; } //------------------------------------------------------------------------------ bool PrinterOPOS::SaveCheck(int CheckId) //Сохранение чека для последующей распечатки (суммы) { if(dModule->UserType) //true - Юредическое лицо; false - физическое лицо dModule->SaveCheck(CheckId,dModule->UserId,0,getGoodsPrice()); else dModule->SaveCheck(CheckId,0,dModule->UserId,getGoodsPrice()); } //--------------------------------------------------------------------------- bool PrinterOPOS::deleteCheck(int CheckId) //Удаление чека { dModule->UpdateChek(CheckId); } //--------------------------------------------------------------------------- bool PrinterOPOS::PrintLine(WideString line) { //Не фискальная печать long err = fp->PrintNormal(2, WideString(WCHAR(0x1B)) + WideString(WCHAR(0x7C)) + line + L"\n"); return err==0; } //--------------------------------------------------------------------------- //sales - true продажа, false - возврат //check - номер чека если не задан =0 то генериться //TODO Фича это фиксировать не распечатанные чеки потом печатать их bool PrinterOPOS::PrintCheck(bool sales, int check) //Тестовая печать { long err=0; Currency cur=0; Currency cAllPrice=0; bool bOk=true; try { //Считаем сумму в чеке (заранее на случай ошибки) cAllPrice=getGoodsPrice(); if(check==0) //если не задан номер чека то генерим сдесь { CheckId++; //Номер чека check=CheckId; SaveSetup(); //сохраняем номер чека } if(fp->RecEmpty) throw Exception("Нет бумаги!"); if(!DayOpened()) if(!OpenShift()) throw Exception("Не удалось открыть смену"); Currency UnitPrice,AllPriceDrb,AllPrice; long Qnt,vat; BSTR* bstr; WideString sStr; BSTR sTmp=new OLECHAR[255]; sTmp=L""; long MbsResponceCode; CURRENCY Amount1,Amount2; //Установка номера отдела //vat=11; sTmp=L"отдел"; //err = fp->DirectIO(4, &vat, &sTmp); //if (err) goto ErrHandler; if(sales) fp->FiscalReceiptType = FPTR_RT_SALES; //Чек продажи else fp->FiscalReceiptType=FPTR_RT_SERVICE; //Чек возврата //Фискальная печать (с заголовком или без) err = fp->BeginFiscalReceipt(true); if (err) throw Exception(getLastError(&err)); //251 //Товары из списка в драйвер for(int i=0;iCount;i++) { Goods* goods=(Goods*)FListGoods->Items[i]; VarCyFromR8(goods->price*100,&Amount1); Amount2.int64=0; err=fp->PrintRecItem(goods->name+L"-/--/-0", Amount1, goods->count, goods->VatInfo, Amount2, L"Шт"); if (err) throw Exception(getLastError(&err)); } ClearGoods(); //установка номера чека (Перед PrintRecTotal) err = fp->DirectIO(3, &CheckId, &sTmp); if (err) throw Exception(getLastError(&err)); //Установка значений массива налоговых ставок //setNalog(1,12); //Формирования итога в чеке Amount2.int64=0; VarCyFromR8(cAllPrice*100,&Amount1); err=fp->PrintRecTotal(Amount1, Amount1, L"na"); if (err) throw Exception(getLastError(&err)); err = fp->EndFiscalReceipt(True); //Окончания печати, в нем происходит печать дополнительного трейлера (AdditionalTrailer) и обрезка бумаги if(err) throw Exception(getLastError(&err)); } catch(Exception &exception) { printerOPOS->ClearGoods(); if(err==251) Crashed=true; //Кабздец saveLog("Ошибка при печати чека: N: "+IntToStr(dModule->UserId)+" сумма: "+CurrToStr(cAllPrice)+" Чек: "+IntToStr(CheckId)+". "+exception.Message); dModule->SendError(err,"Ошибка при печати чека: N: "+IntToStr(dModule->UserId)+" сумма: "+IntToStr(cAllPrice)+" Чек: "+IntToStr(CheckId)+". "+exception.Message); bOk=false; } catch(...) { printerOPOS->ClearGoods(); if(err==251) Crashed=true; //Кабздец saveLog("Не опознанная ошибка при печати чека: N: "+IntToStr(dModule->UserId)+" сумма: "+IntToStr(cAllPrice)+" Чек: "+IntToStr(CheckId)); dModule->SendError(1,"Не опознанная ошибка при печати чека: N: "+IntToStr(dModule->UserId)+" сумма: "+IntToStr(cAllPrice)+" Чек: "+IntToStr(CheckId)); bOk=false; } //сброс чека if(!bOk) { fp->PrintRecVoid(L""); } return bOk; } //--------------------------------------------------------------------------- void PrinterOPOS::Cut() //Отрезать бумагу { FState = fp->PrintNormal(2, WideString(WCHAR(0x1B)) + WideString(WCHAR(0x7C)) + "75P"); //обрезка бумаги if(FState!=0) saveLog(IntToStr(FState)+"(PrinterOPOS::Cut)"+GetStatusDescription(FState)); } //--------------------------------------------------------------------------- //Получить текущее состояние принтера (чтоб проверить готовность печати) int PrinterOPOS::GetState() { if(!fp->DeviceEnabled)FState=245; //if(FState!=0) return FState; //проверяем //Надо добавить лог // fp->ResultCodeExtended return 0; } //--------------------------------------------------------------------------- //Проверка роботоспособности принтера bool PrinterOPOS::isOk() { long err=0; try { if(Crashed) throw Exception("Драйвер принтера разрушен!"); if(fp->RecEmpty) throw "Нет бумаги!"; AnsiString str; if(!DayOpened()) OpenShift(); if(DayOpened()) { //Проверка на длину смены TDateTime dt=getDateOpenShift(); str=getLastError(&err); if(err) throw Exception(getLastError(&err)); dModule->getDate(); if(HoursBetween(dt,dModule->date)>24) //Разница не более 24 часов throw Exception("[1]Смена не закрыта"); } else throw Exception("Принтер не работает!"); BSTR bstr=new OLECHAR[50]; err=fp->GetDate(&bstr); if(err) throw Exception(getLastError(&err)); } catch(Exception &exception) { if(err==251) Crashed=true; //Кабздец saveLog("(PrinterOPOS::isOk)"+exception.Message); dModule->SendError(1,"(PrinterOPOS::isOk)"+exception.Message); return false; } catch (...) { if(err==251) Crashed=true; //Кабздец saveLog("(PrinterOPOS::isOk)Неопознанная ошибка"); dModule->SendError(1,"(PrinterOPOS::isOk)Неопознанная ошибка"); return false; } return true; } //--------------------------------------------------------------------------- //Получить дату открытия смены TDateTime PrinterOPOS::getDateOpenShift() { long iTmp,err; BSTR sTmp=new OLECHAR[50]; err = fp->DirectIO(13, &iTmp, &sTmp); if(err!=0) return 0; return StrToDateTime(sTmp); } //--------------------------------------------------------------------------- //Описание статуса AnsiString PrinterOPOS::GetStatusDescription(long err) { AnsiString str; try { if(err<256) str=kkm_err_msg_ru[err]; if(str=="") str="Нет описания ошибки"; }catch(...) { saveLog("(PrinterOPOS::GetStatusDescription) Нет описания ошибки"); return "Нет описания ошибки"; } return str; } //--------------------------------------------------------------------------- //Получить код последней ошибки AnsiString PrinterOPOS::getLastError(long* err) { BSTR str=new OLECHAR[255]; str=L"Нет описания ошибки!"; try { fp->DirectIO(1, err, &str); }catch(...) { saveLog("(PrinterOPOS::getLastError) Нет описания ошибки"); } if(str==L"") return GetStatusDescription(*err); return str; } //--------------------------------------------------------------------------- AnsiString PrinterOPOS::getLastError() { long err; return getLastError(&err); } //--------------------------------------------------------------------------- int PrinterOPOS::getCOMPort() { int n=-1; TRegistry* reg=new TRegistry; reg->RootKey = HKEY_LOCAL_MACHINE; if(reg->OpenKeyReadOnly("\\SOFTWARE\\OLEforRetail\\ServiceOPOS\\FiscalPrinter\\MbsFiscalPrinter")) { n = reg->ReadInteger("COM Num"); } delete reg; return n; } //--------------------------------------------------------------------------- void PrinterOPOS::setCOMPort(int n) { TRegistry* reg=new TRegistry; reg->RootKey = HKEY_LOCAL_MACHINE; if(reg->OpenKey("\\SOFTWARE\\OLEforRetail\\ServiceOPOS\\FiscalPrinter\\MbsFiscalPrinter",true)) { reg->WriteInteger("COM Num",n); } delete reg; } //--------------------------------------------------------------------------- int PrinterOPOS::getBaudRate() { int n=-1; TRegistry* reg=new TRegistry; reg->RootKey = HKEY_LOCAL_MACHINE; if(reg->OpenKeyReadOnly("\\SOFTWARE\\OLEforRetail\\ServiceOPOS\\FiscalPrinter\\MbsFiscalPrinter")) { n = reg->ReadInteger("COM Speed"); } delete reg; return n; } //--------------------------------------------------------------------------- void PrinterOPOS::setBaudRate(int val) { TRegistry* reg=new TRegistry; reg->RootKey = HKEY_LOCAL_MACHINE; if(reg->OpenKey("\\SOFTWARE\\OLEforRetail\\ServiceOPOS\\FiscalPrinter\\MbsFiscalPrinter",true)) { reg->WriteInteger("COM Speed",val); } delete reg; } //--------------------------------------------------------------------------- //Печать отчёта и закрытие смены bool PrinterOPOS::PrintZReport() { int sum=getDaySum(); long err=fp->PrintZReport(); if(err==0) { dModule->addZReport(sum);//Послать на сервер время снятия Z отчёта dModule->addPaper(-142*2);//Для подсчёта конца бумаги }else { saveLog("Ошибка печати Z отчёта"); dModule->SendError(1,"Ошибка печати Z отчёта"); } return err==0; } //--------------------------------------------------------------------------- //Печать суточного отчёта без закрытия смены void PrinterOPOS::PrintXReport() { fp->PrintXReport(); } //--------------------------------------------------------------------------- //Открытие смены bool PrinterOPOS::OpenShift() { long n=1; BSTR str=L"Платёжный терминал 1"; long err=fp->DirectIO (6,&n,&str); //Передал номер и название кассира if(err!=0) saveLog(IntToStr(err)+"(PrinterOPOS::OpenShift)"+GetStatusDescription(err)); err=fp->SetVatValue(1,L"120"); return err==0; } //--------------------------------------------------------------------------- bool PrinterOPOS::DayOpened() { return fp->DayOpened; } //--------------------------------------------------------------------------- bool PrinterOPOS::setTrainingMode(bool val) { if(getTrainingMode()==val) return true; long err; if(val) err=fp->BeginTraining(); else err=fp->EndTraining(); AnsiString str=getLastError(&err); if(err!=0) saveLog(IntToStr(err)+"(PrinterOPOS::setTrainingMode)"+str); return err==0; } //--------------------------------------------------------------------------- bool PrinterOPOS::getTrainingMode() { return fp->TrainingModeActive; } //--------------------------------------------------------------------------- //Заполнение массива с суммами по налоговым ставкам bool PrinterOPOS::setNalog(long n,float stavka) { // long s=stavka*100; // WideString str=IntToStr(s); BSTR s1=L"120"; long err = fp->DirectIO(2, &n, &s1); if(err!=0) saveLog(IntToStr(err)+"(PrinterOPOS::OpenShift)"+GetStatusDescription(err)); return err==0; } //--------------------------------------------------------------------------- //Дневной итог int PrinterOPOS::getDaySum() { try { long n=1; BSTR str=new OLECHAR[20]; long err=fp->DirectIO (300,&n,&str); if(err!=0)throw Exception("["+IntToStr(err)+"]"+GetStatusDescription(err)); return StrToInt(str)/100.0; }catch(...) { saveLog("(PrinterOPOS::getDaySum) Нет описания ошибки"); dModule->SendError(1,"(PrinterOPOS::getDaySum) Нет описания ошибки"); return 0; } }