summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLester Caine <lester@lsces.co.uk>2026-06-14 10:49:24 +0100
committerLester Caine <lester@lsces.co.uk>2026-06-14 10:49:24 +0100
commite8aa652c4f0d2f780171d7bbd2c351c05a1025e9 (patch)
tree2ee249d771ee88e7d3d1cc209a3048fd37161b56
parent3866f6c02cfae647d04e9123392b608e0d0aa3a6 (diff)
downloadstock-e8aa652c4f0d2f780171d7bbd2c351c05a1025e9.tar.gz
stock-e8aa652c4f0d2f780171d7bbd2c351c05a1025e9.tar.bz2
stock-e8aa652c4f0d2f780171d7bbd2c351c05a1025e9.zip
Add multi-user (kitelf) stock filtering and PBLD prebuild movement type
- list_movements/list_stock: filter by user_id (kitelf) with breadcrumb navigation; creator names in list_movements are clickable filter links - list_movements: unified part_content_id replaces separate assembly_content_id/component_content_id URL params; type-aware breadcrumb and qty column (assembly kit count vs component qty) - StockMovement::getList(): $partId/$partIsAsm collapse cmp/asm into one variable; unified part_qty/part_qty_type SELECT; PBLD added to all ref_type IN() lists and sort subqueries; lc.user_id added to SELECT - New PBLD (Prebuild) movement type: add_prebuild.php/tpl creates PBLD movements (assemblies only, BOM exploded, optional note); add_requisition retired from UI; PBLD handled in edit/view with isBuild/isPbld flags; view/edit show Build Date/Completed labels for PBLD - schema_inc.php: PBLD registered in stockmovement reference xref items - view_movement.tpl: updated to <header>/<section> pattern with kitelf breadcrumb; getDirection() explicit for PBLD Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--add_prebuild.php112
-rwxr-xr-xadmin/schema_inc.php3
-rw-r--r--edit_movement.php13
-rw-r--r--includes/classes/StockMovement.php68
-rw-r--r--list_movements.php72
-rw-r--r--list_stock.php32
-rw-r--r--templates/add_prebuild.tpl119
-rw-r--r--templates/edit_movement.tpl10
-rw-r--r--templates/list_movements.tpl31
-rw-r--r--templates/list_stock.tpl11
-rwxr-xr-xtemplates/menu_stock.tpl2
-rwxr-xr-xtemplates/stock_simple_list_inc.tpl2
-rwxr-xr-xtemplates/view_component.tpl2
-rw-r--r--templates/view_movement.tpl22
-rw-r--r--view_movement.php5
15 files changed, 412 insertions, 92 deletions
diff --git a/add_prebuild.php b/add_prebuild.php
new file mode 100644
index 0000000..2c0e9fe
--- /dev/null
+++ b/add_prebuild.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * @package stock
+ * @subpackage functions
+ */
+
+namespace Bitweaver\Stock;
+
+use Bitweaver\KernelTools;
+
+require_once '../kernel/includes/setup_inc.php';
+
+global $gBitSystem, $gBitSmarty, $gBitUser, $gBitDb;
+
+$gBitSystem->verifyPermission( 'p_stock_create' );
+
+if( !empty( $_REQUEST['fCancel'] ) ) {
+ header( 'Location: '.STOCK_PKG_URL.'list_movements.php' );
+ die;
+}
+
+if( !empty( $_REQUEST['fCreate'] ) ) {
+ $targetContentId = isset( $_REQUEST['assembly_content_id'] ) && is_numeric( $_REQUEST['assembly_content_id'] )
+ ? (int)$_REQUEST['assembly_content_id'] : null;
+ $kitCount = isset( $_REQUEST['kit_count'] ) && is_numeric( $_REQUEST['kit_count'] ) && (float)$_REQUEST['kit_count'] > 0
+ ? (float)$_REQUEST['kit_count'] : 1;
+ $title = trim( $_REQUEST['title'] ?? '' );
+ $rqRef = trim( $_REQUEST['rq_ref'] ?? '' );
+
+ if( !$targetContentId ) {
+ $errors[] = KernelTools::tra( 'Please select an assembly.' );
+ } elseif( $title === '' ) {
+ $errors[] = KernelTools::tra( 'Please enter a build reference.' );
+ } else {
+ $targetRow = $gBitDb->getRow(
+ "SELECT `title` FROM `".BIT_DB_PREFIX."liberty_content`
+ WHERE `content_id` = ? AND `content_type_guid` = 'stockassembly'",
+ [ $targetContentId ]
+ );
+ if( !$targetRow ) {
+ $errors[] = KernelTools::tra( 'Assembly not found.' );
+ } else {
+ $movement = new StockMovement();
+ $paramHash = [
+ 'title' => $title,
+ 'content_type_guid' => STOCKMOVEMENT_CONTENT_TYPE_GUID,
+ 'edit' => $rqRef,
+ ];
+ if( $movement->store( $paramHash ) ) {
+ $pbldHash = [
+ 'content_id' => $movement->mContentId,
+ 'item' => 'PBLD',
+ 'xkey' => $title,
+ 'fAddXref' => 1,
+ ];
+ $movement->storeXref( $pbldHash );
+ $assemblyHash = [
+ 'content_id' => $movement->mContentId,
+ 'item' => 'ASSEMBLY',
+ 'xref' => $targetContentId,
+ 'xkey' => (string)$kitCount,
+ 'xkey_ext' => $targetRow['title'],
+ 'edit' => 'stockassembly',
+ 'fAddXref' => 1,
+ ];
+ $movement->storeXref( $assemblyHash );
+ $movement->explodeFromAssembly( $targetContentId, $kitCount );
+ header( 'Location: '.STOCK_PKG_URL.'edit_movement.php?content_id='.$movement->mContentId );
+ die;
+ }
+ $errors = $movement->mErrors;
+ }
+ }
+}
+
+$assembly = new StockAssembly();
+$asmHash = [ 'show_empty' => true, 'sort_mode' => 'title_asc', 'max_records' => 1000 ];
+$assemblyList = $assembly->getList( $asmHash );
+
+$preselect = isset( $_REQUEST['assembly_content_id'] ) && is_numeric( $_REQUEST['assembly_content_id'] )
+ ? (int)$_REQUEST['assembly_content_id'] : null;
+$kitCount = isset( $_REQUEST['kit_count'] ) && is_numeric( $_REQUEST['kit_count'] )
+ ? (float)$_REQUEST['kit_count'] : 1;
+
+$itemIds = array_column( $assemblyList, 'content_id' );
+$klidMap = [];
+if( $itemIds ) {
+ $klidRows = $gBitDb->getAll(
+ "SELECT x.`content_id`, x.`xkey` FROM `".BIT_DB_PREFIX."liberty_xref` x
+ WHERE x.`item` = 'KLID' AND x.`content_id` IN (".implode( ',', array_fill( 0, count( $itemIds ), '?' ) ).")",
+ $itemIds
+ );
+ foreach( $klidRows as $r ) { $klidMap[$r['content_id']] = $r['xkey']; }
+}
+$itemListJson = json_encode( array_map(
+ fn( $i ) => [ 'id' => (int)$i['content_id'], 'text' => $i['title'], 'klid' => $klidMap[$i['content_id']] ?? '' ],
+ $assemblyList
+) );
+$preselectTitle = '';
+if( $preselect ) {
+ foreach( $assemblyList as $item ) {
+ if( (int)$item['content_id'] === $preselect ) { $preselectTitle = $item['title']; break; }
+ }
+}
+
+$gBitSmarty->assign( 'itemListJson', $itemListJson );
+$gBitSmarty->assign( 'preselect', $preselect );
+$gBitSmarty->assign( 'preselectTitle', $preselectTitle );
+$gBitSmarty->assign( 'kitCount', $kitCount );
+$gBitSmarty->assign( 'errors', $errors ?? [] );
+
+$gBitSystem->display( 'bitpackage:stock/add_prebuild.tpl', KernelTools::tra( 'Create Prebuild' ), [ 'display_mode' => 'edit' ] );
diff --git a/admin/schema_inc.php b/admin/schema_inc.php
index edf8463..e9ab919 100755
--- a/admin/schema_inc.php
+++ b/admin/schema_inc.php
@@ -163,8 +163,9 @@ $xrefTypes[] = "INSERT INTO `{$X}liberty_xref_group` (`x_group`,`content_type_gu
// assembly item — links the movement to its assembly or component (multiple=1 for future multi-item reqns)
$xrefItems[] = "INSERT INTO `{$X}liberty_xref_item` (`item`,`content_type_guid`,`x_group`,`cross_ref_title`,`multiple`,`role_id`,`cross_ref_href`,`template`,`data`) VALUES ('ASSEMBLY','stockmovement','assembly','Assembly',1,3,'','assembly',NULL)";
-// reference items — REQN=out, TRANS=in from elf, ORDER=in from supplier
+// reference items — REQN=out to kitlocker, PBLD=out prebuild, TRANS=in from elf, ORDER=in from supplier
$xrefItems[] = "INSERT INTO `{$X}liberty_xref_item` (`item`,`content_type_guid`,`x_group`,`cross_ref_title`,`multiple`,`role_id`,`cross_ref_href`,`template`,`data`) VALUES ('REQN', 'stockmovement','reference','Requisition',1,3,'','text',NULL)";
+$xrefItems[] = "INSERT INTO `{$X}liberty_xref_item` (`item`,`content_type_guid`,`x_group`,`cross_ref_title`,`multiple`,`role_id`,`cross_ref_href`,`template`,`data`) VALUES ('PBLD','stockmovement','reference','Prebuild', 1,3,'','text',NULL)";
$xrefItems[] = "INSERT INTO `{$X}liberty_xref_item` (`item`,`content_type_guid`,`x_group`,`cross_ref_title`,`multiple`,`role_id`,`cross_ref_href`,`template`,`data`) VALUES ('TRANS','stockmovement','reference','Transfer', 1,3,'','text',NULL)";
$xrefItems[] = "INSERT INTO `{$X}liberty_xref_item` (`item`,`content_type_guid`,`x_group`,`cross_ref_title`,`multiple`,`role_id`,`cross_ref_href`,`template`,`data`) VALUES ('ORDER','stockmovement','reference','Order', 1,3,'','text',NULL)";
diff --git a/edit_movement.php b/edit_movement.php
index 1167efd..defc9f4 100644
--- a/edit_movement.php
+++ b/edit_movement.php
@@ -47,7 +47,7 @@ if( !empty( $_REQUEST['fSave'] ) ) {
if( !empty( $_REQUEST['movement_type'] ) && isset( $refTypes[$_REQUEST['movement_type']] ) ) {
$existingRef = $gBitDb->getRow(
"SELECT `xref_id` FROM `".BIT_DB_PREFIX."liberty_xref`
- WHERE `content_id`=? AND `item` IN ('REQN','TRANS','ORDER') ORDER BY `xorder`",
+ WHERE `content_id`=? AND `item` IN ('REQN','TRANS','ORDER','PBLD') ORDER BY `xorder`",
[ $gContent->mContentId ]
);
$refHash = [
@@ -66,7 +66,7 @@ if( !empty( $_REQUEST['fSave'] ) ) {
if( !empty( $_REQUEST['ordered_date'] ) && ($ts = parseMovementDate( $_REQUEST['ordered_date'] )) ) {
$gBitDb->query(
"UPDATE `".BIT_DB_PREFIX."liberty_xref` SET `start_date`=?
- WHERE `content_id`=? AND `item` IN ('REQN','TRANS','ORDER')",
+ WHERE `content_id`=? AND `item` IN ('REQN','TRANS','ORDER','PBLD')",
[ date( 'Y-m-d H:i:s', $ts ), $gContent->mContentId ]
);
}
@@ -187,8 +187,11 @@ $gBitSmarty->assign( 'orderedDateVal', $orderedDateVal );
$gBitSmarty->assign( 'receivedDateVal', $receivedDateVal );
$gBitSmarty->assign( 'contactLookupUrl', CONTACT_PKG_URL.'includes/lookup_contact.php' );
-$isReqn = ( ( $gContent->mInfo['ref_type'] ?? '' ) === 'REQN' );
-if( $isReqn ) {
+$refType = $gContent->mInfo['ref_type'] ?? '';
+$isReqn = $refType === 'REQN';
+$isPbld = $refType === 'PBLD';
+$isBuild = in_array( $refType, [ 'REQN', 'PBLD' ] );
+if( $isBuild ) {
$assembly = new StockAssembly();
$asmHash = [ 'show_empty' => true, 'sort_mode' => 'title_asc', 'max_records' => 1000 ];
$assemblyList = $assembly->getList( $asmHash );
@@ -212,6 +215,8 @@ if( $isReqn ) {
) );
}
$gBitSmarty->assign( 'isReqn', $isReqn );
+$gBitSmarty->assign( 'isPbld', $isPbld );
+$gBitSmarty->assign( 'isBuild', $isBuild );
$gBitSmarty->assign( 'refTypes', $refTypes );
$gBitSmarty->assign( 'errors', $gContent->mErrors );
diff --git a/includes/classes/StockMovement.php b/includes/classes/StockMovement.php
index 57e8146..178944d 100644
--- a/includes/classes/StockMovement.php
+++ b/includes/classes/StockMovement.php
@@ -93,24 +93,24 @@ class StockMovement extends LibertyContent {
, uue.`login` AS `modifier_user`, uue.`real_name` AS `modifier_real_name`
, uuc.`login` AS `creator_user`, uuc.`real_name` AS `creator_real_name`
, (SELECT FIRST 1 x.`start_date` FROM `{$X}liberty_xref` x
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER')
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER','PBLD')
ORDER BY x.`xorder`) AS ref_start_date
, (SELECT FIRST 1 xi.`cross_ref_title` FROM `{$X}liberty_xref` x
JOIN `{$X}liberty_xref_item` xi ON xi.`item` = x.`item` AND xi.`content_type_guid` = 'stockmovement'
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER')
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER','PBLD')
ORDER BY x.`xorder`) AS ref_type_title
, (SELECT FIRST 1 x.`xref` FROM `{$X}liberty_xref` x
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER')
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER','PBLD')
ORDER BY x.`xorder`) AS ref_contact_id
, (SELECT FIRST 1 lc2.`title` FROM `{$X}liberty_xref` x
JOIN `{$X}liberty_content` lc2 ON lc2.`content_id` = x.`xref`
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER')
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER','PBLD')
ORDER BY x.`xorder`) AS ref_contact_name
, (SELECT FIRST 1 x.`item` FROM `{$X}liberty_xref` x
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER')
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER','PBLD')
ORDER BY x.`xorder`) AS ref_type
, (SELECT FIRST 1 x.`data` FROM `{$X}liberty_xref` x
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER')
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER','PBLD')
ORDER BY x.`xorder`) AS ref_from_data
FROM `".BIT_DB_PREFIX."liberty_content` lc
LEFT JOIN `".BIT_DB_PREFIX."users_users` uue ON uue.`user_id` = lc.`modifier_user_id`
@@ -208,9 +208,9 @@ class StockMovement extends LibertyContent {
*/
public function getDirection(): string {
$refType = $this->mInfo['ref_type'] ?? null;
- if( $refType === 'REQN' ) return 'O';
+ if( in_array( $refType, [ 'REQN', 'PBLD' ] ) ) return 'O';
if( in_array( $refType, [ 'TRANS', 'ORDER' ] ) ) return 'I';
- return 'O';
+ return '';
}
/** @return bool TRUE when lc.event_time is non-zero (movement has been received). */
@@ -301,19 +301,22 @@ class StockMovement extends LibertyContent {
$whereSql = " AND lc.`content_type_guid` = '".STOCKMOVEMENT_CONTENT_TYPE_GUID."'";
- if( !empty( $pListHash['ref_type'] ) && in_array( $pListHash['ref_type'], [ 'REQN', 'TRANS', 'ORDER' ] ) ) {
+ if( !empty( $pListHash['ref_type'] ) && in_array( $pListHash['ref_type'], [ 'REQN', 'TRANS', 'ORDER', 'PBLD' ] ) ) {
$joinSql .= " INNER JOIN `".BIT_DB_PREFIX."liberty_xref` xrf ON xrf.`content_id` = lc.`content_id` AND xrf.`item` = ?";
$bindVars[] = $pListHash['ref_type'];
}
+ $partId = 0;
+ $partIsAsm = false;
if( $this->verifyId( $pListHash['assembly_content_id'] ?? 0 ) ) {
+ $partId = (int)$pListHash['assembly_content_id'];
+ $partIsAsm = true;
$joinSql .= " INNER JOIN `".BIT_DB_PREFIX."liberty_xref` xasm ON xasm.`content_id` = lc.`content_id` AND xasm.`item` = 'ASSEMBLY' AND xasm.`xref` = ?";
- $bindVars[] = (int)$pListHash['assembly_content_id'];
- }
- $cmpContentId = $this->verifyId( $pListHash['component_content_id'] ?? 0 ) ? (int)$pListHash['component_content_id'] : 0;
- if( $cmpContentId ) {
+ $bindVars[] = $partId;
+ } elseif( $this->verifyId( $pListHash['component_content_id'] ?? 0 ) ) {
+ $partId = (int)$pListHash['component_content_id'];
$whereSql .= " AND EXISTS (SELECT 1 FROM `".BIT_DB_PREFIX."liberty_xref` xcf
- WHERE xcf.`content_id` = lc.`content_id` AND xcf.`item` IN ('SGL','PRT','SHT','VOL') AND xcf.`xref` = $cmpContentId)";
+ WHERE xcf.`content_id` = lc.`content_id` AND xcf.`item` IN ('SGL','PRT','SHT','VOL') AND xcf.`xref` = $partId)";
}
if( $this->verifyId( $pListHash['user_id'] ?? 0 ) ) {
$whereSql .= " AND lc.`user_id` = ?";
@@ -330,8 +333,8 @@ class StockMovement extends LibertyContent {
$orderby = match( $sortMode ) {
'event_time_asc' => ' ORDER BY lc.event_time ASC',
'event_time_desc' => ' ORDER BY lc.event_time DESC',
- 'ref_start_date_asc' => ' ORDER BY (SELECT FIRST 1 x.start_date FROM '.BIT_DB_PREFIX.'liberty_xref x WHERE x.content_id=lc.content_id AND x.item IN (\'REQN\',\'TRANS\',\'ORDER\') ORDER BY x.xorder) ASC',
- 'ref_start_date_desc' => ' ORDER BY (SELECT FIRST 1 x.start_date FROM '.BIT_DB_PREFIX.'liberty_xref x WHERE x.content_id=lc.content_id AND x.item IN (\'REQN\',\'TRANS\',\'ORDER\') ORDER BY x.xorder) DESC',
+ 'ref_start_date_asc' => ' ORDER BY (SELECT FIRST 1 x.start_date FROM '.BIT_DB_PREFIX.'liberty_xref x WHERE x.content_id=lc.content_id AND x.item IN (\'REQN\',\'TRANS\',\'ORDER\',\'PBLD\') ORDER BY x.xorder) ASC',
+ 'ref_start_date_desc' => ' ORDER BY (SELECT FIRST 1 x.start_date FROM '.BIT_DB_PREFIX.'liberty_xref x WHERE x.content_id=lc.content_id AND x.item IN (\'REQN\',\'TRANS\',\'ORDER\',\'PBLD\') ORDER BY x.xorder) DESC',
default => !empty( $sortMode )
? ' ORDER BY '.$this->mDb->convertSortmode( $sortMode )
: ' ORDER BY lc.last_modified DESC',
@@ -351,26 +354,31 @@ class StockMovement extends LibertyContent {
);
$X = BIT_DB_PREFIX;
- $cmpQtySelect = $cmpContentId
- ? ", (SELECT FIRST 1 x.`item` FROM `{$X}liberty_xref` x
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('SGL','PRT','SHT','VOL') AND x.`xref` = $cmpContentId
- ORDER BY x.`xorder`) AS cmp_qty_type,
- (SELECT SUM(CAST(x.`xkey` AS DOUBLE PRECISION)) FROM `{$X}liberty_xref` x
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('SGL','PRT','SHT','VOL') AND x.`xref` = $cmpContentId) AS cmp_qty"
- : ", CAST(NULL AS VARCHAR(4)) AS cmp_qty_type, CAST(NULL AS DOUBLE PRECISION) AS cmp_qty";
+ if( $partIsAsm ) {
+ $partQtySelect = ", CAST(NULL AS VARCHAR(4)) AS part_qty_type
+ , CAST(xasm.`xkey` AS DOUBLE PRECISION) AS part_qty";
+ } elseif( $partId ) {
+ $partQtySelect = ", (SELECT FIRST 1 x.`item` FROM `{$X}liberty_xref` x
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('SGL','PRT','SHT','VOL') AND x.`xref` = $partId
+ ORDER BY x.`xorder`) AS part_qty_type
+ , (SELECT SUM(CAST(x.`xkey` AS DOUBLE PRECISION)) FROM `{$X}liberty_xref` x
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('SGL','PRT','SHT','VOL') AND x.`xref` = $partId) AS part_qty";
+ } else {
+ $partQtySelect = ", CAST(NULL AS VARCHAR(4)) AS part_qty_type, CAST(NULL AS DOUBLE PRECISION) AS part_qty";
+ }
$query = "SELECT lc.`content_id`, lc.`title`, lc.`created`, lc.`last_modified`, lc.`event_time`,
- uu.`login`, uu.`real_name`,
+ lc.`user_id`, uu.`login`, uu.`real_name`,
(SELECT FIRST 1 x.`item` FROM `{$X}liberty_xref` x
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER')
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER','PBLD')
ORDER BY x.`xorder`) AS ref_type,
(SELECT FIRST 1 x.`xkey` FROM `{$X}liberty_xref` x
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER')
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER','PBLD')
ORDER BY x.`xorder`) AS ref_key,
(SELECT FIRST 1 x.`start_date` FROM `{$X}liberty_xref` x
- WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER')
+ WHERE x.`content_id` = lc.`content_id` AND x.`item` IN ('REQN','TRANS','ORDER','PBLD')
ORDER BY x.`xorder`) AS ref_start_date
- $cmpQtySelect
+ $partQtySelect
$selectSql
FROM `{$X}liberty_content` lc
INNER JOIN `{$X}users_users` uu ON uu.`user_id` = lc.`user_id`
@@ -456,7 +464,7 @@ class StockMovement extends LibertyContent {
if( $ref !== '' ) {
$existingRow = $this->mDb->getRow(
"SELECT `xref_id`, `item` FROM `".BIT_DB_PREFIX."liberty_xref`
- WHERE `content_id` = ? AND `item` IN ('REQN','TRANS','ORDER') ORDER BY `xorder`",
+ WHERE `content_id` = ? AND `item` IN ('REQN','TRANS','ORDER','PBLD') ORDER BY `xorder`",
[ $this->mContentId ]
);
// Preserve existing type if already set; default to TRANS for new rows
@@ -482,7 +490,7 @@ class StockMovement extends LibertyContent {
$this->mDb->query(
"UPDATE `".BIT_DB_PREFIX."liberty_xref`
SET `start_date` = ?
- WHERE `content_id` = ? AND `item` IN ('REQN','TRANS','ORDER')",
+ WHERE `content_id` = ? AND `item` IN ('REQN','TRANS','ORDER','PBLD')",
[ date( 'Y-m-d H:i:s', $ts ), $this->mContentId ]
);
}
diff --git a/list_movements.php b/list_movements.php
index 1172fd3..700c824 100644
--- a/list_movements.php
+++ b/list_movements.php
@@ -11,34 +11,62 @@ global $gBitSystem, $gBitSmarty, $gBitDb;
$gBitSystem->verifyPermission( 'p_stock_view' );
-$componentContentId = isset( $_REQUEST['component_content_id'] ) && is_numeric( $_REQUEST['component_content_id'] )
- ? (int)$_REQUEST['component_content_id'] : null;
+$partContentId = isset( $_REQUEST['part_content_id'] ) && is_numeric( $_REQUEST['part_content_id'] )
+ ? (int)$_REQUEST['part_content_id'] : null;
+
+$partTitle = '';
+$partType = '';
+$partSize = null;
+if( $partContentId ) {
+ $pRow = $gBitDb->getRow(
+ "SELECT `title`, `content_type_guid` FROM `".BIT_DB_PREFIX."liberty_content` WHERE `content_id` = ?",
+ [ $partContentId ]
+ );
+ $partTitle = $pRow['title'] ?? '';
+ $partType = match( $pRow['content_type_guid'] ?? '' ) {
+ 'stockassembly' => 'assembly',
+ 'stockcomponent' => 'component',
+ default => '',
+ };
+ if( $partType === 'component' ) {
+ $ps = $gBitDb->getOne(
+ "SELECT CAST(x.`xkey` AS DOUBLE PRECISION) FROM `".BIT_DB_PREFIX."liberty_xref` x
+ WHERE x.`content_id` = ? AND x.`item` = 'PRT'",
+ [ $partContentId ]
+ );
+ $partSize = $ps ? (float)$ps : null;
+ }
+}
+
+$listHash = $_REQUEST;
+if( $partContentId && $partType === 'assembly' ) {
+ $listHash['assembly_content_id'] = $partContentId;
+} elseif( $partContentId && $partType === 'component' ) {
+ $listHash['component_content_id'] = $partContentId;
+}
+unset( $listHash['part_content_id'] );
$movement = new StockMovement();
-$listHash = $_REQUEST;
$movementList = $movement->getList( $listHash );
-$componentTitle = '';
-$partSize = null;
-if( $componentContentId ) {
- $componentTitle = $gBitDb->getOne(
- "SELECT `title` FROM `".BIT_DB_PREFIX."liberty_content` WHERE `content_id` = ?",
- [ $componentContentId ]
- ) ?: '';
- $ps = $gBitDb->getOne(
- "SELECT CAST(x.`xkey` AS DOUBLE PRECISION) FROM `".BIT_DB_PREFIX."liberty_xref` x
- WHERE x.`content_id` = ? AND x.`item` = 'PRT'",
- [ $componentContentId ]
+$filterUserId = isset( $_REQUEST['user_id'] ) && is_numeric( $_REQUEST['user_id'] ) ? (int)$_REQUEST['user_id'] : null;
+$filterUserName = '';
+if( $filterUserId ) {
+ $uRow = $gBitDb->getRow(
+ "SELECT `login`, `real_name` FROM `".BIT_DB_PREFIX."users_users` WHERE `user_id` = ?",
+ [ $filterUserId ]
);
- $partSize = $ps ? (float)$ps : null;
+ $filterUserName = $uRow['real_name'] ?: $uRow['login'] ?: '';
}
-$gBitSmarty->assign( 'listInfo', $listHash['listInfo'] );
-$gBitSmarty->assign( 'movementList', $movementList );
-$gBitSmarty->assign( 'filterType', $_REQUEST['ref_type'] ?? '' );
-$gBitSmarty->assign( 'assemblyContentId', isset( $_REQUEST['assembly_content_id'] ) && is_numeric( $_REQUEST['assembly_content_id'] ) ? (int)$_REQUEST['assembly_content_id'] : null );
-$gBitSmarty->assign( 'componentContentId', $componentContentId );
-$gBitSmarty->assign( 'componentTitle', $componentTitle );
-$gBitSmarty->assign( 'partSize', $partSize );
+$gBitSmarty->assign( 'listInfo', $listHash['listInfo'] );
+$gBitSmarty->assign( 'movementList', $movementList );
+$gBitSmarty->assign( 'filterType', $_REQUEST['ref_type'] ?? '' );
+$gBitSmarty->assign( 'partContentId', $partContentId );
+$gBitSmarty->assign( 'partTitle', $partTitle );
+$gBitSmarty->assign( 'partType', $partType );
+$gBitSmarty->assign( 'partSize', $partSize );
+$gBitSmarty->assign( 'filterUserId', $filterUserId );
+$gBitSmarty->assign( 'filterUserName', $filterUserName );
$gBitSystem->display( 'bitpackage:stock/list_movements.tpl', 'Movements', [ 'display_mode' => 'list' ] );
diff --git a/list_stock.php b/list_stock.php
index 796161d..6ad176e 100644
--- a/list_stock.php
+++ b/list_stock.php
@@ -18,15 +18,31 @@ $assemblyContentId = isset( $_REQUEST['assembly_content_id'] ) && is_numeric( $_
? (int)$_REQUEST['assembly_content_id'] : null;
$kitCount = isset( $_REQUEST['kit_count'] ) && is_numeric( $_REQUEST['kit_count'] ) && (float)$_REQUEST['kit_count'] > 0
? (float)$_REQUEST['kit_count'] : 1;
+$filterUserId = isset( $_REQUEST['user_id'] ) && is_numeric( $_REQUEST['user_id'] )
+ ? (int)$_REQUEST['user_id'] : null;
+$filterUserName = '';
+if( $filterUserId ) {
+ $uRow = $gBitDb->getRow(
+ "SELECT `login`, `real_name` FROM `".BIT_DB_PREFIX."users_users` WHERE `user_id` = ?",
+ [ $filterUserId ]
+ );
+ $filterUserName = $uRow['real_name'] ?: $uRow['login'] ?: '';
+}
$X = BIT_DB_PREFIX;
$bindVars = [];
if( $assemblyContentId ) {
- // BOM view: start from BOM items so components with no movements still appear
+ // BOM view: start from BOM items so components with no movements still appear.
+ // Bind var order must match ? positions in the SQL: user_id (in subquery SELECT) first,
+ // then assemblyContentId (in FROM JOIN), then find (in WHERE).
+ $userSubSql = '';
+ if( $filterUserId ) {
+ $userSubSql = " AND mc.`user_id` = ?";
+ $bindVars[] = $filterUserId;
+ }
$findSql = $find !== '' ? " AND UPPER(lc.`title`) LIKE ?" : '';
- if( $find !== '' ) $bindVars[] = '%'.strtoupper( $find ).'%';
$query = "SELECT lc.`content_id`, lc.`title`, lc.`data`,
bom.`item` AS qty_type,
@@ -49,7 +65,8 @@ if( $assemblyContentId ) {
AND mc.`content_type_guid` = 'stockmovement'
WHERE mx.`xref` = lc.`content_id`
AND mx.`item` = bom.`item`
- AND mx.`xkey` SIMILAR TO '[0-9]+(\.[0-9]+)?') AS stock_level
+ AND mx.`xkey` SIMILAR TO '[0-9]+(\.[0-9]+)?'
+ $userSubSql) AS stock_level
FROM `{$X}liberty_content` lc
INNER JOIN `{$X}liberty_xref` bom ON bom.`content_id` = ?
AND bom.`item` IN ('SGL','PRT','SHT','VOL')
@@ -58,11 +75,16 @@ if( $assemblyContentId ) {
$findSql
ORDER BY bom.`xorder`";
$bindVars[] = $assemblyContentId;
+ if( $find !== '' ) $bindVars[] = '%'.strtoupper( $find ).'%';
} else {
// General list: only components with movement history
$whereSql = '';
+ if( $filterUserId ) {
+ $whereSql .= " AND mc.`user_id` = ?";
+ $bindVars[] = $filterUserId;
+ }
if( $find !== '' ) {
- $whereSql = " AND UPPER(lc.`title`) LIKE ?";
+ $whereSql .= " AND UPPER(lc.`title`) LIKE ?";
$bindVars[] = '%'.strtoupper( $find ).'%';
}
@@ -187,5 +209,7 @@ $gBitSmarty->assign( 'find', $find );
$gBitSmarty->assign( 'showBom', (bool)$assemblyContentId );
$gBitSmarty->assign( 'showShortages', $showShortages );
$gBitSmarty->assign( 'kitCount', $kitCount );
+$gBitSmarty->assign( 'filterUserId', $filterUserId );
+$gBitSmarty->assign( 'filterUserName', $filterUserName );
$gBitSystem->display( 'bitpackage:stock/list_stock.tpl', 'Stock Levels', [ 'display_mode' => 'list' ] );
diff --git a/templates/add_prebuild.tpl b/templates/add_prebuild.tpl
new file mode 100644
index 0000000..2f1755b
--- /dev/null
+++ b/templates/add_prebuild.tpl
@@ -0,0 +1,119 @@
+{strip}
+<div class="edit stock">
+ <div class="header">
+ <h1>{tr}Create Prebuild{/tr}</h1>
+ </div>
+
+ <div class="body">
+ {formfeedback error=$errors}
+
+ {form id="addPrebuildForm" ipackage="stock" ifile="add_prebuild.php"}
+
+ <div class="form-group">
+ {formlabel label="Build Ref" for="title" mandatory="y"}
+ {forminput}
+ <input type="text" class="form-control" name="title" id="title"
+ value="{$smarty.request.title|escape}" placeholder="BUILD-2026-001" />
+ {/forminput}
+ </div>
+
+ <div class="form-group">
+ {formlabel label="Assembly" for="assembly_search" mandatory="y"}
+ {forminput}
+ <input type="hidden" name="assembly_content_id" id="assembly_content_id"
+ value="{$preselect|default:''|escape}" />
+ <div style="position:relative">
+ <input type="text" class="form-control" id="assembly_search"
+ autocomplete="off"
+ value="{$preselectTitle|escape}"
+ placeholder="Type to search…" />
+ <ul id="assembly_dropdown" class="dropdown-menu"
+ style="display:none;position:absolute;width:100%;z-index:1000;max-height:220px;overflow-y:auto"></ul>
+ </div>
+ {/forminput}
+ </div>
+
+ <div class="form-group">
+ {formlabel label="Qty" for="kit_count"}
+ {forminput}
+ <input type="number" class="form-control input-sm" name="kit_count"
+ id="kit_count" min="1" step="1" style="width:6em"
+ value="{$kitCount|escape}" />
+ {/forminput}
+ </div>
+
+ <div class="form-group">
+ {formlabel label="Against RQ" for="rq_ref"}
+ {forminput}
+ <input type="text" class="form-control" name="rq_ref" id="rq_ref"
+ value="{$smarty.request.rq_ref|escape}"
+ placeholder="RQ number if building against a requisition" />
+ {/forminput}
+ </div>
+
+ <div class="form-group submit">
+ <input type="submit" class="btn btn-default" name="fCancel" value="{tr}Cancel{/tr}" />
+ <input type="submit" class="btn btn-primary" name="fCreate" value="{tr}Create Prebuild{/tr}" />
+ </div>
+
+ {/form}
+ </div>
+</div>
+{/strip}
+<script>
+(function($) {
+ var items = {$itemListJson};
+ var $input = $('#assembly_search');
+ var $hidden = $('#assembly_content_id');
+ var $dd = $('#assembly_dropdown');
+
+ $input.on('input', function() {
+ var q = this.value.toLowerCase().trim();
+ $dd.hide().empty();
+ $hidden.val('');
+ if (!q) return;
+ var matched = items.filter(function(i) {
+ return i.text.toLowerCase().indexOf(q) !== -1 || (i.klid && i.klid.toLowerCase().indexOf(q) !== -1);
+ });
+ if (!matched.length) return;
+ matched.forEach(function(i) {
+ var label = i.text + (i.klid ? ' (' + i.klid + ')' : '');
+ $dd.append($('<li>').append(
+ $('<a>').attr('href', '#').data('id', i.id).data('text', i.text).text(label)
+ ));
+ });
+ $dd.show();
+ });
+
+ $(document).on('mousedown', '#assembly_dropdown a', function(e) {
+ e.preventDefault();
+ $input.val($(this).data('text'));
+ $hidden.val($(this).data('id'));
+ $dd.hide().empty();
+ });
+
+ $input.on('blur', function() {
+ setTimeout(function() { $dd.hide(); }, 150);
+ });
+
+ $input.on('keydown', function(e) {
+ if (!$dd.is(':visible')) return;
+ var $links = $dd.find('a');
+ var idx = $links.index($dd.find('li.active a'));
+ if (e.key === 'ArrowDown') {
+ e.preventDefault();
+ $links.parent().removeClass('active');
+ $links.eq(idx + 1 < $links.length ? idx + 1 : 0).parent().addClass('active');
+ } else if (e.key === 'ArrowUp') {
+ e.preventDefault();
+ $links.parent().removeClass('active');
+ $links.eq(idx > 0 ? idx - 1 : $links.length - 1).parent().addClass('active');
+ } else if (e.key === 'Enter') {
+ var $active = $dd.find('li.active a');
+ if ($active.length) { e.preventDefault(); $active.trigger('mousedown'); }
+ } else if (e.key === 'Escape') {
+ $dd.hide();
+ }
+ });
+}(jQuery));
+</script>
diff --git a/templates/edit_movement.tpl b/templates/edit_movement.tpl
index c4081dc..19dc70d 100644
--- a/templates/edit_movement.tpl
+++ b/templates/edit_movement.tpl
@@ -24,7 +24,7 @@
{/forminput}
</div>
- {if !$isReqn}
+ {if !$isBuild}
{if $refTypes}
<div class="form-group">
{formlabel label="Movement Type" mandatory="y"}
@@ -65,7 +65,7 @@
{/if}
<div class="form-group">
- {formlabel label="Ordered" for="ordered_date"}
+ {formlabel label="{if $isPbld}Build Date{else}Ordered{/if}" for="ordered_date"}
{forminput}
<input type="text" class="form-control input-small" name="ordered_date" id="ordered_date"
placeholder="dd/mm/yyyy" value="{$orderedDateVal|escape}" maxlength="10" />
@@ -73,7 +73,7 @@
</div>
<div class="form-group">
- {formlabel label="Received" for="received_date"}
+ {formlabel label="{if $isPbld}Completed{else}Received{/if}" for="received_date"}
{forminput}
<input type="text" class="form-control input-small" name="received_date" id="received_date"
placeholder="dd/mm/yyyy" value="{$receivedDateVal|escape}" maxlength="10" />
@@ -110,7 +110,7 @@
{if $gXrefInfo->mGroups}
{jstabs}
{foreach $gXrefInfo->mGroups as $xrefGroup}
- {if $xrefGroup->mXGroup neq 'reference' && ($xrefGroup->mXGroup neq 'assembly' || $isReqn)}
+ {if $xrefGroup->mXGroup neq 'reference' && ($xrefGroup->mXGroup neq 'assembly' || $isBuild)}
{include file=$gContent->getXrefListTemplate($xrefGroup->mTemplate)
xrefGroup=$xrefGroup
allow_add=true
@@ -120,7 +120,7 @@
{/jstabs}
{/if}
- {if !$isReqn}
+ {if !$isBuild}
{* ── Upload CSV (orders/transfers only) ── *}
<h4>{tr}Upload CSV{/tr}</h4>
{form enctype="multipart/form-data" ipackage="stock" ifile="edit_movement.php"}
diff --git a/templates/list_movements.tpl b/templates/list_movements.tpl
index 442f29d..424fff9 100644
--- a/templates/list_movements.tpl
+++ b/templates/list_movements.tpl
@@ -4,16 +4,18 @@
<div class="floaticon hidden-print">
<button type="button" class="btn btn-link" onclick="window.print()">{biticon ipackage="icons" iname="document-print" iexplain="Print"}</button>
{if $gBitUser->hasPermission('p_stock_create')}
- <a href="{$smarty.const.STOCK_PKG_URL}add_requisition.php">{biticon ipackage="icons" iname="list-add" iexplain="Add Requisition"}</a>
+ <a href="{$smarty.const.STOCK_PKG_URL}add_prebuild.php">{biticon ipackage="icons" iname="package-x-generic" iexplain="Add Prebuild"}</a>
<a href="{$smarty.const.STOCK_PKG_URL}edit_movement.php">{biticon ipackage="icons" iname="view-task-add" iexplain="Add Movement"}</a>
{/if}
<form class="minifind" action="{$smarty.const.STOCK_PKG_URL}list_movements.php" method="get">
- {if $componentContentId}<input type="hidden" name="component_content_id" value="{$componentContentId|escape}" />{/if}
+ {if $partContentId}<input type="hidden" name="part_content_id" value="{$partContentId|escape}" />{/if}
+ {if $filterUserId}<input type="hidden" name="user_id" value="{$filterUserId|escape}" />{/if}
<div class="form-inline">
<div class="form-group">
<select name="ref_type" class="form-control input-sm">
<option value="">{tr}All types{/tr}</option>
<option value="REQN"{if $filterType eq 'REQN'} selected="selected"{/if}>{tr}Requisition (out){/tr}</option>
+ <option value="PBLD"{if $filterType eq 'PBLD'} selected="selected"{/if}>{tr}Prebuild (out){/tr}</option>
<option value="TRANS"{if $filterType eq 'TRANS'} selected="selected"{/if}>{tr}Transfer (in){/tr}</option>
<option value="ORDER"{if $filterType eq 'ORDER'} selected="selected"{/if}>{tr}Order (in){/tr}</option>
</select>
@@ -27,13 +29,18 @@
</div>
</form>
</div>
- <h1>{tr}Movements{/tr}{if $componentTitle} — {$componentTitle|escape}{/if}</h1>
+ <h1>{tr}Movements{/tr}{if $partTitle} — {$partTitle|escape}{/if}</h1>
+ <small>
+ <a href="{$smarty.const.STOCK_PKG_URL}list_movements.php">{tr}Movements{/tr}</a>
+ {if $partTitle}&rsaquo; <a href="{$smarty.const.STOCK_PKG_URL}view_{$partType}.php?content_id={$partContentId}">{$partTitle|escape}</a>{/if}
+ {if $filterUserName}&rsaquo; <a href="{$smarty.const.STOCK_PKG_URL}list_movements.php?user_id={$filterUserId}">{$filterUserName|escape}</a>{/if}
+ </small>
</header>
<section class="body">
- {if $componentContentId}
- <p><a class="btn btn-xs btn-default" href="{$smarty.const.STOCK_PKG_URL}view_component.php?content_id={$componentContentId}">&larr; {tr}Back to component{/tr}</a></p>
+ {if $partContentId}
+ <p><a class="btn btn-xs btn-default" href="{$smarty.const.STOCK_PKG_URL}view_{$partType}.php?content_id={$partContentId}">&larr; {if $partType eq 'assembly'}{tr}Back to assembly{/tr}{else}{tr}Back to component{/tr}{/if}</a></p>
{/if}
<table class="table table-striped table-hover">
@@ -41,7 +48,7 @@
<tr>
<th>{smartlink ititle="Reference" isort="title"}</th>
<th>{tr}Type{/tr}</th>
- {if $componentContentId}<th class="text-right">{tr}Qty{/tr}</th>{/if}
+ {if $partContentId}<th class="text-right">{tr}Qty{/tr}</th>{/if}
<th>{smartlink ititle="Ordered" isort="ref_start_date" ifile="list_movements.php" ipackage="stock"}</th>
<th>{smartlink ititle="Received" isort="event_time" ifile="list_movements.php" ipackage="stock"}</th>
<th>{smartlink ititle="Date" isort="created_desc"}</th>
@@ -54,13 +61,17 @@
<tr>
<td><a href="{$mov.display_url|escape}">{$mov.title|escape}</a></td>
<td>{$mov.ref_type|escape|default:'—'}</td>
- {if $componentContentId}
- <td class="text-right">{if $mov.cmp_qty_type eq 'PRT' && $partSize > 0}{math equation="q/p" q=$mov.cmp_qty p=$partSize format="%.2f"}{elseif $mov.cmp_qty_type eq 'SHT'}{$mov.cmp_qty|string_format:"%.2f"}{else}{$mov.cmp_qty|string_format:"%.0f"}{/if} {$mov.cmp_qty_type|escape}</td>
+ {if $partContentId}
+ {if $partType eq 'assembly'}
+ <td class="text-right">{$mov.part_qty|string_format:"%.0f"}</td>
+ {else}
+ <td class="text-right">{if $mov.part_qty_type eq 'PRT' && $partSize > 0}{math equation="q/p" q=$mov.part_qty p=$partSize format="%.2f"}{elseif $mov.part_qty_type eq 'SHT'}{$mov.part_qty|string_format:"%.2f"}{else}{$mov.part_qty|string_format:"%.0f"}{/if} {$mov.part_qty_type|escape}</td>
+ {/if}
{/if}
<td>{if $mov.ref_start_date}{$mov.ref_start_date|bit_short_date}{else}—{/if}</td>
<td>{if $mov.event_time}{$mov.event_time|bit_short_date}{else}—{/if}</td>
<td>{$mov.created|bit_short_date}</td>
- <td>{$mov.real_name|default:$mov.login|escape}</td>
+ <td><a href="{$smarty.const.STOCK_PKG_URL}list_movements.php?user_id={$mov.user_id}">{$mov.real_name|default:$mov.login|escape}</a></td>
{if $gBitUser->hasPermission('p_stock_update')}
<td>
<a href="{$smarty.const.STOCK_PKG_URL}edit_movement.php?content_id={$mov.content_id}">{biticon ipackage="icons" iname="edit" iexplain="Edit"}</a>
@@ -74,7 +85,7 @@
</table>
<nav>
- {pagination ref_type=$filterType find=$smarty.request.find|default:'' component_content_id=$componentContentId|default:'' assembly_content_id=$assemblyContentId|default:''}
+ {pagination ref_type=$filterType find=$smarty.request.find|default:'' part_content_id=$partContentId|default:'' user_id=$filterUserId|default:''}
</nav>
</section>
diff --git a/templates/list_stock.tpl b/templates/list_stock.tpl
index 3250427..5a992f3 100644
--- a/templates/list_stock.tpl
+++ b/templates/list_stock.tpl
@@ -5,13 +5,14 @@
<button type="button" class="btn btn-link" onclick="window.print()">{biticon ipackage="icons" iname="document-print" iexplain="Print"}</button>
{if $showShortages}
<a class="btn btn-link"
- href="{$smarty.const.STOCK_PKG_URL}list_stock.php?shortages=1{if $assemblyContentId}&amp;assembly_content_id={$assemblyContentId|escape:'url'}&amp;kit_count={$kitCount|escape:'url'}{/if}&amp;format=csv">{biticon ipackage="icons" iname="text-csv" iexplain="Download CSV"}</a>
+ href="{$smarty.const.STOCK_PKG_URL}list_stock.php?shortages=1{if $assemblyContentId}&amp;assembly_content_id={$assemblyContentId|escape:'url'}&amp;kit_count={$kitCount|escape:'url'}{/if}{if $filterUserId}&amp;user_id={$filterUserId|escape:'url'}{/if}&amp;format=csv">{biticon ipackage="icons" iname="text-csv" iexplain="Download CSV"}</a>
{if $gBitUser->hasPermission('p_stock_create')}
<a class="btn btn-link"
- href="{$smarty.const.STOCK_PKG_URL}add_order.php?shortages=1{if $assemblyContentId}&amp;assembly_content_id={$assemblyContentId|escape:'url'}&amp;kit_count={$kitCount|escape:'url'}{/if}{if $find}&amp;find={$find|escape:'url'}{/if}">{biticon ipackage="icons" iname="view-task-add" iexplain="Create Order"}</a>
+ href="{$smarty.const.STOCK_PKG_URL}add_order.php?shortages=1{if $assemblyContentId}&amp;assembly_content_id={$assemblyContentId|escape:'url'}&amp;kit_count={$kitCount|escape:'url'}{/if}{if $find}&amp;find={$find|escape:'url'}{/if}{if $filterUserId}&amp;user_id={$filterUserId|escape:'url'}{/if}">{biticon ipackage="icons" iname="view-task-add" iexplain="Create Order"}</a>
{/if}
{/if}
<form class="minifind" action="{$smarty.const.STOCK_PKG_URL}list_stock.php" method="get">
+ {if $filterUserId}<input type="hidden" name="user_id" value="{$filterUserId|escape}" />{/if}
<div class="form-inline">
<div class="form-group">
<input type="hidden" name="assembly_content_id" id="ls_asm_id" value="{$assemblyContentId|default:''|escape}" />
@@ -45,12 +46,16 @@
<button type="submit" class="btn btn-default btn-sm">{tr}Go{/tr}</button>
{if $showBom && $gBitUser->hasPermission('p_stock_create')}
<a class="btn btn-warning btn-sm"
- href="{$smarty.const.STOCK_PKG_URL}add_requisition.php?assembly_content_id={$assemblyContentId}&amp;kit_count={$kitCount}">{tr}Create Requisition{/tr}</a>
+ href="{$smarty.const.STOCK_PKG_URL}add_prebuild.php?assembly_content_id={$assemblyContentId}&amp;kit_count={$kitCount}{if $filterUserId}&amp;user_id={$filterUserId}{/if}">{tr}Create Prebuild{/tr}</a>
{/if}
</div>
</form>
</div>
<h1>{tr}Stock Levels{/tr}{if $assemblyTitle} — {$assemblyTitle|escape}{/if}</h1>
+ <small>
+ <a href="{$smarty.const.STOCK_PKG_URL}list_stock.php">{tr}Stock Levels{/tr}</a>
+ {if $filterUserName}&rsaquo; <a href="{$smarty.const.STOCK_PKG_URL}list_stock.php?user_id={$filterUserId}">{$filterUserName|escape}</a>{/if}
+ </small>
</header>
<section class="body">
diff --git a/templates/menu_stock.tpl b/templates/menu_stock.tpl
index 2f8a327..8794dbf 100755
--- a/templates/menu_stock.tpl
+++ b/templates/menu_stock.tpl
@@ -12,7 +12,7 @@
<li><a class="item" href="{$smarty.const.STOCK_PKG_URL}edit_component.php">{biticon ipackage="icons" iname="kt-add-filters" iexplain="Create a Component" ilocation=menu}</a></li>
<li><a class="item" href="{$smarty.const.STOCK_PKG_URL}edit_assembly.php">{biticon ipackage="icons" iname="view-list-icons" iexplain="Create an Assembly" ilocation=menu}</a></li>
<li><a class="item" href="{$smarty.const.STOCK_PKG_URL}edit_movement.php">{biticon ipackage="icons" iname="view-task-add" iexplain="Add Movement" ilocation=menu}</a></li>
- <li><a class="item" href="{$smarty.const.STOCK_PKG_URL}add_requisition.php">{biticon ipackage="icons" iname="view-task-child-add" iexplain="Create Requisition" ilocation=menu}</a></li>
+ <li><a class="item" href="{$smarty.const.STOCK_PKG_URL}add_prebuild.php">{biticon ipackage="icons" iname="package-x-generic" iexplain="Create Prebuild" ilocation=menu}</a></li>
{/if}
</ul>
{/strip}
diff --git a/templates/stock_simple_list_inc.tpl b/templates/stock_simple_list_inc.tpl
index 8ff0126..4f3347f 100755
--- a/templates/stock_simple_list_inc.tpl
+++ b/templates/stock_simple_list_inc.tpl
@@ -8,7 +8,7 @@
{/if}
<a title="{tr}Print Parts List{/tr}" href="{$smarty.const.STOCK_PKG_URL}print_bom.php?content_id={$gContent->mContentId}">{biticon ipackage="icons" iname="document-print" iexplain="Print Parts List"}</a>
<a title="{tr}View Stock{/tr}" href="{$smarty.const.STOCK_PKG_URL}list_stock.php?assembly_content_id={$gContent->mContentId}">{biticon ipackage="icons" iname="package-x-generic" iexplain="View Stock"}</a>
- <a title="{tr}View Movements{/tr}" href="{$smarty.const.STOCK_PKG_URL}list_movements.php?assembly_content_id={$gContent->mContentId}">{biticon ipackage="icons" iname="go-next" iexplain="View Movements"}</a>
+ <a title="{tr}View Movements{/tr}" href="{$smarty.const.STOCK_PKG_URL}list_movements.php?part_content_id={$gContent->mContentId}">{biticon ipackage="icons" iname="go-next" iexplain="View Movements"}</a>
{if $gContent->hasAdminPermission()}
<a title="{tr}Delete Assembly{/tr}" href="{$smarty.const.STOCK_PKG_URL}edit_assembly.php?content_id={$gContent->mContentId}&amp;delete=1">{biticon ipackage="icons" iname="user-trash" iexplain="Delete Assembly"}</a>
{/if}
diff --git a/templates/view_component.tpl b/templates/view_component.tpl
index 80466e7..c5d94b5 100755
--- a/templates/view_component.tpl
+++ b/templates/view_component.tpl
@@ -59,7 +59,7 @@
{/if}
</tbody>
</table>
- <a class="btn btn-default btn-xs" href="{$smarty.const.STOCK_PKG_URL}list_movements.php?component_content_id={$gContent->mContentId}">{tr}Stock history{/tr}</a>
+ <a class="btn btn-default btn-xs" href="{$smarty.const.STOCK_PKG_URL}list_movements.php?part_content_id={$gContent->mContentId}">{tr}Stock history{/tr}</a>
{/jstab}
{/jstabs}
</section>
diff --git a/templates/view_movement.tpl b/templates/view_movement.tpl
index f038cb6..b815618 100644
--- a/templates/view_movement.tpl
+++ b/templates/view_movement.tpl
@@ -1,15 +1,19 @@
{strip}
<div class="display stock">
- <div class="header">
+ <header>
<div class="floaticon">
- {if $gBitUser->hasPermission('p_stock_create')}
+ {if $gContent->hasUpdatePermission()}
<a title="{tr}Edit{/tr}" href="{$smarty.const.STOCK_PKG_URL}edit_movement.php?content_id={$gContent->mContentId}">{biticon ipackage="icons" iname="edit" iexplain="Edit Movement"}</a>
{/if}
</div>
<h1>{$gContent->getTitle()|escape}</h1>
- </div>
+ <small>
+ <a href="{$smarty.const.STOCK_PKG_URL}list_movements.php">{tr}Movements{/tr}</a>
+ &rsaquo; <a href="{$smarty.const.STOCK_PKG_URL}list_movements.php?user_id={$gContent->mInfo.user_id}">{$gContent->mInfo.creator|escape}</a>
+ </small>
+ </header>
- <div class="body">
+ <section class="body">
<dl class="dl-horizontal">
<dt>{tr}Type{/tr}</dt>
@@ -26,11 +30,11 @@
<dt>{tr}Created{/tr}</dt>
<dd>{$gContent->mInfo.created|bit_short_datetime} {tr}by{/tr} {$gContent->mInfo.creator|escape}</dd>
{if $gContent->mInfo.ref_start_date}
- <dt>{tr}Ordered{/tr}</dt>
+ <dt>{if $isPbld}{tr}Build Date{/tr}{else}{tr}Ordered{/tr}{/if}</dt>
<dd>{$gContent->mInfo.ref_start_date|bit_short_date}</dd>
{/if}
- <dt>{tr}Received{/tr}</dt>
- <dd>{if $gContent->isReceived()}{$gContent->mInfo.event_time|bit_short_date}{else}{tr}Pending{/tr}{/if}</dd>
+ <dt>{if $isPbld}{tr}Completed{/tr}{else}{tr}Received{/tr}{/if}</dt>
+ <dd>{if $gContent->isReceived()}{$gContent->mInfo.event_time|bit_short_date}{else}{if $isPbld}{tr}In progress{/tr}{else}{tr}Pending{/tr}{/if}{/if}</dd>
{if $gContent->mInfo.last_modified neq $gContent->mInfo.created}
<dt>{tr}Modified{/tr}</dt>
<dd>{$gContent->mInfo.last_modified|bit_short_datetime} {tr}by{/tr} {$gContent->mInfo.editor|escape}</dd>
@@ -44,7 +48,7 @@
{if $gXrefInfo->mGroups}
{jstabs}
{foreach $gXrefInfo->mGroups as $xrefGroup}
- {if $xrefGroup->mXGroup neq 'reference' && ($xrefGroup->mXGroup neq 'assembly' || $isReqn)}
+ {if $xrefGroup->mXGroup neq 'reference' && ($xrefGroup->mXGroup neq 'assembly' || $isBuild)}
{include file=$gContent->getXrefListTemplate($xrefGroup->mTemplate)
xrefGroup=$xrefGroup
allow_add=false
@@ -54,6 +58,6 @@
{/jstabs}
{/if}
- </div><!-- end .body -->
+ </section><!-- end .body -->
</div><!-- end .stock -->
{/strip}
diff --git a/view_movement.php b/view_movement.php
index 395fce0..eca7f01 100644
--- a/view_movement.php
+++ b/view_movement.php
@@ -23,6 +23,9 @@ $gBitSystem->setCanonicalLink( $gContent->getDisplayUrl() );
$gContent->loadXrefInfo();
$gBitSmarty->assign( 'gXrefInfo', $gContent->mXrefInfo );
-$gBitSmarty->assign( 'isReqn', ( ( $gContent->mInfo['ref_type'] ?? '' ) === 'REQN' ) );
+$refType = $gContent->mInfo['ref_type'] ?? '';
+$gBitSmarty->assign( 'isReqn', $refType === 'REQN' );
+$gBitSmarty->assign( 'isPbld', $refType === 'PBLD' );
+$gBitSmarty->assign( 'isBuild', in_array( $refType, [ 'REQN', 'PBLD' ] ) );
$gBitSystem->display( 'bitpackage:stock/view_movement.tpl', $gContent->getTitle() );