Протокол модуля iKKM-Offline


Версия документа 1
July 2. 2020

Вступление

Данный модуль не путать с iKKM API, так как это совершенно другой модуль и функционал. Основная идея заключается в том, что сам iKKM является инициатором обмена данными, и используется как кассовый и банковский терминал без дополнительного оборудования.

Выберите подходящую для вас логику работы:

  1. Отправка чеков на сервер: после каждой регистрации или при закрытии смены.
  2. Получение справочника товаров: весь справочник товаров или онлайн при каждой регистрации.

Все чеки за смену

JSON отправляемый на сервер:

{
  "DocumentType": "SingleShiftExport",
  "DocumentsCount": "4",
  "PosDetails": {
    "factorySN": "000117184200028",
    "posRegNum": "0000"
  },
  "ShiftAbout": {
    "cashierID": "166",
    "shiftCloseTime": "2020-07-03 23:05:54",
    "shiftId": "27",
    "shiftOpenTime": "2020-07-02 01:01:45"
  },
  "ShiftData": [
    {
      "chequeHeader": {
        "cheqDate": "2020-07-02 01:01:45",
        "cheqType": "sale",
        "chequeId": "229"
      },
      "chequeTable": [
        {
          "barcode": "00000000",
          "code": "00000000",
          "price": "17888987.00",
          "qty": "1.00",
          "section": "1",
          "sum": "17888987.00"
        }
      ],
      "payments": [
        {
          "income": "17888987",
          "type": "cash"
        }
      ]
    },
    {
      "chequeHeader": {
        "cheqDate": "2020-07-02 01:02:50",
        "cheqType": "sale",
        "chequeId": "230"
      },
      "chequeTable": [
        {
          "barcode": "",
          "code": "1",
          "price": "17888987.00",
          "qty": "1.00",
          "section": "1",
          "sum": "17888987.00"
        }
      ],
      "payments": [
        {
          "income": "17888987",
          "type": "cash"
        }
      ]
    }
    .....
    ....
    ..
      ]
    }
  ]
}

Структура на GoLang:

type Item struct {
    DocumentType   string `json:"DocumentType"`
    DocumentsCount string `json:"DocumentsCount"`
    PosDetails     struct {
        FactorySN string `json:"factorySN"`
        PosRegNum string `json:"posRegNum"`
    } `json:"PosDetails"`
    ShiftAbout struct {
        CashierID      string `json:"cashierID"`
        ShiftCloseTime string `json:"shiftCloseTime"`
        ShiftID        string `json:"shiftId"`
        ShiftOpenTime  string `json:"shiftOpenTime"`
    } `json:"ShiftAbout"`
    ShiftData []struct {
        ChequeHeader struct {
            CheqDate string `json:"cheqDate"`
            CheqType string `json:"cheqType"`
            ChequeID string `json:"chequeId"`
        } `json:"chequeHeader"`
        ChequeTable []struct {
            Barcode string `json:"barcode"`
            Code    string `json:"code"`
            Price   string `json:"price"`
            Qty     string `json:"qty"`
            Section string `json:"section"`
            Sum     string `json:"sum"`
        } `json:"chequeTable"`
        Payments []struct {
            Income string `json:"income"`
            Type   string `json:"type"`
        } `json:"payments"`
    } `json:"ShiftData"`
}

  • пример ответа от сервера:
/// Примеры ответов от сервера
{
  "DocumentType" : "SingleShiftExport" или "SaveAllDocumentResult", // тип обработанного документа
  "Result": "success", 
  // success - удачно
  // error - не продолжать  
  // warning - просто показать на экране сообщение из ErrorText, но считается как успех.
  "ErrorText": "" // если есть ошибка или предупреждение.
}

  • пример ответа GoLang:
 c.JSON(200, gin.H{
            "Result": "success", // success | error | warning 
            "ErrorText": "none",
            "DocumentType": "SingleShiftExport",
        })

  1. Отправка данного запроса возможна только если нет открытых смен, нет очереди в ОФД

  2. После получения success, удалить все данные ShiftData

POST запроса:  http://[url-журнал-документов]/SaveAllDocuments [url-журнал-документов] - адрес сервера и URL настраивается на iKKM в меню Настройки - Импорт - журнал 


Синхронизация справочника товаров

Пример удачного GET запроса:
 http://[url-spravochnik-1c]/GetPriceList?ikkmserial=11700012233 [url-spravochnik-1c] - адрес сервера и URL настраивается на iKKM в меню Настройки - Импорт - Справочник товаров

Ответ в UTF-8 JSON:

{
  "DocumentType" : "PriceList", // Нужен на будущее, если появятся доп. экспортированные док-ты
  "ItemsCount": "1", // Количество загружаемых элементов
  "Items": [
  // массив всех элементов
    {
    "ID": "123", // from 1c ([a-zA-Z0-9]{1,128})
    "Name": "Kafe with Milk", // max 256 (UTF-8) [а-яА-Я a-zA-Z0-9 \n\r!:?.,+*&$#@%=_;////]{1,256}
    "Price": "1002", // digits, delmitter .(dot)
    "Tax": "12", //  digits , delmitter .(dot)
    "Barcode": "1234567890" // max 13 digits, only digits (\\d{10-13})
    // Тип элемента - Услуга, Товар, Прочее. [на будущее]
    // Единица измерения - Литры, Штуки. [на будущее]
    // Номер секции для фиск. [на будущее]
    }
    //, {....}
  ],
  "Result": "success", // error - не продолжать | warning 
  "ErrorText": "error text" // если есть ошибка или предупреждение.
}


Получение чека при регистрации [Online]

{
    "chequeHeader": {
        "cashierID": "166",
        "cashierName": "Алия кассир",
        "cheqDate": "2017-09-14 15:36:05",
        "cheqType": "sale",
        "chequeId": "1138",
        "shiftId": "191"
    },
    "chequeTable": [{
        "code": "222",
        "barcode": "112121212",
        "price": "2550.00",
        "qty": "1.00",
        "section": "1",
        "sum": "2550.00"
    }, {
        "code": "none",
        "barcode": "112121212",
        "price": "400.00",
        "qty": "2.00",
        "section": "1",
        "sum": "800.00"
    }],
    "payments": [{
        "income": "2988",
        "type": "cash"
    }, {
        "income": "1000",
        "type": "bank"
    }, {
        "outcome": "638.00",
        "type": "cash"
    }],
    "posDetails": {
        "factorySN": "000118164600196",
        "posRegNum": "000000000006"
    }
}

формат ответа см - “Выгрузка всех продаж за смену (POST)”


Загрузка товаров во время работы [Online]


Получить номенклатуру по штрих-коду

При каждой регистрации, если товара нет в базе, iKKM отправляет запрос на сервер. После этого кэширует ( записывает в бд) данный товар для дальнейшего использования. Удаление (каждый раз, раз в смену, никогда)  настраиваются в свойствах импорта iKKM.

http://[url-spravochnik-1c]/GetByBarcode?barcode=4606260002219

Пример сервера на GoLang:

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", Hello)
    r.HandleFunc("/GetByBarcode", BarCodeHandler).Methods("GET")
    http.Handle("/", r)
    fmt.Println("Starting up on 8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func Hello(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintln(w, "Hello world!")
}

func BarCodeHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    json := simplejson.New()
    json.Set("ID","ЦБ-00004019")
    json.Set("Name","Какао 100 г")
    json.Set("Barcode","4606260002219")
    json.Set("Measure","шт")
    json.Set("Price","255")
    json.Set("Tax","12")
    json.Set("Result","success")

    payload, err := json.MarshalJSON()
    if err != nil {
        log.Println(err)
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(payload)
}

Отработка ошибок

func BarCodeHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    barcode := r.URL.Query().Get("barcode")
    json := simplejson.New()
    switch sampleScenarios := barcode; sampleScenarios {
    case "11144444": // sample 1
        json.Set("Result", "success")
        json.Set("ID", "111")
        json.Set("Name", "Какао 100 г")
        json.Set("Price", "255")
        json.Set("Tax", "12")
        json.Set("Barcode", "11144444")
    case "22244444": // sample 2 (другой налог)
        json.Set("Result", "success")
        json.Set("ID", "222")
        json.Set("Name", "Beer Paulainer Длинное наименование названия
товара с описанием #2823 арт 123.567 конец")
        json.Set("Price", "2550")
        json.Set("Tax", "3")
        json.Set("Barcode", "22244444")
    case "33344444": // sample 3(ответ с тормозами)
        json.Set("Result", "success")
        json.Set("ID", "333")
        json.Set("Name", "Slow Beer")
        json.Set("Price", "550")
        json.Set("Tax", "3")
        json.Set("Barcode", "33344444")
        time.Sleep(5000 * time.Millisecond)
    case "00044444": // sample 4(нули в коде и штрихкоде)
        json.Set("Result", "success")
        json.Set("ID", "004")
        json.Set("Name", "ZERO Beer описание товара со средней длиной
товара")
        json.Set("Price", "2870")
        json.Set("Tax", "3")
        json.Set("Barcode", "00044444")
    case "55544444": // sample 5(кривой налог)
        json.Set("Result", "success")
        json.Set("ID", "005")
        json.Set("Name", "BAD Beer")
        json.Set("Price", "2870")
        json.Set("Tax", "36")
        json.Set("Barcode", "00044444")
    case "66644444": // sample 6(цена с тиинками)
        json.Set("Result", "success")
        json.Set("ID", "006")
        json.Set("Name", "Float Beer")
        json.Set("Price", "214.23")
        json.Set("Tax", "12")
        json.Set("Barcode", "66644444")
    case "77744444": // sample 7 (кривой json)
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte("{ME is very:+ broken JSON}"))
    default: // ошибка по умолчанию
        json.Set("Result", "error")
        json.Set("ErrorText", "Bad bar code! Плохой!")
    }
    payload, err := json.MarshalJSON()
    if err != nil {
        log.Println(err)
    }
    w.Header().Set("Content-Type", "application/json")
    w.Write(payload)
    fmt.Printf("-> barcodeReq: %s, result %s\n", barcode,
json.Get("Result"))
}

Ваша проблема решена?