189 | | = Трансакции |
| 189 | = Процедури |
| 190 | |
| 191 | == Креирај нарачка и резервира залиха од партија |
| 192 | |
| 193 | Примa влезни параметри ({{{buyer_id}}}, {{{batch_id}}}, {{{quantity}}}) и креира нарачка, ред во {{{ORDER_BATCHES}}} и автоматски пресметува {{{total_price}}}. Ако нема доволно {{{units_per_batch}}} враќа грешка и откажува. |
| 194 | |
| 195 | {{{ |
| 196 | CREATE PROCEDURE create_order_from_batch(IN p_buyer_id VARCHAR(50), IN p_batch_id VARCHAR(50), IN p_quantity INT) |
| 197 | BEGIN |
| 198 | DECLARE v_product_id VARCHAR(50); |
| 199 | DECLARE v_price DECIMAL(18,2); |
| 200 | DECLARE v_units INT; |
| 201 | DECLARE v_total DECIMAL(18,2); |
| 202 | DECLARE v_order_id VARCHAR(50); |
| 203 | SELECT product_id, units_per_batch INTO v_product_id, v_units FROM BATCHES WHERE id = p_batch_id FOR UPDATE; |
| 204 | IF v_units IS NULL THEN |
| 205 | SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Batch not found'; |
| 206 | END IF; |
| 207 | IF p_quantity > v_units THEN |
| 208 | SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient units in batch'; |
| 209 | END IF; |
| 210 | SELECT price INTO v_price FROM PRODUCTS WHERE id = v_product_id; |
| 211 | SET v_total = v_price * p_quantity; |
| 212 | SET v_order_id = CONCAT('o_',UUID()); |
| 213 | INSERT INTO ORDERS (id, date, status, estimated_delivery_date, buyer_id, receiver_id) VALUES (v_order_id, CURRENT_DATE, 'pending', DATE_ADD(CURRENT_DATE, INTERVAL 7 DAY), p_buyer_id, p_buyer_id); |
| 214 | INSERT INTO ORDER_BATCHES (id, order_id, batch_id, quantity, price_per_unit, total_price, created_at, updated_at) VALUES (CONCAT('ob_',UUID()), v_order_id, p_batch_id, p_quantity, v_price, v_total, NOW(), NOW()); |
| 215 | UPDATE BATCHES SET units_per_batch = units_per_batch - p_quantity WHERE id = p_batch_id; |
| 216 | END; |
| 217 | }}} |
| 218 | |
| 219 | == Поврат на партија и креирање кредит нота |
| 220 | |
| 221 | Примa {{{order_batch_id}}} и {{{reason}}}; враќа количина во {{{BATCHES}}}, ажурира {{{ORDER_BATCHES}}} и креира негативен {{{PAYMENTS}}} запис како кредит. |
| 222 | |
| 223 | {{{ |
| 224 | CREATE PROCEDURE return_batch_and_credit(IN p_order_batch_id VARCHAR(50), IN p_reason VARCHAR(255)) |
| 225 | BEGIN |
| 226 | DECLARE v_batch_id VARCHAR(50); |
| 227 | DECLARE v_quantity INT; |
| 228 | DECLARE v_total DECIMAL(18,2); |
| 229 | DECLARE v_order_id VARCHAR(50); |
| 230 | SELECT batch_id, quantity, total_price, order_id INTO v_batch_id, v_quantity, v_total, v_order_id FROM ORDER_BATCHES WHERE id = p_order_batch_id FOR UPDATE; |
| 231 | IF v_batch_id IS NULL THEN |
| 232 | SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Order batch not found'; |
| 233 | END IF; |
| 234 | UPDATE BATCHES SET units_per_batch = units_per_batch + v_quantity WHERE id = v_batch_id; |
| 235 | UPDATE ORDER_BATCHES SET quantity = 0, total_price = 0.00, updated_at = NOW() WHERE id = p_order_batch_id; |
| 236 | INSERT INTO PAYMENTS (id, order_id, amount, currency, due_date, exchange_rate, payment_date, payment_method, payment_status) VALUES (CONCAT('pay_cr_',UUID()), v_order_id, -v_total, 'EUR', CURRENT_DATE, 1.00, CURRENT_DATE, 'credit_note', 'refunded'); |
| 237 | END; |
| 238 | }}} |
| 239 | |
| 240 | == Автоматско консолидирање на нарачки за транспорт |
| 241 | |
| 242 | Примa {{{transport_id}}} и список на {{{order_ids}}} ({{{CSV}}}); поврзува секоја нарачка со транспорт и ажурира статус на {{{orders}}} на {{{'in_transit'}}}. |
| 243 | |
| 244 | {{{ |
| 245 | CREATE PROCEDURE consolidate_orders_to_transport(IN p_transport_id VARCHAR(50), IN p_order_ids TEXT) |
| 246 | BEGIN |
| 247 | DECLARE v_order_id VARCHAR(50); |
| 248 | DECLARE v_pos INT DEFAULT 1; |
| 249 | DECLARE v_len INT; |
| 250 | DECLARE v_token VARCHAR(100); |
| 251 | SET v_len = CHAR_LENGTH(p_order_ids); |
| 252 | WHILE v_pos <= v_len DO |
| 253 | SET v_token = TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(p_order_ids, ',', v_pos), ',', -1)); |
| 254 | IF v_token <> '' THEN |
| 255 | UPDATE ORDERS SET transport_id = p_transport_id, status = 'in_transit' WHERE id = v_token; |
| 256 | END IF; |
| 257 | SET v_pos = v_pos + 1; |
| 258 | IF v_pos > 1000 THEN LEAVE; END IF; |
| 259 | END WHILE; |
| 260 | END; |
| 261 | }}} |
| 262 | |
| 263 | == Евиденција на увоз со конверзија на валута |
| 264 | |
| 265 | Примa {{{order_id}}} и {{{exchange_rate}}}; ажурира {{{PAYMENTS.exchange_rate}}} и пресметува {{{amount}}} во локална валута, поставува {{{payment_status}}} на {{{'pending'}}}. |
| 266 | |
| 267 | {{{ |
| 268 | CREATE PROCEDURE record_import_with_exchange(IN p_order_id VARCHAR(50), IN p_exchange_rate DECIMAL(12,4)) |
| 269 | BEGIN |
| 270 | DECLARE v_amount DECIMAL(18,2); |
| 271 | SELECT amount INTO v_amount FROM PAYMENTS WHERE order_id = p_order_id FOR UPDATE; |
| 272 | IF v_amount IS NULL THEN |
| 273 | SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Payment not found for order'; |
| 274 | END IF; |
| 275 | UPDATE PAYMENTS SET exchange_rate = p_exchange_rate, payment_status = 'pending' WHERE order_id = p_order_id; |
| 276 | END; |
| 277 | }}} |
| 278 | |
| 279 | == Симулација на рок на траење и известување за истекување |
| 280 | |
| 281 | Примa {{{days_ahead}}} и враќа листа со {{{batch_id}}} и {{{days_until_expiry}}} за сите серии кои истекуваат во рок од {{{days_ahead}}}; дополнително ажурира поле {{{status}}} во {{{BATCHES}}}. |
| 282 | |
| 283 | {{{ |
| 284 | CREATE PROCEDURE upcoming_expirations(IN p_days_ahead INT) |
| 285 | BEGIN |
| 286 | SELECT id AS batch_id, DATEDIFF(expiration_date, CURRENT_DATE) AS days_until_expiry FROM BATCHES WHERE DATEDIFF(expiration_date, CURRENT_DATE) <= p_days_ahead; |
| 287 | END; |
| 288 | }}} |
| 289 | |
| 290 | = Погледи |
| 291 | |
| 292 | == {{{active_orders_overview}}} |
| 293 | |
| 294 | Преглед на активни (processing, pending, in_transit) нарачки со вкупна сума и број на различни партији. |
| 295 | |
| 296 | {{{ |
| 297 | CREATE VIEW active_orders_overview AS |
| 298 | SELECT |
| 299 | o.id AS order_id, |
| 300 | o.date AS order_date, |
| 301 | o.status, |
| 302 | o.buyer_id, |
| 303 | o.receiver_id, |
| 304 | COALESCE(SUM(ob.total_price),0) AS total_order_value, |
| 305 | COUNT(DISTINCT ob.batch_id) AS batches_count |
| 306 | FROM ORDERS o |
| 307 | LEFT JOIN ORDER_BATCHES ob ON ob.order_id = o.id |
| 308 | WHERE o.status IN ('processing','pending','in_transit') |
| 309 | GROUP BY o.id, o.date, o.status, o.buyer_id, o.receiver_id; |
| 310 | }}} |
| 311 | |
| 312 | == {{{product_stock_summary}}} |
| 313 | |
| 314 | {{{ |
| 315 | CREATE VIEW product_stock_summary AS |
| 316 | SELECT |
| 317 | p.id AS product_id, |
| 318 | p.name AS product_name, |
| 319 | p.unit_of_measure, |
| 320 | COALESCE(SUM(b.units_per_batch),0) AS total_units_available, |
| 321 | COALESCE(AVG(p.price),0) AS avg_price |
| 322 | FROM PRODUCTS p |
| 323 | LEFT JOIN BATCHES b ON b.product_id = p.id |
| 324 | GROUP BY p.id, p.name, p.unit_of_measure; |
| 325 | }}} |
| 326 | |
| 327 | == {{{client_financial_position}}} |
| 328 | |
| 329 | Финансиска состојба по клиент — вкупно фактурирано, платено и останат долг. |
| 330 | |
| 331 | {{{ |
| 332 | CREATE VIEW client_financial_position AS |
| 333 | SELECT |
| 334 | c.id AS client_id, |
| 335 | c.name AS client_name, |
| 336 | COALESCE(SUM(pay.amount),0) AS total_invoiced_amount, |
| 337 | COALESCE(SUM(CASE WHEN pay.payment_status IN ('paid','refunded') THEN pay.amount ELSE 0 END),0) AS total_cleared_amount, |
| 338 | COALESCE(SUM(CASE WHEN pay.payment_status NOT IN ('paid','refunded') THEN pay.amount ELSE 0 END),0) AS outstanding_amount |
| 339 | FROM CLIENTS c |
| 340 | LEFT JOIN ORDERS o ON o.buyer_id = c.id |
| 341 | LEFT JOIN PAYMENTS pay ON pay.order_id = o.id |
| 342 | GROUP BY c.id, c.name; |
| 343 | }}} |
| 344 | |
| 345 | == {{{batches_near_expiry}}} |
| 346 | |
| 347 | Список на партији кои истекуваат во следните 30 дена (може да се користи и со WHERE услов за други периоди). |
| 348 | |
| 349 | {{{ |
| 350 | CREATE VIEW batches_near_expiry AS |
| 351 | SELECT |
| 352 | b.id AS batch_id, |
| 353 | b.batch_code, |
| 354 | b.product_id, |
| 355 | p.name AS product_name, |
| 356 | b.expiration_date, |
| 357 | DATEDIFF(b.expiration_date, CURRENT_DATE) AS days_until_expiry, |
| 358 | b.units_per_batch |
| 359 | FROM BATCHES b |
| 360 | LEFT JOIN PRODUCTS p ON p.id = b.product_id |
| 361 | WHERE DATEDIFF(b.expiration_date, CURRENT_DATE) BETWEEN 0 AND 30; |
| 362 | }}} |
| 363 | |
| 364 | == {{{transport_load_summary}}} |
| 365 | |
| 366 | Консолидиран преглед на транспортните единици со вкупен број на нарачки и вкупна тежина (приближно, користи net_weight * quantity). |
| 367 | |
| 368 | {{{ |
| 369 | CREATE VIEW transport_load_summary AS |
| 370 | SELECT |
| 371 | t.id AS transport_id, |
| 372 | t.name AS transport_name, |
| 373 | t.departure_point, |
| 374 | t.arrival_point, |
| 375 | COUNT(DISTINCT o.id) AS orders_count, |
| 376 | COALESCE(SUM(b.net_weight * ob.quantity),0) AS approximate_total_net_weight |
| 377 | FROM TRANSPORTS t |
| 378 | LEFT JOIN ORDERS o ON o.transport_id = t.id |
| 379 | LEFT JOIN ORDER_BATCHES ob ON ob.order_id = o.id |
| 380 | LEFT JOIN BATCHES b ON b.id = ob.batch_id |
| 381 | GROUP BY t.id, t.name, t.departure_point, t.arrival_point; |
| 382 | }}} |