Мартенски обновления в Cyber ERP: Нови възможности за автоматизация и ефективност

Въведение

Пролетта дойде с вълнуващи новини за 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. Подобрена валидация

Всяка операция сега преминава през многослойна валидация:

  1. Синтактична – правилен формат на данните
  2. Бизнес логика – допустимост на операцията
  3. Счетоводна – балансираност на записите
  4. Периодова – валидност на датите спрямо отворени периоди

Ползи за потребителите

  • По-бърза работа – операциите се изпълняват за секунди
  • 🛡️ По-малко грешки – валидациите хващат проблеми преди запис
  • 📊 По-добра проследяемост – всичко се логва детайлно
  • 🔒 Сигурност – затворените периоди са защитени от промени

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 Сканиране“:

  1. Качване – drag & drop или избор на файл
  2. Преглед – визуализация на документа
  3. Обработка – индикатор за прогрес
  4. Верификация – редактиране на извлечените данни
  5. Запис – директно в системата или като чернова

Икономия на време

Според тестовете с реални клиенти:

  • Ръчно въвеждане: 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 номера →                                     │
└─────────────────────────────────────────────────────────────────┘

Проверки и валидации

Системата извършва автоматични проверки:

  1. Контролни съотношения
  • Сума на данъчните основи = Общо
  • ДДС = Данъчна основа × Ставка
  • Сторнирания са правилно свързани
  1. Крос-валидации
  • ДДС номера съответстват на държавата
  • Дати са в отворени периоди
  • Валутите са консистентни
  1. Бизнес правила
  • ВЕОД има валиден ДДС номер
  • Освободени доставки имат основание
  • Над лимита за касови продажби

Заключение

Юнското обновление на Cyber ERP дава реални резултати:

ФункцияИкономия на времеНамаляване на грешки
Обновено ядро60% по-бързо40% по-малко
Mistral AI OCR85% по-бързо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

Вашият коментар