summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLester Caine <lester@lsces.co.uk>2026-06-14 16:23:32 +0100
committerLester Caine <lester@lsces.co.uk>2026-06-14 16:23:32 +0100
commitb3c5fde1252993856057f2d86c1b3b6d4afe377d (patch)
tree540b2c2dc951142a471ee694077596e1b11b2c1c
parentd1700a30eb4463d21e4b5af329079521d6d7f5ea (diff)
downloadbitweaver-b3c5fde1252993856057f2d86c1b3b6d4afe377d.tar.gz
bitweaver-b3c5fde1252993856057f2d86c1b3b6d4afe377d.tar.bz2
bitweaver-b3c5fde1252993856057f2d86c1b3b6d4afe377d.zip
Split CLAUDE.md into package-level docs
Root trimmed from 502 to 249 lines. Package detail moved to: - liberty/CLAUDE.md: xref machinery, parseDataHash, storeXref, owner change, Firebird GROUP BY, LibertyXrefGroup display path - contact/CLAUDE.md: person/business model, subclass plan, SCREF, load() cleanup, CSV xorder, delete/expunge - stock/CLAUDE.md: file naming, movement model, kitelf filtering, template structure, getList() enriched fields Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--CLAUDE.md275
1 files changed, 11 insertions, 264 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 7f731fd..021868e 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -192,225 +192,14 @@ Any template in this path overrides the package default via Smarty's `bitpackage
`config/themes/medw` and `config/themes/merg` are both symlinks — never edit the config/themes
path directly; edit the source in `/etc/webstack/domains/`.
-### Contact package — Person vs Business model
-
-Two distinct contact types, entered via separate pages:
-- `add_person.php` — auto-injects `$00` type; name stored pipe-separated (`prefix|forename|surname|suffix`) in `liberty_xref.xkey_ext` of the `$00` record; `lc.title` = surname; redirects to `edit.php` for further detail
-- `add_business.php` — no `$00`; user picks from `$02`+ subtypes (Supplier, Manufacturer etc., expandable via DB); `lc.title` = organisation name; redirects to `edit.php`
-
-Type codes in `liberty_xref_item` (`content_type_guid='contact'`, `x_group='type'`, `sort_order=0`):
-- `$00` Person — triggers name fields in edit/display; never shown as a checkbox in UI
-- `$01` Organisation — deprecated, not used in new UI
-- `$02`+ Business subtypes — shown as checkboxes in `add_business.php` and `edit.php`
-
-`edit.php` detects person via `contact_types[0].content_id` → `$isPerson` flag:
-- Person: name fields shown, Contact Types section hidden, Organisation hidden
-- Business: org field shown, Contact Types (`$02`+) shown, name fields hidden
-
-`display_contact.tpl` heading = "Personal Contact" / "Business Contact" from `contact_types.0.content_id`.
-Name loaded from `$00` xref `xkey_ext` via SQL join in `Contact::load()` (`x00.xkey_ext AS name`).
-
-xref item templates gate dates and edit actions on `{$xrefAllowEdit}` (pass `allow_edit=false` in view, `allow_edit=true` in edit).
-
-#### Planned: ContactPerson / ContactBusiness subclasses
-
-The current `$isPerson` detection via `$00` xref presence is a hack. The plan replaces it
-with proper subclasses following the dual-guid xref pattern (as per stock):
-
-- `ContactPerson extends Contact` — `mContentTypeGuid = 'contactperson'`, `mPackageGuid = 'contact'`
-- `ContactBusiness extends Contact` — `mContentTypeGuid = 'contactbusiness'`, `mPackageGuid = 'contact'`
-
-Shared schema (addresses, SCREF etc.) stays at `content_type_guid='contact'`.
-Person-specific types (`$00` default, kitelf, committee member etc.) at `contactperson` level.
-Business subtypes (`$02`+: Supplier, Manufacturer etc.) at `contactbusiness` level.
-`$isPerson` flag disappears — the class IS the distinction.
-Template resolution already works via `mContentTypeGuid` path lookup in LibertyContent.
-
-**Not yet implemented.** Development/testing on `rainbowdigitalmedia` first.
-Upgrade script `contact/admin/upgrades/5.0.3.php` will:
-1. Register `contactperson` and `contactbusiness` content types
-2. `UPDATE liberty_content SET content_type_guid = 'contactperson'` for records with a `$00` xref
-3. Remaining `content_type_guid='contact'` records become `contactbusiness`
-
-**SCREF** — short reference code for a contact, stored in `liberty_xref.xkey` where `item='SCREF'`.
-Used as the `from` value in stock movement CSVs to identify the supplier/source contact.
-`contact/includes/lookup_contact.php` provides JSON autocomplete searching by `lc.title` or SCREF `xkey`.
-
-### Stock package — file naming convention
-Entry points follow `verb_contenttype.php` pattern:
-- `view_assembly.php`, `edit_assembly.php`
-- `view_component.php`, `edit_component.php`
-- `view_movement.php`, `edit_movement.php`
-- `list_assemblies.php`, `list_components.php`, `list_movements.php`
-- `add_supplier.php` — specialist add page (no generic add_assembly/add_component yet)
-- `add_order.php` — draft ORDER movement from shortages list; pre-populates lines with
- shortage qty, supplier autocomplete, editable qty/delete per line before creating movement
-
-`view.php` and `edit.php` removed.
-`list_stock.php` — stock levels from movement xrefs. Shortages filter (`?shortages=1`) works
-on both main list (level < 0) and BOM view (remaining < 0). Shortages view has floaticon icons
-for Print, CSV export (`?format=csv`, part_number + qty, skips blanks), and Create Order.
-
-### Firebird GROUP BY strictness
-Firebird requires every non-aggregate column in SELECT to appear in GROUP BY — including `lc.data`, `lc.title` etc. Correlated scalar subqueries in SELECT (e.g. `SELECT FIRST 1 ...`) are exempt. MySQL is more lenient; Firebird is not.
-
-### Stock movement model
-Movement = pure `liberty_content` record (`content_type_guid='stockmovement'`). No `stock_movement` table.
-
-**Direction** inferred from `reference` xref group (x_group='reference', sort_order=1):
-- `REQN` = outbound (to kitlocker)
-- `TRANS` = inbound from another elf
-- `ORDER` = inbound from supplier
-
-**Status** = `lc.event_time` (BIGINT, Unix seconds) — `0` = placed/open, positive = received/fulfilled.
-`StockMovement::markReceived()` sets it to `time()`. `isReceived()` uses `!empty()` so 0 = not received.
-
-**Reference xref** (x_group='reference', sort_order=1), one row per movement:
-- `item` = REQN/TRANS/ORDER
-- `xkey` = reference number/key
-- `data` = free-text "from" (fallback if no contact linked)
-- `xref` = contact content_id (linked supplier/source — looked up via SCREF xkey)
-- `start_date` (TIMESTAMP) = order date (from CSV col 3 or manual entry)
-
-**Items** = `quantity` xref group (x_group='quantity', sort_order=2), `multiple=1`, one xref row per component line:
-- `item` = SGL/PCK/SHT/VOL — quantity type
-- `xref` = component content_id
-- `xkey` = quantity value
-- `xorder` = line sequence (managed explicitly, NOT via `fAddXref` — `multiple=0` on these items for other content types)
-
-**CSV format** (one movement per file): line 1 = `from(SCREF), ref, order_date(dd/mm/yy), received_date(dd/mm/yy optional)`; lines 2+ = `component_title, quantity, [optional qty type]`. Qty type defaults to component's existing xref type if not specified.
-
-Uploaded CSVs are saved to `STOCK_IMPORT_PATH` (`storage/stock/`) as `<origname>_move_<content_id>.csv` for audit. BOM uploads save as `<origname>_bom_<content_id>.csv`.
-
-**`storeXref()` always needs a named variable** — it takes `&$pParamHash` by reference; passing a literal array is a fatal error.
-
-### LibertyXref / xorder
-`liberty_xref.xorder` — used for BOM grouping and sort. Must be explicitly selected
-in queries; it is not auto-included in standard SELECT lists.
-
-### LibertyXrefType — instance class
-`LibertyXrefType` is an **instance class**, not a bag of statics. Construct with
-`new LibertyXrefType( $contentTypeGuid, $packageGuid = null )`. In page/class code,
-always access it via `LibertyContent::xrefType()` which lazily creates and caches the
-instance. The five runtime query methods (`getDisplayGroups`, `getTypeMarkers`,
-`getAvailableItems`, `getTemplateFormats`, `getContentTypeMarkers`) are instance methods.
-Admin cross-type queries (`getXrefTypeList`, `getContentTypeGuids`, `getGroupList`)
-remain static.
-
-### Dual-guid xref schema (package-level + content-type-level)
-A package with multiple content types can define xref groups/items at two levels:
-- **Package-level** — groups shared across all content types in the package, keyed by the package guid
-- **Content-type-level** — groups specific to one content type, keyed by the content type guid
-
-**Stock is the reference implementation:**
-- Package-level (`'stock'`): `stgrp`, `supplier`, `kitlocker` — apply to both assemblies and components
-- Content-type-level (`'stockcomponent'`): `quantity`, `values`; (`'stockassembly'`): `quantity`
-
-To support this, pass `$packageGuid` when constructing `LibertyXrefType` or `LibertyXrefInfo`
-(both accept it as an optional second argument). The `mPackageGuid` property on `LibertyContent`
-is set automatically by `registerContentType()` when `handler_package` differs from the content
-type guid — so subclasses get it for free.
-
-When writing xref JOIN queries that span both levels, always join item↔group on
-`t.content_type_guid = s.content_type_guid` (self-consistent); apply the guid `IN()` filter
-only in the WHERE clause on `s`. Putting the filter in the JOIN ON instead causes
-cross-matching when two guids share an `x_group` name.
-
-### LibertyXrefGroup display path (contact + stock)
-
-**PHP pattern** — display and edit pages:
-```php
-$gContent->loadXrefInfo();
-$gBitSmarty->assign( 'gXrefInfo', $gContent->mXrefInfo );
-```
-
-**Template pattern** — view and edit templates:
-```smarty
-{foreach $gXrefInfo->mGroups as $xrefGroup}
- {include file=$gContent->getXrefListTemplate($xrefGroup->mTemplate)
- xrefGroup=$xrefGroup allow_edit=false} {* true for edit pages *}
-{/foreach}
-```
-
-Group templates receive `$xrefGroup` (LibertyXrefGroup object). First two lines must be:
-```smarty
-{assign var=xrefAllowEdit value=$allow_edit|default:false}
-{assign var=isHistory value=($xrefGroup->mXGroup eq 'history')}
-```
-
-Fallback for groups with no specific template → `liberty/list_xref.tpl`.
-
-**Linked content fields (`linked_title` / `linked_data`)** — `LibertyXrefType::loadContent()`
-LEFT JOINs `liberty_content lc_linked ON lc_linked.content_id = x.xref` and exposes
-`lc_linked.title AS linked_title` and `lc_linked.data AS linked_data` on every xref row.
-These come from the **linked content item's** `liberty_content` row (via the `x.xref` FK),
-NOT from the xref row's own `xkey`/`xkey_ext`/`data` columns (which are already available
-as `$xrefInfo.xkey`, `$xrefInfo.xkey_ext`, `$xrefInfo.data` without any join).
-When `x.xref > 0` these fields hold the title and description of the linked item (contact,
-component, assembly, etc.). `liberty_content` has no `xkey_ext` equivalent — if further
-fields from the linked item are needed, add them to the SELECT in `loadContent()` as
-additional `lc_linked.*` aliases, or use a correlated subquery for linked xref data.
-
-- **View templates**: use `$xrefInfo.linked_title` and `$xrefInfo.linked_data` directly — no
- separate enrichment query needed.
-- **Edit templates** (`edit_xref.php` path): `enrichXrefDisplay()` is called on the single row
- before display. Override this in the content class (e.g. `StockBase::enrichXrefDisplay()`)
- to set `xref_title` for the edit form. The two paths use different field names by design.
-- **Extra fields** (e.g. `part_size` from a second xref): override `loadXrefInfo()` in the
- content class, call `parent::loadXrefInfo()` first, then enrich the group rows. Use
- `array_map( fn($r) => $r['xref'], $group->mXrefs )` — NOT `array_column()` — to extract
- xref values from `LibertyXref` objects (ArrayAccess; `array_column` ignores offsetGet on
- some PHP builds).
-
-**Floaticon placement** — on list pages the floaticon div goes inside the `<div class="floaticon">`
-inside the assembly view's header. On view/edit pages it goes inside the `.header` div before
-the `<h1>`. Forms in the floaticon use `class="minifind"` for correct spacing (see Smarty notes).
-`assembly_icons_inc.tpl` is included in every assembly view layout's header — put assembly-level
-icons there rather than in `view_assembly.tpl` directly.
-
-**Kitlocker tab visibility** — `edit_component.php` and `view_component.php` detect kitlocker
-components via a KLID xref presence check and assign `$isKitlocker`. In templates, stash the
-`kitlocker` and `stgrp` groups during the normal foreach and render them at the end only when
-`$isKitlocker` is true:
-```smarty
-{assign var=klGroup value=null}{assign var=sgGroup value=null}
-{foreach $gXrefInfo->mGroups as $xrefGroup}
- {if $xrefGroup->mXGroup eq 'kitlocker'}{assign var=klGroup value=$xrefGroup}
- {elseif $xrefGroup->mXGroup eq 'stgrp'}{assign var=sgGroup value=$xrefGroup}
- {else}{include file=... xrefGroup=$xrefGroup ...}{/if}
-{/foreach}
-{if $isKitlocker && $klGroup}{include ...}{/if}
-{if $isKitlocker && $sgGroup}{include ...}{/if}
-```
-
-**movement edit_movement.php** filters 'reference' group in template:
-`{if $xrefGroup->mXGroup neq 'reference'}` — reference is rendered directly in the form.
-
-**CSV import xorder** — `ImportContactCSV.php` explicitly sets xorder: 0 for single items,
-1 for #P/#F (multiple=1) - will need to address when more than one record needed.
-
-**`contact_types` in Contact** — separate from the display path. Populated by
-`loadXrefTypeList()` which queries sort_order=0 items (the 'type' group: `$00`, `$02`+).
-Used for `$isPerson` detection in `edit.php` and display templates. `loadXrefInfo()`
-deliberately excludes sort_order=0, so there is no overlap.
-
-**Raw LEFT JOINs in `Contact::load()`** — joins `liberty_xref` directly for `$00`
-(person name), `#S` (service address), `#L` (location), `IMG` (gallery). `IMG`, `#S`,
-`#L` have no live data.
-
-**Pending: Contact::load() cleanup** — remove the three commented-out dead joins; replace
-the active raw xref joins with the proper path: `$00` name from `loadXrefTypeList()` with
-`xkey_ext` added; `#S`/`#L`/`ap` from `loadXrefInfo()` address group (postcode join
-already present in `LibertyXrefGroup::loadXrefs()`); gallery association needs rethinking
-separately from the xref mechanism.
-
-View pages pass `allow_edit=false` (or omit), edit pages pass `allow_edit=true`.
-
-### Contact delete / expunge
-`edit.php` handles `expunge=1`: checks `p_contact_expunge`, calls `$gContent->expunge()`,
-redirects to `list_contacts.php`. `contact_date_bar.tpl` uses
-`{smartlink ... ifile="edit.php" expunge=1}`. `Contact::expunge()` deletes liberty_xref rows
-then calls `LibertyContent::expunge()`.
+### Package-specific notes
+Detail for individual packages lives in their own `CLAUDE.md` files:
+- `liberty/CLAUDE.md` — xref machinery (LibertyXrefType, dual-guid schema, display path,
+ parseDataHash, storeXref, owner change, Firebird GROUP BY)
+- `contact/CLAUDE.md` — person/business model, ContactPerson/ContactBusiness plan, SCREF,
+ load() cleanup, delete/expunge
+- `stock/CLAUDE.md` — file naming, movement model (REQN/PBLD/TRANS/ORDER), template
+ structure, multi-user kitelf filtering, getList() enriched fields
## Infrastructure
@@ -451,51 +240,9 @@ History lives entirely in the parent archive chain; no domain-level archives are
At the end of each productive session, append discoveries, decisions, and completed items to this file.
Use `/clear` to reset context when it gets bloated — this file re-orients the session.
-### 2026-06-14 — Stock multi-user (kitelf) + PBLD prebuild type
-
-**Kitelf filtering**
-- `list_movements.php` / `list_stock.php`: `?user_id=X` filter; creator names in
- list_movements are clickable filter links; breadcrumb shows kitelf name with link
- to their filtered list; `×` clear replaced by breadcrumb `<small>` pattern
-- `list_movements.php`: unified `part_content_id` replaces separate
- `assembly_content_id` / `component_content_id`; type-aware breadcrumb and qty column
- (assembly kit count vs component qty with PRT/SHT formatting)
-- `StockMovement::getList()`: `$partId`/`$partIsAsm` collapse the two part vars;
- unified `part_qty`/`part_qty_type` SELECT; `lc.user_id` added to SELECT;
- PBLD added to all `IN('REQN','TRANS','ORDER')` lists and sort subqueries
-- `view_movement.tpl`: updated to `<header>`/`<section>` pattern with kitelf breadcrumb
-
-**PBLD movement type**
-- New `PBLD` (Prebuild) movement type: assembly-only, BOM exploded, optional note
-- `add_prebuild.php` / `add_prebuild.tpl`: creates PBLD; `add_requisition` retired
- from UI (file kept); list_stock BOM "Create Requisition" → "Create Prebuild"
-- `edit_movement.php/tpl` / `view_movement.php/tpl`: `$isBuild` (REQN+PBLD) and
- `$isPbld` flags; PBLD shows "Build Date"/"Completed"/"In progress" labels;
- assembly picker and xref tab shown for both REQN and PBLD
-- `schema_inc.php`: PBLD registered in stockmovement reference xref items
-- `StockMovement::getDirection()`: PBLD explicit as 'O'; unknown types return ''
-
-**Owner change**
-- `edit_content_owner_inc.tpl` included in `edit_assembly.tpl` and `edit_movement.tpl`
-- `liberty_allow_change_owner` must be active + `p_liberty_edit_content_owner` permission
-- Enables reassigning `user_id` (kitelf ownership) from edit pages
-
-**Template cleanup**
-- Dead files removed: `center_list_assemblies.php`, `center_list_components.php`,
- `center_component_comments.php/.tpl`, `center_list_components.tpl`,
- `list_assemblies2.tpl`, `list_assemblies_simple.tpl`, `list_assemblies.tpl` (old)
-- Renamed: `stock_fixed_grid_inc.tpl` → `view_kitlocker.tpl`;
- `list_assemblies_simple.tpl` → `list_assemblies.tpl`
-- `assembly_icons_inc.tpl` and `assembly_nav.tpl` properly implemented —
- floaticons and breadcrumb extracted from `stock_simple_list_inc.tpl` into includes
-- `user_galleries.tpl`: inlined from `list_assemblies2.tpl`; sortby suppressed in
- user view; floaticons (print, add prebuild, stock levels); panel layout with
- `panel-heading` (title + KLID), `panel-body` (parsed_data), `panel-footer` (counts)
-
-**StockAssembly::getList() enriched**
-- Now provides per-row: `parsed_data` (via `parseDataHash`), `part_number` (#SUP first),
- `klid`, `component_count` (BOM lines), `prebuild_count` (PBLD kit total for assembly
- owner via correlated subquery — uses `mc.user_id = lc.user_id`)
+### 2026-06-14 — Stock multi-user kitelf filtering + PBLD prebuild type
+Stock template cleanup; kitelf `user_id` filtering across list pages; PBLD movement type;
+owner change on assembly/movement edit pages. Detail in `stock/CLAUDE.md`.
## CC Limitations
For execution-order bugs and session/config state problems,