Первые шаги
Приступим к интеграции IKKM в вашем Android приложение. Мы рассмотрим простейший случай, когда в приложении имеется Activity, содержащая таблицу товаров. Для этого воспользуемся библиотеками okhttp3, которая упрощает работу с http запросами и gson.
Приступим к интеграции IKKM в вашем Android приложение. Мы рассмотрим простейший случай, когда в приложении имеется Activity, содержащая таблицу товаров. Для этого воспользуемся библиотеками okhttp3, которая упрощает работу с http запросами и gson.
Для начала добавим в файл app/build.gradle зависимость.
dependencies { ... implementation 'com.squareup.okhttp3:okhttp:4.2.2' implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.2.2' implementation 'com.google.code.gson:gson:2.8.6' }
Параметры подключения для работы с устройством
public class IkkmHelper { private SharedPreferences sharedPreferences; // где будем хранить config параметры private Context m_context; // передаем с какого Activity работаем private String ip_ikkm; // ip адрес устройства (192.168.0.100) private String key_ikkm; // при каждом фискальном чеке меняется ключ доступа (токенизация, малая безопасность в локальной сети), в остальных случаях ключ не меняется private int trackdocument_ikkm; // номер текущего фискального чека в аппарате, нужен для банковской операции, возможен другой алгоритм с использованием timestamp private boolean ikkm_is_present; // метка проверки доступности терминала по сети private boolean check_is_payed; // метка оплаты чека картой за номером trackdocument_ikkm+1 private String errorIKKM; // текущая ошибка private OkHttpClient client; // для http запросов private HashMap<String,String> errorMap= new HashMap<>(); // если требуется расшифровка ошибки от устройства IkkmHelper(Context context) { sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); m_context = context; ip_ikkm = sharedPreferences.getString("ip_address_ikkm", "192.168.0.100"); // спросим актуальный ip адрес устройства key_ikkm = sharedPreferences.getString("api_key_ikkm", "12345678"); // токен для API ikkm_is_present = false; check_is_payed = false; trackdocument_ikkm = 0; errorIKKM = "no connect to ikkm"; // не найдено устройство в сети client = new OkHttpClient(); //Beep for service and get check number for track number doApiCheckBeep(true); // just checking ikkm and take current number of check(bill). if(false) - no signal beep. // if need extended answer error //loadErrorMap(); }
Используем AsyncTask для организации взаимодействия с IKKM
private void sendPOST_to_ikkm(final String api_url){ //async request @SuppressLint("StaticFieldLeak") class GetAPI_Result extends AsyncTask<Void, String, String> { ..... @Override protected void onPreExecute() { ..... } @Override protected String doInBackground(Void... params) { String Answer_from_ikkm = "not found ikkm"; RequestBody formBody = new FormBody.Builder() .add("key", key_ikkm) .build(); Request request = new Request.Builder() .url("http://" + ip_ikkm + ":8080" + api_url) .post(formBody) .build(); try (Response response = client.newCall(request).execute()) { publishProgress("Receiving..."); String apiResult = Objects.requireNonNull(response.body()).string().trim(); String apiMessage = response.message().trim(); if (response.isSuccessful()){ ... return Answer_from_ikkm; } @Override protected void onProgressUpdate(String... progress) { super.onProgressUpdate(progress); tvText.setText(progress[0]); } @Override protected void onPostExecute(String answer) { super.onPostExecute(answer); if(answer.equals("Ok")) { if (dialog != null )dialog.dismiss(); }else {
В конструктор ItemData мы передаем строчку из табличной части
public class ItemData { private String name; private String price; private String qty; private String sum; private String taxid; // код налога в ikkm ( пример: 1 - безНДС, 2 - НДС) public ItemData(String name, String price, String qty, String sum, String taxid) { this.name = name; this.price = price; this.qty = qty; this.sum = sum; this.taxid = taxid; }
Создаем Array (табличная часть чека с наименованием, ценой, кол-вом, суммой и кодом налога) и передаем в JSON
печатаем фискальный чек!
public void testPrintFiscalCheck() { ArrayList<ItemData> ItemList = new ArrayList<ItemData>(); ItemList.add(0,new ItemData("чай","200","2","400", "7")); ItemList.add(1,new ItemData("булка","1600","1","1600", "8")); doFiscal(0,2000.00, 1200.00, 800.00, 0,0, new Gson().toJson(ItemList)); // пригодился gson } public void doFiscal(int docType, double total, double cash, double bank, double tara, double credit, String OfdItemDataJSON) { String _docType = "sale"; if (docType == 2) _docType = "saleReturn"; if (docType == 3) _docType = "buy"; if (docType == 4) _docType = "buyReturn"; String api_url = "/api/?" + _docType + "=" + String.format(Locale.US,"%.2f",total); if (cash > 0){ api_url += "&cash=" + String.format(Locale.US,"%.2f",cash); // сколько дали наличности } if (bank > 0){ api_url += "&bank=" + String.format(Locale.US,"%.2f",bank); // сколько прошло по банковской карте } if (tara > 0) { api_url += "&tara=" + String.format(Locale.US, "%.2f", tara); // иногда используют как подарочный сертификат } if (credit > 0) { api_url += "&credit=" + String.format(Locale.US, "%.2f", credit); // для регистрации отпуска в кредит ( например: отпуск товара с доставкой, оплата потом) } api_url += "&itemdata=" + OfdItemDataJSON; sendPOST_to_ikkm(api_url); // отправили http запрос в ikkm }
Требование о закрытии фискальной смены (после 24 часов работы), реализуется командой
public void doReportZX(int repType){ // repType = 0 - x отчет, любые цифры - z-отчет(закрытие смены) String url = "/apizreport"; if (repType == 0) url = "/apixreport"; sendPOST_to_ikkm(url); }
Запрос на печать последнего зарегистрированного чека, бывают разные ситуации, бумага закончилась в момент печати и т.п. Дубликаты других документов (Z отчет, чек и т.д.) можно распечатать используя интерфейс на самом аппарате.
public void doPrintLastCheque() { sendPOST_to_ikkm("/apicheck/printLastDocument"); // запрос на печать дубликата чека. }
Разберем работу с банковской частью ikkm
public void startBank(double bankAmount) { String amount = String.format(Locale.US, "%.2f", bankAmount); // сколько хотим принять на терминале, сумма! // перед данным запросом запускаем doApiCheckBeep(boolean beep) - узнаем состояние устройства (доступно, какой номер последнего чека) if(ikkm_is_present){ callBackFromBank_ikkm(); // может данный чек оплачен? check_is_payed if (!check_is_payed) { String _trackdocument_ikkm = String.valueOf(trackdocument_ikkm); sendPOST_to_ikkm("/apibank/?message=purchase&amount=" + amount + "&trackdocument=" + _trackdocument_ikkm); }else { Toast.makeText(m_context, "Check #"+trackdocument_ikkm + " is Payed", Toast.LENGTH_SHORT).show(); } }else{ Toast.makeText(m_context, errorIKKM, Toast.LENGTH_SHORT).show(); } }
Подготовим разметку
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".tools.IKKM.IkkmHelperActivity"> <Button android:id="@+id/check_ikkm" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Check" app:layout_constraintBottom_toTopOf="@+id/z_report_ikkm" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:ignore="MissingConstraints" /> <Button android:id="@+id/z_report_ikkm" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Z Report" app:layout_constraintBottom_toTopOf="@+id/bank_ikkm" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/check_ikkm" tools:ignore="MissingConstraints" /> <Button android:id="@+id/bank_ikkm" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bank" app:layout_constraintBottom_toBottomOf="@id/z_report_ikkm" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/beep_ikkm" tools:ignore="MissingConstraints" /> <Button android:id="@+id/beep_ikkm" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Beep" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/bank_ikkm" tools:ignore="MissingConstraints" /> </androidx.constraintlayout.widget.ConstraintLayout>
Activity
import android.os.Bundle; import android.view.View; import android.widget.Button; import androidx.appcompat.app.AppCompatActivity; import kz.isoft.orangekassa.R; public class IkkmHelperActivity extends AppCompatActivity { private IkkmHelper ikkmHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ikkm_activity); ikkmHelper = new IkkmHelper(IkkmHelperActivity.this); Button button_beep = findViewById(R.id.beep_ikkm); button_beep.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { ikkmHelper.doApiCheckBeep(true); } }); Button button_z_report = findViewById(R.id.z_report_ikkm); button_z_report.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { ikkmHelper.doReportZX(1); } }); Button button_check_report = findViewById(R.id.check_ikkm); button_check_report.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { ikkmHelper.testPrintFiscalCheck(); } }); Button button_bank = findViewById(R.id.bank_ikkm); button_bank.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { ikkmHelper.startBank(1005); } }); }
начнем
import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Color; import android.os.AsyncTask; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import androidx.preference.PreferenceManager; import com.google.gson.Gson; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; import java.util.Objects; import kz.isoft.orangekassa.data.model.ItemData; import okhttp3.FormBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; public class IkkmHelper { private SharedPreferences sharedPreferences; private Context m_context; private String ip_ikkm; private String key_ikkm; private int trackdocument_ikkm; private boolean ikkm_is_present; private boolean check_is_payed; private String errorIKKM; private OkHttpClient client; private HashMap<String,String> errorMap= new HashMap<>(); IkkmHelper(Context context) { sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); m_context = context; ip_ikkm = sharedPreferences.getString("ip_address_ikkm", ""); key_ikkm = sharedPreferences.getString("api_key_ikkm", "12345678"); ikkm_is_present = false; check_is_payed = false; trackdocument_ikkm = 0; errorIKKM = "no connect to ikkm"; // client = new OkHttpClient(); //Beep for service and get check number for track number doApiCheckBeep(true); // just checking ikkm and take current number of check(bill). if(false) - no signal beep. //if need extended //loadErrorMap(); } void doApiCheckBeep(boolean beep) { if(beep) { sendPOST_to_ikkm("/apicheck/?beep=true"); }else{ sendPOST_to_ikkm("/apicheck/"); } } public void doReportZX(int repType){ String url = "/apizreport"; if (repType == 0) url = "/apixreport"; sendPOST_to_ikkm(url); } public void doPrintLastCheque() { sendPOST_to_ikkm("/apicheck/printLastDocument"); } public void startBank(double bankAmount) { String amount = String.format(Locale.US, "%.2f", bankAmount); if(ikkm_is_present){ callBackFromBank_ikkm(); if (!check_is_payed) { String _trackdocument_ikkm = String.valueOf(trackdocument_ikkm); sendPOST_to_ikkm("/apibank/?message=purchase&amount=" + amount + "&trackdocument=" + _trackdocument_ikkm); }else { Toast.makeText(m_context, "Check #"+trackdocument_ikkm + " is Payed", Toast.LENGTH_SHORT).show(); } }else{ Toast.makeText(m_context, errorIKKM, Toast.LENGTH_SHORT).show(); } } // HTTP POST request //Example FiscalCheck public void testPrintFiscalCheck() { ArrayList<ItemData> ItemList = new ArrayList<ItemData>(); ItemList.add(0,new ItemData("чай","200","2","400", "7")); ItemList.add(1,new ItemData("булка","1600","1","1600", "8")); doFiscal(0,2000.00, 1200.00, 800.00, 0,0, new Gson().toJson(ItemList)); } public void doFiscal(int docType, double total, double cash, double bank, double tara, double credit, String OfdItemDataJSON) { String _docType = "sale"; if (docType == 2) _docType = "saleReturn"; if (docType == 3) _docType = "buy"; if (docType == 4) _docType = "buyReturn"; String api_url = "/api/?" + _docType + "=" + String.format(Locale.US,"%.2f",total); if (cash > 0){ api_url += "&cash=" + String.format(Locale.US,"%.2f",cash); } if (bank > 0){ api_url += "&bank=" + String.format(Locale.US,"%.2f",bank); } if (tara > 0) { api_url += "&tara=" + String.format(Locale.US, "%.2f", tara); } if (credit > 0) { api_url += "&credit=" + String.format(Locale.US, "%.2f", credit); } api_url += "&itemdata=" + OfdItemDataJSON; sendPOST_to_ikkm(api_url); } private void callBackFromBank_ikkm(){ final String _trackdocument_ikkm = String.valueOf(trackdocument_ikkm); @SuppressLint("StaticFieldLeak") class GetCallBack_ResultBank extends AsyncTask<Void, String, String> { @Override protected String doInBackground(Void... params) { String Answer_from_ikkm = "not found ikkm?"; RequestBody formBody = new FormBody.Builder() .add("key", key_ikkm) .build(); Request request = new Request.Builder() .url("http://" + ip_ikkm + ":8080/dump/bank/" + _trackdocument_ikkm +"/trackdocument") .post(formBody) .build(); try (Response response = client.newCall(request).execute()) { if (response.isSuccessful()) { switch (response.code()) { case 200: String jsonRespon = Objects.requireNonNull(response.body()).string(); try { JSONObject jsonObject = new JSONObject(jsonRespon); Answer_from_ikkm = jsonObject.getString("transactionResult"); } catch (final JSONException e) { Answer_from_ikkm = "error parse json"; } break; case 203: Answer_from_ikkm = "duplicate request!"; break; case 202: Answer_from_ikkm = "check mode!"; break; default: Answer_from_ikkm = "Error answer"; break; } }else { if (response.code() == 400){ //error from ikkm Answer_from_ikkm = "Error " + response.message(); } throw new IOException("Unexpected code " + response); } } catch (IOException e) { e.printStackTrace(); } return Answer_from_ikkm; } @Override protected void onPostExecute(String answer) { super.onPostExecute(answer); if(answer.equals("success")) { check_is_payed = true; }else { check_is_payed = false; } // Toast.makeText(m_context, Answer_from_ikkm_body, Toast.LENGTH_SHORT).show(); } } GetCallBack_ResultBank gTask = new GetCallBack_ResultBank(); gTask.execute(); } private void sendPOST_to_ikkm(final String api_url){ //async request @SuppressLint("StaticFieldLeak") class GetAPI_Result extends AsyncTask<Void, String, String> { private LinearLayout ll = new LinearLayout(m_context); private ProgressBar progressBar = new ProgressBar(m_context); private TextView tvText = new TextView(m_context); private AlertDialog.Builder builder = new AlertDialog.Builder(m_context); private AlertDialog dialog; @Override protected void onPreExecute() { ll.setOrientation(LinearLayout.HORIZONTAL); int llPadding = 30; ll.setPadding(llPadding, llPadding, llPadding, llPadding); ll.setGravity(Gravity.CENTER); LinearLayout.LayoutParams llParam = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); llParam.gravity = Gravity.CENTER; ll.setLayoutParams(llParam); progressBar.setIndeterminate(true); progressBar.setPadding(0, 0, llPadding, 0); progressBar.setLayoutParams(llParam); llParam = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); llParam.gravity = Gravity.CENTER; tvText.setText("Starting ..."); tvText.setTextColor(Color.parseColor("#000000")); tvText.setTextSize(20); tvText.setLayoutParams(llParam); ll.addView(progressBar); ll.addView(tvText); builder.setCancelable(true); builder.setView(ll); dialog = builder.create(); dialog.show(); // Window window = dialog.getWindow(); // if (window != null) { // WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); // layoutParams.copyFrom(dialog.getWindow().getAttributes()); // layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; // layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; // dialog.getWindow().setAttributes(layoutParams); // } } @Override protected String doInBackground(Void... params) { String Answer_from_ikkm = "not found ikkm"; RequestBody formBody = new FormBody.Builder() .add("key", key_ikkm) .build(); Request request = new Request.Builder() .url("http://" + ip_ikkm + ":8080" + api_url) .post(formBody) .build(); try (Response response = client.newCall(request).execute()) { publishProgress("Receiving..."); String apiResult = Objects.requireNonNull(response.body()).string().trim(); String apiMessage = response.message().trim(); if (response.isSuccessful()){ switch (response.code()){ case 200: if (isInteger(apiMessage)){ Answer_from_ikkm = "Ok"; // Take number of check trackdocument_ikkm = Integer.parseInt(apiMessage)+1; ikkm_is_present = true; //safe api_key to setting if (apiResult.length() == 8) { Answer_from_ikkm = "Ok"; SharedPreferences.Editor edit = sharedPreferences.edit(); edit.putString("api_key_ikkm", apiResult); edit.apply(); } }else{ switch (apiMessage){ case "bank-is-opened": Answer_from_ikkm = "Error check #"+trackdocument_ikkm+" not payed"; for(int i = 0; i < 3; i++) { // Ваш код publishProgress("Wait card..." + String.valueOf(i)+"0 sec (30 sec)"); try { Thread.sleep(8000); } catch(InterruptedException e) { e.printStackTrace(); } callBackFromBank_ikkm(); if(check_is_payed){ Answer_from_ikkm = "Check #"+trackdocument_ikkm+" is payed, OK!"; break; } } break; case "z-report-started": Answer_from_ikkm = "ZX-report-started"; break; case "ikkm-busy": Answer_from_ikkm = "ikkm-busy"; break; default: break; } } break; case 203: Answer_from_ikkm = "duplicate request!"; break; case 202: Answer_from_ikkm = "check mode!"; break; default: Answer_from_ikkm = "Error answer"; break; } }else { if (response.code() == 400){ //error from ikkm Answer_from_ikkm = "Error " + apiMessage; } throw new IOException("Unexpected code " + response); } } catch (IOException e) { e.printStackTrace(); } return Answer_from_ikkm; } @Override protected void onProgressUpdate(String... progress) { super.onProgressUpdate(progress); tvText.setText(progress[0]); } @Override protected void onPostExecute(String answer) { super.onPostExecute(answer); if(answer.equals("Ok")) { if (dialog != null )dialog.dismiss(); }else { progressBar.setVisibility(View.GONE); tvText.setText(answer); errorIKKM = answer; // if (dialog != null )dialog.dismiss(); } } } GetAPI_Result gTask = new GetAPI_Result(); gTask.execute(); } private String getErrorText(String code){ if (errorMap.containsKey(code)) return errorMap.get(code); return "неизвестная ошибка iKKM "+code; } private void loadErrorMap(){ errorMap.put("no-web-api-key-provided","-7 Не передан web-api ключ"); errorMap.put("incorrect-web-api-key","-1 Не верный web-api ключ"); errorMap.put("ikkm-is-blocked","-100 ОФД заблокировал iKKM"); errorMap.put("check-external-printer","-6 Нет связи с внешним принтером"); errorMap.put("check-printer-or-batt","-6 Нет бумаги или низкий заряд батареи"); errorMap.put("sale-param-error","-9 Ошибка передачи параметра"); errorMap.put("buy-param-error","-9 Ошибка передачи параметра"); errorMap.put("saleRet-param-error","-9 Ошибка передачи параметра"); errorMap.put("buyRet-param-error","-9 Ошибка передачи параметра"); errorMap.put("check-all-params","-7 Проверьте все параметры"); errorMap.put("payment-params-missed","-7 Параметры оплаты отсутствуют"); errorMap.put("cache-param-error","-7 Ошибка передачи параметра"); errorMap.put("bank-param-error","-7 Ошибка передачи параметра"); errorMap.put("tara-param-error","-7 Ошибка передачи параметра"); errorMap.put("credit-param-error","-7 Ошибка передачи параметра"); errorMap.put("cache-not-in-range","-7 Параметр за пределами значений"); errorMap.put("bank-not-in-range","-7 Параметр за пределами значений"); errorMap.put("tara-not-in-range","-7 Параметр за пределами значений"); errorMap.put("credit-not-in-range","-7 Параметр за пределами значений"); errorMap.put("sale-lq-one","-7 Значение меньше 1"); errorMap.put("sale-hq-20mil","-7 Значение больше 20000000"); errorMap.put("cash-lq-sale","-10 Наличность меньше суммы чека"); errorMap.put("bank-hq-sale","-10 Оплата по банку должна быть равна сумме продажи"); errorMap.put("tara-hq-sale","-10 Значение меньше 1"); errorMap.put("credit-hq-sale","-10 Значение меньше 1"); errorMap.put("bank-tara-credit-hq-sale","-13 Оплата безналом больше чем сумма чека"); errorMap.put("no-cache-hq-sale","-13 Чек оплачен безналом, наличность запрещена"); errorMap.put("no-mixed-payment-allowed","-13 Смешанный вид оплаты выключен в настройках"); errorMap.put("change-not-allowed","-13 Сдача запрещена в данной операции"); errorMap.put("tax-invlid-num","-14 Неправильный код налога"); errorMap.put("tax-not-found","-14 Код налога не найден"); errorMap.put("cash-lq-tax","-12 Сумма чека (с налогом) больше принятой оплаты"); errorMap.put("no-cash-in-pos","-15 Нет наличности в кассе"); errorMap.put("internal-error","-99 Внутренняя ошибка кассы (повреждена БД)"); errorMap.put("wrong-cashier","-11 Смену открыл другой кассир"); errorMap.put("shift-gt-24h","-3 Смена отрыта более 24 часов"); errorMap.put("no-last-document","-7 Нет последнего документа"); errorMap.put("print-lines-parse-error","-7 Ошибка парсинга параметра print"); errorMap.put("only-master-cashier","-16 Операцию может провести только старший кассир"); errorMap.put("shift-gt-7days","-3 Смена открыта более 7 дней"); errorMap.put("incorrect-method","-8 Неправильно указан метод"); errorMap.put("ikkm-need-update","-19 прошивка iKKM не совместима, обновите iKKM"); errorMap.put("check-printer","Ошибка принтера iKKM"); errorMap.put("unknown-error","-99 Неизвестная ошибка"); } private static String toJSON(Object object) throws JSONException, IllegalAccessException { String str = ""; Class c = object.getClass(); JSONObject jsonObject = new JSONObject(); for (Field field : c.getDeclaredFields()) { field.setAccessible(true); String name = field.getName(); String value = String.valueOf(field.get(object)); jsonObject.put(name, value); } // System.out.println(jsonObject.toString()); return jsonObject.toString(); } private static boolean isInteger(String s) { try { Integer.parseInt(s); } catch(NumberFormatException e) { return false; } catch(NullPointerException e) { return false; } // only got here if we didn't return false return true; }