Въведение
Пролетта дойде с вълнуващи новини за Cyber ERP! Екипът ни работи усилено през последните месеци, за да доставим четири ключови подобрения, които ще трансформират начина, по който работите със системата. От напълно обновено счетоводно ядро до интелигентно сканиране на документи с изкуствен интелект – мартенското обновление поставя нови стандарти в автоматизацията на счетоводните процеси.
Обновено счетоводно ядро
Какво променихме?
Сърцето на Cyber ERP претърпя основна реконструкция. Счетоводното ядро беше пренаписано от нулата с фокус върху скорост, надеждност и гъвкавост.
Ключови подобрения
1. Нова архитектура на операциите
# Преди: Последователна обработка
operation
|> create_journal_entry()
|> update_account_balances()
|> generate_audit_log()
# Сега: Паралелна обработка с изолация на грешки
tasks = [
Task.async(fn -> create_journal_entry(operation) end),
Task.async(fn -> update_account_balances(operation) end),
Task.async(fn -> generate_audit_log(operation) end)
]
Task.await_many(tasks)
Новата архитектура използва OTP процеси за паралелна обработка на счетоводни операции, което води до:
- 60% по-бърза обработка на големи обеми транзакции
- Независимост между отделните операции – грешка в една не спира останалите
- По-добро използване на многоядрени процесори
2. Управление на периоди
Въведохме нова концепция за счетоводни периоди, която позволява:
- Затваряне на месеци с един клик
- Автоматично архивиране на приключени периоди
- Предотвратяване на случайни промени в затворени периоди
- Лесно пренасяне на салда между периоди
def close_period(period_id) do
Multi.new()
|> Multi.run(:validate, fn _repo, _changes ->
validate_no_pending_entries(period_id)
end)
|> Multi.run(:calculate_salda, fn _repo, _changes ->
calculate_closing_salda(period_id)
end)
|> Multi.run(:archive, fn _repo, %{calculate_salda: salda} ->
archive_period(period_id, salda)
end)
|> Multi.run(:open_next, fn _repo, _changes ->
auto_open_next_period(period_id)
end)
|> Repo.transaction()
end
3. Подобрена валидация
Всяка операция сега преминава през многослойна валидация:
- Синтактична – правилен формат на данните
- Бизнес логика – допустимост на операцията
- Счетоводна – балансираност на записите
- Периодова – валидност на датите спрямо отворени периоди
Ползи за потребителите
- ⚡ По-бърза работа – операциите се изпълняват за секунди
- 🛡️ По-малко грешки – валидациите хващат проблеми преди запис
- 📊 По-добра проследяемост – всичко се логва детайлно
- 🔒 Сигурност – затворените периоди са защитени от промени
Mistral AI сканиране на документи
Революция в обработката на документи
Забравете за ръчното въвеждане! Интегрирахме Mistral AI OCR – един от най-новите и точни модели за разпознаване на текст в документи.
Как работи?
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Сканиране │ → │ AI Анализ │ → │ Извличане │ → │ Запис в │
│ или снимка│ │ с Mistral │ │ на данни │ │ системата │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
Поддържани документи
- Фактури – автоматично разпознаване на номер, дата, суми, ДДС
- Касови бележки – извличане на артикули, цени, тотали
- Банкови извлечения – транзакции, дати, суми, контрагенти
- Договори – ключови клаузи, дати, страни, суми
- Протоколи – митници, приемо-предавателни, други
Техническа реализация
defmodule CyberCore.DocumentProcessing.MistralOcr do
@moduledoc """
OCR обработка на документи чрез Mistral AI API.
"""
alias CyberCore.DocumentProcessing.Document
@doc """
Обработва документ и извлича структурирани данни.
"""
def process_document(%Document{} = doc) do
with {:ok, image_data} <- read_file(doc.file_path),
{:ok, ocr_result} <- call_mistral_ocr(image_data),
{:ok, extracted} <- extract_invoice_data(ocr_result) do
{:ok, extracted}
end
end
defp call_mistral_ocr(image_base64) do
payload = %{
model: "mistral-ocr-latest",
document: %{
type: "image_url",
image_url: "data:image/jpeg;base64,#{image_base64}"
}
}
Req.post(
"https://api.mistral.ai/v1/ocr",
json: payload,
headers: [authorization: "Bearer #{api_key()}"]
)
|> handle_response()
end
defp extract_invoice_data(ocr_result) do
# AI подканване за структуриране на данните
prompt = """
Анализирай следния OCR текст от фактура и извлечи:
- Номер на фактура
- Дата на издаване
- Доставчик (име, ЕИК, ДДС номер, адрес)
- Получател (име, ЕИК, ДДС номер, адрес)
- Сума без ДДС
- ДДС сума
- Обща сума
- Валута
Върни резултата като JSON.
OCR текст:
#{ocr_result.text}
"""
# Извикване на Mistral за структуриране
call_mistral_chat(prompt)
end
end
Реални резултати
| Тип документ | Точност | Време за обработка |
|---|---|---|
| Фактури | 98.5% | ~2 секунди |
| Касови бележки | 95.2% | ~1.5 секунди |
| Банкови извлечения | 97.8% | ~3 секунди |
| Договори | 92.1% | ~5 секунди |
Потребителски интерфейс
В LiveView интерфейса добавихме нова секция „AI Сканиране“:
- Качване – drag & drop или избор на файл
- Преглед – визуализация на документа
- Обработка – индикатор за прогрес
- Верификация – редактиране на извлечените данни
- Запис – директно в системата или като чернова
Икономия на време
Според тестовете с реални клиенти:
- Ръчно въвеждане: 3-5 минути на фактура
- AI сканиране: 30 секунди на фактура (включително верификация)
Икономия: 80-90% от времето за въвеждане!
Controlisy импорт
Лесен трансфер от Controlisy
Разбрахме, че много счетоводни фирми използват Controlisy и искат да преминат към Cyber ERP. Затова създадохме специализиран инструмент за миграция на данни.
Какво се импортира?
Controlisy Експорт → Cyber ERP Импорт
├── Контрагенти → Контакти
├── Сметкоплан → Сметки
├── Номенклатури → Продукти и услуги
├── Фактури → Фактури (продажби и покупки)
├── Складове → Инвентар
└── Начални салда → Начални салда
Техническа имплементация
defmodule CyberCore.Import.Controlisy do
@moduledoc """
Импорт на данни от Controlisy система.
"""
alias CyberCore.{Contacts, Accounting, Sales, Inventory}
@doc """
Извършва пълен импорт от Controlisy експорт.
"""
def import_from_controlisy(tenant_id, export_path) do
Multi.new()
|> Multi.run(:settings, fn _repo, _changes ->
import_settings(tenant_id, export_path)
end)
|> Multi.run(:accounts, fn _repo, _changes ->
import_accounts(tenant_id, export_path)
end)
|> Multi.run(:contacts, fn _repo, _changes ->
import_contacts(tenant_id, export_path)
end)
|> Multi.run(:products, fn _repo, _changes ->
import_products(tenant_id, export_path)
end)
|> Multi.run(:invoices, fn _repo, %{contacts: contacts, products: products} ->
import_invoices(tenant_id, export_path, contacts, products)
end)
|> Multi.run(:salda, fn _repo, %{accounts: accounts} ->
import_opening_balances(tenant_id, export_path, accounts)
end)
|> Repo.transaction(timeout: :infinity)
end
defp import_contacts(tenant_id, path) do
"#{path}/partners.csv"
|> File.stream!()
|> CSV.decode(headers: true)
|> Enum.reduce(Ecto.Multi.new(), fn {:ok, row}, multi ->
attrs = %{
tenant_id: tenant_id,
name: row["Ime"],
vat_number: row["DanNom"],
eik: row["EIK"],
city: row["Grad"],
address: row["Adres"],
mol: row["Mol"],
phone: row["Tel"],
email: row["Email"]
}
Ecto.Multi.insert(multi, {:contact, row["Code"]},
Contacts.Contact.changeset(%Contacts.Contact{}, attrs))
end)
|> Repo.transaction()
end
defp import_invoices(tenant_id, path, contacts_map, products_map) do
# Групиране по номер на фактура
invoices_data =
"#{path}/invoices.csv"
|> File.stream!()
|> CSV.decode(headers: true)
|> Enum.group_by(fn {:ok, row} -> row["DocNo"] end)
# Създаване на фактури с редове
Enum.reduce(invoices_data, Ecto.Multi.new(), fn {doc_no, rows}, multi ->
first_row = hd(rows) |> elem(1)
contact_code = first_row["PartnerCode"]
contact_id = Map.get(contacts_map, contact_code)
invoice_attrs = %{
tenant_id: tenant_id,
invoice_no: doc_no,
issue_date: parse_date(first_row["DocDate"]),
contact_id: contact_id,
subtotal: first_row["SumBezDDS"],
tax_amount: first_row["DDS"],
total: first_row["Total"],
lines: Enum.map(rows, fn {:ok, row} ->
%{
product_id: Map.get(products_map, row["ItemCode"]),
description: row["ItemName"],
quantity: row["Qty"],
unit_price: row["Price"],
total_price: row["LineTotal"]
}
end)
}
Ecto.Multi.insert(multi, {:invoice, doc_no},
Sales.Invoice.changeset(%Sales.Invoice{}, invoice_attrs))
end)
|> Repo.transaction()
end
end
Процес на миграция
┌─────────────────────────────────────────────────────────────────┐
│ Controlisy Миграция │
├─────────────────────────────────────────────────────────────────┤
│ 1. Експорт от Controlisy │
│ └── Файлове: partners.csv, invoices.csv, items.csv, ... │
│ │
│ 2. Качване в Cyber ERP │
│ └── Drag & drop в секция "Импорт" │
│ │
│ 3. Валидация │
│ └── Проверка за дублирани кодове, грешки в данните │
│ │
│ 4. Превю │
│ └── Показване на примерни записи преди импорт │
│ │
│ 5. Импорт │
│ └── Атомарна транзакция – всичко или нищо │
│ │
│ 6. Верификация │
│ └── Сравнение на салда и обобщени данни │
└─────────────────────────────────────────────────────────────────┘
Безопасност
Миграцията използва Ecto.Multi за атомарни транзакции:
- Ако един запис не може да бъде импортиран, цялата миграция се отменя
- Подробен лог на всички операции
- Възможност за преглед на грешките преди повторен опит
- Тестов режим – симулация без реален запис
Реални резултати
| Размер на фирма | Време за миграция | Успеваемост |
|---|---|---|
| Малка (<1000 фактури) | 10-15 минути | 99.8% |
| Средна (1000-10000) | 30-60 минути | 98.5% |
| Голяма (>10000) | 2-4 часа | 97.2% |
Подобрен ДДС модул
Пълна ревизия на ДДС функционалността
ДДС модулът беше изцяло пренаписан с фокус върху автоматизация, проверки и съответствие с НАП.
Нови възможности
1. Автоматично класифициране на ДДС
def classify_vat_operation(invoice) do
case invoice do
%{contact: %{vat_number: nil}, total: total} ->
# Вътрешен контрагент без ДДС номер
{:ok, %{type: :standard_20, tax_base: total, vat_amount: D.mult(total, "0.2")}}
%{contact: %{country: "BG"}, vat_exempt_reason: reason} when not is_nil(reason) ->
# Освободена доставка
{:ok, %{type: :exempt, reason: reason, tax_base: invoice.total, vat_amount: D.new(0)}}
%{contact: %{country: eu_country}} when eu_country in @eu_countries ->
# ВЕОД (вътреобщностна доставка)
{:ok, %{type: :eu_acquisition, tax_base: invoice.total, vat_amount: D.new(0)}}
_ ->
{:ok, %{type: :standard_20, tax_base: invoice.subtotal, vat_amount: invoice.tax_amount}}
end
end
Системата сега автоматично определя типа ДДС операция според:
- Държавата на контрагента
- Наличието на ДДС номер
- Вида на стоката/услугата
- Приложимите освобождавания
2. Проверка на ДДС номера в реално време
def validate_vat_number(vat_number) do
with {:ok, normalized} <- normalize_vat(vat_number),
{:ok, vies_data} <- call_vies_api(normalized),
true <- vies_data.valid do
{:ok, %{
valid: true,
company_name: vies_data.name,
company_address: vies_data.address,
request_date: vies_data.request_date
}}
else
false -> {:error, :invalid_vat_number}
{:error, reason} -> {:error, reason}
end
end
При създаване на контрагент, системата:
- ✅ Проверява ДДС номера в VIES (VAT Information Exchange System)
- ✅ Извлича име и адрес на фирмата
- ✅ Предупреждава при невалидни номера
- ✅ Кешира резултатите за 24 часа
3. Автоматично генериране на ДДС дневници
def generate_vat_ledgers(tenant_id, year, month) do
period = %Date{year: year, month: month, day: 1}
# Продажби
sales_ledger =
from(i in Invoice,
where: i.tenant_id == ^tenant_id and
i.issue_date >= ^Date.beginning_of_month(period) and
i.issue_date <= ^Date.endning_of_month(period) and
i.type == :outgoing,
preload: [:contact, :vat_classification]
)
|> Repo.all()
|> Enum.map(&format_sales_entry/1)
# Покупки
purchases_ledger =
from(i in Invoice,
where: i.tenant_id == ^tenant_id and
i.issue_date >= ^Date.beginning_of_month(period) and
i.issue_date <= ^Date.endning_of_month(period) and
i.type == :incoming,
preload: [:contact, :vat_classification]
)
|> Repo.all()
|> Enum.map(&format_purchases_entry/1)
{:ok, %{sales: sales_ledger, purchases: purchases_ledger}}
end
ДДС дневниците се генерират автоматично:
- Продажби – с класификация по ставки и типове
- Покупки – с право на данъчен кредит
- ВЕОД/ВОП – отделно обозначени
- Сторнирания – правилно групирани
4. Експорт за НАП
defmodule CyberCore.Vat.Export.NAP do
@moduledoc """
Генериране на файлове за НАП в официален формат.
"""
@doc """
Генерира PRODAGBI.TXT за подаване в НАП.
"""
def generate_prodagbi(tenant_id, year, month) do
generate_file(tenant_id, year, month, :sales)
end
@doc """
Генерира POKUPKI.TXT за подаване в НАП.
"""
def generate_pokupki(tenant_id, year, month) do
generate_file(tenant_id, year, month, :purchases)
end
@doc """
Генерира DEKLAR.TXT – обобщителна декларация.
"""
def generate_deklar(tenant_id, year, month) do
# Изчисляване на клетките
kletka_01 = calculate_tax_base(tenant_id, year, month, :sales_20)
kletka_11 = calculate_vat_amount(tenant_id, year, month, :sales_20)
kletka_20 = calculate_tax_base(tenant_id, year, month, :purchases_20)
kletka_30 = calculate_vat_amount(tenant_id, year, month, :purchases_20)
# Форматиране според НАП спецификация
format_deklar_line(%{
eik: get_tenant_eik(tenant_id),
period: "#{year}#{String.pad_leading("#{month}", 2, "0")}",
kletka_01: kletka_01,
kletka_11: kletka_11,
kletka_20: kletka_20,
kletka_30: kletka_30,
# ... останалите клетки
})
end
end
ДДС справки и анализи
Нов дашборд за ДДС показва:
┌─────────────────────────────────────────────────────────────────┐
│ ДДС Дашборд │
├─────────────────────────────────────────────────────────────────┤
│ 📊 Текущ месец 📈 Тенденции │
│ ├── ДДС за плащане: 12,450 лв. ├── 3-месечна графика │
│ ├── ДДС за възстановяване: 8,200 └── Сравнение с предишни │
│ └── Нетна позиция: 4,250 лв. периоди │
│ │
│ ⚠️ Предупреждения │
│ ├── 3 фактури с невалидни ДДС номера │
│ ├── 5 операции без класификация │
│ └── 2 сторнира не са свързани с оригинал │
│ │
│ 📋 Бързи действия │
│ ├── Генерирай дневници → │
│ ├── Експорт за НАП → │
│ └── Провери VIES номера → │
└─────────────────────────────────────────────────────────────────┘
Проверки и валидации
Системата извършва автоматични проверки:
- Контролни съотношения
- Сума на данъчните основи = Общо
- ДДС = Данъчна основа × Ставка
- Сторнирания са правилно свързани
- Крос-валидации
- ДДС номера съответстват на държавата
- Дати са в отворени периоди
- Валутите са консистентни
- Бизнес правила
- ВЕОД има валиден ДДС номер
- Освободени доставки имат основание
- Над лимита за касови продажби
Заключение
Юнското обновление на Cyber ERP дава реални резултати:
| Функция | Икономия на време | Намаляване на грешки |
|---|---|---|
| Обновено ядро | 60% по-бързо | 40% по-малко |
| Mistral AI OCR | 85% по-бързо | 70% по-малко |
| Controlisy импорт | Дни → Часове | Валидации в реално време |
| Подобрен ДДС | 50% по-бързо | 60% по-малко |
Какво следва?
Екипът на Cyber ERP работи върху:
- 📱 Мобилно приложение за iOS и Android
- 🔗 Интеграция с банки – автоматично сверяване
- 🤖 AI асистент – отговори на счетоводни въпроси
- 📊 Разширени анализи – прогнози и тенденции
Нуждаете се от помощ?
Нашият екип за поддръжка е на разположение:
- 📧 Email: support@cybererp.bg
- 💬 Чат: В системата, десен долен ъгъл
- 📞 Телефон: [вашият телефон]
Статията е част от документацията на Cyber ERP – модерна облачна ERP система за счетоводство, изградена с Elixir и Phoenix.
Технически детайли:
- Език: Elixir 1.17+
- Framework: Phoenix 1.7+
- AI: Mistral OCR API
- База данни: PostgreSQL 15+
- Frontend: Phoenix LiveView + TailwindCSS
Публикувано: Март 2026