Files
orderPRO/bin/backfill_payment_transition_111.php
Jacek Pyziak 5cf531d718 feat(111): payment transition event for Allegro+shopPRO re-import
Re-import zamowienia wykrywa tranzycje payment_status 0/1->2 i emituje
payment.status_changed, dzieki czemu chain reguly automatyzacji #7 zmienia
status na w_realizacji.

- OrderImportRepository: rozdzielenie paymentTransition (event 0/1->2) i
  statusOverwriteAllowed (preservacja status_code z Phase 62)
- AllegroOrderImportService + ShopproOrdersSyncService: emit
  payment.status_changed na re-imporcie (gate !wasCreated && wasPaymentTransition)
- bin/backfill_payment_transition_111.php: jednorazowy CLI dla zamowien
  payment_status=2 && status_code='nieoplacone' (Allegro + shopPRO)
- Naprawa luki znalezionej w analizie zamowienia #864

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:35:14 +02:00

107 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
/**
* Backfill (Phase 111): zamowienia z payment_status=2 i status_code='nieoplacone'
* (source=allegro lub shoppro) zostaja przestawione na status 'w_realizacji'.
* Zmiana idzie przez OrdersRepository::updateOrderStatus, dzieki czemu powstaje
* wpis w order_status_history i order_activity_log.
*
* Naprawia luke z Phase 98 (gate $wasCreated dla order.imported) — zamowienia
* zaimportowane przed potwierdzeniem platnosci, ktore utknely w 'nieoplacone'
* mimo opłacenia.
*
* Idempotentny: drugie uruchomienie na czystej bazie nie zrobi nic.
*
* Flagi:
* --dry-run pokaz tylko liste kandydatow, bez zmian
* --use-remote uzyj DB_HOST_REMOTE zamiast DB_HOST (dla operacji recznych agenta)
*/
use App\Core\Database\ConnectionFactory;
use App\Core\Support\Env;
use App\Modules\Orders\OrdersRepository;
$basePath = dirname(__DIR__);
require $basePath . '/vendor/autoload.php';
Env::load($basePath . '/.env');
/** @var array<string, mixed> $dbConfig */
$dbConfig = require $basePath . '/config/database.php';
$dryRun = in_array('--dry-run', $argv, true);
$useRemote = in_array('--use-remote', $argv, true);
if ($useRemote) {
$remoteHost = (string) Env::get('DB_HOST_REMOTE', '');
if ($remoteHost !== '') {
$dbConfig['host'] = $remoteHost;
echo '[db] using DB_HOST_REMOTE for this run' . PHP_EOL;
}
}
$pdo = ConnectionFactory::make($dbConfig);
echo 'Backfill 111: orders nieoplacone + payment_status=2 -> w_realizacji' . PHP_EOL;
echo $dryRun ? '[mode] dry-run' . PHP_EOL : '[mode] apply' . PHP_EOL;
const TARGET_CODE = 'w_realizacji';
$repository = new OrdersRepository($pdo);
$sql = "SELECT id, internal_order_number, source, integration_id "
. "FROM orders "
. "WHERE source IN ('allegro', 'shoppro') "
. "AND payment_status = 2 "
. "AND LOWER(COALESCE(status_code, '')) = 'nieoplacone' "
. "ORDER BY id ASC";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
$total = count($rows);
$updated = 0;
$skipped = 0;
echo '[scan] candidates: ' . $total . PHP_EOL;
foreach ($rows as $row) {
$orderId = (int) $row['id'];
$internalNumber = (string) ($row['internal_order_number'] ?? '');
$source = (string) ($row['source'] ?? '');
$integrationId = (int) ($row['integration_id'] ?? 0);
$label = sprintf('#%d %s (source=%s, integration=%d)', $orderId, $internalNumber, $source, $integrationId);
if ($dryRun) {
echo ' [dry-run] ' . $label . ' -> ' . TARGET_CODE . PHP_EOL;
continue;
}
try {
$ok = $repository->updateOrderStatus(
$orderId,
TARGET_CODE,
'import',
'Backfill 111'
);
if ($ok) {
$updated++;
echo ' [ok] ' . $label . ' -> ' . TARGET_CODE . PHP_EOL;
} else {
$skipped++;
echo ' [skip] ' . $label . ' (updateOrderStatus returned false)' . PHP_EOL;
}
} catch (Throwable $exception) {
$skipped++;
fwrite(STDERR, ' [err] ' . $label . ': ' . $exception->getMessage() . PHP_EOL);
}
}
echo PHP_EOL;
echo 'Backfill 111: total=' . $total . ' updated=' . $updated . ' skipped=' . $skipped . PHP_EOL;
echo 'Done.' . PHP_EOL;