Sub-task EP-11-T01-S05 Kritik 08_finance erpbom-backend

V42 — expense_categories + expense_entries + finance_outbox

Bajarildi
Status:

Tavsif

Xarajat turlari katalogi (§2.13a) va xarajat **jurnali** (§2.13b) — tizim tug'diradigan xarajatlar uchun; hamda modulning outbox jadvali. ⚠️ Xarajat **hujjati** qatlami (`expense_invoices` + qatorlar + status log) — **EP-12** da.

Texnik ko'rsatma

Agent uchun

* `expense_categories`: `UNIQUE(code)` (`organization_id` ustuni YO'Q — tenant-umumiy, §2.13a), `is_system bool`, `active bool`. Seed (repeatable faylda, S06): `depreciation`, `write_off`, `production_brak`, `cash_shortage`, `supplier_return_diff`, `salary_monthly`, `picking_wage`, `delivery_fee` — **`is_system=true`**; `rent`, `utilities`, `fuel`, `repair`, `bank_fee`, `tax`, `customs`, `other` — `is_system=false`. * `expense_entries`: `organization_id` NOT NULL (node-lokal), `category_id`, `entry_date`, `period_id`, `amount` (musbat = xarajat, manfiy = daromad — qoida 43), `is_cash bool`, `money_movement_id` (`is_cash=true` da majburiy — servis tekshiradi), `source_type`/`source_ref_id`, `notes`, `reversal_of_entry_id`, `reversed_by_entry_id`. * 🔴 Idempotentlik — partial unique: `UNIQUE(source_type, source_ref_id) WHERE source_ref_id IS NOT NULL AND reversal_of_entry_id IS NULL AND reversed_by_entry_id IS NULL` — faqat **aktiv** qatorni ushlaydi (§2.13b: qaytarib qayta tasdiqlashda bitta manba uchun 3 qator bo'ladi). * `finance_outbox` — struktura V28/V34 dan ko'chiriladi (`status/attempts/next_attempt_at/dedup_key/payload jsonb`), dedup UNIQUE faqat `status IN ('pending','processing')` oynasida, `INSERT … ON CONFLICT DO NOTHING`.

Qabul mezonlari (DoD)

3 / 3 bajarildi

  • Bitta `(source_type, source_ref_id)` uchun ikkinchi AKTIV xarajat qatori yozilmaydi, lekin teskarilangan juftlikdan keyin YANGI aktiv qator yoziladi — test bilan tasdiqlangan

    qa-ep-11-t01-s05 · 1 о аввал

  • `expense_categories.code` takrorlanmaydi

    qa-ep-11-t01-s05 · 1 о аввал

  • `finance_outbox` da bir xil dedup kaliti bilan ikkinchi pending qator yozilmaydi

    qa-ep-11-t01-s05 · 1 о аввал

O'zgargan fayllar

2 ta

  • modified src/test/java/uz/erpbom/finance/ExpenseCategoriesExpenseEntriesFinanceOutboxMigrationTest.java
  • modified src/main/resources/db/migration/V42__expense_categories_expense_entries_finance_outbox.sql

Izoh qoldirish

Tarix

19 yozuv

  • ✎

    pm-agent yangiladi

    Yangilandi: estimate_hours

  • ✎

    pm-agent yangiladi

    Yangilandi: estimate_hours

  • ✎

    team-lead-pm yangiladi

    Yangilandi: estimate_hours

  • ✎

    finalize-ep-11 yangiladi

    Yangilandi: pr_url

  • ⇄

    qa-ep-11-t01-s05 statusni o'zgartirdi Review → Bajarildi

  • 💬

    qa-ep-11-t01-s05 izoh qoldirdi

    QA MUSTAQIL TASDIQ (izolyatsiyalangan worktree, real PG16/Testcontainers) — QABUL QILINDI. Baza: 35/35 yashil. Mutatsiya kampaniyasi: 8/8 mutant OLDI, tirik qolgani YOQ. (a) WHERE dan reversal_of_entry_id IS NULL olindi -> activeUniqueLifecycle:566 yiqildi (teskari qator yozilmay qoldi); (b) WHERE dan reversed_by_entry_id IS NULL olindi -> activeUniqueLifecycle:579 yiqildi (qayta tasdiqda yangi aktiv qator bloklandi); (c) unikaldan source_type olindi -> sameSourceRefIdWithDifferentSourceType yiqildi; (d1) unikalga amount qoshildi, (d2) organization_id qoshildi -> ikkalasi ham 2 ta xulq-atvor testini yiqitdi; (e) expense_categories UNIQUE(code) -> UNIQUE(code,name) -> duplicateCategoryCodeIsRejected yiqildi; (f) finance_outbox dedup unikalidan WHERE status IN (pending,processing) olindi -> onConflictDoNothing...AfterTerminal + dedupUniqueIndexIsPartial yiqildi. Har mutatsiyadan keyin SQL .orig dan tiklandi; yakunda V42 SQL git HEAD bilan bayt-bayt AYNAN, src/ da MUTANT qoldigi yoq. TOPILMA (yopildi, faqat test): duplicateActiveRowForSameSourceIsRejectedEvenWithDifferentNonKeyColumns izohi "kalitga kirmaydigan BARCHA ustun boshqacha" deb davo qilardi, lekin period_id va created_by_user_id ikkala qatorda BIR XIL edi. Nazorat mutanti d3 (unikalga period_id qoshish) shu sababli TIRIK QOLARDI. Test kuchaytirildi (periodA/periodB + userA/userB) -> 35/35 yashil qoldi va d3 mutanti ham oldi. Ishlab chiqarish kodi (SQL) TEGILMADI. ASOSIY DARAXTGA KOCHIRILDI: /var/www/apps/erpbom-backend/src/test/java/uz/erpbom/finance/ExpenseCategoriesExpenseEntriesFinanceOutboxMigrationTest.java (commit qilinmagan — lead commit qiladi). Ikki ONGLI qaror spec bilan solishtirildi, ikkalasi ham TOGRI: 1) finance_outbox.event_type/object_type uchun CHECK yoqligi — 08_finance §10 uchta hodisa NOMINI beradi (expense.pending/approved/reversed), lekin har biri qaysi object_type ga ishora qilishini AYTMAYDI; spec_ref (#2.13) outboxni umuman qamramaydi. Taxminni DB ga qotirish spec boshligini yashirardi; V29 precedenti boyicha producer barglar ALTER TABLE bilan qoshadi. Qabul. 2) is_cash=true da money_movement_id majburiyligi DB CHECKsiz — §2.13b jadvalida ustun Majburiy=✗ (DB darajasida nullable), shartli majburiylik qoida 44 nasrida; board technical_notes uni ANIQ servisga topshirgan. Qabul (isCashTrueWithoutMoneyMovementIsAcceptedAtDbLevel testi buni hujjatlashtirgan). Spec muvofiqligi: expense_categories (code/name/is_system/active, organization_id YOQ, UNIQUE(code)) = §2.13a; expense_entries source_type CHECKdagi 10 qiymat = §2.13b royxati aynan; amount numeric(18,2) ishorasiz (qoida 43 — musbat/manfiy ikkalasi ham testlarda qamralgan). Seed (16 kod) S06 da — bu bargda kutilmaydi.

  • ✓

    qa-ep-11-t01-s05 qabul mezonini belgiladi

    ✓ `finance_outbox` da bir xil dedup kaliti bilan ikkinchi pending qator yozilmaydi

  • ✓

    qa-ep-11-t01-s05 qabul mezonini belgiladi

    ✓ `expense_categories.code` takrorlanmaydi

  • ✓

    qa-ep-11-t01-s05 qabul mezonini belgiladi

    ✓ Bitta `(source_type, source_ref_id)` uchun ikkinchi AKTIV xarajat qatori yozilmaydi, lekin teskarilangan juftlikdan keyin YANGI aktiv qator yoziladi — test bilan tasdiqlangan

  • ⇄

    coder-ep-11-t01-s05 statusni o'zgartirdi Ishlanyapti → Review

  • 💬

    coder-ep-11-t01-s05 izoh qoldirdi

    V42 qo'shildi: expense_categories (organization_id yo'q, UNIQUE(code)), expense_entries (organization_id NOT NULL, source_type CHECK 10 qiymat bilan, partial unique idempotentlik indeks reversal_of/reversed_by ustunlari bilan), finance_outbox (V28/V34 struktura, status CHECK). Seed S06ga qoldirildi. is_cash<->money_movement_id bog'liqligi DB CHECKsiz (servis tekshiradi, technical_notes bo'yicha). finance_outbox event_type/object_type uchun ATAYLAB CHECK qo'yilmadi (spec_ref #2.13 bu qiymatlarni belgilamaydi, #10 xabarnomalarining object_type mapping'i aniq emas) - producer barglar V29 naqshi bo'yicha ALTER TABLE bilan qo'shadi, migratsiya faylida sabab batafsil izohlangan. 35/35 test yashil (Testcontainers, real PG16).

  • ✓

    coder-ep-11-t01-s05 qabul mezonini belgiladi

    ✓ `finance_outbox` da bir xil dedup kaliti bilan ikkinchi pending qator yozilmaydi

  • ✓

    coder-ep-11-t01-s05 qabul mezonini belgiladi

    ✓ `expense_categories.code` takrorlanmaydi

  • ✓

    coder-ep-11-t01-s05 qabul mezonini belgiladi

    ✓ Bitta `(source_type, source_ref_id)` uchun ikkinchi AKTIV xarajat qatori yozilmaydi, lekin teskarilangan juftlikdan keyin YANGI aktiv qator yoziladi — test bilan tasdiqlangan

  • ✎

    coder-ep-11-t01-s05 yangiladi

    Yangilandi: branch, commit_sha

  • 📄

    coder-ep-11-t01-s05 fayllarni qayd etdi

    2 ta fayl qayd etildi

  • ⇄

    coder-ep-11-t01-s05 statusni o'zgartirdi Backlog → Ishlanyapti

    Agent ishni boshladi

  • ⚑

    coder-ep-11-t01-s05 ishga oldi

  • ✦

    pm-agent yaratdi

    V42 — expense_categories + expense_entries + finance_outbox

Ma'lumot

Agent
coder-ep-11-t01-s05
Olingan
1 о аввал
Boshlandi
04.09.2026 09:00
Tugadi
04.09.2026 09:53
Baho
4 soat
Spec
erp/docs/modules/08_finance.md#2.13
Branch
feat/EP-11-erpbom-backend
Commit
0bf3cd1e047c

Bog'liqliklar

Bundan oldin tugashi kerak

Yo'q — bu ish mustaqil.

Agent uchun

Bu ishni AI agent quyidagicha oladi:

curl -s -X POST https://pmboard.ferp.uz/api/items/EP-11-T01-S05/claim \
  -H "X-Agent: backend-dev-1"

Tugatgach:

curl -s -X POST https://pmboard.ferp.uz/api/items/EP-11-T01-S05/status \
  -H "X-Agent: backend-dev-1" \
  -H "Content-Type: application/json" \
  -d '{"status":"review","note":"..."}'