1.14.7
7 MR sudah merged ke main โ belum naik production. Validasi sebelum tagging.
Catatan: temuan ini dari pattern review otomatis โ beberapa mungkin false positive. Validasi manual tetap perlu sebelum tag production.
Refactor besar pada activation flow โ pisah CPP Change Package dari standard activation, guard antar job, completion service rewrite.
File: app/jobs/postpaid_change_package_job.rb
Tidak ada guard return if postpaid_activation.waiting? di awal perform. Dengan sidekiq_options retry: 5, jika job crash setelah sukses call ESB tapi sebelum save_api_log selesai โ rescue memanggil failed!, override status :waiting jadi :failed. Retry berikutnya kirim request CPP kedua ke ESB untuk MSISDN yang sama.
File: app/jobs/postpaid_activation_job.rb
Ketika order.cpp_type_change_package? true, hanya log error + return. Activation tetap di state awal (draft/pending) selamanya. User dan admin tidak tahu ada yang salah.
File: app/jobs/check_cpp_status_job.rb
Simulated service memakai OpenStruct.new(...). Jika update_activation_success akses field yang tidak ada, return nil silently. Data nil tersimpan ke database tanpa error.
File: app/controllers/customers/activations_controller.rb
flash[:alert] = "Aksi ini tidak berlaku untuk order ubah paket." โ hardcoded. Inconsistent dengan sync_change_package yang sudah pakai I18n.t().
File: app/jobs/postpaid_change_package_job.rb
Jika error di save_api_log (setelah activation sudah :waiting), rescue paksa set :failed. ESB sudah terima request, internal state salah. CheckCppStatusJob tidak poll activation yang :failed.
File: app/views/shared/orders/_postpaid_activations.html.erb
order.postpaid_activations.any? { |pa| pa.retry_reservation? } โ iterasi Ruby di view, trigger N+1 jika tidak ada eager-load yang sesuai.
File: app/services/activations/completion_service.rb
Variable di-set tapi tidak pernah dipakai sebagai guard. check_and_complete_order selalu dipanggil meski already_activated = true.
File: app/jobs/postpaid_activation_job.rb
Diubah dari PostpaidActivation.with_msisdn.find(...) ke PostpaidActivation.find(...). Akses msisdn_number lazy load โ extra query per job execution.
File: app/services/orders/change_package_service.rb
Activation di-update ke :pending via update_all sebelum loop enqueue. Jika enqueue gagal mid-loop (Redis down), sebagian activation stuck di :pending tanpa job. Tidak ada rollback.
File: spec/jobs/postpaid_activation_job_spec.rb
47 baris test dihapus, tidak ada pengganti yang cover behavior lama.
Penandaan dokumen dari order sebelumnya โ fitur inheritance dokumen lintas order untuk customer yang sama.
File: app/policies/order_policy.rb
Method user_is_sales_for_customer? kasih akses secured_document jika user adalah sales untuk ANY order milik customer X. Sales rep order A bisa lihat dokumen order B (different sales, same customer). Violates least-privilege & order boundary security.
Fix: tambah cek apakah user adalah sales_id untuk THIS order spesifik, atau whitelist hanya inheritable documents.
File: app/models/order.rb
Query pakai customer.orders โ bergantung pada order.customer association yg bisa nil/stale. Multi-tenant scenario bisa expose order milik customer lain.
Fix: pakai Order.where(customer_id: self.customer_id) eksplisit + guard nil.
File: app/models/order.rb
Pakai .joins("#{document_type}_attachment".to_sym) untuk dynamic Active Storage join. Walaupun Rails has_one_attached generate association akta_attachment, dynamic symbol generation rawan typo dan tidak validate document_type. Jika document_type bukan dari whitelist โ bisa raise atau return wrong.
Fix: validasi document_type vs DocumentApproval::DOCUMENT_TYPES dulu sebelum dynamic join.
File: app/models/order.rb
Tidak ada validasi document_type dari DocumentApproval::DOCUMENT_TYPES. Invalid type silent return nil โ sembunyikan bug dev/integration.
File: app/models/order.rb
fpn masuk NON_INHERITABLE_DOCUMENTS tapi requires_fpn_document? = true untuk add_member orders. Kalau add_member memang butuh inheritance fpn, ini contradicting business logic.
File: app/views/shared/_document_approval_list.html.erb
Link View untuk dokumen dari previous order pakai rails_blob_path(prev_order.send(document_type), ...) โ ini URL signed time-limited ke blob langsung, tanpa cek secured_document? policy. Siapa saja yang punya URL bisa akses dokumen KYC sensitif tanpa authentikasi. Beda dengan View button current order yang lewat sales_document_preview_page_portal_sales_order_path (controller + policy check).
Fix: pakai portal_secured_document_path(prev_order, document_type: document_type) atau endpoint custom yang verify authorization sebelum serve blob.
File: app/views/shared/_document_approval_list.html.erb
Ketika document_type == 'npwp' dan ada prev_order dengan NPWP, view menampilkan: (1) dokumen PDF dari prev_order โ mungkin punya nama/alamat lama, tapi (2) NPWP name/address inline edit di bawah tetap tampil dari customer.npwp_name dan customer.npwp_address (data current). Jika data NPWP customer sudah berubah sejak order lama, verificator lihat PDF lama tapi inline data berbeda โ bisa approve berdasarkan data yang salah.
Fix: saat dokumen dari prev_order, tampilkan NPWP name/address dari prev_order.customer (snapshot lama), atau sembunyikan inline edit karena dokumen yang dipakai adalah dari order sebelumnya.
File: spec/models/order_spec.rb
Test pakai .where.twice mock tanpa verify actual query count reduce. Cache rusak, test masih hijau.
Fix: pakai ActiveRecord::QueryRecorder untuk count real DB queries.
Ubah query filter dokumen yang sync ke Siebel SFTP โ termasuk skipped/non-approved untuk active orders.
where.not(status: :skipped) include rejected ke Siebel
File: app/services/siebel_sftp_uploader_service.rb
Query lama: where(status: :approved). Query baru: where.not(status: :skipped). Match: pending(0), approved(1), rejected(2). Rejected docs sekarang ikut sync ke Siebel CRM โ itu dokumen yang gagal verifikasi, harusnya tidak pernah masuk ke CRM. Data integrity hancur.
Fix: pakai positive filter where(status: [:approved]) atau eksplisit exclude rejected: where(status: :approved).where.not(status: :skipped).
File: app/services/siebel_sftp_uploader_service.rb + spec
Diff hapus Order.statuses[:delivery] dari array. Tapi spec baru tetap test ekspektasi :delivery orders TIDAK upload. Mismatch antara intent code dan spec โ kemungkinan bug regression atau salah satu salah.
Fix: tentukan dulu apakah :delivery harus sync atau tidak, sinkronkan code & spec.
File: spec/services/siebel_sftp_uploader_service_spec.rb
Test "uploads approved and rejected documents but skips skipped ones" eksplisit ekspektasi rejected_document_approval ter-upload. Spec validasi behavior yang salah dari business perspective.
File: app/services/siebel_sftp_uploader_service.rb
CSV ditulis dulu, mark_documents_as_uploaded baru dipanggil end-of-batch via update_all. Jika service interrupt mid-flow (OOM, SIGTERM, timeout), file di SFTP sudah ada tapi flag upload_siebel_sftp_at belum di-set. Run berikutnya re-upload duplicate.
Fix: mark per-document segera setelah upload sukses, bukan batch end.
File: app/services/siebel_sftp_uploader_service.rb
Jika satu batch gagal semua (Siebel down, SFTP timeout), offset hanya naik failed_count. Kalau ordering query change, possible reprocess same docs. ID-based pagination lebih aman.
Fitur label group MSISDN โ delete, sorting, i18n. Touch dashboard customer + admin + JS controller.
File: app/controllers/customers/dashboards_controller.rb
Pakai update_all langsung tanpa transaction. Kalau gagal di tengah (DB connection drop, server crash), state DB partial โ sebagian MSISDN lost label, sebagian tetap.
Fix: ActiveRecord::Base.transaction { affected.update_all(...) }
File: app/javascript/controllers/msisdn_group_edit_controller.js
await res.json() dipanggil tanpa cek res.ok. Kalau server return 5xx dengan body HTML, parse error โ unhandled promise rejection. User dapat alert kosong/aneh.
File: app/helpers/dashboard_helper.rb
dashboard_i18n_namespace cek has_role?(:customer), tapi controller authorize via has_role?(:user). Branch elsif tidak pernah ke-trigger โ fallback selalu dipake. Effectively dead branch + i18n namespace bisa salah.
File: app/javascript/controllers/msisdn_group_edit_controller.js
Fallback string i18n (Menyimpan..., Gagal menyimpan, dst) hardcoded Indonesian. User English-locale dapat pesan Indonesia kalau data attr missing.
File: config/routes.rb
Customer dashboards punya endpoint :group_labels, :update_activation_group, :bulk_update_activation_group, :delete_group_label. Admin dashboards cuma :index, :usage. Inkonsistensi โ admin tidak bisa pakai fitur ini, padahal mungkin perlu untuk supervise.
File: app/views/shared/dashboards/_msisdn_group_block.html.erb
t("#{ns}.label") dipanggil tapi key 'label' tidak ada di admin/en.yml & customers/en.yml. Akan tampil "translation missing".
File: app/javascript/controllers/msisdn_sort_controller.js
Akses data-sort-msisdn, data-sort-type tanpa null-check. Row tanpa attribute โ undefined comparison โ unstable sort.
Tambah quota fields & dashboard untuk product_type platinum_retail. Touch product model, helper, view admin.
File: app/models/product.rb
Diff update quota_product? include platinum_retail. Tapi method serupa di model lain (mis: show_quota_member_limit?) dan helper masih cek shared_quota? || fixed_quota? manual. Inconsistency: field visibility config vs predicate vs view condition perlu di-grep ulang seluruhnya.
Action: grep -rn "shared_quota? || fixed_quota?" app/ dan pastikan semua sudah pakai quota_product?.
File: app/views/customers/dashboards/index.html.erb + admin
Customer dashboard view masih ternary shared_quota? ? blue : purple, padahal MR ekspektasi platinum_retail = amber badge. Kalau view ini tidak diubah, badge salah.
File: app/views/admin/products/show.html.erb
Condition @product.shared_quota? || @product.fixed_quota? tidak include platinum_retail โ admin form tidak render quota fields untuk platinum_retail. Helper config sudah include, tapi view block-nya.
File: app/views/admin/products/show.html.erb
Cek hanya fixed_quota? untuk per-MSISDN label. platinum_retail tampil label "Pool" yang salah.
File: app/models/product.rb
Refactor scope ke nama baru tapi semua call site di-grep dulu. Kalau ada controller/view yang masih panggil scope lama โ NoMethodError.
Fix manage order search/sort/filter & 500 error sim card. Scope kecil, mostly bug fixes.
File: app/models/order.rb:736
orders.order(sort_column => safe_sort_direction) โ string key & string value. Rails Arel terima keduanya, tapi Lead.rb:186 dan Product.rb:283 pakai .to_sym. Inkonsistensi pattern, bukan bug langsung.
Note: agent original klaim ini HIGH; gue downgrade ke MEDIUM karena Rails accept string key di hash-form .order().
File: spec/models/order_spec.rb:191-203
Test "sorts asc/desc without raising" tidak verify hasil urutan beneran. Kalau sort silently broken, test tetap green. Sort test lain di file sama (line 2601+) sudah proper.
File: app/models/order.rb
Sort column dari user input. Verify scope filter_and_sort punya allowlist column name (bukan whatever user kirim). Diff harus include validasi.
Action manual: cek implementation filter_and_sort punya SORTABLE_COLUMNS = %w[...].freeze + raise jika di luar allowlist.
Penyesuaian verification flow โ fixed quota orders, returning customers, skip document, waive activation fee.
File: app/models/document_approval.rb:111
Cek !order.payment_expired_at? tanpa DB lock. Dua call parallel bisa lewat cek yang sama, dua-duanya advance_order_to_payment!. Akibat: duplicate VerificatorMailer.approval_notification, duplicate push notif, audit log duplikat.
Fix: order.with_lock { ... } atau guard form_stage != :payment_stage.
File: app/models/document_approval.rb:101
Setelah update document_approval status :skipped + purge attachment, code order.reload + cek all_document_approved_and_optional_document_uploaded?. Reload tidak refresh in-memory association โ possibly stale data di method check.
Fix: pakai order.reload(includes: :document_approvals) atau pass updated state eksplisit.
customer&.eligible_for_fast_track? bypass restriction
File: app/controllers/customers/verifications_controller.rb:89
@order.customer&.eligible_for_fast_track? โ kalau customer nil, condition false, redirect tidak terjadi. Returning customer object tanpa linked customer record bisa bypass fast-track restriction.
File: app/models/document_approval.rb:82
order.update! dan approval.update! dua DB call terpisah tanpa transaction. Kalau approval.update gagal validasi, order.waive_activation_fee sudah ke-set false โ state inconsistent.
File: app/models/document_approval.rb:66
Method bisa dipanggil dua kali (Sidekiq retry, network retry, double-click). Tidak ada cek return false if approval.skipped? di awal. Akibat: duplicate VerificatorMailer.optional_document_skipped email.
File: app/models/order.rb:593
all_required_documents_approved? return true untuk returning_customer? tanpa cek requires_mou?. requires_mou? = !returning_customer?, tapi fixed_quota_product? bisa override. Possible gap: fixed quota re-order considered approved tanpa MoU.
File: app/controllers/sales/verifications_controller.rb:38
@order.mou.attached? || !@order.requires_mou? tanpa reload. Customer upload MoU paralel + sales klik verify โ state stale, valid submission ditolak.
File: app/models/document_approval.rb:91
deliver_later tanpa rescue. Mailer config error โ exception bubble ke generic rescue => e di controller. Mailer issues tertelan rescue umum, tidak mudah di-debug.