Rate Rounding Rules
A rate rounding rule is a named price convention for a workspace, such as “round down to the nearest 5, ending in .99”. A product group that derives its rates from a parent can select one rule, and its derived nightly, weekly and monthly prices then land on that convention instead of on the exact figure the percentage produces. Several groups can select the same rule, so editing the rule changes all of them.
Key Concepts
Section titled “Key Concepts”What a rule contains
Section titled “What a rule contains”A rule has a name and one currency-neutral definition:
| Part | Meaning |
|---|---|
stepUnits |
Whole major currency units between allowed prices, from 1 to 100. |
ending |
Where an allowed price sits relative to each multiple of the step. |
direction |
How the starting price is picked: DOWN, UP or NEAREST. A tie in NEAREST takes the higher price. |
The ending is either FRACTION, with hundredths from 0 to 99 (for example 99 for .99), or ONE_UNIT_UNDER,
one major unit below each multiple. ONE_UNIT_UNDER needs a step greater than 1 and takes no hundredths: leave
the field out when you send it. In responses hundredths is null for that ending.
A derived group never ends up at or above the price of its parent when its percentage is negative. In that case the price steps to the allowed price below.
Versions
Section titled “Versions”Every rule has an integer version. A new rule starts at version 0. A change to the name or definition moves it
up by one. Changing a rule or deleting it needs the expectedVersion you last read. If someone else changed the rule
in between, the request fails with RATE_ROUNDING_RULE_VERSION_CONFLICT and nothing is written. Read the rule
again and retry.
Selecting a rule on a group
Section titled “Selecting a rule on a group”A group selects a rule through its derived rate strategy, in one of three ways:
assignRoundingRuleselects a rule and keeps the group’s percentage and parent as they are.unassignRoundingRuleturns rounding off and keeps the percentage and parent.rateStrategy.updatesaves the percentage and the rule together. LeavingroundingRuleIdout keeps the current selection. Sendingnullclears it.
A group and its rule must be in the same workspace. A new or restored derived strategy starts with rounding off.
When prices change
Section titled “When prices change”Saving a selection on a group changes that group’s prices straight away. Editing a rule’s arithmetic or deleting a rule reprices every group that selects it in the background. The response to those requests means the change was saved, not that every price has been recalculated, so stored prices can briefly lag.
Deleting a rule clears it from every group that selected it, including archived groups. Those groups go back to unrounded prices. Creating a rule or renaming it never changes prices.
Prices of existing bookings and quotes you have already issued are not recalculated when a rule is edited or deleted. Only the catalog prices used for new bookings and new quotes change.
GraphQL API
Section titled “GraphQL API”RateRoundingRule
Section titled “RateRoundingRule”| Field | Type | Description |
|---|---|---|
id |
ID! |
Identifier used to read, update, delete or assign the rule. |
name |
String! |
Label for the rule, up to 150 characters. |
version |
Int! |
Content revision used for optimistic concurrency. |
definition |
RateRoundingDefinition! |
The arithmetic: stepUnits, ending and direction. |
usage |
RateRoundingUsage |
status (IN_USE or NOT_IN_USE) and productGroupCount. |
usage is null, with an error, if it cannot be worked out. The rest of the rule is still returned. A missing usage
is never reported as zero groups.
DerivedProductRateStrategy.roundingRule
Section titled “DerivedProductRateStrategy.roundingRule”roundingRule: RateRoundingRule on a derived product group’s rate strategy is the rule the group currently selects.
It is null when rounding is off.
Queries
Section titled “Queries”List the rules of a workspace
Section titled “List the rules of a workspace”You need permission to read the workspace and permission to read its product groups, whichever route you use:
awc.workspace, as below, or a catalog route such as catalog.productGroup(id).workspace.rateRoundingRules. Rules
are ordered by name and then ID.
A page holds at most 25 rules and first defaults to 25. The name filter takes the usual string operators.
query RateRoundingRules($workspace: ID!) { awc { workspace(id: $workspace) { rateRoundingRules(first: 25) { nodes { id name version definition { stepUnits ending { kind hundredths } direction } usage { status productGroupCount } } pageInfo { hasNextPage endCursor } totalCount } } }}{ "workspace": "workspace-id" }An authorised caller with no rules gets an empty list. A caller without access gets an error and null for the
collection.
Get a rule
Section titled “Get a rule”query RateRoundingRule($id: ID!) { catalog { rateRoundingRule(id: $id) { id name version definition { stepUnits ending { kind hundredths } direction } usage { status productGroupCount } } }}{ "id": "rule-id" }A rule that does not exist, was deleted, or belongs to a workspace where the caller holds none of the workspace
permissions returns null with the error code RATE_ROUNDING_RULE_NOT_FOUND. The three cases look the same, so the
response never confirms that a rule exists. The same applies to roundingRule on a group’s derived rate strategy,
to reading a rule’s usage and to updating or deleting a rule.
Get the rule a group selects
Section titled “Get the rule a group selects”query ProductGroupRounding($id: ID!) { catalog { productGroup(id: $id) { rateStrategy { ... on DerivedProductRateStrategy { modifierPercentage roundingRule { id name definition { stepUnits ending { kind hundredths } direction } } } } } }}{ "id": "product-group-id" }Mutations
Section titled “Mutations”Each mutation returns only an id. Read the rule or the group again to see the stored result.
Create a rule
Section titled “Create a rule”mutation CreateRateRoundingRule($input: CreateRateRoundingRuleMutationInput!) { catalog { rateRoundingRule { create(input: $input) { id } } }}{ "input": { "workspaceId": "workspace-id", "rule": { "name": "Nightly convention", "definition": { "stepUnits": 5, "ending": { "kind": "FRACTION", "hundredths": 99 }, "direction": "DOWN" } } }}Creating a rule twice creates two rules. clientMutationId is accepted but not returned, and it does not
deduplicate.
Update a rule
Section titled “Update a rule”Send only what changes. A definition replaces the whole arithmetic. An empty rule or an explicit null is
rejected.
mutation UpdateRateRoundingRule($input: UpdateRateRoundingRuleMutationInput!) { catalog { rateRoundingRule { update(input: $input) { id } } }}{ "input": { "ruleId": "rule-id", "expectedVersion": 0, "rule": { "name": "Nightly convention revised" } }}Delete a rule
Section titled “Delete a rule”mutation DeleteRateRoundingRule($input: DeleteRateRoundingRuleMutationInput!) { catalog { rateRoundingRule { delete(input: $input) { id } } }}{ "input": { "ruleId": "rule-id", "expectedVersion": 1 } }Select or clear a rule on a group
Section titled “Select or clear a rule on a group”mutation AssignRoundingRule($input: AssignProductGroupRoundingRuleMutationInput!) { catalog { productGroup { rateStrategy { assignRoundingRule(input: $input) { id } } } }}{ "input": { "productGroupId": "product-group-id", "roundingRuleId": "rule-id" } }mutation UnassignRoundingRule($input: UnassignProductGroupRoundingRuleMutationInput!) { catalog { productGroup { rateStrategy { unassignRoundingRule(input: $input) { id } } } }}{ "input": { "productGroupId": "product-group-id" } }Save the percentage and the rule together
Section titled “Save the percentage and the rule together”mutation UpdateRateStrategy($input: UpdateProductGroupRateStrategyMutationInput!) { catalog { productGroup { rateStrategy { update(input: $input) { id } } } }}{ "input": { "productGroupId": "product-group-id", "rateStrategy": { "strategy": "DERIVE_FROM_PARENT", "derived": { "modifierPercentage": -5, "roundingRuleId": "rule-id" } } }}Errors
Section titled “Errors”| Code | Meaning |
|---|---|
VALIDATION_ERROR |
The name or definition is not valid, or a rule ID is empty. The error lists which input fields to fix. |
RATE_ROUNDING_RULE_VERSION_CONFLICT |
expectedVersion is out of date. Read the rule and try again. |
RATE_ROUNDING_RULE_NOT_FOUND |
The rule does not exist, was deleted, or is in a workspace where the caller has no access. While the permission service is unreachable, a rule can also read as not found. |
USER_NOT_ALLOWED_ERROR |
The caller has some access to the workspace but not the permission this needs, or the permission check returned an error. Reading and writing are separate permissions. |
Error messages are fixed text and never repeat what you sent.
Relationships
Section titled “Relationships”- A rule belongs to one Workspace
- A rule is selected by derived Product Groups
- A rule changes the prices held on Products for the groups that select it