summaryrefslogtreecommitdiff
path: root/import
diff options
context:
space:
mode:
authorLester Caine <lester@lsces.co.uk>2026-05-26 14:55:36 +0100
committerLester Caine <lester@lsces.co.uk>2026-05-26 14:55:36 +0100
commit70c559432ece25dd3def8c64ddb7c1908b4d49e0 (patch)
tree9627e43fbe0d0eb082e4d54fa47773cda77e58f7 /import
parent0579f237e179c8815c1c44d9c5cef94ad2a33588 (diff)
downloadstock-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.php95
-rw-r--r--import/ImportComponent.php65
-rw-r--r--import/load_assemblies.php72
-rw-r--r--import/load_components.php58
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' );