Price is a rate function, not a scalar (#22), and #15 bars any model from evaluating one — so the shapes have to be closed AND computable. This replaces the src/pricing seam with a composition rather than an enumeration: one base schedule deciding unit price, plus closed modifiers, plus an optional floor. Three vendors on one small RFP already produced five cells of the alternative cross-product, and two of the three are the same object wearing different decoration. The scenario vocabulary is closed too, and published with the RFP, so every bid in a field is priced against identical assumptions. A structure reading a variable the RFP never declared is rejected at submission, while the vendor can still fix it. Abstention is typed. `input_missing` is separated from `shape_unsupported` because Chesapeake's shape is fully supported and its unit price simply is not in the document — that is recoverable by asking, and collapsing the two throws away the only actionable fact. Null is never zero, never the vendor's stated figure, and never a partial sum. The derivation is the record. A bare scalar can only assert; a protest asks why a bid ranked where it did. Lines accumulate as exact rationals in bigint and round once, half-up. A scenario amendment voids derived costs rather than recomputing them. Exceptions carry a closed consequence union. Anacostia's EX-3 says the vendor cannot bid produce at all if declined — a nullable price delta reads that as "no cost impact", the inverse of the truth, on the most consequential of the three. Two defects the corpus check caught that review would not have: - The band ladder priced two ways depending on a flag. `to` is inclusive, and subtracting those bounds in the marginal path lost the unit between bands — one in 21,400, invisible in the total and wrong in the record. - The published scenario was quantizing the price before the evaluator saw it. Twelve basis-point shares moved the seasonal factor by 3e-5, about $19 on a $624,000 line. Weights, not shares: a school calendar publishes instructional days, an exact integer, and the ratio is taken last. npm run check:pricing Potomac computes $589,570.00 and not the stated $589,970.00; Chesapeake abstains naming base.unitPrice; Anacostia is $649,792.00 with the calendar and unpriceable without it; a 3-of-4-line bid withholds its total. Migrations generated in two passes so drizzle-kit never needed the interactive rename prompt. No SQL hand-edited. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
31 lines
2.9 KiB
SQL
31 lines
2.9 KiB
SQL
CREATE TYPE "public"."scenario_variable_name" AS ENUM('siteCount', 'deliveriesPerWeek', 'instructionalWeeks');--> statement-breakpoint
|
|
CREATE TYPE "public"."exception_consequence" AS ENUM('price_delta', 'withdrawal', 'unquantified');--> statement-breakpoint
|
|
CREATE TYPE "public"."unpriced_reason" AS ENUM('shape_unsupported', 'input_missing', 'line_incomplete', 'withdrawal_conditioned');--> statement-breakpoint
|
|
CREATE TABLE "scenario_variable" (
|
|
"scenario_id" uuid NOT NULL,
|
|
"name" "scenario_variable_name" NOT NULL,
|
|
"value" numeric(14, 3) NOT NULL,
|
|
CONSTRAINT "scenario_variable_scenario_id_name_pk" PRIMARY KEY("scenario_id","name")
|
|
);
|
|
--> statement-breakpoint
|
|
ALTER TABLE "scenario_variable" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
|
|
ALTER TABLE "derived_cost" ALTER COLUMN "amount" DROP NOT NULL;--> statement-breakpoint
|
|
ALTER TABLE "evaluation_scenario" ADD COLUMN "monthly_weights" integer[];--> statement-breakpoint
|
|
ALTER TABLE "price_line" ADD COLUMN "required" boolean DEFAULT true NOT NULL;--> statement-breakpoint
|
|
ALTER TABLE "derived_cost" ADD COLUMN "reason" "unpriced_reason";--> statement-breakpoint
|
|
ALTER TABLE "derived_cost" ADD COLUMN "amount_if_declined" numeric(16, 2);--> statement-breakpoint
|
|
ALTER TABLE "derived_cost" ADD COLUMN "reason_if_declined" "unpriced_reason";--> statement-breakpoint
|
|
ALTER TABLE "derived_cost" ADD COLUMN "breakdown" jsonb NOT NULL;--> statement-breakpoint
|
|
ALTER TABLE "derived_cost" ADD COLUMN "evaluator_version" integer NOT NULL;--> statement-breakpoint
|
|
ALTER TABLE "derived_cost" ADD COLUMN "inputs_hash" text NOT NULL;--> statement-breakpoint
|
|
ALTER TABLE "derived_cost" ADD COLUMN "voided_by_amendment_id" uuid;--> statement-breakpoint
|
|
ALTER TABLE "exception" ADD COLUMN "target_section" text;--> statement-breakpoint
|
|
ALTER TABLE "exception" ADD COLUMN "labelled" boolean NOT NULL;--> statement-breakpoint
|
|
ALTER TABLE "exception" ADD COLUMN "consequence" "exception_consequence" NOT NULL;--> statement-breakpoint
|
|
ALTER TABLE "exception" ADD COLUMN "amount_if_declined" numeric(14, 2);--> statement-breakpoint
|
|
ALTER TABLE "price_quote" ADD COLUMN "attachment_document_id" uuid;--> statement-breakpoint
|
|
ALTER TABLE "scenario_variable" ADD CONSTRAINT "scenario_variable_scenario_id_evaluation_scenario_id_fk" FOREIGN KEY ("scenario_id") REFERENCES "public"."evaluation_scenario"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
ALTER TABLE "price_quote" ADD CONSTRAINT "price_quote_attachment_document_id_response_document_id_fk" FOREIGN KEY ("attachment_document_id") REFERENCES "public"."response_document"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
CREATE POLICY "scenario_variable_read" ON "scenario_variable" AS PERMISSIVE FOR SELECT TO public USING (class_released(release_level_for_solicitation(
|
|
(SELECT solicitation_id FROM evaluation_scenario WHERE id = scenario_id)
|
|
), 'scenario_quantity')); |