| @@ -467,23 +467,33 @@ | ||
| 467 | 467 | return $inserted; |
| 468 | 468 | } |
| 469 | 469 | |
| 470 | 470 | /** |
| 471 | - * Insert availability rules | |
| 471 | + * Insert availability rules. | |
| 472 | + * | |
| 473 | + * Sample JSON only knows the legacy columns (`recurrence_type`, | |
| 474 | + * `capacity_value`, `interval`, `day_of_month`). The new admin React UI | |
| 475 | + * binds to a parallel set (`rule_type`, `seats_total`, `interval_days`, | |
| 476 | + * `interval_start_date`). We populate both so a freshly imported sample | |
| 477 | + * dataset is immediately editable in the new UI without needing the | |
| 478 | + * idempotent {@see InstallerService::maybeNormalizeAvailabilityRulesLegacyData()} | |
| 479 | + * heal-step to fix it on next admin_init. | |
| 472 | 480 | */ |
| 473 | 481 | public function insert_availability_rules($data, $trip_ids) |
| 474 | 482 | { |
| 475 | 483 | $inserted = 0; |
| 476 | - | |
| 484 | + | |
| 477 | 485 | foreach ($data as $item) { |
| 478 | 486 | $trip_slug = $item['trip_slug']; |
| 479 | 487 | unset($item['trip_slug']); |
| 480 | - | |
| 488 | + | |
| 481 | 489 | if (!isset($trip_ids[$trip_slug])) { |
| 482 | 490 | continue; |
| 483 | 491 | } |
| 484 | 492 | $item['trip_id'] = $trip_ids[$trip_slug]; |
| 485 | - | |
| 493 | + | |
| 494 | + $item = $this->mapLegacyAvailabilityRuleToNewSchema($item); | |
| 495 | + | |
| 486 | 496 | // Convert arrays to JSON |
| 487 | 497 | if (isset($item['days_of_week']) && is_array($item['days_of_week'])) { |
| 488 | 498 | $item['days_of_week'] = json_encode($item['days_of_week']); |
| 489 | 499 | } |
| @@ -489,16 +499,80 @@ | ||
| 489 | 499 | } |
| 490 | 500 | if (isset($item['recurrence_pattern']) && is_array($item['recurrence_pattern'])) { |
| 491 | 501 | $item['recurrence_pattern'] = json_encode($item['recurrence_pattern']); |
| 492 | 502 | } |
| 493 | - | |
| 503 | + | |
| 494 | 504 | $result = $this->wpdb->insert($this->availability_rules_table, $item); |
| 495 | 505 | if ($result) { |
| 496 | 506 | $inserted++; |
| 497 | 507 | } |
| 498 | 508 | } |
| 499 | - | |
| 509 | + | |
| 500 | 510 | return $inserted; |
| 511 | + } | |
| 512 | + | |
| 513 | + /** | |
| 514 | + * Map a sample-data row written against the legacy availability-rule | |
| 515 | + * schema onto the new-schema columns the admin UI reads. | |
| 516 | + * | |
| 517 | + * Mirrors the heal logic in | |
| 518 | + * {@see InstallerService::maybeNormalizeAvailabilityRulesLegacyData()} | |
| 519 | + * so write-time and read-time-repair stay in lock-step. | |
| 520 | + * | |
| 521 | + * @param array<string, mixed> $item | |
| 522 | + * @return array<string, mixed> | |
| 523 | + */ | |
| 524 | + private function mapLegacyAvailabilityRuleToNewSchema(array $item): array | |
| 525 | + { | |
| 526 | + $recurrence = isset($item['recurrence_type']) ? (string) $item['recurrence_type'] : 'weekly'; | |
| 527 | + $intervalRaw = isset($item['interval']) ? (int) $item['interval'] : 1; | |
| 528 | + $intervalNorm = $intervalRaw > 0 ? $intervalRaw : 1; | |
| 529 | + | |
| 530 | + if (!isset($item['rule_type']) || $item['rule_type'] === '' || $item['rule_type'] === null) { | |
| 531 | + switch ($recurrence) { | |
| 532 | + case 'daily': | |
| 533 | + $item['rule_type'] = 'interval'; | |
| 534 | + if (!isset($item['interval_days'])) { | |
| 535 | + $item['interval_days'] = $intervalNorm; | |
| 536 | + } | |
| 537 | + if (!isset($item['interval_start_date']) && !empty($item['start_date'])) { | |
| 538 | + $item['interval_start_date'] = $item['start_date']; | |
| 539 | + } | |
| 540 | + break; | |
| 541 | + case 'monthly': | |
| 542 | + case 'yearly': | |
| 543 | + $item['rule_type'] = 'monthly'; | |
| 544 | + break; | |
| 545 | + case 'custom': | |
| 546 | + $item['rule_type'] = 'interval'; | |
| 547 | + if (!isset($item['interval_days'])) { | |
| 548 | + $item['interval_days'] = $intervalNorm; | |
| 549 | + } | |
| 550 | + if (!isset($item['interval_start_date']) && !empty($item['start_date'])) { | |
| 551 | + $item['interval_start_date'] = $item['start_date']; | |
| 552 | + } | |
| 553 | + break; | |
| 554 | + case 'weekly': | |
| 555 | + default: | |
| 556 | + $item['rule_type'] = 'weekly'; | |
| 557 | + break; | |
| 558 | + } | |
| 559 | + } | |
| 560 | + | |
| 561 | + // seats_total mirrors capacity_value when capacity is fixed (the | |
| 562 | + // sample dataset doesn't model percentage capacity). CapacityService | |
| 563 | + // and the React table read seats_total directly. | |
| 564 | + $capacityType = $item['capacity_type'] ?? 'fixed'; | |
| 565 | + if ( | |
| 566 | + !isset($item['seats_total']) | |
| 567 | + && isset($item['capacity_value']) | |
| 568 | + && (int) $item['capacity_value'] > 0 | |
| 569 | + && $capacityType === 'fixed' | |
| 570 | + ) { | |
| 571 | + $item['seats_total'] = (int) $item['capacity_value']; | |
| 572 | + } | |
| 573 | + | |
| 574 | + return $item; | |
| 501 | 575 | } |
| 502 | 576 | |
| 503 | 577 | /** |
| 504 | 578 | * Insert itinerary days. Returns composite key => day_id mapping. |