db->table_exists('goods_receipts') || !$this->db->table_exists('goods_receipt_lines')) { return; } $this->load->library('PurchaseService'); $receipts = $this->db ->select('DISTINCT gr.id, gr.received_by', false) ->from('goods_receipts gr') ->join('goods_receipt_lines grl', 'grl.goods_receipt_id=gr.id') ->join('item_barcodes ib', 'ib.source_receipt_line_id=grl.id', 'left') ->where('gr.status', 'posted') ->where('grl.item_id IS NOT NULL', null, false) ->where('ib.id IS NULL', null, false) ->get()->result(); foreach ($receipts as $receipt) { // Seluruh method bersifat idempotent: hanya bagian tracking/ledger // yang belum terbentuk yang dilengkapi. try { $this->purchaseservice->finalizeReceiptTracking((int) $receipt->id); $this->purchaseservice->syncReceiptLedger((int) $receipt->id); $this->purchaseservice->clearTransitForReceipt( (int) $receipt->id, (int) $receipt->received_by ); } catch (Throwable $error) { // Histori lama yang belum memiliki konfigurasi tracking lengkap // tidak boleh menggagalkan pemulihan penerimaan baru lainnya. log_message('error', 'Pemulihan tracking penerimaan #'. $receipt->id.' dilewati: '.$error->getMessage()); } } } public function down() { // Data hasil pemulihan merupakan audit trail transaksi dan tidak dihapus. } }