complete structure revamp
This commit is contained in:
371
README.md
371
README.md
@@ -5,165 +5,318 @@ Go SDK для работы с API кассы ВЧАСНО - украинской
|
|||||||
## Установка
|
## Установка
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go get git.jeezft.xyz/rk/go-vchasno-kassa
|
go get gitea.jeezft.xyz/jeezft/go-vchasno-kassa
|
||||||
```
|
```
|
||||||
|
|
||||||
## Быстрый старт
|
## Быстрый старт
|
||||||
|
|
||||||
|
### Базовое использование
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.jeezft.xyz/rk/go-vchasno-kassa"
|
"gitea.jeezft.xyz/jeezft/go-vchasno-kassa"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
client := vchasno.NewClient(vchasno.Config{
|
client := vchasno.NewClient(vchasno.Config{
|
||||||
Token: "your-api-token-here",
|
Token: "your-api-token-here",
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Проверка подключения
|
response, err := client.QuickSell(ctx, 100.00)
|
||||||
if err := client.Ping(ctx); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Создание фискального чека
|
|
||||||
receipt := vchasno.FiscalReceipt{
|
|
||||||
Cashier: "Иванов И.И.",
|
|
||||||
CashierTaxID: "1234567890",
|
|
||||||
Items: []vchasno.ReceiptItem{
|
|
||||||
{
|
|
||||||
Name: "Parking on obj1",
|
|
||||||
Code: "PARK001",
|
|
||||||
Price: vchasno.NewMoney(10.00),
|
|
||||||
Quantity: 2.0,
|
|
||||||
Amount: vchasno.NewMoney(20.00),
|
|
||||||
Tax: vchasno.NewTax(4.0, vchasno.NewMoney(0.80)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Payments: []vchasno.Payment{
|
|
||||||
{
|
|
||||||
Type: "cash",
|
|
||||||
Amount: vchasno.NewMoney(20.00),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Total: vchasno.NewMoney(20.00),
|
|
||||||
TaxTotal: vchasno.NewMoney(0.80),
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := client.CreateFiscalReceipt(ctx, receipt)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Fiscal receipt created: %s", response.FiscalNumber)
|
log.Printf("Sale created: %s", response.Info.Doccode)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### С дефолтными параметрами
|
||||||
|
|
||||||
|
```go
|
||||||
|
client := vchasno.NewClient(vchasno.Config{
|
||||||
|
Token: "your-api-token-here",
|
||||||
|
Cashier: "Иванов",
|
||||||
|
Source: "parking",
|
||||||
|
Defaults: &vchasno.DefaultParams{
|
||||||
|
ProductName: "Парковка",
|
||||||
|
Comment: "Оплата парковки",
|
||||||
|
Taxgrp: "1",
|
||||||
|
PayType: api.PayTypeCash,
|
||||||
|
DefaultTimeout: 30 * time.Second,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
response, _ := client.QuickSell(ctx, 50.00)
|
||||||
|
```
|
||||||
|
|
||||||
|
### С Builder Pattern
|
||||||
|
|
||||||
|
```go
|
||||||
|
response, err := client.NewSellParams().
|
||||||
|
Name("Парковка VIP").
|
||||||
|
Price(150.00).
|
||||||
|
Cnt(2).
|
||||||
|
Comment("2 часа").
|
||||||
|
PayCash().
|
||||||
|
ExecuteDefault()
|
||||||
|
```
|
||||||
|
|
||||||
## Функциональность
|
## Функциональность
|
||||||
|
|
||||||
### Работа с фискальными чеками
|
### Работа со сменами
|
||||||
- Создание фискальных чеков через единый API эндпоинт `/api/v3/fiscal/execute`
|
- Открытие смены (`OpenShift`)
|
||||||
- Получение информации о фискализованном чеке
|
- Закрытие смены с Z-отчетом (`CloseShift`)
|
||||||
- Отмена фискальных чеков
|
|
||||||
- Поддержка различных типов оплаты (наличные, карта)
|
|
||||||
- Автоматический расчет НДС и налогов
|
|
||||||
|
|
||||||
### Отчеты
|
### Продажи
|
||||||
- X-отчет (промежуточный отчет без обнуления)
|
- Создание чеков с оплатой наличными
|
||||||
- Z-отчет (итоговый отчет с обнулением кассы)
|
- Создание чеков с оплатой картой
|
||||||
|
- Поддержка дополнительных данных клиента
|
||||||
|
- Автоматический расчет сумм
|
||||||
|
|
||||||
### Валидация данных
|
### Z-отчет содержит
|
||||||
- Проверка корректности сумм и количества
|
- Количество чеков (продажа/возврат)
|
||||||
- Валидация обязательных полей
|
- Сводку по налогам
|
||||||
- Проверка соответствия общей суммы и оплат
|
- Информацию о платежах
|
||||||
|
- Остатки в кассе
|
||||||
|
- Детальную информацию по налоговым группам
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
api/
|
||||||
|
├── client.go - Клиент API
|
||||||
|
├── constants.go - Константы (типы задач, платежей)
|
||||||
|
├── requests.go - Структуры запросов
|
||||||
|
├── responses.go - Структуры ответов
|
||||||
|
├── helpers.go - Вспомогательные функции
|
||||||
|
└── fiscal.go - Методы API
|
||||||
|
```
|
||||||
|
|
||||||
## Примеры использования
|
## Примеры использования
|
||||||
|
|
||||||
Полные примеры использования находятся в папке `examples/`.
|
### 1. Быстрая продажа (QuickSell)
|
||||||
|
|
||||||
```bash
|
|
||||||
cd examples
|
|
||||||
go run main.go
|
|
||||||
```
|
|
||||||
|
|
||||||
## Валидация
|
|
||||||
|
|
||||||
SDK включает встроенную валидацию фискальных чеков:
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
receipt := vchasno.FiscalReceipt{
|
response, err := client.QuickSell(ctx, 100.00)
|
||||||
Cashier: "Иванов И.И.",
|
|
||||||
CashierTaxID: "1234567890",
|
|
||||||
Items: []vchasno.ReceiptItem{...},
|
|
||||||
Payments: []vchasno.Payment{...},
|
|
||||||
Total: vchasno.NewMoney(100.00),
|
|
||||||
TaxTotal: vchasno.NewMoney(4.00),
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := receipt.Validate(); err != nil {
|
|
||||||
log.Fatal("Validation error:", err)
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Обработка ошибок
|
Использует все дефолтные параметры из конфигурации.
|
||||||
|
|
||||||
|
### 2. Быстрая продажа с названием
|
||||||
|
|
||||||
```go
|
```go
|
||||||
response, err := client.CreateFiscalReceipt(ctx, receipt)
|
response, err := client.QuickSellNamed(ctx, "Парковка VIP", 150.00)
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, vchasno.ErrMissingToken):
|
|
||||||
log.Println("Отсутствует токен авторизации")
|
|
||||||
default:
|
|
||||||
log.Printf("API error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Работа с деньгами
|
### 3. Builder Pattern - базовый пример
|
||||||
|
|
||||||
SDK использует копейки для точных денежных расчетов:
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Создание суммы в копейках
|
response, err := client.NewSellParams().
|
||||||
price := vchasno.NewMoney(10.50) // 1050 копеек
|
Price(100.00).
|
||||||
|
ExecuteDefault()
|
||||||
// Конвертация обратно в гривны
|
|
||||||
amount := price.ToFloat64() // 10.50
|
|
||||||
|
|
||||||
// Создание налога
|
|
||||||
tax := vchasno.NewTax(4.0, vchasno.NewMoney(0.42)) // 4% НДС
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Типы операций
|
### 4. Builder Pattern - полный пример
|
||||||
|
|
||||||
Через эндпоинт `/api/v3/fiscal/execute` выполняются следующие команды:
|
|
||||||
|
|
||||||
- `create_receipt` - создание фискального чека
|
|
||||||
- `get_receipt` - получение информации о чеке
|
|
||||||
- `cancel_receipt` - отмена чека
|
|
||||||
- `x_report` - получение X-отчета
|
|
||||||
- `z_report` - получение Z-отчета
|
|
||||||
- `ping` - проверка соединения
|
|
||||||
|
|
||||||
## Конфигурация
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
client := vchasno.NewClient(vchasno.Config{
|
response, err := client.NewSellParams().
|
||||||
BaseURL: "https://kasa.vchasno.ua", // необязательно, по умолчанию
|
Name("Парковка premium").
|
||||||
Token: "your-token", // обязательно
|
Price(200.00).
|
||||||
Timeout: 30 * time.Second, // необязательно
|
Cnt(2).
|
||||||
HTTPClient: &http.Client{...}, // необязательно
|
Disc(20.00).
|
||||||
|
Comment("Скидка 10%").
|
||||||
|
Taxgrp("1").
|
||||||
|
PayCash().
|
||||||
|
ExecuteDefault()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Оплата картой через Builder
|
||||||
|
|
||||||
|
```go
|
||||||
|
response, err := client.NewSellParams().
|
||||||
|
Name("Услуга парковки").
|
||||||
|
Price(150.00).
|
||||||
|
PayCard("411111****1111", "305299", "123456789012", "123456").
|
||||||
|
Userinfo("user@example.com", "+380501234567").
|
||||||
|
ExecuteDefault()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Традиционный способ (без Builder)
|
||||||
|
|
||||||
|
```go
|
||||||
|
response, err := client.Sell(ctx, vchasno.SellParams{
|
||||||
|
Name: "Товар",
|
||||||
|
Cnt: 2,
|
||||||
|
Price: 50.00,
|
||||||
|
Taxgrp: "1",
|
||||||
|
PayType: api.PayTypeCash,
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 7. Полный рабочий цикл
|
||||||
|
|
||||||
|
```go
|
||||||
|
client := vchasno.NewClient(vchasno.Config{
|
||||||
|
Token: "your-token",
|
||||||
|
Defaults: &vchasno.DefaultParams{
|
||||||
|
ProductName: "Парковка",
|
||||||
|
Taxgrp: "1",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
client.OpenShift(ctx)
|
||||||
|
|
||||||
|
client.NewSellParams().Price(50.00).PayCash().ExecuteDefault()
|
||||||
|
client.NewSellParams().Price(100.00).PayCash().ExecuteDefault()
|
||||||
|
client.NewSellParams().Price(75.00).PayCard("411111****1111", "305299", "123456789012", "123456").ExecuteDefault()
|
||||||
|
|
||||||
|
zReport, _ := client.CloseShift(ctx)
|
||||||
|
fmt.Printf("Итого за смену: %.2f\n", zReport.Info.Safe)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. Изменение дефолтов в процессе работы
|
||||||
|
|
||||||
|
```go
|
||||||
|
client.SetDefaults(vchasno.DefaultParams{
|
||||||
|
ProductName: "VIP Парковка",
|
||||||
|
Comment: "VIP зона",
|
||||||
|
Taxgrp: "2",
|
||||||
|
PayType: api.PayTypeCard,
|
||||||
|
DefaultTimeout: 60 * time.Second,
|
||||||
|
})
|
||||||
|
|
||||||
|
response, _ := client.QuickSell(ctx, 200.00)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. Множественные продажи
|
||||||
|
|
||||||
|
```go
|
||||||
|
prices := []float64{50.00, 75.00, 100.00, 125.00}
|
||||||
|
|
||||||
|
for _, price := range prices {
|
||||||
|
client.NewSellParams().
|
||||||
|
Price(price).
|
||||||
|
PayCash().
|
||||||
|
ExecuteDefault()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10. Собственный таймаут
|
||||||
|
|
||||||
|
```go
|
||||||
|
response, err := client.NewSellParams().
|
||||||
|
Price(100.00).
|
||||||
|
ExecuteWithTimeout(45 * time.Second)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Хелперы и удобные функции
|
||||||
|
|
||||||
|
### DefaultParams - дефолтные параметры
|
||||||
|
|
||||||
|
При создании клиента можно задать дефолтные значения, которые будут использоваться автоматически:
|
||||||
|
|
||||||
|
```go
|
||||||
|
client := vchasno.NewClient(vchasno.Config{
|
||||||
|
Token: "your-token",
|
||||||
|
Defaults: &vchasno.DefaultParams{
|
||||||
|
ProductName: "Парковка",
|
||||||
|
Comment: "Оплата услуг",
|
||||||
|
Taxgrp: "1",
|
||||||
|
PayType: api.PayTypeCash,
|
||||||
|
DefaultTimeout: 30 * time.Second,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Builder Pattern
|
||||||
|
|
||||||
|
Builder Pattern позволяет строить параметры продажи в цепочке вызовов:
|
||||||
|
|
||||||
|
```go
|
||||||
|
client.NewSellParams().
|
||||||
|
Name("Товар").
|
||||||
|
Price(100.00).
|
||||||
|
Cnt(2).
|
||||||
|
Disc(10.00).
|
||||||
|
Comment("Комментарий").
|
||||||
|
Taxgrp("1").
|
||||||
|
PayCash().
|
||||||
|
ExecuteDefault()
|
||||||
|
```
|
||||||
|
|
||||||
|
Доступные методы Builder:
|
||||||
|
- `Name(string)` - название товара
|
||||||
|
- `Price(float64)` - цена
|
||||||
|
- `Cnt(int)` - количество
|
||||||
|
- `Disc(float64)` - скидка
|
||||||
|
- `Comment(string)` - комментарий
|
||||||
|
- `Taxgrp(string)` - налоговая группа
|
||||||
|
- `PayCash()` - оплата наличными
|
||||||
|
- `PayCard(cardmask, bankID, rrnCode, authCode)` - оплата картой
|
||||||
|
- `Userinfo(email, phone)` - данные клиента
|
||||||
|
- `Build()` - получить SellParams
|
||||||
|
- `Execute(ctx)` - выполнить с контекстом
|
||||||
|
- `ExecuteWithTimeout(duration)` - выполнить с таймаутом
|
||||||
|
- `ExecuteDefault()` - выполнить с дефолтным таймаутом
|
||||||
|
|
||||||
|
### QuickSell методы
|
||||||
|
|
||||||
|
Для быстрых продаж:
|
||||||
|
|
||||||
|
```go
|
||||||
|
client.QuickSell(ctx, 100.00)
|
||||||
|
|
||||||
|
client.QuickSellNamed(ctx, "Парковка", 100.00)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Изменение дефолтов
|
||||||
|
|
||||||
|
Дефолтные параметры можно изменить в любой момент:
|
||||||
|
|
||||||
|
```go
|
||||||
|
client.SetDefaults(vchasno.DefaultParams{
|
||||||
|
ProductName: "Новое название",
|
||||||
|
PayType: api.PayTypeCard,
|
||||||
|
})
|
||||||
|
|
||||||
|
defaults := client.GetDefaults()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Константы
|
||||||
|
|
||||||
|
### Типы задач
|
||||||
|
- `api.TaskOpenShift = 0` - Открытие смены
|
||||||
|
- `api.TaskSell = 1` - Продажа
|
||||||
|
- `api.TaskZReport = 11` - Z-отчет
|
||||||
|
|
||||||
|
### Типы платежей
|
||||||
|
- `api.PayTypeCash = 0` - Оплата наличными
|
||||||
|
- `api.PayTypeCard = 2` - Оплата картой
|
||||||
|
|
||||||
|
## Структуры ответов
|
||||||
|
|
||||||
|
### SellResponse
|
||||||
|
Используется для продаж и открытия смены. Содержит базовую информацию о документе.
|
||||||
|
|
||||||
|
### ZReportResponse
|
||||||
|
Используется для закрытия смены. Содержит детальную информацию:
|
||||||
|
- `Receipt` - статистика по чекам
|
||||||
|
- `Summary` - итоговые суммы
|
||||||
|
- `Taxes` - разбивка по налогам
|
||||||
|
- `Pays` - способы оплаты
|
||||||
|
- `Money` - движение наличных
|
||||||
|
- `Cash` - остатки по безналичным
|
||||||
|
|
||||||
## API Reference
|
## API Reference
|
||||||
|
|
||||||
Полная документация API ВЧАСНО доступна по адресу: https://documenter.getpostman.com/view/26351974/2s93shy9To
|
Полная документация API ВЧАСНО: https://documenter.getpostman.com/view/26351974/2s93shy9To
|
||||||
17
api/client.go
Normal file
17
api/client.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import "resty.dev/v3"
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
token string
|
||||||
|
resty *resty.Client
|
||||||
|
apiBaseURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(token string) *Client {
|
||||||
|
return &Client{
|
||||||
|
token: token,
|
||||||
|
resty: resty.New(),
|
||||||
|
apiBaseURL: "https://kasa.vchasno.ua/api/v3",
|
||||||
|
}
|
||||||
|
}
|
||||||
16
api/constants.go
Normal file
16
api/constants.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskOpenShift = 0
|
||||||
|
TaskSell = 1
|
||||||
|
TaskZReport = 11
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PayTypeCash = 0
|
||||||
|
PayTypeCard = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PaySystemParkingPos = "parking_pos"
|
||||||
|
)
|
||||||
103
api/fiscal.go
Normal file
103
api/fiscal.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) executeRequest(ctx context.Context, request FiscalRequest, response interface{}) error {
|
||||||
|
resp, err := c.resty.R().
|
||||||
|
SetContext(ctx).
|
||||||
|
SetHeader("Authorization", c.token).
|
||||||
|
SetBody(request).
|
||||||
|
Post(c.apiBaseURL + "/fiscal/execute")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("request failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.IsError() {
|
||||||
|
return fmt.Errorf("api error: %v", resp.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode() != 200 {
|
||||||
|
return fmt.Errorf("unexpected status code: %d", resp.StatusCode())
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(body, response); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) OpenShift(ctx context.Context, cashier string) (*SellResponse, error) {
|
||||||
|
request := FiscalRequest{
|
||||||
|
Fiscal: Fiscal{
|
||||||
|
Task: TaskOpenShift,
|
||||||
|
Cashier: cashier,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var response SellResponse
|
||||||
|
if err := c.executeRequest(ctx, request, &response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) CloseShift(ctx context.Context, cashier string) (*ZReportResponse, error) {
|
||||||
|
request := FiscalRequest{
|
||||||
|
Fiscal: Fiscal{
|
||||||
|
Task: TaskZReport,
|
||||||
|
Cashier: cashier,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var response ZReportResponse
|
||||||
|
if err := c.executeRequest(ctx, request, &response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SellParams struct {
|
||||||
|
Cashier string
|
||||||
|
Source string
|
||||||
|
Rows []ReceiptRow
|
||||||
|
Pays []ReceiptPay
|
||||||
|
Userinfo *Userinfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Sell(ctx context.Context, params SellParams) (*SellResponse, error) {
|
||||||
|
receipt := NewReceipt(params.Rows, params.Pays)
|
||||||
|
|
||||||
|
request := FiscalRequest{
|
||||||
|
Source: params.Source,
|
||||||
|
Fiscal: Fiscal{
|
||||||
|
Task: TaskSell,
|
||||||
|
Cashier: params.Cashier,
|
||||||
|
Receipt: &receipt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Userinfo != nil {
|
||||||
|
request.Userinfo = *params.Userinfo
|
||||||
|
}
|
||||||
|
|
||||||
|
var response SellResponse
|
||||||
|
if err := c.executeRequest(ctx, request, &response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
49
api/helpers.go
Normal file
49
api/helpers.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
func NewReceiptRow(name string, cnt int, price float64, taxgrp string) ReceiptRow {
|
||||||
|
return ReceiptRow{
|
||||||
|
Name: name,
|
||||||
|
Cnt: cnt,
|
||||||
|
Price: price,
|
||||||
|
Taxgrp: taxgrp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReceiptPayCash(sum float64, comment string) ReceiptPay {
|
||||||
|
return ReceiptPay{
|
||||||
|
Type: PayTypeCash,
|
||||||
|
Sum: sum,
|
||||||
|
Comment: comment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReceiptPayCard(sum float64, cardmask, bankID, rrnCode, authCode string) ReceiptPay {
|
||||||
|
return ReceiptPay{
|
||||||
|
Type: PayTypeCard,
|
||||||
|
Sum: sum,
|
||||||
|
Paysys: PaySystemParkingPos,
|
||||||
|
Cardmask: cardmask,
|
||||||
|
BankID: bankID,
|
||||||
|
Rrn: rrnCode,
|
||||||
|
AuthCode: authCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalculateReceiptSum(rows []ReceiptRow) float64 {
|
||||||
|
sum := 0.0
|
||||||
|
for _, row := range rows {
|
||||||
|
sum += (row.Price - row.Disc) * float64(row.Cnt)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReceipt(rows []ReceiptRow, pays []ReceiptPay) Receipt {
|
||||||
|
return Receipt{
|
||||||
|
Sum: CalculateReceiptSum(rows),
|
||||||
|
Round: 0.00,
|
||||||
|
Disc: 0,
|
||||||
|
DiscType: 0,
|
||||||
|
Rows: rows,
|
||||||
|
Pays: pays,
|
||||||
|
}
|
||||||
|
}
|
||||||
152
api/kasa.go
152
api/kasa.go
@@ -1,152 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"resty.dev/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewKasaInstance(token string) *Kasa {
|
|
||||||
return &Kasa{
|
|
||||||
token: token,
|
|
||||||
resty: resty.New(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Kasa struct {
|
|
||||||
token string
|
|
||||||
resty *resty.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func createReceiptRow(name string, cnt int, price float64, comment string, disc float64, taxgrp string) ReceiptRow {
|
|
||||||
return ReceiptRow{
|
|
||||||
Name: name,
|
|
||||||
Cnt: cnt,
|
|
||||||
Price: price,
|
|
||||||
Disc: disc,
|
|
||||||
Taxgrp: taxgrp,
|
|
||||||
Comment: comment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createReceiptPayCash(PayType int, sum float64, comment string) ReceiptPay {
|
|
||||||
return ReceiptPay{
|
|
||||||
Type: PayType,
|
|
||||||
Sum: sum,
|
|
||||||
Comment: comment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createReceiptPayCard(PayType int, sum float64, comment string, cardmask string, bankID string, rrnCode string, authCode string) ReceiptPay {
|
|
||||||
return ReceiptPay{
|
|
||||||
Type: PayType,
|
|
||||||
Sum: sum,
|
|
||||||
Comment: comment,
|
|
||||||
Paysys: "parking_pos",
|
|
||||||
Cardmask: cardmask,
|
|
||||||
BankID: bankID,
|
|
||||||
Rrn: rrnCode,
|
|
||||||
AuthCode: authCode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getReceiptSum(rows []ReceiptRow, taxgrp string) float64 {
|
|
||||||
sum := 0.0
|
|
||||||
for _, row := range rows {
|
|
||||||
sum += (row.Price - row.Disc) * float64(row.Cnt)
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
|
|
||||||
func createReceipt(PayType int, sum float64, comment string, cardmask string, bankID string, name string, cnt int, price float64, disc float64, taxgrp string, rrnCode string, authCode string) Receipt {
|
|
||||||
rows := []ReceiptRow{createReceiptRow(name, cnt, price, comment, disc, taxgrp)}
|
|
||||||
sum = getReceiptSum(rows, taxgrp)
|
|
||||||
|
|
||||||
r := Receipt{
|
|
||||||
Sum: sum,
|
|
||||||
Round: 0.00,
|
|
||||||
Disc: 0,
|
|
||||||
DiscType: 0,
|
|
||||||
Rows: rows,
|
|
||||||
Pays: []ReceiptPay{createReceiptPayCard(PayType, sum, comment, cardmask, bankID, rrnCode, authCode)},
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Receipt sum:", r.Sum)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// "fiscal": {
|
|
||||||
// "task": 1,
|
|
||||||
// "cashier": "Парковка",
|
|
||||||
// "receipt": {
|
|
||||||
// "sum": 40.00,
|
|
||||||
// "round": 0.00,
|
|
||||||
// "comment_up": "Квитанція за паркування",
|
|
||||||
// "comment_down": "Дякуємо за користування паркінгом!",
|
|
||||||
// "disc": 0.00,
|
|
||||||
// "disc_type": 0,
|
|
||||||
// "rows": [
|
|
||||||
// {
|
|
||||||
// "code": "PARK-3H",
|
|
||||||
// "pop": "Оплата за послуги паркування",
|
|
||||||
// "name": "Парковка, 3 години",
|
|
||||||
// "cnt": 1,
|
|
||||||
// "price": 40.00,
|
|
||||||
// "disc": 0.00,
|
|
||||||
// "taxgrp": "4",
|
|
||||||
// "comment": "Тариф: 3 години"
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
// "pays": [
|
|
||||||
// {
|
|
||||||
// "type": 0,
|
|
||||||
// "sum": 40.00,
|
|
||||||
// "change": 0.00,
|
|
||||||
// "comment": "Оплата готівкою"
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
func createFiscal(source string, cashier string, PayType int, sum float64, comment string, cardmask string, bankID string, name string, cnt int, price float64, disc float64, taxgrp string, rrnCode string, authCode string) FiskalCheck {
|
|
||||||
return FiskalCheck{
|
|
||||||
Source: source,
|
|
||||||
Fiscal: Fiscal{
|
|
||||||
Task: 1,
|
|
||||||
Cashier: cashier,
|
|
||||||
Receipt: createReceipt(PayType, sum, comment, cardmask, bankID, name, cnt, price, disc, taxgrp, rrnCode, authCode),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Kasa) NewSell(ctx context.Context, PayType int, sum float64, comment string, cardmask string, bankID string, name string, cnt int, price float64, disc float64, taxgrp string, rrnCode string, authCode string) (*KasaResponse, error) {
|
|
||||||
fiscal := createFiscal("kasa", "test", PayType, sum, comment, cardmask, bankID, name, cnt, price, disc, taxgrp, rrnCode, authCode)
|
|
||||||
|
|
||||||
// create a POST request to the kasa api https://kasa.vchasno.ua/api/v3/fiscal/execute with resty
|
|
||||||
request, err := k.resty.R().SetBody(fiscal).SetHeader("Authorization", k.token).Post("https://kasa.vchasno.ua/api/v3/fiscal/execute")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create a POST request to the kasa api: %w", err)
|
|
||||||
}
|
|
||||||
if request.IsError() {
|
|
||||||
return nil, fmt.Errorf("failed to create a POST request to the kasa api: %v", request.Error())
|
|
||||||
}
|
|
||||||
if request.StatusCode() != 200 {
|
|
||||||
return nil, fmt.Errorf("failed to create a POST request to the kasa api: %v", request.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := io.ReadAll(request.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(string(body))
|
|
||||||
var response KasaResponse
|
|
||||||
if err := json.Unmarshal(body, &response); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &response, nil
|
|
||||||
}
|
|
||||||
61
api/requests.go
Normal file
61
api/requests.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
type FiscalRequest struct {
|
||||||
|
Source string `json:"source"`
|
||||||
|
Userinfo Userinfo `json:"userinfo,omitempty"`
|
||||||
|
Fiscal Fiscal `json:"fiscal"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Userinfo struct {
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
Phone string `json:"phone,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fiscal struct {
|
||||||
|
Task int `json:"task"`
|
||||||
|
Cashier string `json:"cashier"`
|
||||||
|
Receipt *Receipt `json:"receipt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Receipt struct {
|
||||||
|
Sum float64 `json:"sum"`
|
||||||
|
Round float64 `json:"round"`
|
||||||
|
CommentUp string `json:"comment_up,omitempty"`
|
||||||
|
CommentDown string `json:"comment_down,omitempty"`
|
||||||
|
Disc float64 `json:"disc"`
|
||||||
|
DiscType int `json:"disc_type"`
|
||||||
|
Rows []ReceiptRow `json:"rows"`
|
||||||
|
Pays []ReceiptPay `json:"pays"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReceiptRow struct {
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
|
Pop string `json:"pop,omitempty"`
|
||||||
|
Code1 string `json:"code1,omitempty"`
|
||||||
|
Code2 string `json:"code2,omitempty"`
|
||||||
|
CodeAa []string `json:"code_aa,omitempty"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Cnt int `json:"cnt"`
|
||||||
|
Price float64 `json:"price"`
|
||||||
|
Disc float64 `json:"disc"`
|
||||||
|
Taxgrp string `json:"taxgrp"`
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
CodeA string `json:"code_a,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReceiptPay struct {
|
||||||
|
Type int `json:"type"`
|
||||||
|
Sum float64 `json:"sum"`
|
||||||
|
Change float64 `json:"change,omitempty"`
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
Commission float64 `json:"commission,omitempty"`
|
||||||
|
Paysys string `json:"paysys,omitempty"`
|
||||||
|
Rrn string `json:"rrn,omitempty"`
|
||||||
|
OperType string `json:"oper_type,omitempty"`
|
||||||
|
Cardmask string `json:"cardmask,omitempty"`
|
||||||
|
TermID string `json:"term_id,omitempty"`
|
||||||
|
BankName string `json:"bank_name,omitempty"`
|
||||||
|
BankID string `json:"bank_id,omitempty"`
|
||||||
|
AuthCode string `json:"auth_code,omitempty"`
|
||||||
|
ShowAdditionalInfo bool `json:"show_additional_info,omitempty"`
|
||||||
|
}
|
||||||
124
api/responses.go
Normal file
124
api/responses.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
type BaseResponse struct {
|
||||||
|
Task int `json:"task"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
Ver int `json:"ver"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Device string `json:"device"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Dt string `json:"dt"`
|
||||||
|
Res int `json:"res"`
|
||||||
|
ResAction int `json:"res_action"`
|
||||||
|
Errortxt string `json:"errortxt"`
|
||||||
|
Warnings []string `json:"warnings"`
|
||||||
|
ErrorExtra interface{} `json:"error_extra"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SellResponse struct {
|
||||||
|
BaseResponse
|
||||||
|
Info SellInfo `json:"info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SellInfo struct {
|
||||||
|
Task int `json:"task"`
|
||||||
|
Fisid string `json:"fisid"`
|
||||||
|
Dataid int `json:"dataid"`
|
||||||
|
Doccode string `json:"doccode"`
|
||||||
|
Dt string `json:"dt"`
|
||||||
|
Cashier string `json:"cashier"`
|
||||||
|
Dtype int `json:"dtype"`
|
||||||
|
Isprint int `json:"isprint"`
|
||||||
|
Isoffline bool `json:"isoffline"`
|
||||||
|
Safe float64 `json:"safe"`
|
||||||
|
ShiftLink int `json:"shift_link"`
|
||||||
|
Docno int `json:"docno"`
|
||||||
|
Cancelid string `json:"cancelid,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZReportResponse struct {
|
||||||
|
BaseResponse
|
||||||
|
Info ZReportInfo `json:"info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZReportInfo struct {
|
||||||
|
Task int `json:"task"`
|
||||||
|
Fisid string `json:"fisid"`
|
||||||
|
Dataid int `json:"dataid"`
|
||||||
|
Doccode string `json:"doccode"`
|
||||||
|
Dt string `json:"dt"`
|
||||||
|
Cashier string `json:"cashier"`
|
||||||
|
Dtype int `json:"dtype"`
|
||||||
|
Isprint int `json:"isprint"`
|
||||||
|
Isoffline bool `json:"isoffline"`
|
||||||
|
Safe float64 `json:"safe"`
|
||||||
|
ShiftLink int `json:"shift_link"`
|
||||||
|
Docno int `json:"docno"`
|
||||||
|
Receipt ZReportReceipt `json:"receipt"`
|
||||||
|
Summary ZReportSummary `json:"summary"`
|
||||||
|
Taxes []ZReportTax `json:"taxes"`
|
||||||
|
Pays []ZReportPay `json:"pays"`
|
||||||
|
Money []ZReportMoney `json:"money"`
|
||||||
|
Cash []ZReportMoney `json:"cash"`
|
||||||
|
MoneyTransfer []interface{} `json:"money_transfer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZReportReceipt struct {
|
||||||
|
CountP int `json:"count_p"`
|
||||||
|
CountM int `json:"count_m"`
|
||||||
|
Count14 int `json:"count_14"`
|
||||||
|
CountTransfer int `json:"count_transfer"`
|
||||||
|
LastDocnoP int `json:"last_docno_p"`
|
||||||
|
LastDocnoM int `json:"last_docno_m"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZReportSummary struct {
|
||||||
|
BaseP float64 `json:"base_p"`
|
||||||
|
BaseM float64 `json:"base_m"`
|
||||||
|
TaxexP float64 `json:"taxex_p"`
|
||||||
|
TaxexM float64 `json:"taxex_m"`
|
||||||
|
DiscP float64 `json:"disc_p"`
|
||||||
|
DiscM float64 `json:"disc_m"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZReportTax struct {
|
||||||
|
GrCode int `json:"gr_code"`
|
||||||
|
BaseSumP float64 `json:"base_sum_p"`
|
||||||
|
BaseSumM float64 `json:"base_sum_m"`
|
||||||
|
BaseTaxSumP float64 `json:"base_tax_sum_p"`
|
||||||
|
BaseTaxSumM float64 `json:"base_tax_sum_m"`
|
||||||
|
BaseExSumP float64 `json:"base_ex_sum_p"`
|
||||||
|
BaseExSumM float64 `json:"base_ex_sum_m"`
|
||||||
|
TaxName string `json:"tax_name"`
|
||||||
|
TaxFname string `json:"tax_fname"`
|
||||||
|
TaxLit string `json:"tax_lit"`
|
||||||
|
TaxPercent float64 `json:"tax_percent"`
|
||||||
|
TaxSumP float64 `json:"tax_sum_p"`
|
||||||
|
TaxSumM float64 `json:"tax_sum_m"`
|
||||||
|
ExName string `json:"ex_name"`
|
||||||
|
ExPercent float64 `json:"ex_percent"`
|
||||||
|
ExSumP float64 `json:"ex_sum_p"`
|
||||||
|
ExSumM float64 `json:"ex_sum_m"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZReportPay struct {
|
||||||
|
Type int `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SumP float64 `json:"sum_p"`
|
||||||
|
SumM float64 `json:"sum_m"`
|
||||||
|
RoundPu float64 `json:"round_pu"`
|
||||||
|
RoundPd float64 `json:"round_pd"`
|
||||||
|
RoundMu float64 `json:"round_mu"`
|
||||||
|
RoundMd float64 `json:"round_md"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZReportMoney struct {
|
||||||
|
Type int `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SumP float64 `json:"sum_p"`
|
||||||
|
SumM float64 `json:"sum_m"`
|
||||||
|
RoundPu float64 `json:"round_pu"`
|
||||||
|
RoundPd float64 `json:"round_pd"`
|
||||||
|
RoundMu float64 `json:"round_mu"`
|
||||||
|
RoundMd float64 `json:"round_md"`
|
||||||
|
}
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
type FiskalCheck struct {
|
|
||||||
Source string `json:"source"`
|
|
||||||
Userinfo Userinfo `json:"userinfo"`
|
|
||||||
Fiscal Fiscal `json:"fiscal"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Userinfo struct {
|
|
||||||
Email string `json:"email"`
|
|
||||||
Phone string `json:"phone"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Fiscal struct {
|
|
||||||
Task int `json:"task"`
|
|
||||||
Cashier string `json:"cashier"`
|
|
||||||
Receipt Receipt `json:"receipt"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Receipt struct {
|
|
||||||
Sum float64 `json:"sum"`
|
|
||||||
Round float64 `json:"round"`
|
|
||||||
CommentUp string `json:"comment_up"`
|
|
||||||
CommentDown string `json:"comment_down"`
|
|
||||||
Disc float64 `json:"disc"`
|
|
||||||
DiscType int `json:"disc_type"`
|
|
||||||
Rows []ReceiptRow `json:"rows"`
|
|
||||||
Pays []ReceiptPay `json:"pays"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReceiptRow struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
Pop string `json:"pop,omitempty"`
|
|
||||||
Code1 string `json:"code1"`
|
|
||||||
Code2 string `json:"code2"`
|
|
||||||
CodeAa []string `json:"code_aa,omitempty"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Cnt int `json:"cnt"`
|
|
||||||
Price float64 `json:"price"`
|
|
||||||
Disc float64 `json:"disc"`
|
|
||||||
Taxgrp string `json:"taxgrp"`
|
|
||||||
Comment string `json:"comment"`
|
|
||||||
CodeA string `json:"code_a,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReceiptPay struct {
|
|
||||||
Type int `json:"type"`
|
|
||||||
Sum float64 `json:"sum"`
|
|
||||||
Change float64 `json:"change,omitempty"`
|
|
||||||
Comment string `json:"comment"`
|
|
||||||
Commission float64 `json:"commission,omitempty"`
|
|
||||||
Paysys string `json:"paysys,omitempty"`
|
|
||||||
Rrn string `json:"rrn,omitempty"`
|
|
||||||
OperType string `json:"oper_type,omitempty"`
|
|
||||||
Cardmask string `json:"cardmask,omitempty"`
|
|
||||||
TermID string `json:"term_id,omitempty"`
|
|
||||||
BankName string `json:"bank_name,omitempty"`
|
|
||||||
BankID string `json:"bank_id,omitempty"`
|
|
||||||
AuthCode string `json:"auth_code,omitempty"`
|
|
||||||
ShowAdditionalInfo bool `json:"show_additional_info,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type KasaResponse struct {
|
|
||||||
Task int `json:"task"`
|
|
||||||
Type int `json:"type"`
|
|
||||||
Ver int `json:"ver"`
|
|
||||||
Source string `json:"source"`
|
|
||||||
Device string `json:"device"`
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
Dt string `json:"dt"`
|
|
||||||
Res int `json:"res"`
|
|
||||||
ResAction int `json:"res_action"`
|
|
||||||
Errortxt string `json:"errortxt"`
|
|
||||||
Warnings []string `json:"warnings"`
|
|
||||||
Info Info `json:"info"`
|
|
||||||
ErrorExtra interface{} `json:"error_extra"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Info struct {
|
|
||||||
Task int `json:"task"`
|
|
||||||
Fisid string `json:"fisid"`
|
|
||||||
Dataid int `json:"dataid"`
|
|
||||||
Doccode string `json:"doccode"`
|
|
||||||
Dt string `json:"dt"`
|
|
||||||
Cashier string `json:"cashier"`
|
|
||||||
Dtype int `json:"dtype"`
|
|
||||||
Isprint int `json:"isprint"`
|
|
||||||
Isoffline bool `json:"isoffline"`
|
|
||||||
Safe float64 `json:"safe"`
|
|
||||||
ShiftLink int `json:"shift_link"`
|
|
||||||
Docno int `json:"docno"`
|
|
||||||
Cancelid string `json:"cancelid"`
|
|
||||||
}
|
|
||||||
158
examples.go
Normal file
158
examples.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package vchasno
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.jeezft.xyz/jeezft/go-vchasno-kassa/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleBasicUsage() {
|
||||||
|
client := NewClient(Config{
|
||||||
|
Token: "your-token",
|
||||||
|
Cashier: "Иванов",
|
||||||
|
Source: "parking",
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
response, _ := client.QuickSell(ctx, 100.00)
|
||||||
|
_ = response
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleWithDefaults() {
|
||||||
|
client := NewClient(Config{
|
||||||
|
Token: "your-token",
|
||||||
|
Cashier: "Иванов",
|
||||||
|
Defaults: &DefaultParams{
|
||||||
|
ProductName: "Парковка",
|
||||||
|
Comment: "Оплата парковки",
|
||||||
|
Taxgrp: "1",
|
||||||
|
PayType: api.PayTypeCash,
|
||||||
|
DefaultTimeout: 30 * time.Second,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
response, _ := client.QuickSell(ctx, 50.00)
|
||||||
|
_ = response
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleBuilderPattern() {
|
||||||
|
client := NewClient(Config{
|
||||||
|
Token: "your-token",
|
||||||
|
Cashier: "Иванов",
|
||||||
|
Defaults: &DefaultParams{
|
||||||
|
ProductName: "Парковка",
|
||||||
|
Taxgrp: "1",
|
||||||
|
PayType: api.PayTypeCash,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
response, _ := client.NewSellParams().
|
||||||
|
Price(100.00).
|
||||||
|
Cnt(2).
|
||||||
|
Comment("Парковка на 2 часа").
|
||||||
|
ExecuteDefault()
|
||||||
|
|
||||||
|
_ = response
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleBuilderWithCard() {
|
||||||
|
client := NewClient(Config{
|
||||||
|
Token: "your-token",
|
||||||
|
})
|
||||||
|
|
||||||
|
response, _ := client.NewSellParams().
|
||||||
|
Name("Услуга парковки").
|
||||||
|
Price(150.00).
|
||||||
|
PayCard("411111****1111", "305299", "123456789012", "123456").
|
||||||
|
Userinfo("user@example.com", "+380501234567").
|
||||||
|
ExecuteDefault()
|
||||||
|
|
||||||
|
_ = response
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleChangingDefaults() {
|
||||||
|
client := NewClient(Config{
|
||||||
|
Token: "your-token",
|
||||||
|
})
|
||||||
|
|
||||||
|
client.SetDefaults(DefaultParams{
|
||||||
|
ProductName: "VIP Парковка",
|
||||||
|
Comment: "VIP зона",
|
||||||
|
Taxgrp: "2",
|
||||||
|
PayType: api.PayTypeCard,
|
||||||
|
DefaultTimeout: 60 * time.Second,
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
response, _ := client.QuickSell(ctx, 200.00)
|
||||||
|
_ = response
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFullWorkflow() {
|
||||||
|
client := NewClient(Config{
|
||||||
|
Token: "your-token",
|
||||||
|
Cashier: "Петров",
|
||||||
|
Defaults: &DefaultParams{
|
||||||
|
ProductName: "Парковка",
|
||||||
|
Taxgrp: "1",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
openResp, _ := client.OpenShift(ctx)
|
||||||
|
_ = openResp
|
||||||
|
|
||||||
|
client.NewSellParams().
|
||||||
|
Price(50.00).
|
||||||
|
PayCash().
|
||||||
|
ExecuteDefault()
|
||||||
|
|
||||||
|
client.NewSellParams().
|
||||||
|
Price(100.00).
|
||||||
|
Cnt(2).
|
||||||
|
PayCard("411111****1111", "305299", "123456789012", "123456").
|
||||||
|
ExecuteDefault()
|
||||||
|
|
||||||
|
zReport, _ := client.CloseShift(ctx)
|
||||||
|
_ = zReport
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleMultipleSales() {
|
||||||
|
client := NewClient(Config{
|
||||||
|
Token: "your-token",
|
||||||
|
Defaults: &DefaultParams{
|
||||||
|
ProductName: "Парковка",
|
||||||
|
Taxgrp: "1",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
prices := []float64{50.00, 75.00, 100.00, 125.00}
|
||||||
|
|
||||||
|
for _, price := range prices {
|
||||||
|
client.NewSellParams().
|
||||||
|
Price(price).
|
||||||
|
PayCash().
|
||||||
|
ExecuteDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleWithDiscount() {
|
||||||
|
client := NewClient(Config{
|
||||||
|
Token: "your-token",
|
||||||
|
})
|
||||||
|
|
||||||
|
response, _ := client.NewSellParams().
|
||||||
|
Name("Парковка premium").
|
||||||
|
Price(200.00).
|
||||||
|
Disc(20.00).
|
||||||
|
Comment("Скидка 10%").
|
||||||
|
PayCash().
|
||||||
|
ExecuteDefault()
|
||||||
|
|
||||||
|
_ = response
|
||||||
|
}
|
||||||
250
vchasno.go
250
vchasno.go
@@ -7,51 +7,247 @@ import (
|
|||||||
"gitea.jeezft.xyz/jeezft/go-vchasno-kassa/api"
|
"gitea.jeezft.xyz/jeezft/go-vchasno-kassa/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Vchasno struct {
|
type Client struct {
|
||||||
api *api.Kasa
|
api *api.Client
|
||||||
|
cashier string
|
||||||
|
source string
|
||||||
|
defaults *DefaultParams
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
type Config struct {
|
||||||
PayTypeCash = 0
|
Token string
|
||||||
PayTypeCard = 2
|
Cashier string
|
||||||
)
|
Source string
|
||||||
|
Defaults *DefaultParams
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultParams struct {
|
||||||
|
ProductName string
|
||||||
|
Comment string
|
||||||
|
Taxgrp string
|
||||||
|
PayType int
|
||||||
|
DefaultTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(config Config) *Client {
|
||||||
|
if config.Cashier == "" {
|
||||||
|
config.Cashier = "cashier"
|
||||||
|
}
|
||||||
|
if config.Source == "" {
|
||||||
|
config.Source = "api"
|
||||||
|
}
|
||||||
|
|
||||||
|
defaults := config.Defaults
|
||||||
|
if defaults == nil {
|
||||||
|
defaults = &DefaultParams{
|
||||||
|
Taxgrp: "1",
|
||||||
|
PayType: api.PayTypeCash,
|
||||||
|
DefaultTimeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if defaults.Taxgrp == "" {
|
||||||
|
defaults.Taxgrp = "1"
|
||||||
|
}
|
||||||
|
if defaults.DefaultTimeout == 0 {
|
||||||
|
defaults.DefaultTimeout = 30 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
api: api.NewClient(config.Token),
|
||||||
|
cashier: config.Cashier,
|
||||||
|
source: config.Source,
|
||||||
|
defaults: defaults,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SetDefaults(defaults DefaultParams) {
|
||||||
|
c.defaults = &defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetDefaults() DefaultParams {
|
||||||
|
return *c.defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) OpenShift(ctx context.Context) (*api.SellResponse, error) {
|
||||||
|
return c.api.OpenShift(ctx, c.cashier)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) CloseShift(ctx context.Context) (*api.ZReportResponse, error) {
|
||||||
|
return c.api.CloseShift(ctx, c.cashier)
|
||||||
|
}
|
||||||
|
|
||||||
type SellParams struct {
|
type SellParams struct {
|
||||||
PayType int
|
|
||||||
Sum float64
|
|
||||||
Comment string
|
|
||||||
Name string
|
Name string
|
||||||
Cnt int
|
Cnt int
|
||||||
Price float64
|
Price float64
|
||||||
Disc float64
|
Disc float64
|
||||||
Taxgrp string
|
Taxgrp string
|
||||||
CardParams CardParams
|
Comment string
|
||||||
|
PayType int
|
||||||
|
CardParams *CardParams
|
||||||
|
Userinfo *api.Userinfo
|
||||||
}
|
}
|
||||||
|
|
||||||
type CardParams struct {
|
type CardParams struct {
|
||||||
Cardmask string
|
Cardmask string
|
||||||
BankID string
|
BankID string
|
||||||
|
|
||||||
RrnCode string
|
RrnCode string
|
||||||
AuthCode string
|
AuthCode string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVchasno(token string) *Vchasno {
|
func (c *Client) NewSellParams() *SellParamsBuilder {
|
||||||
return &Vchasno{
|
return &SellParamsBuilder{
|
||||||
api: api.NewKasaInstance(token),
|
client: c,
|
||||||
}
|
params: SellParams{
|
||||||
}
|
Name: c.defaults.ProductName,
|
||||||
|
|
||||||
func (v *Vchasno) NewSell(params SellParams) (*api.KasaResponse, error) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
return v.api.NewSell(ctx, params.PayType, params.Sum, params.Comment, params.CardParams.Cardmask, params.CardParams.BankID, params.Name, params.Cnt, params.Price, params.Disc, params.Taxgrp, params.CardParams.RrnCode, params.CardParams.AuthCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetDefaultParams() SellParams {
|
|
||||||
return SellParams{
|
|
||||||
PayType: PayTypeCard,
|
|
||||||
Taxgrp: "1",
|
|
||||||
Cnt: 1,
|
Cnt: 1,
|
||||||
|
Taxgrp: c.defaults.Taxgrp,
|
||||||
|
Comment: c.defaults.Comment,
|
||||||
|
PayType: c.defaults.PayType,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SellParamsBuilder struct {
|
||||||
|
client *Client
|
||||||
|
params SellParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) Name(name string) *SellParamsBuilder {
|
||||||
|
b.params.Name = name
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) Cnt(cnt int) *SellParamsBuilder {
|
||||||
|
b.params.Cnt = cnt
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) Price(price float64) *SellParamsBuilder {
|
||||||
|
b.params.Price = price
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) Disc(disc float64) *SellParamsBuilder {
|
||||||
|
b.params.Disc = disc
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) Taxgrp(taxgrp string) *SellParamsBuilder {
|
||||||
|
b.params.Taxgrp = taxgrp
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) Comment(comment string) *SellParamsBuilder {
|
||||||
|
b.params.Comment = comment
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) PayCash() *SellParamsBuilder {
|
||||||
|
b.params.PayType = api.PayTypeCash
|
||||||
|
b.params.CardParams = nil
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) PayCard(cardmask, bankID, rrnCode, authCode string) *SellParamsBuilder {
|
||||||
|
b.params.PayType = api.PayTypeCard
|
||||||
|
b.params.CardParams = &CardParams{
|
||||||
|
Cardmask: cardmask,
|
||||||
|
BankID: bankID,
|
||||||
|
RrnCode: rrnCode,
|
||||||
|
AuthCode: authCode,
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) Userinfo(email, phone string) *SellParamsBuilder {
|
||||||
|
b.params.Userinfo = &api.Userinfo{
|
||||||
|
Email: email,
|
||||||
|
Phone: phone,
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) Build() SellParams {
|
||||||
|
return b.params
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) Execute(ctx context.Context) (*api.SellResponse, error) {
|
||||||
|
return b.client.Sell(ctx, b.params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) ExecuteWithTimeout(timeout time.Duration) (*api.SellResponse, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
return b.client.Sell(ctx, b.params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SellParamsBuilder) ExecuteDefault() (*api.SellResponse, error) {
|
||||||
|
return b.ExecuteWithTimeout(b.client.defaults.DefaultTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Sell(ctx context.Context, params SellParams) (*api.SellResponse, error) {
|
||||||
|
if params.Taxgrp == "" {
|
||||||
|
params.Taxgrp = c.defaults.Taxgrp
|
||||||
|
}
|
||||||
|
if params.Name == "" {
|
||||||
|
params.Name = c.defaults.ProductName
|
||||||
|
}
|
||||||
|
if params.Comment == "" {
|
||||||
|
params.Comment = c.defaults.Comment
|
||||||
|
}
|
||||||
|
if params.Cnt == 0 {
|
||||||
|
params.Cnt = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
row := api.NewReceiptRow(params.Name, params.Cnt, params.Price, params.Taxgrp)
|
||||||
|
if params.Disc > 0 {
|
||||||
|
row.Disc = params.Disc
|
||||||
|
}
|
||||||
|
if params.Comment != "" {
|
||||||
|
row.Comment = params.Comment
|
||||||
|
}
|
||||||
|
|
||||||
|
var pay api.ReceiptPay
|
||||||
|
sum := (params.Price - params.Disc) * float64(params.Cnt)
|
||||||
|
|
||||||
|
if params.PayType == api.PayTypeCard && params.CardParams != nil {
|
||||||
|
pay = api.NewReceiptPayCard(
|
||||||
|
sum,
|
||||||
|
params.CardParams.Cardmask,
|
||||||
|
params.CardParams.BankID,
|
||||||
|
params.CardParams.RrnCode,
|
||||||
|
params.CardParams.AuthCode,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
pay = api.NewReceiptPayCash(sum, params.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.api.Sell(ctx, api.SellParams{
|
||||||
|
Cashier: c.cashier,
|
||||||
|
Source: c.source,
|
||||||
|
Rows: []api.ReceiptRow{row},
|
||||||
|
Pays: []api.ReceiptPay{pay},
|
||||||
|
Userinfo: params.Userinfo,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SellWithTimeout(params SellParams, timeout time.Duration) (*api.SellResponse, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
return c.Sell(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) QuickSell(ctx context.Context, price float64) (*api.SellResponse, error) {
|
||||||
|
return c.Sell(ctx, SellParams{
|
||||||
|
Price: price,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) QuickSellNamed(ctx context.Context, name string, price float64) (*api.SellResponse, error) {
|
||||||
|
return c.Sell(ctx, SellParams{
|
||||||
|
Name: name,
|
||||||
|
Price: price,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user