89 KiB
Обзор проекта Invoice Az
Оглавление
- Общая информация
- Архитектура проекта
- Технологический стек
- Структура файлов
- Основные модули
- DocTypes
- API интеграция
- Рабочие процессы
- Установка и настройка
- Разработка
Общая информация
Название проекта: Invoice Az (Invoices Azərbaycan) Версия: 0.0.1 Автор: Jey ERP (info@jeyerp.az) Лицензия: Unlicense (Общественное достояние) Требования: Python 3.10+
Назначение
Invoice Az - это приложение интеграции для Frappe/ERPNext, которое обеспечивает двустороннюю синхронизацию с государственной системой электронного налогообложения Азербайджана E-Taxes (https://new.e-taxes.gov.az).
Основные возможности
- Импорт счетов-фактур - автоматический импорт входящих счетов от поставщиков
- Экспорт счетов-фактур - отправка исходящих счетов клиентам через E-Taxes
- Управление справочниками - синхронизация товаров, единиц измерения, контрагентов
- Интеллектуальное сопоставление - автоматическое сопоставление данных между системами с точностью 95%
- ASAN аутентификация - интеграция с государственной системой аутентификации ASAN Login
- Массовые операции - обработка до 200 счетов за один запрос
- Отслеживание и аудит - полная история операций и связей между документами
Архитектура проекта
Структура директорий
/home/frappe/frappe-bench/apps/invoice_az/
├── README.md # Краткая документация
├── claude.md # Подробная документация на русском (34 КБ)
├── QA_SEND_INVOICE.md # Q&A по отправке счетов
├── pyproject.toml # Конфигурация Python проекта
├── license.txt # Лицензия
├── invoice_az/ # Основная директория приложения
│ ├── api.py # API для закупок (4,320 строк)
│ ├── auth.py # ASAN аутентификация (968 строк)
│ ├── sales_api.py # API для продаж (887 строк)
│ ├── send_sales_api.py # Отправка счетов (730 строк)
│ ├── hooks.py # Интеграция с Frappe (292 строк)
│ ├── client/ # JavaScript клиентские скрипты
│ │ ├── etaxes_common.js # Общие утилиты (1,052 строки)
│ │ ├── purchase_order.js # Заказы на покупку (1,532 строки)
│ │ ├── sales_order.js # Заказы на продажу (1,585 строк)
│ │ ├── sales_invoice.js # Счета на оплату (1,856 строк)
│ │ └── [списки для справочников] # 4 файла по ~900 строк
│ └── invoice_az/ # Определения DocTypes
│ └── doctype/ # 17 кастомных DocTypes
│ ├── asan_login/ # Хранение токенов ASAN
│ ├── e_taxes_settings/ # Настройки E-Taxes
│ ├── e_taxes_purchase/ # Входящие счета
│ ├── e_taxes_sales/ # Исходящие счета
│ ├── e_taxes_item/ # Товары из E-Taxes
│ ├── e_taxes_unit/ # Единицы измерения
│ ├── e_taxes_customers/ # Клиенты
│ ├── e_taxes_suppliers/ # Поставщики
│ └── [mapping doctypes] # Таблицы сопоставления
Уровни приложения
-
Уровень представления (Frontend)
- JavaScript клиентские скрипты
- Интеграция с Frappe UI
- Диалоги и формы
- Списки с возможностью массовых операций
-
Уровень бизнес-логики (Backend)
- Python модули API
- Обработка данных E-Taxes
- Интеллектуальное сопоставление
- Создание документов в ERPNext
-
Уровень данных (Database)
- 17 кастомных DocTypes
- Связи с стандартными DocTypes ERPNext
- Индексированные поля для быстрого поиска
-
Внешние интеграции
- REST API E-Taxes
- ASAN Login аутентификация
- Планировщик задач Frappe
Технологический стек
Backend
| Технология | Назначение |
|---|---|
| Python 3.10+ | Основной язык программирования |
| Frappe Framework | Веб-фреймворк для бизнес-приложений |
| Frappe ORM | Работа с базой данных |
| requests | HTTP клиент для API |
| difflib.SequenceMatcher | Нечеткое сопоставление (95%) |
| re (regex) | Обработка строк и валидация |
| json | Сериализация/десериализация данных |
Frontend
| Технология | Назначение |
|---|---|
| JavaScript ES6+ | Клиентские скрипты |
| jQuery | Манипуляция DOM |
| Frappe UI | Готовые UI компоненты |
| frappe.call() | AJAX коммуникация |
Database
| Технология | Назначение |
|---|---|
| MariaDB | Реляционная СУБД |
| InnoDB | Storage engine для всех DocTypes |
| Индексы | etaxes_item_name, etaxes_unit_name, etaxes_party_name |
Внешние API
- E-Taxes REST API (https://new.e-taxes.gov.az)
- Аутентификация: Bearer Token через ASAN Login
- Формат: JSON
- Протокол: HTTPS
- Ограничение: 100мс между запросами
Инструменты разработки
- ruff - Линтинг и форматирование Python
- eslint - Линтинг JavaScript
- prettier - Форматирование кода
- pyupgrade - Модернизация синтаксиса Python
- pre-commit - Git хуки для качества кода
Структура файлов
Статистика кода
| Категория | Количество | Строки кода |
|---|---|---|
| Python модули | 5 основных | 7,198 строк |
| JavaScript файлы | 9 файлов | 9,786 строк |
| DocTypes | 17 кастомных | ~80 файлов |
| Документация | 3 MD файла | 50 КБ |
| Конфигурация | 5 файлов | ~2.5 КБ |
Основные конфигурационные файлы
pyproject.toml - Метаданные проекта, зависимости, настройки ruff/eslint
[project]
name = "invoice_az"
version = "0.0.1"
requires-python = ">=3.10"
[tool.ruff]
line-length = 110
target-version = "py310"
hooks.py - Точки интеграции с Frappe
- Обработчики событий DocType
- Задачи планировщика (каждые 4 минуты)
- Клиентские скрипты
- Хуки жизненного цикла
.editorconfig - Настройки редактора
- Отступы, окончания строк
- Кодировка UTF-8
.gitignore - Правила исключения Git
- Байт-код Python
- node_modules
- Логи и кэш
Основные модули
1. api.py (4,320 строк)
Назначение: Основной модуль для работы с входящими счетами-фактурами
Ключевые функции:
@frappe.whitelist()
def load_items_from_invoices(from_date, to_date, page, company, page_size=200)
# Загрузка счетов из E-Taxes за период
# Пагинация, кэширование (5 минут)
# Возвращает список счетов с деталями
@frappe.whitelist()
def create_purchase_order(invoice_data, company, set_warehouse=None, supplier=None, update_rate=True, expand_desc=True)
# Создание Purchase Order из счета E-Taxes
# Интеллектуальное сопоставление товаров/поставщиков
# Автоматическое создание недостающих записей
def load_items()
# Синхронизация справочника товаров из E-Taxes
# Массовая загрузка до 200 записей
def load_units()
# Синхронизация единиц измерения
def load_parties()
# Синхронизация контрагентов (клиенты/поставщики)
Интеллектуальное сопоставление:
def find_best_match(etaxes_name, existing_names):
# Использует difflib.SequenceMatcher
# Порог совпадения: 95%
# Поддержка азербайджанских символов (ə, ö, ü, ğ, ş, ç, ı)
# Автоматическое сопоставление при высокой схожести
2. auth.py (968 строк)
Назначение: Аутентификация через ASAN Login и управление токенами
Этапы аутентификации:
- Инициализация входа
@frappe.whitelist()
def initiate_asan_login()
# Получение session_id от E-Taxes
# Возврат QR кода для мобильного приложения ASAN
- Проверка статуса
@frappe.whitelist()
def check_asan_login_status(session_id, docname)
# Опрос статуса каждые 2 секунды
# Обработка подтверждения через мобильное приложение
- Выбор сертификата
@frappe.whitelist()
def select_certificate(session_id, cert_id, docname)
# Получение access_token
- Выбор налогоплательщика
@frappe.whitelist()
def select_taxpayer(taxpayer_id, docname)
# Получение main_token для работы с E-Taxes API
# Сохранение в Asan Login doctype
Автоматическое обновление токена:
def renew_token_background()
# Cron задача: каждые 4 минуты
# Пропускает, если нет активности
# Обрабатывает ошибки 401/500
# Устанавливает auth_status при неудаче
3. sales_api.py (887 строк)
Назначение: Обработка исходящих счетов-фактур
Основные функции:
@frappe.whitelist()
def load_sales_invoices_from_etaxes(from_date, to_date, page, company, page_size=200)
# Загрузка исходящих счетов из E-Taxes
# Структура аналогична load_items_from_invoices
@frappe.whitelist()
def create_sales_order(invoice_data, company, set_warehouse=None, customer=None, update_rate=True, expand_desc=True)
# Создание Sales Order из счета E-Taxes
# Сопоставление клиентов и товаров
# Автоматическое создание недостающих записей
4. send_sales_api.py (730 строк)
Назначение: Отправка счетов в E-Taxes
Процесс отправки:
@frappe.whitelist()
def send_sales_invoice_to_etaxes(sales_invoice_name)
# 1. Валидация данных
# - Проверка customer.tax_id (ИНН)
# - Проверка item.product_group_code
# - Защита от дубликатов
# 2. Генерация серийного номера
serial_number = generate_serial_number()
# 3. Формирование JSON payload
invoice_json = {
"receiver": {"name": customer_name, "tin": tin},
"invoiceLines": [
{
"productName": item_name,
"quantity": qty,
"pricePerUnit": rate,
"vatRate": vat_rate,
"vatAmount": vat_amount
}
],
"comment": comments
}
# 4. Отправка в E-Taxes
response = submit_invoice(invoice_json)
# 5. Автоматическая подпись через ASAN
sign_invoice_with_asan(invoice_id)
# 6. Сохранение результата
create_etaxes_sales_outbox_record()
5. hooks.py (292 строки)
Назначение: Интеграция с Frappe Framework
Обработчики событий:
doc_events = {
"Purchase Order": {
"on_trash": "invoice_az.api.unlink_etaxes_record",
"on_cancel": "invoice_az.api.unlink_etaxes_record",
},
"Sales Order": {
"on_trash": "invoice_az.sales_api.unlink_sales_etaxes_record",
"on_cancel": "invoice_az.sales_api.unlink_sales_etaxes_record",
}
}
Планировщик задач:
scheduler_events = {
"cron": {
"*/4 * * * *": [ # Каждые 4 минуты
"invoice_az.auth.renew_token_background"
]
}
}
Клиентские скрипты:
doctype_js = {
"Purchase Order": "client/purchase_order.js",
"Sales Order": "client/sales_order.js",
"Sales Invoice": "client/sales_invoice.js",
"Purchase Invoice": "client/purchase_invoice.js"
}
DocTypes
Конфигурационные DocTypes (4)
1. Asan Login
Назначение: Хранение токенов и данных аутентификации ASAN
Основные поля:
session_id- ID сессии ASANaccess_token- Токен доступаmain_token- Основной токен для E-Taxes APIcertificate_id- ID выбранного сертификатаtaxpayer_id- ID налогоплательщикаtaxpayer_name- Название организацииauth_status- Статус: Authenticated / Not Authenticatedlast_activity- Время последней активностиtoken_expiry- Время истечения токена
Методы:
get_headers()- Формирование заголовков для API запросов
2. E-Taxes Settings
Назначение: Центральная конфигурация приложения
Основные поля:
api_base_url- Базовый URL E-Taxes APIcompany- Компания по умолчаниюdefault_warehouse- Склад по умолчаниюitem_mappings- Таблица сопоставления товаровunit_mappings- Таблица сопоставления единицparty_mappings- Таблица сопоставления контрагентовauto_create_items- Автосоздание товаровfuzzy_match_threshold- Порог нечеткого совпадения (0.95)
3. E-Taxes Purchase
Назначение: Отслеживание импортированных счетов закупок
Основные поля:
invoice_id- ID счета в E-Taxesinvoice_serial_number- Серийный номерinvoice_date- Дата счетаsupplier_name- Название поставщикаsupplier_tin- ИНН поставщикаtotal_amount- Общая суммаvat_amount- Сумма НДСpurchase_order- Связь с Purchase Orderpurchase_invoice- Связь с Purchase Invoiceverification_code- Код проверкиstatus- Статус: New / Mapped / Imported
4. E-Taxes Sales
Назначение: Отслеживание экспортированных счетов продаж
Аналогичная структура E-Taxes Purchase, но для исходящих счетов
Справочные DocTypes (5)
5. E-Taxes Item
Назначение: Товары/услуги из E-Taxes
Основные поля:
etaxes_item_code- Код товара в E-Taxesetaxes_item_name- Название (индексировано)category- Категорияunit_code- Код единицы измеренияmapped_item- Связь с Item (ERPNext)mapping_status- Статус: Unmapped / Auto-Mapped / Manually Mapped
6. E-Taxes Unit
Назначение: Единицы измерения из E-Taxes
Основные поля:
etaxes_unit_code- Код единицыetaxes_unit_name- Название (индексировано)mapped_uom- Связь с UOM (ERPNext)mapping_status- Статус сопоставления
7. E-Taxes Customers
Назначение: Клиенты из E-Taxes
Основные поля:
customer_tin- ИНН клиентаcustomer_name- Названиеmapped_customer- Связь с Customer (ERPNext)mapping_status- Статус сопоставления
8. E-Taxes Suppliers
Назначение: Поставщики из E-Taxes
Основные поля:
supplier_tin- ИНН поставщикаsupplier_name- Названиеmapped_supplier- Связь с Supplier (ERPNext)mapping_status- Статус сопоставления
9. E-Taxes Parties
Назначение: Универсальные записи контрагентов
Основные поля:
party_tin- ИНН контрагентаparty_name- Названиеparty_type- Тип: Customer / Supplier / Both
Таблицы сопоставления (5 Child Tables)
10. E-Taxes Item Mapping
etaxes_item_code- Код товара E-Taxeserp_item_code- Код товара ERPNextmapping_type- Тип: Auto / Manual
11. E-Taxes Unit Mapping
etaxes_unit_code- Код единицы E-Taxeserp_uom- UOM в ERPNext
12. E-Taxes Customer Mappings
etaxes_customer_tin- ИНН клиента E-Taxeserp_customer- Customer в ERPNext
13. E-Taxes Supplier Mappings
etaxes_supplier_tin- ИНН поставщика E-Taxeserp_supplier- Supplier в ERPNext
14. E-Taxes Party Mapping
etaxes_party_tin- ИНН контрагента E-Taxeserp_party- Контрагент в ERPNextparty_type- Тип контрагента
Отслеживание отправки (1)
15. E-Taxes Sales Outbox
Назначение: Журнал отправленных счетов в E-Taxes
Основные поля:
sales_invoice- Связь с Sales Invoiceetaxes_invoice_id- ID счета в E-Taxesserial_number- Серийный номерverification_code- Код проверкиsend_date- Дата отправкиsend_status- Статус: Pending / Sent / Signed / Failederror_message- Сообщение об ошибке
Тестовый DocType (1)
16. TestAPI
Назначение: Разработка и отладка API вызовов
API интеграция
Эндпоинты E-Taxes
Аутентификация
Инициализация входа
POST /api/po/auth/public/v1/initiate
Проверка статуса
POST /api/po/auth/public/v1/status/{session_id}
Выбор сертификата
POST /api/po/auth/public/v1/certificate/select
Body: {"sessionId": "...", "certificateId": "..."}
Выбор налогоплательщика
POST /api/po/auth/public/v1/taxpayer/select
Body: {"accessToken": "...", "taxpayerId": "..."}
Обновление токена
POST /api/po/auth/public/v1/renew
Headers: {"x-authorization": "Bearer {main_token}"}
Работа со счетами
Получение входящих счетов (inbox)
POST /api/po/invoice/public/v2/invoice/find.inbox
Body: {
"from": "2024-01-01",
"to": "2024-12-31",
"page": 0,
"size": 200
}
Получение исходящих счетов (outbox)
POST /api/po/invoice/public/v2/invoice/find.outbox
Body: {
"from": "2024-01-01",
"to": "2024-12-31",
"page": 0,
"size": 200
}
Детали счета
GET /api/po/invoice/public/v2/invoice/{invoice_id}
Создание счета
POST /api/po/invoice/public/v2/invoice
Body: {
"receiver": {
"name": "ООО Компания",
"tin": "1234567890"
},
"invoiceLines": [
{
"productName": "Товар 1",
"quantity": 10,
"pricePerUnit": 100,
"vatRate": 18,
"vatAmount": 180
}
]
}
Генерация серийного номера
POST /api/po/invoice/public/v1/generateSerialNumber/defaultInvoice
Подпись счета через ASAN
POST /api/po/invoice/public/v1/invoice/sign/withAsanImza
Body: {"invoiceId": "..."}
Заголовки запросов
Все запросы к E-Taxes API требуют следующих заголовков:
headers = {
"Content-Type": "application/json",
"x-authorization": f"Bearer {main_token}",
"User-Agent": "Mozilla/5.0...",
"Accept": "application/json"
}
Обработка ошибок
try:
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
# Токен истек - обновить токен
renew_token()
elif e.response.status_code == 500:
# Ошибка сервера - повторить с задержкой
time.sleep(1)
retry_request()
else:
frappe.log_error(str(e), "E-Taxes API Error")
Ограничения API
- Размер страницы: максимум 200 записей
- Задержка между запросами: минимум 100мс
- Время жизни токена: обновление каждые 4 минуты
- Кэширование: 5 минут для списков счетов
Рабочие процессы
Процесс импорта счета закупки (Inbox)
┌─────────────────────────────────────────────────────────┐
│ 1. Пользователь открывает Purchase Order │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 2. Нажимает кнопку "Load from E-Taxes" │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 3. Выбирает диапазон дат (from_date, to_date) │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 4. Frontend → load_items_from_invoices() │
│ - Проверка кэша (5 минут) │
│ - Если нет - запрос к E-Taxes API │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 5. E-Taxes API возвращает список счетов │
│ - Номер счета, дата │
│ - Поставщик (название, ИНН) │
│ - Список позиций (товар, кол-во, цена) │
│ - НДС │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 6. Отображается браузер счетов │
│ - Таблица со всеми счетами │
│ - Фильтрация, поиск │
│ - Просмотр позиций счета │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 7. Пользователь выбирает счет и нажимает "Import" │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 8. create_purchase_order(invoice_data) │
│ │
│ A. Сопоставление поставщика: │
│ - Поиск по ИНН в Supplier │
│ - Если нет - нечеткое сопоставление по имени │
│ - Если совпадение < 95% - создать нового │
│ │
│ B. Сопоставление товаров: │
│ ┌─ Для каждой позиции счета ─┐ │
│ │ │ │
│ │ 1. Поиск в Item Mapping │ │
│ │ 2. Нечеткое сопоставление │ │
│ │ 3. Если нет - создать Item │ │
│ └──────────────────────────────┘ │
│ │
│ C. Сопоставление единиц измерения: │
│ - Поиск в UOM Mapping │
│ - Если нет - создать UOM │
│ │
│ D. Создание Purchase Order: │
│ - Заполнение полей │
│ - Добавление позиций (items) │
│ - Расчет сумм и НДС │
│ - Сохранение документа │
│ │
│ E. Создание E-Taxes Purchase record: │
│ - Сохранение ID счета E-Taxes │
│ - Связь с Purchase Order │
│ - Статус: Imported │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 9. Purchase Order готов к отправке (Submit) │
└─────────────────────────────────────────────────────────┘
Процесс экспорта счета продажи (Outbox)
┌─────────────────────────────────────────────────────────┐
│ 1. Пользователь создает Sales Invoice в ERPNext │
│ - Выбирает клиента (обязательно tax_id) │
│ - Добавляет позиции (обязательно product_group_code)│
│ - Заполняет количество, цену │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 2. Отправляет документ (Submit) │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 3. Нажимает "Send to E-Taxes" │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 4. Показывается первое подтверждение │
│ "Вы уверены, что хотите отправить счет?" │
└─────────────────────┬───────────────────────────────────┘
│ Да
▼
┌─────────────────────────────────────────────────────────┐
│ 5. Показывается второе подтверждение │
│ "Это действие необратимо. Продолжить?" │
└─────────────────────┬───────────────────────────────────┘
│ Да
▼
┌─────────────────────────────────────────────────────────┐
│ 6. send_sales_invoice_to_etaxes() │
│ │
│ A. Валидация данных: │
│ - Проверка customer.tax_id (ИНН) │
│ - Проверка items.product_group_code │
│ - Проверка дубликатов в Outbox │
│ │
│ B. Генерация серийного номера: │
│ POST /generateSerialNumber/defaultInvoice │
│ Получение уникального номера для счета │
│ │
│ C. Формирование JSON payload: │
│ { │
│ "serialNumber": "...", │
│ "receiver": { │
│ "name": "ООО Клиент", │
│ "tin": "1234567890" │
│ }, │
│ "invoiceLines": [ │
│ { │
│ "productName": "Товар", │
│ "productGroupCode": "12345", │
│ "quantity": 10, │
│ "pricePerUnit": 100, │
│ "totalPrice": 1000, │
│ "vatRate": 18, │
│ "vatAmount": 180 │
│ } │
│ ], │
│ "totalAmount": 1180, │
│ "comment": "Примечание" │
│ } │
│ │
│ D. Отправка в E-Taxes: │
│ POST /api/po/invoice/public/v2/invoice │
│ Получение invoice_id │
│ │
│ E. Автоматическая подпись через ASAN: │
│ POST /invoice/sign/withAsanImza │
│ Body: {"invoiceId": "..."} │
│ │
│ F. Создание E-Taxes Sales Outbox record: │
│ - Сохранение invoice_id │
│ - Сохранение serial_number │
│ - Сохранение verification_code │
│ - Связь с Sales Invoice │
│ - Статус: Sent & Signed │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 7. Показывается сообщение об успехе │
│ "Счет успешно отправлен в E-Taxes" │
│ "Invoice ID: 12345" │
│ "Serial Number: ABC-123" │
└─────────────────────────────────────────────────────────┘
Процесс аутентификации ASAN
┌─────────────────────────────────────────────────────────┐
│ 1. Пользователь открывает ASAN Login doctype │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 2. Нажимает "Login with ASAN" │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 3. initiate_asan_login() │
│ POST /api/po/auth/public/v1/initiate │
│ Получение session_id и qrCodeValue │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 4. Отображается QR код │
│ "Отсканируйте QR код в мобильном приложении ASAN" │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 5. Опрос статуса каждые 2 секунды │
│ check_asan_login_status(session_id) │
│ POST /api/po/auth/public/v1/status/{session_id} │
│ │
│ Статусы: │
│ - PENDING → продолжить опрос │
│ - APPROVED → переход к шагу 6 │
│ - REJECTED → показать ошибку │
└─────────────────────┬───────────────────────────────────┘
│ APPROVED
▼
┌─────────────────────────────────────────────────────────┐
│ 6. Получение списка сертификатов │
│ Отображается диалог выбора сертификата │
│ - Название сертификата │
│ - Срок действия │
│ - Организация │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 7. Пользователь выбирает сертификат │
│ select_certificate(session_id, cert_id) │
│ POST /certificate/select │
│ Получение access_token │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 8. Получение списка налогоплательщиков │
│ Отображается диалог выбора налогоплательщика │
│ - Название организации │
│ - ИНН │
│ - Тип налогоплательщика │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 9. Пользователь выбирает налогоплательщика │
│ select_taxpayer(taxpayer_id) │
│ POST /taxpayer/select │
│ Получение main_token │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 10. Сохранение в ASAN Login doctype: │
│ - session_id │
│ - access_token │
│ - main_token ← используется для всех API запросов │
│ - certificate_id │
│ - taxpayer_id │
│ - taxpayer_name │
│ - auth_status = "Authenticated" │
│ - last_activity = now() │
│ - token_expiry = now() + 4 minutes │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 11. Аутентификация завершена │
│ "Вы успешно вошли как [taxpayer_name]" │
└─────────────────────────────────────────────────────────┘
Процесс автоматического обновления токена
┌─────────────────────────────────────────────────────────┐
│ Cron задача: Каждые 4 минуты │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ renew_token_background() │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Проверка: Есть ли активные ASAN Login записи? │
└─────────────────────┬───────────────────────────────────┘
│ Нет → Завершить
│ Да ↓
▼
┌─────────────────────────────────────────────────────────┐
│ Проверка: Была ли активность за последние 10 минут? │
└─────────────────────┬───────────────────────────────────┘
│ Нет → Завершить (экономия ресурсов)
│ Да ↓
▼
┌─────────────────────────────────────────────────────────┐
│ Отправка запроса на обновление токена │
│ POST /api/po/auth/public/v1/renew │
│ Headers: {"x-authorization": "Bearer {main_token}"} │
└─────────────────────┬───────────────────────────────────┘
│
▼
├─ Успех (200) ─────────────────────┐
│ │
│ ┌──────────────────────────────┐ │
│ │ Получение нового main_token │ │
│ │ Обновление ASAN Login record │ │
│ │ last_activity = now() │ │
│ │ token_expiry = now() + 4min │ │
│ └──────────────────────────────┘ │
│ │
├─ Ошибка 401 (Unauthorized) ───────┤
│ │
│ ┌──────────────────────────────┐ │
│ │ Токен истек │ │
│ │ auth_status = "Not Auth..." │ │
│ │ Требуется повторный вход │ │
│ └──────────────────────────────┘ │
│ │
└─ Ошибка 500 (Server Error) ───────┤
│
┌──────────────────────────────┐ │
│ Задержка 1 секунда │ │
│ Повторная попытка │ │
│ Если неудача - лог ошибки │ │
└──────────────────────────────┘ │
│
┌────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Следующий запуск через 4 минуты │
└─────────────────────────────────────────────────────────┘
Установка и настройка
Требования
- Frappe Framework (версия 14+)
- ERPNext (версия 14+)
- Python 3.10+
- MariaDB 10.6+
- Node.js 16+
Установка через bench
- Переход в директорию bench
cd frappe-bench
- Получение приложения
bench get-app https://github.com/your-org/invoice_az.git
- Установка на сайт
bench --site your-site.local install-app invoice_az
- Миграция базы данных
bench --site your-site.local migrate
- Очистка кэша
bench --site your-site.local clear-cache
- Перезапуск bench
bench restart
Первоначальная настройка
1. Настройка ASAN Login
- Откройте "ASAN Login" в Desk
- Создайте новую запись
- Нажмите "Login with ASAN"
- Отсканируйте QR код мобильным приложением ASAN
- Подтвердите вход в приложении
- Выберите сертификат
- Выберите налогоплательщика
- Сохраните запись
2. Настройка E-Taxes Settings
- Откройте "E-Taxes Settings"
- Заполните основные поля:
- API Base URL:
https://new.e-taxes.gov.az - Company: Ваша компания по умолчанию
- Default Warehouse: Склад по умолчанию
- API Base URL:
- Настройте параметры:
- Auto Create Items: Включить/выключить автосоздание товаров
- Fuzzy Match Threshold: 0.95 (95% совпадения)
- Сохраните настройки
3. Настройка кастомных полей (в приложении jey_erp)
Убедитесь, что следующие кастомные поля существуют:
Customer:
tax_id(Data) - ИНН клиента
Item:
product_group_code(Data) - Код товарной группы
Sales Invoice Item:
- Поля для расчета НДС
Синхронизация справочников
После установки рекомендуется синхронизировать справочные данные:
-
Синхронизация товаров
- Откройте "E-Taxes Item"
- Нажмите "Sync from E-Taxes"
- Дождитесь завершения загрузки
-
Синхронизация единиц измерения
- Откройте "E-Taxes Unit"
- Нажмите "Sync from E-Taxes"
-
Синхронизация контрагентов
- Откройте "E-Taxes Parties"
- Нажмите "Sync from E-Taxes"
Разработка
Настройка окружения разработки
- Клонирование репозитория
cd frappe-bench/apps
git clone https://github.com/your-org/invoice_az.git
- Установка pre-commit хуков
cd invoice_az
pre-commit install
- Установка зависимостей разработки
pip install ruff pytest
npm install -g eslint prettier
Структура кода
Python модули (invoice_az/)
api.py- API для закупокauth.py- Аутентификацияsales_api.py- API для продажsend_sales_api.py- Отправка счетовhooks.py- Интеграция с Frappe
JavaScript клиенты (invoice_az/client/)
etaxes_common.js- Общие утилитыpurchase_order.js- Заказы на покупкуsales_order.js- Заказы на продажуsales_invoice.js- Счета на оплату[entity]_list.js- Списки справочников
DocTypes (invoice_az/invoice_az/doctype/)
- Каждый DocType в отдельной папке
- Файлы:
.py,.js,.json,test_*.py
Соглашения кодирования
Python (ruff)
- Длина строки: 110 символов
- Целевая версия: Python 3.10
- Кавычки: двойные
- Отступы: табы
- Правила: F, E, W, I, UP, B
JavaScript (eslint)
- Кавычки: одинарные
- Точка с запятой: обязательна
- Отступы: табы
Тестирование
Запуск тестов Python
cd frappe-bench
bench --site your-site.local run-tests --app invoice_az
Запуск конкретного теста
bench --site your-site.local run-tests --test invoice_az.invoice_az.doctype.asan_login.test_asan_login
Запуск линтера
ruff check invoice_az/
ruff format invoice_az/
Логирование и отладка
Логирование ошибок
frappe.log_error(
title="E-Taxes API Error",
message=str(error),
reference_doctype="E-Taxes Purchase",
reference_name=purchase_name
)
Информационные логи
import logging
logger = logging.getLogger(__name__)
logger.info(f"Processing invoice {invoice_id}")
Отладочный вывод
frappe.msgprint(f"Debug: {variable}")
print(f"Debug: {variable}") # Для консоли bench
Создание нового DocType
- Создание через bench
bench --site your-site.local new-doctype "E-Taxes New Feature"
- Редактирование JSON
{
"name": "E-Taxes New Feature",
"module": "Invoice Az",
"autoname": "field:name",
"fields": [
{
"fieldname": "name",
"fieldtype": "Data",
"label": "Name",
"reqd": 1
}
]
}
- Создание Python контроллера
import frappe
from frappe.model.document import Document
class ETaxesNewFeature(Document):
def validate(self):
# Валидация при сохранении
pass
def on_submit(self):
# Действия при отправке
pass
- Создание JavaScript контроллера
frappe.ui.form.on('E-Taxes New Feature', {
refresh: function(frm) {
// Логика формы
}
});
Создание нового API endpoint
@frappe.whitelist()
def my_new_api_function(param1, param2):
"""
Описание функции
Args:
param1: Описание параметра 1
param2: Описание параметра 2
Returns:
dict: Результат операции
"""
try:
# Валидация прав
frappe.only_for("System Manager")
# Логика обработки
result = process_data(param1, param2)
return {
"success": True,
"data": result
}
except Exception as e:
frappe.log_error(str(e), "API Error")
return {
"success": False,
"error": str(e)
}
Вызов из JavaScript:
frappe.call({
method: 'invoice_az.api.my_new_api_function',
args: {
param1: 'value1',
param2: 'value2'
},
callback: function(r) {
if (r.message.success) {
frappe.msgprint('Success!');
} else {
frappe.msgprint('Error: ' + r.message.error);
}
}
});
Добавление нового клиентского скрипта
- Создание файла
invoice_az/client/my_script.js
frappe.ui.form.on('My DocType', {
refresh: function(frm) {
frm.add_custom_button(__('My Action'), function() {
// Логика кнопки
});
},
field_name: function(frm) {
// Обработчик изменения поля
}
});
- Регистрация в hooks.py
doctype_js = {
"My DocType": "client/my_script.js"
}
Git workflow
Создание новой фичи:
git checkout -b feature/my-new-feature
# Разработка
git add .
git commit -m "feat: Add my new feature"
git push origin feature/my-new-feature
# Создание Pull Request
Pre-commit хуки автоматически проверят:
- Линтинг Python (ruff)
- Линтинг JavaScript (eslint)
- Форматирование (prettier)
- Синтаксис Python (pyupgrade)
Производительность и оптимизация
Кэширование:
# Кэш на 5 минут
cache_key = f"etaxes_invoices_{from_date}_{to_date}"
cached_data = frappe.cache().get_value(cache_key)
if cached_data:
return cached_data
data = fetch_from_api()
frappe.cache().set_value(cache_key, data, expires_in_sec=300)
Массовые операции:
# Вместо цикла с save()
for item in items:
item.save()
# Используйте bulk_insert
frappe.db.bulk_insert(
"E-Taxes Item",
["name", "etaxes_item_code", "etaxes_item_name"],
[[item.name, item.code, item.name] for item in items]
)
Индексация:
# В DocType JSON
"fields": [
{
"fieldname": "etaxes_item_name",
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
"search_index": 1 # Создает индекс
}
]
Документация
Существующие файлы документации
-
README.md (685 байт)
- Краткое описание проекта
- Инструкции по установке
- Инструкции для разработчиков (pre-commit)
-
claude.md (34 КБ, 1,400+ строк)
- Подробная документация на русском языке
- Архитектура системы
- Описание API
- Примеры использования
- Технические детали
-
QA_SEND_INVOICE.md (15 КБ)
- 27 вопросов и ответов
- Функциональность отправки счетов
- Технические спецификации
- Примеры использования
Примеры файлов
invoice.txt - Пример JSON для создания счета invoice asan.txt - Пример JSON для подписи через ASAN
Вопросы и Ответы (Q&A) - Отправка Sales Invoice в E-Taxes
Дата: 2025-12-15 Задача: Добавление функционала отправки Sales Invoice в E-Taxes
Технические вопросы и ответы (27 вопросов)
1. Аутентификация
Q: Должны ли мы использовать существующую систему аутентификации ASAN Login (которая уже есть в приложении для импорта инвойсов), или нужны отдельные credentials для отправки?
A: Использовать существующий ASAN Login
2. Доступность кнопки
Q: Когда кнопка 'Отправить в E-Taxes' должна быть доступна?
A: После Submit (рекомендуется) - кнопка доступна только после того, как Sales Invoice в статусе Submitted
3. Процесс подписания
Q: Должен ли процесс подписания (sign/withAsanImza) происходить автоматически после создания инвойса, или это должна быть отдельная кнопка?
A: Да, автоматически после создания. Кастомные поля создаются в приложении jey_erp (файл custom_fields.py).
4. Данные клиента (TIN и Object Name)
Q: Где хранится информация о клиенте для отправки в E-Taxes (TIN, objectName)?
A: Поле TIN - это tax_id. Поле object_name пока создается как тестовое текстовое поле, потом будет заменено.
5. Product Group Code для товаров
Q: Как определять productGroupCode для товаров (items)?
A: Есть поле в Item, которое называется product_group_code, текстового типа.
6. Сохранение данных после отправки
Q: Какие данные нужно сохранить после успешной отправки?
A:
- Invoice ID
- Serial Number
- Verification Code
- Статус отправки
7. Обработка ошибок API
Q: Как обрабатывать ошибки API?
A: Показать ошибку и остановить процесс.
8. Тип НДС для товаров
Q: Как определять тип НДС для товаров (vat18, vat0, vatFree, exempt)?
A: В файле custom_fields.py уже созданы поля для Sales Invoice Item:
vat_18_percent_with_amount- VAT 18% with amountvat_amount- VAT Amount (calculated)vat_0_percent_with_amount- VAT 0% with amountamount_without_vat- Amount without VATvat_free_amount- VAT free amounttax_article- Tax Article (Link to Tax Article doctype)
Пример кода из custom_fields.py:
"Sales Invoice Item": [
dict(
fieldname='vat_18_percent_with_amount',
label='VAT 18% with amount',
fieldtype='Currency',
insert_after='amount',
in_list_view=1,
read_only=1,
columns=2,
precision=2
),
dict(
fieldname='vat_amount',
label='VAT Amount',
fieldtype='Currency',
insert_after='vat_18_percent_with_amount',
in_list_view=1,
columns=2,
precision=2,
read_only=1,
description='Calculated as: VAT 18% with amount - Amount'
),
# ... другие поля
]
9. Unit Code для единиц измерения
Q: Откуда брать unitCode для единиц измерения?
A: Поле unit_code необязательно. Можно просто использовать текстовое имя единицы измерения из табличной части Sales Invoice Item. E-Taxes принимает любой текст (даже "test123"), но для стандартных единиц можно использовать следующие коды:
{
"items": [
{"code": "666", "name": {"az": "ədəd", "ru": "штук", "en": "piece"}},
{"code": "391836", "name": {"az": "km", "ru": "км", "en": "km"}},
{"code": "529937", "name": {"az": "kq", "ru": "кг", "en": "kg"}},
{"code": "581879", "name": {"az": "l", "ru": "л", "en": "l"}},
{"code": "207201", "name": {"az": "m", "ru": "м", "en": "m"}},
{"code": "6666", "name": {"az": "m2", "ru": "м2", "en": "m2"}},
{"code": "839111", "name": {"az": "m3", "ru": "м3", "en": "m3"}},
{"code": "525166", "name": {"az": "q", "ru": "г", "en": "g"}},
{"code": "116094", "name": {"az": "sm", "ru": "см", "en": "cm"}},
{"code": "864211", "name": {"az": "sm3", "ru": "см3", "en": "cm3"}},
{"code": "544580", "name": {"az": "t", "ru": "т", "en": "t"}}
]
}
10. Дополнительные поля (senderObjectCode, vehicleRegistrationNumber)
Q: Что делать с полями senderObjectCode и vehicleRegistrationNumber?
A: Оставить пустыми
11. Предотвращение повторной отправки
Q: Должны ли мы предотвращать повторную отправку?
A: Да, блокировать повтор
12. Текст кнопки
Q: Какой текст должен быть на кнопке отправки?
A: "Send to E-Taxes"
13. Комментарии в инвойсе
Q: Что делать с полями invoiceComment и invoiceComment2?
A: Есть поля comment и additional_comment в Sales Invoice.
14. Диалог подтверждения
Q: Показывать ли диалог подтверждения перед отправкой?
A: Да, два диалога подтверждения:
- "Send this invoice to E-Taxes?"
- "Are you sure?"
15. Кнопка в списке инвойсов
Q: Нужна ли кнопка в list view (списке инвойсов)?
A: Пока что без этого. Возможно будет добавлено позже.
16. Генерация ID для товаров
Q: Как генерировать ID для товаров (item id в формате '44f5986a-2d3c-45d6-bb14-79a899******')?
A: Случайный UUID
17. Фиксированные поля JSON
Q: Какие значения использовать для полей type, kind, sourceSystem?
A: Использовать значения как в примере:
type= "current"kind= "defaultInvoice"sourceSystem= "lk"
18. Валидация данных
Q: Нужно ли валидировать данные перед отправкой?
A: Да, необходимо проверить:
- TIN клиента (tax_id)
- Product codes у товаров
- Unit codes (опционально)
19. Маппинг единиц измерения (UOM)
Q: Как маппить единицы измерения из ERPNext UOM в коды E-Taxes? Например, если в Sales Invoice Item указано 'Nos' или 'Unit' - какой код использовать?
A: Просто брать имя единицы измерения из табличной части. Поле unit_code необязательно, E-Taxes принимает текстовое название.
20. Pricing поля для товаров
Q: Что использовать для pricePerUnit и cost в items? В примере оба = 1, но у нас есть rate, amount и т.д.
A:
rate→pricePerUnitamount→cost
21. Кнопка после отправки
Q: Что делать с кнопкой после успешной отправки (ты говорил блокировать повтор)?
A: Заменить кнопку на индикатор статуса с информацией об отправке.
22. Язык сообщений
Q: На каком языке показывать сообщения об ошибках и подтверждения?
A: English
23. Receiver Name (юридическое название)
Q: Про receiver.name - нужно искать клиента в E-Taxes Customer Mappings, найти соответствующий E-Taxes Customers и оттуда брать name?
A: Да, из E-Taxes Customers. Искать соответствие в e-taxes-settings (в том, который помечен как default), там будет название из E-Taxes.
24. Частичная ошибка (создание успешно, подписание failed)
Q: Если создание инвойса (invoice create) прошло успешно, но подписание (signing) провалилось - что делать?
A: Сохранить invoice ID и serial number, но показать ошибку подписания. Установить статус = "Created, not signed".
25. Progress dialog
Q: Нужно ли показывать progress dialog во время отправки (как в существующей функции импорта)?
A: Простой loading индикатор (frappe.show_alert с loading).
26. Тексты подтверждения
Q: Тексты для двух диалогов подтверждения - какие должны быть?
A:
- Первый диалог: "Send this invoice to E-Taxes?"
- Второй диалог: "Are you sure?"
27. Tracking doctype
Q: Нужно ли создавать tracking doctype (типа E-Taxes Sales) для отправленных инвойсов?
A: Да, создать E-Taxes Sales Outbox для отслеживания отправленных инвойсов.
Техническая информация по API Endpoints
1. Generate Serial Number
POST https://new.e-taxes.gov.az/api/po/invoice/public/v1/generateSerialNumber/defaultInvoice
Response:
{
"serialNumber": "MT25121128****"
}
2. Create Invoice
POST https://new.e-taxes.gov.az/api/po/invoice/public/v2/invoice
Response:
{
"id": "b7bd1f20-4bdb-4e7b-bf18-c1f9bd******"
}
3. Sign Invoice
POST https://new.e-taxes.gov.az/api/po/invoice/public/v1/invoice/sign/withAsanImza
Request:
{
"type": "send",
"invoiceIds": ["invoice-id-here"]
}
Response:
{
"status": "ok",
"error": null,
"verificationCode": "4147"
}
Примеры файлов с данными
Пример создания инвойса:
/home/frappe/frappe-bench/apps/invoice_az/invoice.txt
Пример подписания:
/home/frappe/frappe-bench/apps/invoice_az/invoice asan.txt
Кастомные поля:
/home/frappe/frappe-bench/apps/jey_erp/jey_erp/custom_fields.py
Существующие поля
Sales Invoice Item:
- vat_18_percent_with_amount
- vat_amount
- vat_0_percent_with_amount
- amount_without_vat
- vat_free_amount
- tax_article
Sales Invoice:
- comment
- additional_comment
Customer:
- tax_id (TIN)
Item:
- product_group_code
Workflow процесса отправки Sales Invoice
┌─────────────────────────────────────────────────────────┐
│ 1. Пользователь нажимает "Send to E-Taxes" │
│ на submitted Sales Invoice │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 2. Первый диалог подтверждения: │
│ "Send this invoice to E-Taxes?" │
└─────────────────────┬───────────────────────────────────┘
│ Да
▼
┌─────────────────────────────────────────────────────────┐
│ 3. Второй диалог подтверждения: │
│ "Are you sure?" │
└─────────────────────┬───────────────────────────────────┘
│ Да
▼
┌─────────────────────────────────────────────────────────┐
│ 4. Показать loading индикатор │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 5. Валидация данных: │
│ ✓ Проверка tax_id клиента (TIN) │
│ ✓ Проверка product_group_code у всех товаров │
│ ✓ Проверка что инвойс еще не отправлен │
│ (блокировка повтора через Outbox) │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 6. Получение serial number от API │
│ POST /generateSerialNumber/defaultInvoice │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 7. Формирование JSON payload: │
│ │
│ receiver: │
│ - name: из E-Taxes Customers (через mapping) │
│ - tin: из tax_id │
│ - objectName: из custom field │
│ │
│ items: (для каждого item) │
│ - id: случайный UUID │
│ - productName: item_name │
│ - productGroupCode: product_group_code │
│ - quantity: qty │
│ - pricePerUnit: rate │
│ - cost: amount │
│ - unit: UOM name (текст) │
│ │
│ VAT mapping: │
│ - vat_18_percent_with_amount → vat18 │
│ - vat_0_percent_with_amount → vat0 │
│ - amount_without_vat → exempt │
│ - vat_free_amount → vatFree │
│ │
│ comments: │
│ - invoiceComment: comment │
│ - invoiceComment2: additional_comment │
│ │
│ Фиксированные значения: │
│ - type: "current" │
│ - kind: "defaultInvoice" │
│ - sourceSystem: "lk" │
│ - senderObjectCode: "" (пусто) │
│ - vehicleRegistrationNumber: "" (пусто) │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 8. Отправка на создание инвойса │
│ POST /api/po/invoice/public/v2/invoice │
│ Получение invoice_id │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 9. Автоматическое подписание инвойса │
│ POST /invoice/sign/withAsanImza │
│ Body: {"type": "send", "invoiceIds": [...]} │
│ Получение verification_code │
└─────────────────────┬───────────────────────────────────┘
│
├─ Успех ────────────────────────────┐
│ │
│ Статус: "Sent and Signed" │
│ │
└─ Ошибка подписания ───────────────┤
│
Статус: "Created, not signed" │
Сохранить ID и serial number │
Показать ошибку │
│
┌────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 10. Создание E-Taxes Sales Outbox record │
│ - sales_invoice: link to Sales Invoice │
│ - etaxes_invoice_id: invoice ID │
│ - serial_number: serial number │
│ - verification_code: verification code (if signed) │
│ - send_date: now() │
│ - send_status: статус отправки │
│ - error_message: сообщение об ошибке (if any) │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 11. Сохранение результатов в Sales Invoice │
│ (опционально - через custom fields или связь) │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 12. Заменить кнопку на индикатор статуса │
│ Показать: │
│ - Invoice ID │
│ - Serial Number │
│ - Verification Code │
│ - Статус отправки │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 13. Показать сообщение об успехе пользователю │
│ "Invoice successfully sent to E-Taxes" │
│ или сообщение об ошибке │
└─────────────────────────────────────────────────────────┘
Важные замечания по реализации
- Двойное подтверждение - обязательно два диалога для предотвращения случайной отправки
- Блокировка повтора - проверять существование записи в E-Taxes Sales Outbox
- Частичные ошибки - если создание прошло, но подписание нет - сохранить данные и показать ошибку
- Маппинг клиента - брать название из E-Taxes Customers через маппинг в E-Taxes Settings
- Единицы измерения - можно использовать текстовое название, unit_code необязателен
- VAT поля - использовать существующие кастомные поля из Sales Invoice Item
- UUID для items - генерировать случайный UUID для каждого товара
- Сообщения на английском - все пользовательские сообщения на английском языке
- Loading индикатор - простой frappe.show_alert, не сложный progress dialog
- Tracking - создать E-Taxes Sales Outbox для истории отправок
Контакты и поддержка
Автор: Jey ERP Email: info@jeyerp.az Website: https://jeyerp.az
E-Taxes (Правительство Азербайджана): Website: https://new.e-taxes.gov.az ASAN Login: https://asan.gov.az
Лицензия
Проект распространяется под лицензией Unlicense (общественное достояние).
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any means.
Полный текст лицензии см. в файле license.txt.
Заключение
Проект Invoice Az представляет собой комплексное решение для интеграции Frappe/ERPNext с государственной системой электронного налогообложения Азербайджана E-Taxes.
Ключевые преимущества:
- Полная автоматизация - импорт входящих и экспорт исходящих счетов
- Интеллектуальное сопоставление - автоматическое сопоставление данных с точностью 95%
- Безопасность - интеграция с государственной системой ASAN Login
- Масштабируемость - обработка до 200 счетов за запрос
- Надежность - автоматическое обновление токенов, обработка ошибок, логирование
- Удобство - интуитивный интерфейс, массовые операции, подробные подсказки
Технические характеристики:
- 17 кастомных DocTypes для хранения данных
- 7,198 строк Python кода для бизнес-логики
- 9,786 строк JavaScript кода для клиентской части
- 3 файла документации (50 КБ) для пользователей и разработчиков
- Полная интеграция с стандартными DocTypes ERPNext
Проект активно развивается и поддерживается командой Jey ERP.
Версия документа: 1.0 Дата создания: 2025-12-16 Автор документа: Claude Code