diff options
| author | Lester Caine <lester@lsces.co.uk> | 2026-05-26 14:55:36 +0100 |
|---|---|---|
| committer | Lester Caine <lester@lsces.co.uk> | 2026-05-26 14:55:36 +0100 |
| commit | 70c559432ece25dd3def8c64ddb7c1908b4d49e0 (patch) | |
| tree | 9627e43fbe0d0eb082e4d54fa47773cda77e58f7 /import | |
| parent | 0579f237e179c8815c1c44d9c5cef94ad2a33588 (diff) | |
| download | stock-70c559432ece25dd3def8c64ddb7c1908b4d49e0.tar.gz stock-70c559432ece25dd3def8c64ddb7c1908b4d49e0.tar.bz2 stock-70c559432ece25dd3def8c64ddb7c1908b4d49e0.zip | |
Add xref support, assembly/component views, and import tooling
- add_xref.php, edit_xref.php: xref record add/edit for stock content types
- admin xref group/source pages for stock_assembly and stock_component
- assembly_views/: auto_flow, fixed_grid, position_number, simple_list layouts
- Full assembly and component listing, tree, ordering, and detail pages
- Import tooling (ImportAssembly, ImportComponent, load scripts)
- liberty_plugins for assembly and component data types
- schema_inc.php updated; StockAssembly, StockBase, StockComponent,
StockMovement classes updated with xref group/item/multiple renames
- Templates updated throughout for xref rename (source→item, group→x_group)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'import')
| -rw-r--r-- | import/ImportAssembly.php | 95 | ||||
| -rw-r--r-- | import/ImportComponent.php | 65 | ||||
| -rw-r--r-- | import/load_assemblies.php | 72 | ||||
| -rw-r--r-- | import/load_components.php | 58 |
4 files changed, 290 insertions, 0 deletions
diff --git a/import/ImportAssembly.php b/import/ImportAssembly.php new file mode 100644 index 0000000..44b63f3 --- /dev/null +++ b/import/ImportAssembly.php @@ -0,0 +1,95 @@ +<?php +/** + * Stock assembly / BOM CSV import record loader. + * + * CSV column layout (0-based): + * 0 assembly_title Kit / assembly name + * 1 component_title Component name (must already exist in stock_component) + * 2 quantity_value Numeric quantity in BOM (optional, default 1) + * 3 quantity_item SGL / PCK / SHT / VOL (optional, default SGL) + * 4 item_position Float position (e.g. 1.1, 1.2, 2.1) (optional) + * + * Rows are grouped by assembly_title. The assembly record is created once + * the first time a new title is seen; subsequent rows with the same title + * add further BOM lines to it. + * + * @package stock + */ + +use Bitweaver\Stock\StockAssembly; +use Bitweaver\Liberty\LibertyContent; + +/** + * Process a batch of rows for a single assembly. + * $pRows is an array of raw CSV row arrays, all sharing the same assembly_title. + */ +function StockAssemblyBatchLoad( string $assemblyTitle, array $pRows ): array { + $result = [ 'loaded' => 0, 'skipped' => 0, 'errors' => [] ]; + + // Create or find the assembly + $assembly = new StockAssembly(); + $pHash = [ + 'title' => $assemblyTitle, + 'format_guid' => 'plain', + 'rows_per_page'=> 5, + 'cols_per_page'=> 3, + ]; + if( !$assembly->store( $pHash ) ) { + $result['errors'][] = "Failed to create assembly: $assemblyTitle"; + $result['skipped'] += count( $pRows ); + return $result; + } + + foreach( $pRows as $rowNum => $data ) { + $componentTitle = trim( $data[1] ?? '' ); + if( empty( $componentTitle ) ) { + $result['skipped']++; + $result['errors'][] = "Row $rowNum: empty component title."; + continue; + } + + // Look up component by title + $componentContentId = _stockImportFindComponent( $componentTitle ); + if( !$componentContentId ) { + $result['skipped']++; + $result['errors'][] = "Row $rowNum: component not found — '$componentTitle'"; + continue; + } + + $qtyValue = isset( $data[2] ) && is_numeric( trim($data[2]) ) ? (float)trim( $data[2] ) : 1.0; + $qtySrc = strtoupper( trim( $data[3] ?? 'SGL' ) ); + if( !in_array( $qtySrc, [ 'SGL', 'PCK', 'SHT', 'VOL' ] ) ) { + $qtySrc = 'SGL'; + } + $position = isset( $data[4] ) && is_numeric( trim($data[4]) ) ? (float)trim( $data[4] ) : null; + + // Insert BOM row directly — addItem() handles the assembly_content_id reference + $assembly->addItem( $componentContentId, $position ); + + // Update quantity_value and quantity_item on the map row just inserted + global $gBitDb; + $gBitDb->query( + "UPDATE `".BIT_DB_PREFIX."stock_assembly_component_map` + SET `quantity_value` = ?, `quantity_item` = ? + WHERE `assembly_content_id` = ? AND `item_content_id` = ?", + [ $qtyValue, $qtySrc, $assembly->mContentId, $componentContentId ] + ); + + $result['loaded']++; + } + + return $result; +} + +/** Finds a component's content_id by exact title match. */ +function _stockImportFindComponent( string $title ): ?int { + global $gBitDb; + $contentId = $gBitDb->getOne( + "SELECT fi.`content_id` + FROM `".BIT_DB_PREFIX."stock_component` fi + INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON lc.`content_id` = fi.`content_id` + WHERE lc.`title` = ?", + [ $title ] + ); + return $contentId ? (int)$contentId : null; +} diff --git a/import/ImportComponent.php b/import/ImportComponent.php new file mode 100644 index 0000000..c38091c --- /dev/null +++ b/import/ImportComponent.php @@ -0,0 +1,65 @@ +<?php +/** + * Stock component CSV import record loader. + * + * CSV column layout (0-based): + * 0 title Component name / description + * 1 data Long description (optional) + * 2 part_number Supplier part number → xref #PN (optional) + * 3 quantity_value Numeric stock quantity (optional, default 1) + * 4 quantity_item SGL / PCK / SHT / VOL (optional, default SGL) + * + * @package stock + */ + +use Bitweaver\Stock\StockComponent; +use Bitweaver\Liberty\LibertyContent; + +function StockComponentRecordLoad( array $data ): bool { + $title = trim( $data[0] ?? '' ); + if( empty( $title ) ) { + return false; + } + + $component = new StockComponent(); + + $pHash = [ + 'title' => $title, + 'edit' => trim( $data[1] ?? '' ), + 'format_guid' => 'plain', + ]; + + if( !$component->store( $pHash ) ) { + return false; + } + + // Part number xref (#PN) + $partNumber = trim( $data[2] ?? '' ); + if( $partNumber !== '' ) { + $xrefHash = [ + 'content_id' => $component->mContentId, + 'source' => '#PN', + 'edit' => $partNumber, + 'fAddXref' => 1, + ]; + $component->storeXref( $xrefHash ); + } + + // Quantity xref + $qtyValue = isset( $data[3] ) && is_numeric( trim($data[3]) ) ? (float)trim( $data[3] ) : null; + $qtySrc = strtoupper( trim( $data[4] ?? 'SGL' ) ); + if( !in_array( $qtySrc, [ 'SGL', 'PCK', 'SHT', 'VOL' ] ) ) { + $qtySrc = 'SGL'; + } + if( $qtyValue !== null ) { + $xrefHash = [ + 'content_id' => $component->mContentId, + 'source' => $qtySrc, + 'edit' => $qtyValue, + 'fAddXref' => 1, + ]; + $component->storeXref( $xrefHash ); + } + + return true; +} diff --git a/import/load_assemblies.php b/import/load_assemblies.php new file mode 100644 index 0000000..564dd21 --- /dev/null +++ b/import/load_assemblies.php @@ -0,0 +1,72 @@ +<?php +/** + * Load stock assemblies and their BOM from a CSV file. + * + * Place your CSV at: stock/import/data/assemblies.csv + * + * CSV columns (with header row skipped): + * assembly_title, component_title, quantity_value, quantity_item, item_position + * + * Rows are grouped by assembly_title. Components must already exist + * (run load_components.php first). + * + * @package stock + */ + +namespace Bitweaver\Stock; + +require_once '../../kernel/includes/setup_inc.php'; + +global $gBitSystem, $gBitSmarty, $gBitDb; + +$gBitSystem->verifyPackage( 'stock' ); +$gBitSystem->verifyPermission( 'p_stock_admin' ); + +require_once __DIR__.'/ImportAssembly.php'; + +$csvFile = __DIR__.'/data/assemblies.csv'; +$loaded = 0; +$skipped = 0; +$errors = []; + +if( !file_exists( $csvFile ) ) { + $errors[] = 'CSV file not found: '.$csvFile; +} else { + $handle = fopen( $csvFile, 'r' ); + if( $handle === false ) { + $errors[] = 'Cannot open CSV file.'; + } else { + // Group rows by assembly title + $batches = []; + $rowNum = 0; + while( ( $data = fgetcsv( $handle, 1000, ',' ) ) !== false ) { + $rowNum++; + if( $rowNum === 1 ) { + continue; // skip header + } + $assemblyTitle = trim( $data[0] ?? '' ); + if( empty( $assemblyTitle ) ) { + $skipped++; + $errors[] = "Row $rowNum: empty assembly title, skipped."; + continue; + } + $batches[$assemblyTitle][] = array_merge( [ $rowNum ], $data ); + } + fclose( $handle ); + + // Process each assembly batch + foreach( $batches as $assemblyTitle => $rows ) { + $result = StockAssemblyBatchLoad( $assemblyTitle, $rows ); + $loaded += $result['loaded']; + $skipped += $result['skipped']; + $errors = array_merge( $errors, $result['errors'] ); + } + } +} + +$gBitSmarty->assign( 'loaded', $loaded ); +$gBitSmarty->assign( 'skipped', $skipped ); +$gBitSmarty->assign( 'errors', $errors ); +$gBitSmarty->assign( 'csvFile', $csvFile ); + +$gBitSystem->display( 'bitpackage:stock/import_results.tpl', 'Import Assemblies' ); diff --git a/import/load_components.php b/import/load_components.php new file mode 100644 index 0000000..254af81 --- /dev/null +++ b/import/load_components.php @@ -0,0 +1,58 @@ +<?php +/** + * Load stock components from a CSV file. + * + * Place your CSV at: stock/import/data/components.csv + * + * CSV columns (with header row skipped): + * title, description, part_number, quantity_value, quantity_item + * + * @package stock + */ + +namespace Bitweaver\Stock; + +require_once '../../kernel/includes/setup_inc.php'; + +global $gBitSystem, $gBitSmarty; + +$gBitSystem->verifyPackage( 'stock' ); +$gBitSystem->verifyPermission( 'p_stock_admin' ); + +require_once __DIR__.'/ImportComponent.php'; + +$csvFile = __DIR__.'/data/components.csv'; +$loaded = 0; +$skipped = 0; +$errors = []; + +if( !file_exists( $csvFile ) ) { + $errors[] = 'CSV file not found: '.$csvFile; +} else { + $handle = fopen( $csvFile, 'r' ); + if( $handle === false ) { + $errors[] = 'Cannot open CSV file.'; + } else { + $row = 0; + while( ( $data = fgetcsv( $handle, 1000, ',' ) ) !== false ) { + $row++; + if( $row === 1 ) { + continue; // skip header + } + if( StockComponentRecordLoad( $data ) ) { + $loaded++; + } else { + $skipped++; + $errors[] = "Row $row skipped: ".( trim( $data[0] ?? '' ) ?: '(empty title)' ); + } + } + fclose( $handle ); + } +} + +$gBitSmarty->assign( 'loaded', $loaded ); +$gBitSmarty->assign( 'skipped', $skipped ); +$gBitSmarty->assign( 'errors', $errors ); +$gBitSmarty->assign( 'csvFile', $csvFile ); + +$gBitSystem->display( 'bitpackage:stock/import_results.tpl', 'Import Components' ); |
