Kaizen
Browse modulesPricingpricing/sharedSchemas and constants

Variable: PriceTierFormValue

const PriceTierFormValue: ZodObject<{
  capacityRules: ZodArray<ZodObject<{
     operator: ZodEnum<{
        between: "between";
        greater_than: "greater_than";
        less_than: "less_than";
     }>;
     secondValue: ZodNullable<ZodNumber>;
     value: ZodNumber;
  }, $strip>>;
  defaultPrice: ZodOptional<ZodObject<{
     amountCents: ZodNullable<ZodNumber>;
     durationUnit: ZodOptional<ZodNullable<ZodEnum<{
        day: "day";
        hour: "hour";
        night: "night";
     }>>>;
     isFree: ZodBoolean;
     pricingUnit: ZodEnum<{
        day: "day";
        flat: "flat";
        hour: "hour";
        month: "month";
        night: "night";
        person: "person";
        unit: "unit";
        year: "year";
     }>;
  }, $strip>>;
  description: ZodNullable<ZodString>;
  maxQuantity: ZodOptional<ZodNullable<ZodNumber>>;
  membershipTierId: ZodNullable<ZodString>;
  name: ZodString;
  requireMembership: ZodBoolean;
  requireResidency: ZodBoolean;
  residencyType: ZodNullable<ZodEnum<{
     city: "city";
     state: "state";
     zip_code: "zip_code";
  }>>;
  residencyValues: ZodArray<ZodString>;
}, $strip>;

Defined in: shared/pricing/types/tier-form.ts:230

The value the "Add/Edit Price Tier" modal (PriceTierDialog, PLA-139) collects and hands back through its onSubmit callback. This is the client↔consumer contract only — NOT a persistence shape. The consumer's own backend procedure translates it into the real PricingMatrixService calls:

  • name/description → addRateTier/updateRateTier.
  • requireMembership + membershipTierId → a FIELD_COMPARISON eq condition, whose id is passed in addRateTier/updateRateTierRequirements's requirementConditionIds.
  • requireResidency + residencyType + residencyValues → a single FIELD_COMPARISON in condition whose field is chosen by residencyType and whose value is residencyValues.
  • defaultPrice → TWO calls, because the stored default and the existing cells are different things. Pass it as addRateTier/updateRateTier's defaultPrice so the tier remembers it and future schedule columns get seeded; then, if you want the columns that ALREADY exist to take it, call setCellPrice for each. The stored default deliberately never overwrites an existing cell, so that second step is the caller's explicit choice rather than a side effect. Map isFree to amountCents: 0 (the matrix expresses free as priced-at-zero), and a null amount on a non-free value to defaultPrice: null (no default). defaultPrice.durationUnit goes on BOTH halves — defaultPrice.per on the tier (so the fallback price and any later-seeded column carry the axis) and per on each setCellPrice. See PriceTierDefaultPrice.durationUnit.
  • capacityRules → one GROUP_SIZE condition each, referenced by id in requirementConditionIds (empty array = no capacity constraint). See CapacityRule.
  • maxQuantity → NO taproot call. The one field with no PricingMatrixService destination: purchase-quantity caps are order-time data, not pricing data (PLA-233), so you persist it yourself and enforce it with assertTierQuantityLimits at your own write boundary. See the field's doc.

The superRefine enforces the cross-field invariants so a server consumer can trust parse (the dialog can't be the only guard): a requirement toggle that's on must carry its data, and one that's off must not carry stale data.