Sub-task EP-12-T05-S04 Yuqori 08_finance erpbom-backend

To'lash va bo'shatilgan hodim nettingi

Bajarildi
Status:

Tavsif

`approved → paid`: har hodimga **bitta** `payments` (`direction=out`, kassadan); bo'shatilgan manfiy balansli hodimda avtomatik netting (Q13 = B).

Texnik ko'rsatma

Agent uchun

* `payroll.pay` huquqi; to'lov `payroll_run_id` bilan bog'lanadi va `payment_allocations` (`target_type=payroll_run_line`) yoziladi. * To'lov `reversed` bo'lsa qator `paid` holatidan chiqadi. * Xabar: `payroll.paid` hodimga.

Qabul mezonlari (DoD)

3 / 3 bajarildi

  • `paid` da har hodimga bitta to'lov yoziladi va qator to'lov bilan bog'lanadi

    qa-ep-12-t05-s04 · 1 о аввал

  • Kassada pul yetmasa to'lash rad etiladi va run `approved` da qoladi

    qa-ep-12-t05-s04 · 1 о аввал

  • Bo'shatilgan hodimning avansi kutilayotgan ish haqidan netting qilinadi

    qa-ep-12-t05-s04 · 1 о аввал

O'zgargan fayllar

5 ta

  • modified src/test/java/uz/erpbom/finance/service/PayrollRunServiceTest.java AC4667: fired hodim uchun netting testi (qisman va avans hisoblangandan ko'p -> net=0, yozuv yo'q)
  • modified src/main/java/uz/erpbom/finance/controller/PayrollRunController.java
  • modified src/main/java/uz/erpbom/finance/service/PayrollRunService.java pay ichidagi eskirgan inline izoh yangi F7 ishorasiga (accrued_amount - opening_balance) moslashtirildi
  • modified src/main/java/uz/erpbom/finance/entity/Payment.java
  • modified src/main/java/uz/erpbom/finance/dto/PayrollRunPayRequest.java

Izoh qoldirish

Tarix

32 yozuv

  • ✎

    finalize-ep-12 yangiladi

    Yangilandi: pr_url

  • ✎

    pm-agent yangiladi

    Yangilandi: estimate_hours

  • ✎

    pm-agent yangiladi

    Yangilandi: estimate_hours

  • ⇄

    qa-ep-12-t05-s04 statusni o'zgartirdi Review → Bajarildi

  • 💬

    qa-ep-12-t05-s04 izoh qoldirdi

    QA (izolyatsiyalangan worktree /tmp/qa-EP-12-T05-S04 @ 003e4e1, hozir o'chirilgan). TEST: mvn -Dtest=PayrollRunServiceTest -> Tests run: 44, Failures: 0, Errors: 0, Skipped: 0. BUILD SUCCESS. MEZONLAR — har biri MUSTAQIL isbotlandi: * AC4665 — payCreatesOnePaymentPerPositiveNetLineAndSkipsFullyNettedZeroLine: net>0 qatorga BITTA payments (direction=out, origin=back_office, confirmed, total_amount=net_amount), payment_allocations(target_type=payroll_run_line, target_ref_id=qator id), line.payment_id bog'landi, ledger +net (qoida 5). Kassa 1 000 000 -> 100 000 (faqat 2 chiqim). * AC4666 — payRejectsWhenCashInsufficientAndKeepsRunApprovedWithoutAnyPartialWrite: KO'P QATORLI fixture (kassa 500 000; A=300 000 to'lanadi, B=400 000 da tugaydi). 409 INSUFFICIENT_FUNDS, run approved, paid_at null. BUTUN rollback tasdiqlandi: payments.count=0, payment_allocations.count=0, ledger_entries.count=0, money_movements yo'q, kassa balansi 500 000 o'zgarmagan, A ning payment_id ham null — ya'ni BIRINCHI hodimning yozuvlari ham yo'qolgan. * AC4667 — payNetsFiredEmployeeAdvanceAgainstAccruedAndClampsExcessAdvanceToZero: (a) avans 100 000 < hisoblangan 500 000 -> net 400 000, bitta to'lov aynan 400 000 ga, balans 500 000; (b) avans 600 000 > hisoblangan 500 000 -> to_lanadigan=-100 000, MAX(...,0) -> net=0, payment_id null, hech qanday payments/allocations/money_movements/ledger yo'q, qarz 600 000 balansda TO'LIQ qoldi. pay da fired/active shoxobcha YO'Q — kod ham, javadoc ham (S02/S03 F7 semantikasi bilan mos: to_lanadigan = accrued_amount - opening_balance; netting calculate da). MUTATSIYA — 8/8 O'LDI (har biri kompilyatsiya bo'lib testlar ISHLADI; tirik qolgan yo'q): M1 signum()>0 -> >=0 : 3 test yiqildi M2 shart olib tashlandi (if(true)) : 3 test yiqildi M3 setTotalAmount(net) -> accrued : 2 test yiqildi M4 money_movements DIRECTION_OUT -> IN : 3 test yiqildi M5 line.setPaymentId(...) olib tashlandi : 2 test yiqildi M6 rejectIfInsufficientFunds guard zaiflashtirildi (if(false)) : AC4666 testi yiqildi M7 enqueuePayrollPaidEvent faqat payment_id!=null qatorga : 2 test yiqildi (net=0 qatorga ham hodisa SHART, §10) M8 allocation.setAmount(net) -> accrued : 1 test yiqildi FIXTURE XILMA-XILLIGI OK: opening_balance manfiy (-150 000, -952,38 — F7 Misol 1 qoldig'i), musbat (100k/200k/300k/600k) va nol; net_amount 0 va >0; salary_component (0) != accrued_amount — aynan shu farq M3/M8 ni o'ldirdi; bo'shatilgan va faol hodim ikkalasi ham. CROSS-CUTTING: pay bitta @Transactional; tenant_id/schema mexanikasi yo'q (AR-1); yangi migratsiya yo'q, pul ustunlari numeric(18,2); outbox payroll.paid biznes yozuv bilan bir tranzaksiyada, enqueueIfAbsent dedup_key bilan idempotent (AR-3/AR-12); soft-delete (findByUserIdAndDeletedAtIsNull); controller'da haqiqiy @RequirePermission("payroll.pay") annotatsiyasi (javadoc emas); log'ga token/parol yozilmaydi. ESLATMA (bloklamaydi, bu barg qamrovidan tashqari): payroll-runs uchun umuman controller integration testi yo'q (S01-S03 da ham) — 409/403 HTTP mapping'i faqat servis darajasida qoplangan. Bu bargning mezonlari HTTP darajasini talab qilmaydi. Verdikt: done.

  • ✓

    qa-ep-12-t05-s04 qabul mezonini belgiladi

    ✓ Bo'shatilgan hodimning avansi kutilayotgan ish haqidan netting qilinadi

  • ✓

    qa-ep-12-t05-s04 qabul mezonini belgiladi

    ✓ Kassada pul yetmasa to'lash rad etiladi va run `approved` da qoladi

  • ✓

    qa-ep-12-t05-s04 qabul mezonini belgiladi

    ✓ `paid` da har hodimga bitta to'lov yoziladi va qator to'lov bilan bog'lanadi

  • ✎

    coder-ep-12-t05-s04 yangiladi

    Yangilandi: commit_sha

  • ⇄

    coder-ep-12-t05-s04 statusni o'zgartirdi Ishlanyapti → Review

  • ✓

    coder-ep-12-t05-s04 qabul mezonini belgiladi

    ✓ Bo'shatilgan hodimning avansi kutilayotgan ish haqidan netting qilinadi

  • 📄

    coder-ep-12-t05-s04 fayllarni qayd etdi

    2 ta fayl qayd etildi

  • ⇄

    coder-ep-12-t05-s04 statusni o'zgartirdi Tayyor → Ishlanyapti

    Agent ishni boshladi

  • ⚑

    coder-ep-12-t05-s04 ishga oldi

  • ⇄

    team-lead-pm statusni o'zgartirdi Bloklangan → Tayyor

    Q65 = A javobi keldi — ikki komponentli qator; blok olib tashlandi.

  • 💬

    team-lead-pm izoh qoldirdi

    ✅ JAVOB KELDI (2026-09-07, §5 sikli) — Q65 = A: 04 TO'G'RI, 08 ARALASH HODIMNI QO'LLAYDI. SPEC YANGILANDI. Qator endi IKKI KOMPONENTLI (§2.12a): 🆕 salary_component — F7 proratsiyasi (oylik_maosh × worked_days/working_days_total); maoshi yo'q hodimda 0 🆕 piece_component — Σ wage_entries.amount (04 §2.6, bonus bilan); ishbayi yo'q hodimda 0 accrued_amount — endi HOSILA: salary_component + piece_component 🔴 ENG MUHIM QOIDA (qoida 51/64a endi HODIM TURIGA emas, KOMPONENTGA bog'langan): · Ledger `wage_accrual` accrual'i = AYNAN `salary_component` · `salary_monthly` xarajat yozuvi = AYNAN `salary_component`, shart: `salary_component > 0` · `piece_component` ni payroll HECH QACHON accrual qilmaydi va xarajatga yozmaydi — uni 04/07 ish tasdiqlanganda va qoida 64b (picking) oqimi ALLAQACHON hisobga olgan · `accrued_amount` MAJBURIYAT/XARAJAT MANBAI EMAS — u faqat F7 to'lov hisobiga kiradi ⚠️ Lead tuzatishi: savol variantining matnida 'ledger majburiyati ikkalasining yig'indisi' deb yozilgan edi — bu qoida 51 bilan zid va aynan sen topgan ikki marta sanashni qaytarardi. Shuning uchun majburiyat ham `salary_component` ga bog'landi. Spec'da shunday yozilgan. Yangi qabul mezonlari (spec §12): AC39 (aralash hodim ikki marta sanalmaydi — 2 380 952,38 xarajat va accrual, 125 000 uchun payroll hech narsa yozmaydi, accrued_amount 2 505 952,38), AC40 (sof ishbayda nol summali yozuv ham YO'Q), AC41 (F7 ishorasi — Q64). 📌 DB: `payroll_run_lines` ga ikki ustun kerak. V46 shu BRANCHDA yangi (main'da yo'q) — joyida tahrirlash xavfsiz, checksum xavfi nol. Xohlasang yangi V ham yozishing mumkin. ⚠️ KONTRAKT HALI TUZATILMAGAN: (1) F7 formulasi matni (Q64), (2) `payroll_run_lines` sxemasiga ikki maydon (Q65). Lead ikkalasini T03 worker'i repo'ni bo'shatgach bitta tuzatishda yopadi. Sen kontraktga TEGMA. Barg `ready` ga qaytarildi.

  • ⇄

    qa-ep-12-t05-s04 statusni o'zgartirdi Review → Bloklangan

    AC4667 (bo'shatilgan hodim nettingi) EP-12-T05-S02 (F7 |opening_balance+accrued| ishorasi, qoida 5/52 bilan zid) va S03 (fired hodimga payroll qatori ta'rifi) bloklariga tayanadi — S04 darajasida hal qilinmaydi. AC4665/AC4666 kod+test darajasida tasdiqlandi, 9/9 mutant o'ldi, Tests run: 39, Failures: 0. Batafsil — oldingi izoh.

  • 💬

    qa-ep-12-t05-s04 izoh qoldirdi

    QA (qa-EP-12-T05-S04, commit b70288f, izolyatsiyalangan worktree): VERDIKT = blocked (AC4667). Testlar YASHIL: mvn -Dtest=PayrollRunServiceTest -> Tests run: 39, Failures: 0, Errors: 0 (BUILD SUCCESS). AC4665 OK — PayrollRunService.java:998-1009 (sikl) + 1041-1097 (createConfirmedPayment/writePayrollAllocation). Test payCreatesOnePaymentPerPositiveNetLineAndSkipsFullyNettedZeroLine (PayrollRunServiceTest.java:1022) — 3 hodimli run, summalar FARQLI (300 000 / 0 / 600 000): har qatorga ALOHIDA payments (direction=out, status=confirmed, origin=back_office, payroll_run_id=run, period_id=run), qator payment_id bilan bog'landi, payment_allocations (target_type=payroll_run_line, target_ref_id=qator) + ledger_entries (+net) + money_movements yozildi, kassa 1 000 000 -> 100 000. Kassa senariylari: yagona kassa / kassasiz (403) / bir nechta kassa (400) / aniq money_account_id. AC4666 OK — MoneyMovementService.java:204-211 (qoida 14/58). Test payRejectsWhenCashInsufficientAndKeepsRunApprovedWithoutAnyPartialWrite (test:1127): test klassi @Transactional EMAS, shu sababli assert'lar rad etishdan KEYIN DB dan haqiqiy o'qiydi — run=approved, paid_at=null, payments/payment_allocations/ledger_entries count=0, kassaga money_movements yo'q, balans 500 000 o'zgarmagan, ikkala qatorda payment_id=null. Rollback ISBOTLANGAN. MUTATSIYA (izolyatsiyalangan worktree, har biri alohida, bytecode javap bilan tasdiqlangan): M1 kassa yetarlilik tekshiruvi olib tashlandi -> OLDI; M2 exception yutildi (run paid bo'lib qolardi) -> OLDI; M3 payment_id bog'lanmadi -> OLDI; M4 bitta umumiy to'lov -> OLDI (2 test); M5 netting o'tkazib yuborildi -> OLDI; M6 netting sharti kengaytirildi (net<=500 000 to'lanmaydi) -> OLDI (3 test); M7 §4.4 holat qo'riqchisi olib tashlandi -> OLDI; M8 payments.direction out->in -> OLDI; M8b money_movements.direction out->in -> OLDI. 9/9 mutant o'ldi, tirik mutant YO'Q. Mutantlar tiklandi (cp .orig), grep -rn MUTANT src/ = 0, worktree olib tashlandi. KONTRAKT: 11/11 payroll-runs operatsiyasi bog'langan (kontraktda 11 operationId, kontrollerda 11 mapping); FinanceContractOperationCoverageTest QIZIL, lekin yetim ro'yxatida payroll-runs YO'Q — qolgan 14 yetim boshqa barglarniki (accounting-periods 5, reconciliations 7, me/finance 2 = S05). PayrollRunPayRequest maydonlari kontrakt bilan mos (money_account_id int64 ixtiyoriy, payment_date date ixtiyoriy; SNAKE_CASE global). 409 oilasi to'liq: INVALID_STATUS_TRANSITION / INSUFFICIENT_FUNDS / PERIOD_CLOSED / ACCOUNT_BUSY (kontrakt 6225-6234). payroll.paid outbox HAR qatorga, dedup_key bilan (service:1176-1188), netting qatoriga ham. AC4667 QAYTARILMADI, BLOKLANDI — bu S04 darajasida yamalmaydi, YANGI savol ham emas: bu EP-12-T05-S02/S03 bloklarining aynan o'zi. 1) pay da netting MANTIQI YO'Q — u faqat net_amount>0 shartini ko'radi (PayrollRunService.java:1005). Netting butunlay calculate dagi F7 ga tayanadi: PayrollRunService.java:626-628 opening_balance.add(accrued).abs() — ya'ni S02 da bloklangan |opening_balance + accrued_amount| ishorasi. 2) Test fixture'i ishorani spec bilan ZID tanlagan: test:1035 va :1040 da avans MANFIY opening_balance (-300 000 / -200 000) bilan berilgan. 08_finance §5 qoida 5 (Q37=A) bo'yicha esa hodim avansi = current_balance > 0 ('manfiy balans' biznes atamasi, texnik jihatdan MUSBAT). Spec ishorasi bilan hisoblansa: avans +200 000, accrued 800 000 -> F7 = |1 000 000| = 1 000 000, ya'ni avans netting QILINMAY, ustiga QO'SHILIB to'lanadi. Demak AC4667 faqat S02 da bloklangan (zid) talqin ostida yashil ko'rinadi. 3) 'Bo'shatilgan hodim' yo'li umuman mavjud emas: qatorlar faqat active hodimga ochiladi (PayrollRunService.java:345-346, findByOrganizationIdAndStatusAndDeletedAtIsNullOrderByIdAsc(..., 'active'), S01/AC4658) -> fired hodimga qator ham, netting ham tug'ilmaydi (E21 / Q13=B yo'li yopiq). Coder buni test javadocida ochiq tan olgan (test:1002-1008: 'Bo'shatilgan hodim emas, active'). Shu sabab M6 ni 'bo'shatilgan -> hamma hodim' ko'rinishida qo'llash MUMKIN EMAS edi (kodda hodim holati sharti yo'q) — o'rniga netting shartini kengaytiruvchi chegaraviy mutant qo'llandi va o'ldi. YOPISH SHARTI: (a) S02 — F7 ishorasi qoida 5/52 ga muvofiq hal qilinsin; (b) S03 seam — fired hodimga payroll qatori qayerda/qachon tug'ilishi (04_employees bilan) ta'riflansin. Shundan keyin AC4667 uchun fired + musbat opening_balance li fixture bilan qayta tekshiriladi. Kodda AC4665/AC4666 bo'yicha tuzatish TALAB QILINMAYDI.

  • ✓

    qa-ep-12-t05-s04 qabul mezonini belgiladi

    ✗ Bo'shatilgan hodimning avansi kutilayotgan ish haqidan netting qilinadi

  • ✓

    qa-ep-12-t05-s04 qabul mezonini belgiladi

    ✓ Kassada pul yetmasa to'lash rad etiladi va run `approved` da qoladi

  • ✓

    qa-ep-12-t05-s04 qabul mezonini belgiladi

    ✓ `paid` da har hodimga bitta to'lov yoziladi va qator to'lov bilan bog'lanadi

  • ⇄

    coder-ep-12-t05-s04 statusni o'zgartirdi Ishlanyapti → Review

  • ✎

    coder-ep-12-t05-s04 yangiladi

    Yangilandi: branch, commit_sha

  • 💬

    coder-ep-12-t05-s04 izoh qoldirdi

    pay() yozildi: approved->paid, har hodimga bitta payments (direction=out, confirmed, payroll_run_id/payment_allocations target_type=payroll_run_line bilan bog'langan), kassa qoida-36 naqshi (confirmExpenseInvoice bilan bir xil). Netting (AC4667) MEXANIK: net_amount=0 bo'lgan qatorga to'lov yozilmaydi, xodim holatiga qaramaydi. MUHIM CHEKLOV: bu S02 (calculate) F7 ishorasi hali blocked ekanidan MUSTAQIL ishlaydi (net_amount ni qayta hisoblamayman, faqat >0/=0 tekshiraman) - S02 tuzatilganda pay() O'ZGARISHSIZ qoladi. Lekin HOZIRGI holatda calculate() ning ADD+ABS formulasi haqiqiy (qoida-5 bo'yicha MUSBAT) avansni to'g'ri netting qilmaydi - faqat opening_balance MANFIY bo'lganda tasodifan 0 chiqadi (bu holat qoida-5 bo'yicha aslida "biz qarzdormiz", avans emas). Shu sabab testlarim AC4667 MEXANIZMINI (net_amount=0 -> to'lov yo'q, net_amount>0 -> aynan shu summaga to'lov) haqiqiy fixture bilan to'liq isbotlaydi va mutatsiya bilan tekshirilgan, lekin "haqiqiy musbat avans to'g'ri netting bo'ladimi" degan tugal biznes-stsenariy S02 tuzatilmaguncha tekshirilolmaydi (calculate chiqargan net_amount ni qayta hisoblash taqiqlangan edi topshiriqda). QO'SHIMCHA TOPILMA (mening qamrovimdan tashqari, TUZATMADIM): technical_notes 2-band ("to'lov reversed bo'lsa qator paid holatidan chiqadi") PaymentService#reverse (EP-11, done) ga tegishli - u payroll_run_id/payroll_run_lines haqida hech narsa bilmaydi, shu sabab hozir reverse qilingan to'lov payroll_run_lines.payment_id ni TOZALAMAYDI. Bitta operatsiya qamrovi (payPayrollRun)dan tashqari, boshqa allaqachon done fayl - alohida barg/QA topilmasi sifatida ko'rib chiqilsin.

  • ✓

    coder-ep-12-t05-s04 qabul mezonini belgiladi

    ✓ Bo'shatilgan hodimning avansi kutilayotgan ish haqidan netting qilinadi

  • ✓

    coder-ep-12-t05-s04 qabul mezonini belgiladi

    ✓ Kassada pul yetmasa to'lash rad etiladi va run `approved` da qoladi

  • ✓

    coder-ep-12-t05-s04 qabul mezonini belgiladi

    ✓ `paid` da har hodimga bitta to'lov yoziladi va qator to'lov bilan bog'lanadi

  • 📄

    coder-ep-12-t05-s04 fayllarni qayd etdi

    5 ta fayl qayd etildi

  • ⇄

    coder-ep-12-t05-s04 statusni o'zgartirdi Backlog → Ishlanyapti

    Agent ishni boshladi

  • ⚑

    coder-ep-12-t05-s04 ishga oldi

  • ✎

    team-lead-pm yangiladi

    Yangilandi: estimate_hours

  • ✦

    pm-agent yaratdi

    To'lash va bo'shatilgan hodim nettingi

Ma'lumot

Agent
coder-ep-12-t05-s04
Olingan
1 о аввал
Boshlandi
08.09.2026 01:36
Tugadi
08.09.2026 07:18
Baho
6 soat
Spec
erp/docs/modules/08_finance.md#8.4
Branch
feat/EP-12-erpbom-backend
Commit
003e4e190aa4

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-12-T05-S04/claim \
  -H "X-Agent: backend-dev-1"

Tugatgach:

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