diff options
| -rw-r--r-- | CLAUDE.md | 275 |
1 files changed, 11 insertions, 264 deletions
@@ -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, |
