Category Archives: sadmin

sadmin

OpenSource решение за уеб фактуриране и не само- Odoo

faktura_dhstudio
От standalone приложение към съвременно cloud решение с Odoo
От дълго време се налагаше да преминем към нова система за фактуриране. До скоро ползвах standalone приложение, но с преминаването към евро и необходимостта от достъп от различни локации се наложи спешно да намеря алтернатива.

Защо не standalone приложения?
Разгледах няколко варианта, включително безплатното решение Microinvest Invoice Pro, но не исках да се ограничавам със standalone Windows приложения. Търсех облачно решение с достъп от всяко устройство и място.
Решението: Odoo

Odoo е безплатна бизнес софтуерна система (ERP) с отворен код, която обединява всички основни бизнес процеси на едно място: счетоводство, фактуриране, управление на клиенти (CRM), продажби, склад, човешки ресурси, проекти и много други.
Защо Odoo?
Целта беше проста – да мога да генерирам фактури и отчети от всяка локация, по всяко време. Системата трябваше да:

Работи с евро и лева едновременно
Генерира ДДС справки за внасяне и възстановяване
Създава професионални PDF фактури
Бъде достъпна онлайн от всяко устройство
Не изисква инсталация на отделни компютри

Персонализация
Разработих два custom модула специално за българския пазар:

Български фактури – форматирани според местните изисквания
ДДС анализ и справки – автоматично изчисляване на ДДС за внасяне/възстановяване с визуални отчети

Модулите са написани на Python и са лесни за адаптиране. Ако някой се нуждае от тях, с удоволствие ще ги споделя.
Препоръка
Изключително препоръчвам Odoo за фактуриране, счетоводство и бизнес управление! Системата е като Lego конструктор – започвате с необходимите модули и добавяте функционалност според нуждите си.
Odoo = Lego за счетоводство! 🚀
dds_spravka

PS: Всички изображения като фактури, статистика са примерни докато тествах адоните.

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Да промениш номерацията на цитатите в статия за 1 секунда

vba_scripting_nauchna_statiq

При писане на статия се наложи промяна на литературата и респективно – на номерацията на цитатите. Проблемът е, че само източниците в литературата са 23 броя, а в статията има още много повече цитати. За целта, с помощта на едно просто VBA скриптче за Word, в което описваме стар номер и нов номер, за 1 секунда се преномерират всички цитати в статията и списъкът с литература.

Какво прави скриптчето?
В нашия случай трябва да се променят така: [2] -> [1], [3] -> [2], [4] -> [3], [6] -> [4].
За целта скриптчето прави точно 2 конверсии, т.е.:
[2] -> [], [3] -> [], [4] -> [], [6] -> [].
Умишлено временният стринг е такъв, за да се редуцира вероятността да го има в текста към 0.
Втората конверсия е: [] -> [1], [] -> [2], [] -> [3], [] -> [4].
За да въведем скриптчето в Word, натискаме Alt + F11, след това Insert > Module.
Въвеждаме го там и го изпълняваме с F5.
Ииии – voilà! Имаме си нова номерация в литературата и обновени цитати в статията.

Това беше първата версия на литературата, като се наложи да се коригира последователността:

[1] Pavlova, N. & Marchev, D. Application of artificial intelligence in generation of stem tasks, KNOWLEDGE –International Journal, Vol.66.2, pp. 185-191. 2024

[2] Желязкова, М. STEM в контекста на компетентностния подход в образованието, Образование и технологии, Vol. 13, стр. 206-212. 2022
[3] Кожухаров, М. Въведение в изкуствения интелект, Тракийски университет, ДИПКУ. Стара Загора, 2025, ISBN 978-954-691-114-8
[4] Кожухарова, Д. От дигитална компетентност към дигитална креативност. Академично издателство, Тракийски университет. Стара Загора, 2020, ISBN 978-954-338-166-1

Новата последователност на литературата:

[2] Желязкова, М. STEM в контекста на компетентностния подход в образованието, Образование и технологии, Vol. 13, стр. 206-212. 2022
[3] Кожухаров, М. Въведение в изкуствения интелект, Тракийски университет, ДИПКУ. Стара Загора, 2025, ISBN 978-954-691-114-8
[4] Кожухарова, Д. От дигитална компетентност към дигитална креативност. Академично издателство, Тракийски университет. Стара Загора, 2020, ISBN 978-954-338-166-1
[6] Рашева-Мерджанова, Я. Трансформация на ключовите компетентности на съвременния учител в контекста на социалното взаимодействие. – Стратегии на образователната и научната политика. № 3, с. 243 – 253, 2010

След изпълнение на скриптчето:
[1] Желязкова, М. STEM в контекста на компетентностния подход в образованието, Образование и технологии, Vol. 13, стр. 206-212. 2022
[2] Кожухаров, М. Въведение в изкуствения интелект, Тракийски университет, ДИПКУ. Стара Загора, 2025, ISBN 978-954-691-114-8
[3] Кожухарова, Д. От дигитална компетентност към дигитална креативност. Академично издателство, Тракийски университет. Стара Загора, 2020, ISBN 978-954-338-166-1
[4] Рашева-Мерджанова, Я. Трансформация на ключовите компетентности на съвременния учител в контекста на социалното взаимодействие. – Стратегии на образователната и научната политика. № 3, с. 243 – 253, 2010

Ето го и скриптчето:
Sub MultiReplaceOptimized_Unique()

Dim pairs As Variant
Dim i As Long
Dim oldNum As String, newNum As String
Dim find1 As String, rep1 As String
Dim find2 As String, rep2 As String
Dim rng As Range

' Wywejdame w lqwata chast na masiwa stariq nomer, a w dqsnata nowiqt. Created by Martin Petrov and ChatGPT
pairs = Array( _
Array("2", "1"), _
Array("3", "2"), _
Array("4", "3"), _
Array("6", "4"), _
Array("7", "5"), _
Array("12", "6"), _
Array("9", "7"), _
Array("15", "8"), _
Array("8", "9"), _
Array("14", "10"), _
Array("16", "11"), _
Array("17", "12"), _
Array("10", "13"), _
Array("19", "14"), _
Array("18", "15"), _
Array("20", "16"), _
Array("1", "17"), _
Array("13", "18"), _
Array("11", "19"), _
Array("21", "20"), _
Array("22", "21"), _
Array("23", "22"), _
Array("24", "23") _
)

For i = LBound(pairs) To UBound(pairs)

oldNum = pairs(i)(0)
newNum = pairs(i)(1)

find1 = "[" & oldNum & "]"
rep1 = "[]"

Set rng = ActiveDocument.Content
With rng.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = find1
.Replacement.Text = rep1
.Wrap = wdFindStop
.Execute Replace:=wdReplaceAll
End With

Next i

For i = LBound(pairs) To UBound(pairs)

newNum = pairs(i)(1)

find2 = "[]"
rep2 = "[" & newNum & "]"

Set rng = ActiveDocument.Content
With rng.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = find2
.Replacement.Text = rep2
.Wrap = wdFindStop
.Execute Replace:=wdReplaceAll
End With

Next i

MsgBox "MP Replacer v 1.0. Ready", vbInformation

End Sub

Изключителни благодарности на проф. д.н. Наталия Христова Павлова

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Инсталиране на Windows 11

Преди две години с Вики почистихме и инсталирахме тази машинка с M$ Windows 10.
Днес дойде ред отново за профилактика и инсталиране на M$ Windows 11, тъй като поддръжката на Windows 10 приключва на 14.10.2025 г.

Друго си е, когато сам си почистиш, форматираш, инсталираш и конфигурираш своята M$ Windows 11 машинка.

Имах около 89 127 381 723 въпроса – това как, онова защо, но усещането да си жив GPT не е никак лошо. 🙂

Светли дни идват за DH Studio!

За да заобиколим задължителния M$ Account, стартирахме cmd с Shift + F10 и въведохме:

start ms-cxh:localonly

Така създаваме локален потребител.

Всичко вървеше добре, но при инсталация на Windows 11 на дъното ASRock Steel Legend B450 излезе съобщение, че системата не отговаря на изискванията:

This PC doesn’t currently meet Windows 11 system requirements
This PC doesn’t meet the minimum system requirements to install this version of Windows.
For more information, visit: https://aka.ms/WindowsSysReq

👉 Решението включва:

Активиране на Intel PTT (TPM 2.0):

Влизане в Advanced Mode

Секция Security

Активиране на Intel Platform Trust Technology (PTT)

Позволяване на Secure Boot:

Секция Security

Secure Boot Mode → активиране на Secure Boot

Защо Windows 11?
Заради игрите. Повечето нови заглавия вече са създадени за най-масовата ОС, а в момента това е Windows 11.

И все пак – Windows си остава боза. 😄

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Прехвърляне на Roundcube mail от един хостинг на друг

При прехвърлянето е генериран списък, но писмата все още не са били прехвърлени.
Затова писмата се отварят със съдържанието си, но като заглавие се изписва „message unavailable“.

Решение:
Изтриване на кеша и повторно генериране на индекса:

bash
Copy
Edit
rm -f courierimapuiddb
Още един проблем беше, че има папки, които съществуват в стария Roundcube, но не се появяват в новия.

Решение:
Ръчно да се добавят във файла courierimapsubscribed:

Copy
Edit
INBOX.Archives
INBOX.Archives.2018
INBOX.Archives.2019
INBOX.Archives.2020
INBOX.Archives.2021
INBOX.Archives.2022
INBOX.Drafts
INBOX.Junk
INBOX.Sent
INBOX.alcomet
INBOX.spam
INBOX.Trash

testy .

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Monit мониторинг на incus container

cat /etc/monit/monitrc

CHECK PROGRAM apache2_dobromir_status WITH PATH "/root/monit/check_apache_run.sh dobromir"
IF CONTENT = "apache2 not work" THEN EXEC "/usr/bin/incus exec dobromir --- systemctl start apache2"

CHECK PROGRAM mysql_dobromir_status WITH PATH "/root/monit/check_mysql_run.sh dobromir"
IF CONTENT = "mysql not work" THEN EXEC "/usr/bin/incus exec dobromir --- systemctl start mysql"

CHECK PROGRAM apache2_max_process_dobromir_status WITH PATH "/root/monit/check_apache_max_process.sh dobromir 30"
IF CONTENT = "apache2 max process" THEN EXEC "/usr/bin/incus exec dobromir --- systemctl restart apache2"

root@#:~/monit# cat check_apache_max_process.sh
#!/bin/bash

CONTAINER_NAME=$1
MAX_PROCESS=$2

process_count=$(/usr/bin/incus exec "$CONTAINER_NAME" --- pgrep -c apache2)

# Проверка на броя на процесите
if [ "$process_count" -gt $MAX_PROCESS ]; then
printf "apache2 max process"
exit 1
else
printf "apache2 no max process"
exit 0
fi

root@#:~/monit# cat check_apache_run.sh
#!/bin/bash

CONTAINER_NAME=$1

apache_status=$(/usr/bin/incus exec "$CONTAINER_NAME" --- systemctl is-active apache2)

if [ "$apache_status" != "active" ]; then
printf "apache2 not work"
exit 1
else
printf "apache2 work"
exit 0
fi

root@#:~/monit# cat check_mysql_run.sh
#!/bin/bash

CONTAINER_NAME=$1

apache_status=$(/usr/bin/incus exec "$CONTAINER_NAME" --- systemctl is-active mysql)

if [ "$apache_status" != "active" ]; then
printf "mysql not work"
exit 1
else
printf "mysql work"
exit 0
fi

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

php8

Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given in file.php:206 Stack trace: #0 {main} thrown in file.php on line 206

преди
$num_results=count($r);
след
$num_results=count((array)$r);

--------------------------

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

dhc: Бъг на модула с менюто (menu fix / меню бъг)

MariaDB [d_dar6hd3]> insert into menu_bg (group_id) values ('3');
ERROR 167 (22003): Out of range value for column 'id' at row 1

MariaDB [d_dar6hd3]> describe menu_bg;
+-----------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------------------+------+-----+---------+----------------+
| id | tinyint(3) unsigned | NO | PRI | NULL | auto_increment |
| parent_id | tinyint(3) unsigned | NO | | 0 | |
| title | varchar(255) | NO | | | |
| url | varchar(255) | NO | | | |
| class | varchar(255) | NO | | | |
| position | tinyint(3) unsigned | NO | | 0 | |
| group_id | tinyint(3) unsigned | NO | | 1 | |
| sub_exist | enum('1','0') | YES | | 0 | |
+-----------+---------------------+------+-----+---------+----------------+
8 rows in set (0,001 sec)

MariaDB [d_dar6hd3]> insert into menu_bg (group_id) values ('3');
ERROR 167 (22003): Out of range value for column 'id' at row 1

FIX

alter table menu_bg modify id SMALLINT(3) unsigned NOT NULL AUTO_INCREMENT;

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Laravel vs Core PHP vs Core PHP +Smarty

Core PHP + Smarty -- the Winner

Направихме един малък тест за бързодействие на
Laravel vs (Core PHP + Smarty) vs Core PHP

Очакван резултат, но все пак извод:
CorePHP е топ за супер натоварени сайтове където се гони абсолютен оптимум, а не просто да работи и да се купуват железа безкрай.
+ и -- на Laravel са ясни. Идеята тук е бързодействие, а със Smarty се получава и разделение на front, back.
Всеки проект и ситуация определя какво да се ползва: Laravel, друг framework, Core PHP или друго…
PS0: За тестовете бяха направени три контейнера с абсолютно еднакви параметри на дистро,пакети/apache+nginx proxy+mariadb/,страниците извършиха един и същи брой заявки към db и т.н.
Всичко се изпълни локално в конкретните контейнери/ясно е защо/
Ползван беше apache bench tool: ab -n 10000 -c 200 https://example.com

Ето ги и резултатите:

laravel_vs_core_php_vs_smarty

#Core PHP # > ab -n 10000 -c 100 https://example.com

Server Software: nginx/1.18.0
Document Length: 35030 bytes

Concurrency Level: 100
Time taken for tests: 23.690 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 354230000 bytes
HTML transferred: 350300000 bytes
Requests per second: 389.26 [#/sec] (mean)
Time per request: 256.898 [ms] (mean)
Time per request: 2.569 [ms] (mean, across all concurrent requests)
Transfer rate: 13465.55 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 5 94 43.1 85 373
Processing: 10 161 58.1 154 439
Waiting: 7 79 55.1 67 337
Total: 14 255 73.1 247 581

#######################################################################
#Core PHP + Smarty# >

Concurrency Level: 100
Time taken for tests: 2.585 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 29295000 bytes
HTML transferred: 28902000 bytes
Requests per second: 386.82 [#/sec] (mean)
Time per request: 258.516 [ms] (mean)
Time per request: 2.585 [ms] (mean, across all concurrent requests)
Transfer rate: 11066.38 [Kbytes/sec] received

#######################################################################
#Laravel

Concurrency Level: 100
Time taken for tests: 3.483 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 39052000 bytes
HTML transferred: 37818000 bytes
Requests per second: 287.10 [#/sec] (mean)
Time per request: 348.309 [ms] (mean)
Time per request: 3.483 [ms] (mean, across all concurrent requests)
Transfer rate: 10949.12 [Kbytes/sec] received

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)