diff options
| -rw-r--r-- | import/load_merg_stubs.php | 149 | ||||
| -rw-r--r-- | templates/load_merg_stubs.tpl | 47 |
2 files changed, 196 insertions, 0 deletions
diff --git a/import/load_merg_stubs.php b/import/load_merg_stubs.php new file mode 100644 index 0000000..7ed6ab1 --- /dev/null +++ b/import/load_merg_stubs.php @@ -0,0 +1,149 @@ +<?php +/** + * Create stub components for Rapid Online parts not yet in the system. + * + * Reads a MERG group CSV (merg_group_<X>.csv in storage/stock/), finds every row + * with Source='R' and a non-empty order code, checks whether a #SUP xref for that + * Rapid code already exists, and creates a stub component titled RO<code> for any + * that are missing. + * + * Usage: load_merg_stubs.php?group=E + * Append &dry=1 to preview without writing anything. + * + * @package stock + */ + +namespace Bitweaver\Stock; + +require_once '../../kernel/includes/setup_inc.php'; + +global $gBitSystem, $gBitDb; + +$gBitSystem->verifyPackage( 'stock' ); +$gBitSystem->verifyPermission( 'p_stock_admin' ); + +const RAPID_CONTENT_ID = 109; + +$group = preg_replace( '/[^A-Za-z]/', '', $_REQUEST['group'] ?? '' ); +$dryRun = !empty( $_REQUEST['dry'] ); +$doUpdate = !empty( $_REQUEST['update'] ); + +$csvFile = STOCK_IMPORT_PATH . 'merg_group_' . strtoupper( $group ) . '.csv'; + +$created = 0; +$skipped = 0; +$errors = []; +$rows_out = []; + +if( empty( $group ) ) { + $errors[] = 'No group specified — append ?group=A through ?group=E to the URL.'; +} elseif( !file_exists( $csvFile ) ) { + $errors[] = 'CSV not found: ' . $csvFile; +} else { + $handle = fopen( $csvFile, 'r' ); + if( $handle === false ) { + $errors[] = 'Cannot open: ' . $csvFile; + } else { + $rowNum = 0; + while( ( $data = fgetcsv( $handle, 2000, ',', '"', '' ) ) !== false ) { + $rowNum++; + if( $rowNum <= 2 ) { + continue; // title + header rows + } + + $component = trim( $data[0] ?? '' ); + $orderCode = trim( $data[1] ?? '' ); + $source = strtoupper( trim( $data[2] ?? '' ) ); + + // Skip legend rows (empty component), non-Rapid, or no order code + if( empty( $component ) || $source !== 'R' || empty( $orderCode ) ) { + continue; + } + + // Check for existing #SUP xref for this Rapid code + $existingId = $gBitDb->getOne( + "SELECT `content_id` FROM `".BIT_DB_PREFIX."liberty_xref` + WHERE `item` = '#SUP' AND `xref` = ? AND `xkey` = ?", + [ RAPID_CONTENT_ID, $orderCode ] + ); + + if( $existingId ) { + // Update mode — backfill description on existing RO* stubs + if( $doUpdate ) { + $stubTitle = $gBitDb->getOne( + "SELECT `title` FROM `".BIT_DB_PREFIX."liberty_content` WHERE `content_id` = ?", + [ $existingId ] + ); + if( strncmp( (string)$stubTitle, 'RO', 2 ) === 0 ) { + if( !$dryRun ) { + $stockComponent = new StockComponent( (int)$existingId ); + $stockComponent->load(); + $pHash = [ + 'title' => $stubTitle, + 'edit' => $component, + 'format_guid' => 'bithtml', + ]; + $stockComponent->store( $pHash ); + } + $created++; + $rows_out[] = [ 'code' => $orderCode, 'component' => $component, 'status' => $dryRun ? 'would update' : 'updated', 'content_id' => $existingId ]; + } else { + $skipped++; + $rows_out[] = [ 'code' => $orderCode, 'component' => $component, 'status' => 'skip (renamed)', 'content_id' => $existingId ]; + } + } else { + $skipped++; + $rows_out[] = [ 'code' => $orderCode, 'component' => $component, 'status' => 'exists', 'content_id' => $existingId ]; + } + continue; + } + + $stubTitle = 'RO' . $orderCode; + + if( !$dryRun ) { + $stockComponent = new StockComponent(); + $pHash = [ + 'title' => $stubTitle, + 'edit' => $component, + 'format_guid' => 'bithtml', + ]; + if( !$stockComponent->store( $pHash ) ) { + $errors[] = "Row $rowNum ($orderCode): failed to create component."; + $rows_out[] = [ 'code' => $orderCode, 'component' => $component, 'status' => 'error', 'content_id' => null ]; + continue; + } + + $newContentId = $stockComponent->mContentId; + + $gBitDb->associateInsert( BIT_DB_PREFIX.'liberty_xref', [ + 'xref_id' => $gBitDb->GenID( 'liberty_xref_seq' ), + 'content_id' => $newContentId, + 'item' => '#SUP', + 'xorder' => 1, + 'xref' => RAPID_CONTENT_ID, + 'xkey' => substr( $orderCode, 0, 32 ), + 'last_update_date' => $gBitDb->NOW(), + ] ); + + $created++; + $rows_out[] = [ 'code' => $orderCode, 'component' => $component, 'status' => 'created', 'content_id' => $newContentId ]; + } else { + $rows_out[] = [ 'code' => $orderCode, 'component' => $component, 'status' => 'would create', 'content_id' => null ]; + $created++; + } + } + fclose( $handle ); + } +} + +global $gBitSmarty; +$gBitSmarty->assign( 'group', $group ); +$gBitSmarty->assign( 'dryRun', $dryRun ); +$gBitSmarty->assign( 'doUpdate', $doUpdate ); +$gBitSmarty->assign( 'csvFile', $csvFile ); +$gBitSmarty->assign( 'created', $created ); +$gBitSmarty->assign( 'skipped', $skipped ); +$gBitSmarty->assign( 'errors', $errors ); +$gBitSmarty->assign( 'rows', $rows_out ); + +$gBitSystem->display( 'bitpackage:stock/load_merg_stubs.tpl', 'MERG Stub Import — Group ' . strtoupper( $group ) ); diff --git a/templates/load_merg_stubs.tpl b/templates/load_merg_stubs.tpl new file mode 100644 index 0000000..e55c664 --- /dev/null +++ b/templates/load_merg_stubs.tpl @@ -0,0 +1,47 @@ +{strip} +<div class="display stock"> + <div class="header"> + <h1>MERG Stub Import — Group {$group|upper|escape}{if $dryRun} (dry run){/if}</h1> + </div> + <div class="body"> + <p>File: <code>{$csvFile|escape}</code></p> + <p> + <strong>{$created}</strong> {if $doUpdate}{if $dryRun}would be updated{else}updated{/if}{else}{if $dryRun}would be created{else}created{/if}{/if}. + <strong>{$skipped}</strong> {if $doUpdate}already renamed (skipped){else}already exist (skipped){/if}. + </p> + {if $dryRun} + <p><a href="?group={$group|escape:'url'}{if $doUpdate}&update=1{/if}">Run for real (remove &dry=1)</a></p> + {/if} + + {if $errors} + <h3>Errors</h3> + <ul> + {foreach $errors as $msg}<li>{$msg|escape}</li>{/foreach} + </ul> + {/if} + + {if $rows} + <table class="table table-condensed"> + <thead> + <tr> + <th>Order code</th> + <th>Component (from spreadsheet)</th> + <th>Status</th> + <th>content_id</th> + </tr> + </thead> + <tbody> + {foreach $rows as $r} + <tr class="{if $r.status eq 'created' or $r.status eq 'would create'}success{elseif $r.status eq 'error'}danger{/if}"> + <td><code>{$r.code|escape}</code></td> + <td>{$r.component|escape}</td> + <td>{$r.status|escape}</td> + <td>{if $r.content_id}<a href="{$smarty.const.STOCK_PKG_URL}view_component.php?content_id={$r.content_id}">{$r.content_id}</a>{else}—{/if}</td> + </tr> + {/foreach} + </tbody> + </table> + {/if} + </div> +</div> +{/strip} |
