| | 1 | = Домени = |
| | 2 | |
| | 3 | == Парични вредности со деловни граници == |
| | 4 | |
| | 5 | === Опис === |
| | 6 | |
| | 7 | Доменот `positive_money` дефинира валидни парични вредности со деловни граници — не дозволува негативни суми, ниту неразумно големи износи (над 1.000.000). Се користи за сите парични полиња во базата (цени, плаќања, вкупни износи). |
| | 8 | |
| | 9 | === Имплементација === |
| | 10 | |
| | 11 | {{{ |
| | 12 | CREATE DOMAIN project.positive_money AS NUMERIC(12,2) |
| | 13 | CHECK (VALUE >= 0 AND VALUE < 1000000); |
| | 14 | }}} |
| | 15 | |
| | 16 | == ДДВ со законски дозволени стапки == |
| | 17 | |
| | 18 | === Опис === |
| | 19 | |
| | 20 | Доменот `vat_percentage` дозволува само законски дозволени стапки на ДДВ во Македонија: 0%, 5%, 10% и 18%. Спречува внесување на невалидни стапки како 17% или 19%. |
| | 21 | |
| | 22 | === Имплементација === |
| | 23 | |
| | 24 | {{{ |
| | 25 | CREATE DOMAIN project.vat_percentage AS NUMERIC(5,2) |
| | 26 | CHECK (VALUE IN (0, 5, 10, 18)); |
| | 27 | }}} |
| | 28 | |
| | 29 | == Статус на маса == |
| | 30 | |
| | 31 | === Опис === |
| | 32 | |
| | 33 | Доменот `table_status` дозволува само однапред дефинирани статуси на маса: СЛОБОДНА, ЗАФАТЕНА, РЕЗЕРВИРАНА, НЕДОСТАПНА. Овозможува конзистентност на статусите низ целата апликација. |
| | 34 | |
| | 35 | === Имплементација === |
| | 36 | |
| | 37 | {{{ |
| | 38 | CREATE DOMAIN project.table_status AS TEXT |
| | 39 | CHECK (VALUE IN ('СЛОБОДНА', 'ЗАФАТЕНА', 'РЕЗЕРВИРАНА', 'НЕДОСТАПНА')); |
| | 40 | }}} |
| | 41 | |
| | 42 | == Статус на нарачка == |
| | 43 | |
| | 44 | === Опис === |
| | 45 | |
| | 46 | Доменот `order_status` дозволува само валидни статуси на нарачка во текот на нејзиниот животен циклус: НОВА, ПОРАЧАНО, ПОДГОТВЕНО, СЕРВИРАНО, ПЛАТЕНА, ОТКАЖАНА. |
| | 47 | |
| | 48 | === Имплементација === |
| | 49 | |
| | 50 | {{{ |
| | 51 | CREATE DOMAIN project.order_status AS TEXT |
| | 52 | CHECK (VALUE IN ('НОВА', 'ПОРАЧАНО', 'ПОДГОТВЕНО', 'СЕРВИРАНО', 'ПЛАТЕНА', 'ОТКАЖАНА')); |
| | 53 | }}} |
| | 54 | |
| | 55 | == Тип на инвентарна операција == |
| | 56 | |
| | 57 | === Опис === |
| | 58 | |
| | 59 | Доменот `inventory_operation` дозволува само валидни типови на инвентарни операции: ПОЧЕТНА, ПРИЕМ, ПРОДАЖБА, ОТПИС, КОРЕКЦИЈА. Овозможува конзистентно евидентирање на движењата на залиха. |
| | 60 | |
| | 61 | === Имплементација === |
| | 62 | |
| | 63 | {{{ |
| | 64 | CREATE DOMAIN project.inventory_operation AS TEXT |
| | 65 | CHECK (VALUE IN ('ПОЧЕТНА', 'ПРИЕМ', 'ПРОДАЖБА', 'ОТПИС', 'КОРЕКЦИЈА')); |
| | 66 | }}} |
| | 67 | |
| | 68 | == Комплексна email валидација == |
| | 69 | |
| | 70 | === Опис === |
| | 71 | |
| | 72 | Доменот `valid_email` валидира email адреси со комплексен regex и дополнителни услови: |
| | 73 | * Основен regex за валиден формат |
| | 74 | * Ограничување на должина (макс 255) |
| | 75 | * Забрана за последователни точки (`..`) |
| | 76 | * Забрана за започнување или завршување со точка |
| | 77 | |
| | 78 | === Имплементација === |
| | 79 | |
| | 80 | {{{ |
| | 81 | CREATE DOMAIN project.valid_email AS VARCHAR(255) |
| | 82 | CHECK ( |
| | 83 | VALUE ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' |
| | 84 | AND LENGTH(VALUE) <= 255 |
| | 85 | AND POSITION('..' IN VALUE) = 0 |
| | 86 | AND VALUE !~ '^\.' |
| | 87 | AND VALUE !~ '\.$' |
| | 88 | ); |
| | 89 | }}} |
| | 90 | |
| | 91 | == Македонски мобилен телефонски број == |
| | 92 | |
| | 93 | === Опис === |
| | 94 | |
| | 95 | Доменот `mkd_phone` валидира македонски мобилни телефонски броеви во три дозволени формати: |
| | 96 | * `+389 7X XXX XXX` (меѓународен формат) |
| | 97 | * `0 7X XXX XXX` (домашен формат со водечка нула) |
| | 98 | * `7X XXX XXX` (краток формат) |
| | 99 | |
| | 100 | Дозволува опционални празни места помеѓу групите цифри. |
| | 101 | |
| | 102 | === Имплементација === |
| | 103 | |
| | 104 | {{{ |
| | 105 | CREATE DOMAIN project.mkd_phone AS TEXT |
| | 106 | CHECK ( |
| | 107 | VALUE ~ '^\+389\s?7[0-9]{2}\s?[0-9]{3}\s?[0-9]{3}$' |
| | 108 | OR VALUE ~ '^0\s?7[0-9]{2}\s?[0-9]{3}\s?[0-9]{3}$' |
| | 109 | OR VALUE ~ '^7[0-9]{2}\s?[0-9]{3}\s?[0-9]{3}$' |
| | 110 | ); |
| | 111 | }}} |
| | 112 | |
| | 113 | == bcrypt password hash валидација == |
| | 114 | |
| | 115 | === Опис === |
| | 116 | |
| | 117 | Доменот `password_hash` валидира bcrypt hash вредности во стандарден формат `$2a$XX$...`. Спречува внесување на plain-text лозинки или невалидни hash вредности. Форматот е: `$2a$` или `$2b$` или `$2y$`, потоа два броја за cost фактор, потоа `$`, потоа 53 карактери од base64 алфабетот (`./A-Za-z0-9`). |
| | 118 | |
| | 119 | === Имплементација === |
| | 120 | |
| | 121 | {{{ |
| | 122 | CREATE DOMAIN project.password_hash AS TEXT |
| | 123 | CHECK (VALUE ~ '^\$2[aby]\$\d{2}\$[./A-Za-z0-9]{53}$'); |
| | 124 | }}} |
| | 125 | |
| | 126 | == Примена на домените на постоечки колони == |
| | 127 | |
| | 128 | === Опис === |
| | 129 | |
| | 130 | Домените се применуваат на постоечките колони со `ALTER TABLE ... ALTER COLUMN ... TYPE ...`. Ова автоматски ги валидира сите постоечки вредности против новите ограничувања. |
| | 131 | |
| | 132 | === Имплементација === |
| | 133 | |
| | 134 | {{{ |
| | 135 | ALTER TABLE project.payment |
| | 136 | ALTER COLUMN amount TYPE project.positive_money; |
| | 137 | |
| | 138 | ALTER TABLE project.product |
| | 139 | ALTER COLUMN price TYPE project.positive_money; |
| | 140 | |
| | 141 | ALTER TABLE project.order_item |
| | 142 | ALTER COLUMN unit_price TYPE project.positive_money; |
| | 143 | |
| | 144 | ALTER TABLE project.settings |
| | 145 | ALTER COLUMN vat_percent TYPE project.vat_percentage; |
| | 146 | |
| | 147 | ALTER TABLE project.app_settings |
| | 148 | ALTER COLUMN vat_rate TYPE project.vat_percentage; |
| | 149 | |
| | 150 | ALTER TABLE project.restaurant_table |
| | 151 | ALTER COLUMN status TYPE project.table_status; |
| | 152 | |
| | 153 | ALTER TABLE project.orders |
| | 154 | ALTER COLUMN status TYPE project.order_status; |
| | 155 | |
| | 156 | ALTER TABLE project.inventory |
| | 157 | ALTER COLUMN operation_type TYPE project.inventory_operation; |
| | 158 | |
| | 159 | ALTER TABLE project.ingredient_inventory |
| | 160 | ALTER COLUMN operation_type TYPE project.inventory_operation; |
| | 161 | |
| | 162 | ALTER TABLE project.app_user |
| | 163 | ALTER COLUMN email TYPE project.valid_email, |
| | 164 | ALTER COLUMN password_hash TYPE project.password_hash; |
| | 165 | }}} |
| | 166 | |
| | 167 | == Тестирање == |
| | 168 | |
| | 169 | === Успешно внесување === |
| | 170 | |
| | 171 | {{{ |
| | 172 | INSERT INTO project.payment (order_id, amount, method, payment_date) |
| | 173 | VALUES (1, 100.00, 'КЕШ', NOW()); |
| | 174 | }}} |
| | 175 | |
| | 176 | === Негативна сума (грешка) === |
| | 177 | |
| | 178 | {{{ |
| | 179 | INSERT INTO project.payment (order_id, amount, method, payment_date) |
| | 180 | VALUES (1, -50.00, 'КЕШ', NOW()); |
| | 181 | -- ERROR: value for domain positive_money violates check constraint |
| | 182 | }}} |
| | 183 | |
| | 184 | === Преголема сума (грешка) === |
| | 185 | |
| | 186 | {{{ |
| | 187 | INSERT INTO project.payment (order_id, amount, method, payment_date) |
| | 188 | VALUES (1, 2000000.00, 'КЕШ', NOW()); |
| | 189 | -- ERROR: value for domain positive_money violates check constraint |
| | 190 | }}} |
| | 191 | |
| | 192 | === Невалиден ДДВ (грешка) === |
| | 193 | |
| | 194 | {{{ |
| | 195 | UPDATE project.settings SET vat_percent = 17 WHERE id = 1; |
| | 196 | -- ERROR: value for domain vat_percentage violates check constraint |
| | 197 | }}} |
| | 198 | |
| | 199 | === Невалиден статус на нарачка (грешка) === |
| | 200 | |
| | 201 | {{{ |
| | 202 | UPDATE project.orders SET status = 'НЕПОЗНАТО' WHERE order_id = 1; |
| | 203 | -- ERROR: value for domain order_status violates check constraint |
| | 204 | }}} |
| | 205 | |
| | 206 | === Невалиден email (грешка) === |
| | 207 | |
| | 208 | {{{ |
| | 209 | INSERT INTO project.app_user (username, password_hash, role_id, first_name, last_name, email, active) |
| | 210 | VALUES ('test', '$2a$06$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTU', 1, 'Test', 'User', 'not-an-email', TRUE); |
| | 211 | -- ERROR: value for domain valid_email violates check constraint |
| | 212 | }}} |
| | 213 | |
| | 214 | == Заклучок == |
| | 215 | |
| | 216 | Со 8 кориснички дефинирани домени се постигнува: |
| | 217 | * Централизирана валидација на деловни правила |
| | 218 | * Конзистентност на податоците низ целата база |
| | 219 | * Повторна употреба на исти ограничувања на повеќе колони |
| | 220 | * Полесно одржување — промена на едно место |
| | 221 | * Подобрена читливост на шемата |