diff --git a/source/schemas/common/types/measure.json b/source/schemas/common/types/measure.json new file mode 100644 index 000000000..4d67ce7e5 --- /dev/null +++ b/source/schemas/common/types/measure.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ucp.dev/schemas/common/types/measure.json", + "title": "Measure", + "description": "Represents a quantitative value paired with its physical or standardized unit of measurement.", + "type": "object", + "additionalProperties": true, + "required": ["value", "unit"], + "properties": { + "value": { + "type": "number", + "description": "Precise fractional quantity." + }, + "unit": { + "type": "string", + "description": "Unit of measurement. MUST use 'each' to represent countable goods and SHOULD prefer using standard symbol (e.g., kg for kilogram, lb for pound) for other units." + } + } +} diff --git a/source/schemas/shopping/types/adjustment.json b/source/schemas/shopping/types/adjustment.json index c4fa91fbd..3f9cbdd50 100644 --- a/source/schemas/shopping/types/adjustment.json +++ b/source/schemas/shopping/types/adjustment.json @@ -44,8 +44,16 @@ "description": "Line item ID reference." }, "quantity": { - "type": "integer", - "description": "Signed quantity affected by this adjustment. Negative values represent reductions (e.g. returns); positive values represent additions (e.g. exchanges)." + "anyOf": [ + { + "type": "integer", + "description": "Signed quantity affected by this adjustment. Negative values represent reductions (e.g. returns); positive values represent additions (e.g. exchanges)." + }, + { + "$ref": "../../common/types/measure.json", + "description": "The precise signed fractional quantity and unit of the item affected by this adjustment. Negative values represent reductions (e.g. returns); positive values represent additions (e.g. exchanges)." + } + ] } } }, diff --git a/source/schemas/shopping/types/expectation.json b/source/schemas/shopping/types/expectation.json index ccd01c8e9..314d5dd62 100644 --- a/source/schemas/shopping/types/expectation.json +++ b/source/schemas/shopping/types/expectation.json @@ -26,9 +26,20 @@ "description": "Line item ID reference." }, "quantity": { - "type": "integer", - "minimum": 1, - "description": "Quantity of this item in this expectation." + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "description": "Quantity of this item in this expectation." + }, + { + "$ref": "../../common/types/measure.json", + "properties": { + "value": { "exclusiveMinimum": 0 } + }, + "description": "The precise fractional quantity and unit of the item in this expectation." + } + ] } } }, diff --git a/source/schemas/shopping/types/fulfillment_event.json b/source/schemas/shopping/types/fulfillment_event.json index 5d0ba7e7f..fab60625a 100644 --- a/source/schemas/shopping/types/fulfillment_event.json +++ b/source/schemas/shopping/types/fulfillment_event.json @@ -35,9 +35,20 @@ "description": "Line item ID reference." }, "quantity": { - "type": "integer", - "minimum": 1, - "description": "Quantity fulfilled in this event." + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "description": "Quantity fulfilled in this event." + }, + { + "$ref": "../../common/types/measure.json", + "properties": { + "value": { "exclusiveMinimum": 0 } + }, + "description": "The precise fractional quantity and unit of the item in this event." + } + ] } } }, diff --git a/source/schemas/shopping/types/line_item.json b/source/schemas/shopping/types/line_item.json index 0eba9ed05..7d0e269fa 100644 --- a/source/schemas/shopping/types/line_item.json +++ b/source/schemas/shopping/types/line_item.json @@ -22,9 +22,20 @@ "$ref": "item.json" }, "quantity": { - "type": "integer", - "description": "Quantity of the item being purchased.", - "minimum": 1 + "anyOf": [ + { + "type": "integer", + "description": "Quantity of the item being purchased.", + "minimum": 1 + }, + { + "$ref": "../../common/types/measure.json", + "properties": { + "value": { "exclusiveMinimum": 0 } + }, + "description": "The precise fractional quantity and unit of the item being purchased." + } + ] }, "totals": { "type": "array", diff --git a/source/schemas/shopping/types/order_line_item.json b/source/schemas/shopping/types/order_line_item.json index 5f639dcff..c851e1469 100644 --- a/source/schemas/shopping/types/order_line_item.json +++ b/source/schemas/shopping/types/order_line_item.json @@ -20,26 +20,58 @@ "description": "Product data (id, title, price, image_url)." }, "quantity": { - "type": "object", - "required": ["total", "fulfilled"], - "properties": { - "original": { - "type": "integer", - "minimum": 0, - "description": "Quantity from the original checkout." + "anyOf": [ + { + "type": "object", + "required": ["total", "fulfilled"], + "properties": { + "original": { + "type": "integer", + "minimum": 0, + "description": "Quantity from the original checkout." + }, + "total": { + "type": "integer", + "minimum": 0, + "description": "Current total active quantity. May differ from original due to post-order modifications (e.g., returns or cancellations)." + }, + "fulfilled": { + "type": "integer", + "minimum": 0, + "description": "Quantity fulfilled so far." + } + }, + "description": "Quantity tracking for the line item." }, - "total": { - "type": "integer", - "minimum": 0, - "description": "Current total active quantity. May differ from original due to post-order modifications (e.g., returns or cancellations)." - }, - "fulfilled": { - "type": "integer", - "minimum": 0, - "description": "Quantity fulfilled so far." + { + "type": "object", + "required": ["total", "fulfilled"], + "properties": { + "original": { + "$ref": "../../common/types/measure.json", + "properties": { + "value": { "minimum": 0 } + }, + "description": "The fractional quantity and unit of the item in the original checkout." + }, + "total": { + "$ref": "../../common/types/measure.json", + "properties": { + "value": { "minimum": 0 } + }, + "description": "Current total active fractional quantity. May differ from original due to post-order modifications (e.g., returns or cancellations)." + }, + "fulfilled": { + "$ref": "../../common/types/measure.json", + "properties": { + "value": { "minimum": 0 } + }, + "description": "Fractional quantity fulfilled so far." + } + }, + "description": "Precise fractional tracking for the line item." } - }, - "description": "Quantity tracking for the line item." + ] }, "totals": { "type": "array", diff --git a/source/schemas/shopping/types/product.json b/source/schemas/shopping/types/product.json index 95c4fbdf8..40de390b7 100644 --- a/source/schemas/shopping/types/product.json +++ b/source/schemas/shopping/types/product.json @@ -79,7 +79,7 @@ "items": { "type": "string" }, - "description": "Product tags for categorization and search." + "description": "Product tags for categorization, search, and compliance rules (e.g., restricted goods)." }, "metadata": { "type": "object", diff --git a/source/schemas/shopping/types/variant.json b/source/schemas/shopping/types/variant.json index 9355b5f1a..efe00f1c6 100644 --- a/source/schemas/shopping/types/variant.json +++ b/source/schemas/shopping/types/variant.json @@ -84,13 +84,8 @@ "description": "ISO 4217 currency code." }, "measure": { - "type": "object", - "description": "Product quantity in packaging (e.g., 750ml bottle).", - "required": ["value", "unit"], - "properties": { - "value": { "type": "number", "description": "Package quantity." }, - "unit": { "type": "string", "description": "Unit of measurement." } - } + "$ref": "../../common/types/measure.json", + "description": "Product quantity in packaging (e.g., 750ml bottle)." }, "reference": { "type": "object",