Repository navigation
Places contain places and stock and space by any field — and #31's document flows, which never reached main - #32
Merged
Merged
Conversation
…nt, transfers go through transit, receipts and returns land in the right place, one pull list, scoped allocation, and an Oversold report Ledger plan PR 2 of 4 (phases 5-11; docs/domains/inventory.md, "Document flows"). Stacked on #30. - Landing places per store (receiving, returns, quarantine, transit, pickup), the store an order also draws from, and a move mode, set in Warehouse > Locations and validated to be places of the right type at that store. - 5: the POS stock import plans one physical target per place from the whole file (aliases summed, zero rows honoured, dropped products zeroed at covered places) and sets free stock to physical minus committed; a second run writes nothing. The catch-all store is AppSettings.unmatchedStockStoreLocationId (Settings > Configuration), replacing a store found by the name "Warehouse" (rule 61); unset, unmatched rows are reported, not written (rule 63). DECISIONS #18. - 6: shipped units wait in the source store's transit place; receipts can be partial; cancel returns units to the exact places they left from the journal; an order's transfer carries its commitment; quick vs tracked move modes. - 7: damaged/wrong goods go to quarantine; a PO line for an order arrives committed to it; zero-quantity lines leave the PO status count (rule 39). - 8: owner direction -- a return never holds up the customer (DECISIONS #19). RECEIVED puts the piece in the store's returns place; a per-store returned- stock queue puts it back or writes it off; the journal reads that recorded disposition. Return.quantity is fractional. The New Return screen sent orderId to a route that reads salesOrderId, so every return from it failed. - 9: one pull-list builder for run and order pick lists, active lines only (rule 33), from the order's own committed stock. - 10: allocation draws back stock in reach (own store, then its fulfilment store) before floor models; take-with takes its own store's floor piece first; order resync is a per-product delta so commitments in transit stay. - 11: one open exception per order and product, closed automatically once covered; Reports > Oversold with suggested fixes and CSV; dashboard counts. Part rolls parse as fractions on the transfer, receive and return forms. The returns status route takes sales.return (warehouse staff work the queue); the inventory-exceptions page takes inventory.adjust like its API. Tests: importSync (unit), allocationScope (unit), and stockImportSync, stockTransfer, stockReceive, stockReturns, pullList, allocationScope, oversoldReport integration suites; existing suites updated. Seven mutations (alias summing, delta resync, committed receipts, active-lines filter, draw order, disposition claim, cancel legs) each fail their test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cation.parentId nests buildings, floors and bays, a GROUP holds only places, and Reports › Stock and Space splits each measured floor over the units in it Ledger plan phases 12-14 (docs/domains/inventory.md). - Migration 20261018120000_places_contain_places turns the free-text building/floor columns into GROUP places, parents every place, drops building/floor/area, and refuses to finish if a place would lose its building or floor. - lib/inventory/placeTree.ts holds the tree's rules for every writer (rule 42): same-store active parent, no loops, no group made of a place holding stock or landing it, no deactivating a place with active places inside, no delete with places inside. The ledger refuses stock into a group; a landing place never points at one (landingAllows). - Warehouse > Locations shows the tree, with "Inside" and square footage for every type (it was sent only for floor places, so saving any other place wiped it). - lib/reports/stockBy.ts and /app/reports/stock-by: units and space inside every store, a store or a place, by place, place type or product field. - The stock-locations preset says `inside`, refuses the retired fields instead of dropping them, and exports `inside` only for an active parent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR 3 of the stock-ledger plan (
docs/domains/inventory.md→ "Delivery"): phases 12–14, places and space.This PR also brings PR 2 to
main. #31 merged into its stacked base,feat/stock-places-and-journal, 27 seconds after #30 merged that branch intomain, so #31's work never reachedmain. This branch ismainplus that same commit (its tree is identical to #31's merge,a4129e1), plus this PR's. One PR and one CI run instead of two. The first commit was already reviewed as #31; the second is new.12. Places contain places.
StockLocation.parentIdnests places as deep as a store wants: North Barn › Floor 2 › Stock Sofas › Shelf 3. A small store stays flat. A newGROUPtype (a building, a floor) holds only places, and the ledger refuses stock into one. One set of rules inlib/inventory/placeTree.ts, used by the routes and the preset (rule 42):Warehouse › Locations shows each store's places as a tree, with an "Inside" picker. Square footage can now be entered for any place. Before, it was sent only for floor places, so saving any other place wiped its footage.
The migration (
20261018120000_places_contain_places) turns the free-textbuilding/floorcolumns into GROUP places and nests every place inside them.areawas an index that each place's code already carried, so it is dropped. The migration refuses to finish if any place would lose its building or floor.13. Stock and space (Reports › Stock and Space). Look inside every store, a store, or any place, and group by the places inside it, place type, department, category, type, vendor or collection.
14. The stock-locations preset says where a place is with
inside. It refuses the retiredbuilding/floor/areafields rather than dropping them: dropping them would move every place of an old file to the top of its store. An export always applies back unchanged.What changed
StockLocation.parentId(FKON DELETE RESTRICT);BLDG-<name>andFLR-[<building>-]<n>, parented to their building;building,floorandarea.inventory/placeTree.ts(descendantIds,ancestry,placePath,treeOrder,placeParentProblem,placeTypeProblem,placeDeactivateProblem) andreports/stockBy.ts;locationTypes.ts:GROUPandholdsStock;ledger.ts:assertPlaceAtStorerefuses a group;landingPlaces.ts:landingAllows, so "any type" means any type that holds stock, shared by the store route and the screen;presetSchema(inside, loop and unknown-code checks, retired fields refused),presetSerialize,dbConfigState(exportsinsideonly for an active parent),applyPreset(parents set after every listed place exists; refuses making a group of a place holding stock).parentId;/api/reports/stock-by.reporting.read).inventory.md: "Places contain places"; phases 12–14 ticked; place labels moved to phase 15, where the scanner reads them;config-presets.md:insideand the retired fields;reporting.md: Stock and Space, plus the Oversold row it was missing.Trace / Verification
npm run setup -- --scale=ci(all migrations, then the seed):prisma migrate diff --exit-codeprinted No difference detected.placeParentProblem:stockPlaceTree1/7 failed;stockPlaceTree2/7,stockLocationsPreset1/13;splitSpaceBy2/6,stockBy4/6;stockLocationsPreset(unit) 1/13;stockPlaceTree1/7;stockLocationsPreset1/13;stockPlaceTree1/7.landingAllows, and after a reload no landing-place picker offers it.?by=colouranswers 400 with the list of fields.building/floor/areahave no readers left insrc/,prisma/seedor the shippedconfig/presets.landingPlaceProblem) andLocationsView; both now calllandingAllows.placeTypeProblemis called from the stock-location update route andapplyPreset;placeParentProblemfrom both stock-location write routes.Scan / gate state
npm run check:localon the branch:SONAR_TOKENlocallyThe run covered the final code: nothing changed after it except the rebase onto
main, whose resulting tree is byte-identical to the tree that was checked. CI has never run on the first commit (#31's work): the workflows run only on PRs intomain, and #31 targeted a feature branch. This PR is its first CI run.Not in this PR, and why
L-<id>) move to phase 15 (PR 4), with the scanner that reads them.stockBy.tsandreporting.md.Test plan
🤖 Generated with Claude Code