# Pembatalan Tasklist - Bug Material & Concurrency Fix

Analisis fungsi pembatalan SPK (`MasterData::pembatalanTasklist()`) menemukan 2 *bug* persisten/laten yang kritis:

## 1. Material Masih Terhitung Walau SPK Dibatalkan (Bug Akibat Shift Source of Truth)
Pada sesi sebelumnya, kita mengubah perhitungan material ("Digunakan") agar merujuk ke tabel penugasan `project_komposisi_sub_workoder` (`jenis_transaksi='sub_wo'`).
Namun, di dalam `pembatalanTasklist()`, sistem ternyata **hanya** menghapus (trash=1) record di `project_tasklist` dan mengupdate mundur nominal di BOM utama (`5582`). Sistem **TIDAK** men-*trash* record penugasan `sub_wo` itu sendiri.
Akibatnya: SPK yang sudah dibatalkan akan tetap disum (dijumlahkan) sebagai barang "Digunakan" di pop-up `selectItem`.

## 2. Race Condition (Double Cancel)
`pembatalanTasklist` menginisiasi `$this->db->trans_start()` namun tidak memiliki *Row Locking* (`FOR UPDATE`) pada `project_produk`. Jika user melakukan *double-click* cepat pada tombol Batal, transaksi pembatalan bisa berjalan ganda. Hal ini dapat membuat pengembalian stok (BOM `5582`) bertambah dua kali lipat dan mengacaukan perhitungan keuangan.

## Proposed Changes

### `application/modules/master_project/controllers/MasterData.php`
- **[MODIFY] `pembatalanTasklist()`**
  1. Menambahkan baris `FOR UPDATE` untuk mengunci `project_produk` tepat setelah `$this->db->trans_start()`.
  2. Menambahkan perintah untuk men-trash (`status=0`, `trash=1`) record di `project_komposisi_sub_workoder` (atau `_tambahan`) yang berkaitan dengan `no_spk` yang dibatalkan. Hal ini mutlak agar `getRekapKomposisi()` tidak menjumlahkan material dari SPK yang batal.

## User Review Required
> [!IMPORTANT]
> - Apakah ada perlakuan khusus untuk pembatalan SPK Tambahan Biaya di modul lain? (Logika saat ini: kita akan men-trash tabel `project_komposisi_sub_workoder` maupun `project_komposisi_sub_workoder_tambahan` sesuai dengan *task_type* nya).
> - Setuju dengan rencana perbaikan ini?
