Repository navigation
QE-06: the server prices a configured item from the stored options - #266
Open
futurebuildai wants to merge 6 commits into
Open
futurebuildai wants to merge 6 commits into
futurebuildai wants to merge 6 commits into
Conversation
… (QE-06) A configured item is a door or a millwork piece the dealer configures rather than picks off the shelf. Until now the configurator only made a name for it and the client priced it: the desk computed a price in the browser and sent it with the line, so the price on the quote was the dealerer's number, not the business's. The option catalog now carries the money. base_price_cents and base_cost_cents are what an option contributes when it names the item (the door type row carries the door's base, the species row the board's), price_adjustment and cost_adjustment_cents are what it adds on top. The server prices a configuration as the sum over its selected options of those two, in integer cents, and answers the wire in ten-thousandths. The client sends the choices and reads the money; a price the client sends is read and ignored. The configured item is a product row of its own, marked non-stock: the SKU is NS- plus the product type's short name plus the hash of the canonical configuration, so the same configuration always gives the same SKU and the same row, and two different ones never collide. A non-stock row is left out of the stock reads, the replenishment and the counter's stocked search: a configured item is never counted and never reordered. The configuration travels with the item. It is stored on the quote, order and invoice lines, and the documents show the item as a short description rather than the raw configuration. The purchase order line carries the description only: it is written to the vendor, and the vendor reads the SKU and the description. The desk gains what the dealer asked for: a Configure a door entry in the Quote Builder that opens the configurator beside the quote, a live Add to Quote on the configured item, an Add to Order on the priced door, and a Desk option catalog screen where the dealer sets the money the server prices from. Red first: the build-sku and estimate routes answer the stored options' sum to the cent, the same configuration gives one SKU and one product row, a quote line that carries a configuration is priced by the server whatever the client sent, and the configured row stays out of the stock counts. Signed-off-by: colton <colton@futurebuild.ai>
The branch bound key's route allowlist is deny by default, and the census test holds it against every machine key reachable route in both directions. The configured item's estimate route and the option update route arrived without a decision, so the gate refused them and both gates failed by name. The estimate is a stated limit, like its twin the configurator's build-sku: it prices the dealer wide option catalogue and carries no branch fact. The option update is refused, like the option create: the catalogue is dealer wide and a write to it is not branch scoped. Signed-off-by: colton <colton@futurebuild.ai>
…QE-06) A configured item is made to the configuration, so the dealer holds no stock of it and it is bought from the vendor. The order line it became now says so: the convert raises the purchase order for it, and the purchase order line names the item the way the vendor reads it, the SKU and the short description. An ordinary stocked line is untouched: it is served from the stock the dealer allocated and is never a special order line. The tests carry the names the brief names: TestQE06_BuildSKUServerPrice, TestQE06_SameConfigSameProduct, TestQE06_ConcurrentBuildSKUOneRow (pool size 4, three contenders), TestQE06_ClientPriceIgnored, TestQE06_ConfiguredLineConvertsToSpecialOrderPO and TestQE06_NonStockNotInStockCounts. Signed-off-by: colton <colton@futurebuild.ai>
The configurators had the buttons live but no way to say which document the item belongs on: Add to Quote and Add to Order offered the item and left the dealer to find it. Both configurators now open a picker: the draft quotes or the editable orders, and a way to start a new draft quote. The dealer picks, the item is handed over and the dealer walks to that document, where the item lands on the line at the server's price with its configuration. The order page takes the item too: a draft order gets the line and is saved with it. The configurator hosted by the quote builder keeps its old way: the item goes straight onto the quote the builder is holding, and no picker opens. Red first: the picker lists the drafts and says which one was picked, the door configurator shows the server's price and keeps both buttons live, and the quote builder's configurator adds the item to the quote it holds. Signed-off-by: colton <colton@futurebuild.ai>
The walk now covers the dealer's own path: the door configurator, Add to Quote, the picker, a new draft quote, the line at the server's price with its configuration, the convert, and the vendor purchase order the configured line raised. Signed-off-by: colton <colton@futurebuild.ai>
…QE-06) The catalog's category was folded in SQL and the configuration's in Go, and the two folds disagreed on an upper case letter: SQL replaced the letter with a separator before it lower cased the name, while Go lower cased first and kept the letter. A dealer whose catalog spelled a category with capitals priced every configured item at zero, with no refusal to show for it. Both sides fold the same way now: lower case first, then every run that is neither a letter nor a digit becomes one underscore. Red first: the estimate wire test prices a configuration whose catalog spells its categories with capitals, and it priced at zero. The wire test also covers the two new routes: the estimate answers the stored options' sum to the cent, and the option update sets the money the server prices from on the revision it names. Signed-off-by: colton <colton@futurebuild.ai>
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
A configured item is a door or a millwork piece the dealer configures rather
than picks off the shelf. Until now the configurator only made a name for it
and the client priced it: the desk computed a price in the browser and sent
it with the line, so the price on the quote was the dealer's number, not the
business's.
This PR makes the server own the price of a configured item.
The catalog carries the money.
millwork_optionsgainsbase_price_cents,base_cost_centsandcost_adjustment_cents. The base iswhat an option contributes when it names the item (the door type row carries
the door's base price and base cost, the species row the board's); the
adjustments are what it adds on top. The server prices a configuration as the
sum over its selected options of base plus adjustment, in integer cents, and
answers the wire in ten-thousandths. The client sends the choices and reads
the money; a price the client sends is read and ignored.
The configured item is a product row of its own, marked non-stock. The SKU
is
NS-plus the product type's short name plus the hash of the canonicalconfiguration, so the same configuration always gives the same SKU and the
same row, and two different configurations never collide. A non-stock row is
left out of the stock counts, the replenishment and the counter's stocked
search: a configured item is never counted, never reordered and never offered
at the counter.
The configuration travels with the item. It is stored on the quote, order
and invoice lines, and the documents show the item as a short description
rather than the raw configuration.
A configured item is a special order. The dealer holds no stock of a door
made to a configuration, so the order line it becomes is a special order line
and the convert raises the vendor purchase order for it. That purchase order
line carries the short description: the vendor reads the SKU and the
description, not the configuration.
The desk gains what the dealer asked for. A "Configure a door" entry in
the Quote Builder opens the configurator beside the quote. Both
configurators carry a live "Add to Quote" and "Add to Order": they open a
picker that says where the item goes (the draft quotes or the editable
orders, and a way to start a new draft quote), the dealer picks, and the item
lands on that document's line at the server's price with its configuration.
The configurator hosted by the Quote Builder keeps its old way: the item goes
straight onto the quote the builder is holding. A Desk option catalog screen
is where the dealer sets the money the server prices from.
Routes
POST /api/v1/millwork/estimatePUT /api/v1/millwork/options/{id}POST /api/v1/configurator/build-skuComponent(s) touched
core/migrations/(162 and its down sibling),core/internal/millwork/,core/internal/configurator/,core/internal/product/,core/internal/quote/,core/internal/order/,core/internal/invoice/,core/internal/salesdoc/,core/internal/pos/,core/internal/inventory/,core/internal/dashboard/,core/internal/purchase_order/,core/internal/app/serve/,core/internal/app/seed/,core/api/fragments/web/apps/desk/(the configurators, the Quote Builder, the option catalogscreen, the services and the types),
web/packages/api-client/(thegenerated types),
web/e2e/Type of change
Pre-flight checklist
Run the gates locally before pushing — CI runs them too, but failing locally is
faster.
Backend (
cd core):go build ./...go vet ./...go test ./...Frontend (
cd web/apps/desk):npx tsc --noEmitnpm run lintnpm run buildGeneral:
staging, notmain. (It targetsrefactor/v1, thebranch this lane is cut from, like every other slice PR.)
DECIMAL(19,4)forquantities, money-as-cents in app code, every quantity paired with a UOM).
RegisterRoutesincore/internal/app/serve/serve.go.CONTRIBUTING.mdand agree to license my contribution underthe OpenLBM Standard license governing the file(s) I touched (via the CLA).
AUTH_MODE=devis not introduced into any reachable/production config.Migration notes
Migration 162 adds:
products.is_non_stock(boolean, defaults to FALSE, with its own index):the mark that keeps a configured row out of the stock reads.
millwork_options.base_price_cents,base_cost_cents,cost_adjustment_cents(bigint, default 0): the money the server pricesfrom.
quote_lines.configuration_json,order_lines.configuration_jsonandinvoice_lines.configuration_json(jsonb): the configuration on the line.Every existing row keeps its present meaning: an option with no money
contributes nothing, and a row with no mark is a stocked product. The down
sibling is in
core/migrations/down/162_configured_items_down.sql.The purchase order line carries no configuration column on purpose: a purchase
order is written to the vendor, and its line already shows the configured item
as a short description.
Screenshots / notes for reviewers
The money never leaves the server. The desk's client-side price calculation is
gone: the configurator asks the server for the price and shows what it
answers. A dealer who changes an option's base price sees the configured
item's price change with it.
The tests:
core/internal/configurator/qe06_test.goandcore/internal/quote/qe06_test.gocarry the
TestQE06_*names:TestQE06_BuildSKUServerPrice(the estimateand the build-sku answer the stored options' sum to the cent),
TestQE06_SameConfigSameProduct,TestQE06_ConcurrentBuildSKUOneRow(poolsize 4, three contenders),
TestQE06_ClientPriceIgnored,TestQE06_ConfiguredLineConvertsToSpecialOrderPOandTestQE06_NonStockNotInStockCounts.web/apps/desk/src/lib/configured-item.test.ts,web/apps/desk/src/components/configured/ConfiguredTargetPicker.test.ts,web/apps/desk/src/pages/millwork/DoorConfigurator.test.tsandweb/apps/desk/src/pages/QuoteBuilder.configured.test.tscover thehandoff, the picker, the server price and the quote line.
web/e2e/configured-items.spec.tsdrives the real stack: the server'sprice, the same configuration's same row, the configured line priced by the
server, the desk's Configure a door flow beside the quote, and the
configurator's own path: Add to Quote, the picker, a new draft quote, the
line at the server's price, the convert, and the vendor purchase order the
configured line raised.
Built by MiniMax M3, the coder lane of the QE section lead.