Skip to content

feat(discount): add gift_line_item_id to applied discount#578

Open
prateek-ct wants to merge 4 commits into
Universal-Commerce-Protocol:mainfrom
commercetools:discount-gift-line-items
Open

feat(discount): add gift_line_item_id to applied discount#578
prateek-ct wants to merge 4 commits into
Universal-Commerce-Protocol:mainfrom
commercetools:discount-gift-line-items

Conversation

@prateek-ct

Copy link
Copy Markdown

Description

Some promotions grant a free or discounted line item (buy-one-get-one, free-gift-with-purchase) rather than reducing the price of an item the buyer already has. Today applied_discount can only describe an amount-based effect — a business granting a gift line item has no structured way to link it back to the discount that produced it, so a platform/agent sees an unexplained zero-price item in line_items[].

This adds an optional gift_line_item_id field to applied_discount, pointing at the line_items[] entry the discount granted. It's omitted for ordinary amount-based discounts, so this is backward compatible.

Motivated by implementing the discount extension against a commerce backend whose "gift line item" cart discount type is a common, first-class promotion pattern — this gap surfaced immediately when mapping that feature onto the current schema.

Category (Required)

  • Core Protocol: Changes to the base communication layer, global context, or breaking refactors. (Requires Technical Council approval)
  • Governance/Contributing: Updates to GOVERNANCE.md, CONTRIBUTING.md, or CODEOWNERS. (Requires Governance Council approval)
  • Capability: New schemas (Discovery, Cart, etc.) or extensions. (Requires Maintainer approval)
  • Documentation: Updates to README, or documentations regarding schema or capabilities. (Requires Maintainer approval)
  • Infrastructure: CI/CD, Linters, or build scripts. (Requires DevOps Maintainer approval)
  • Maintenance: Version bumps, lockfile updates, or minor bug fixes. (Requires DevOps Maintainer approval)
  • SDK: Language-specific SDK updates and releases. (Requires DevOps Maintainer approval)
  • Samples / Conformance: Maintaining samples and the conformance suite. (Requires Maintainer approval)
  • UCP Schema: Changes to the ucp-schema tool (resolver, linter, validator). (Requires Maintainer approval)
  • Community Health (.github): Updates to templates, workflows, or org-level configs. (Requires DevOps Maintainer approval)

Related Issues

None filed yet — happy to open one first if maintainers would rather see this go through a fuller proposal before schema changes.

Checklist

  • I have followed the Contributing Guide (including Conventional Commits title requirements and ! for breaking changes).
  • I have updated the documentation (if applicable).
  • My changes pass all local linting and formatting checks.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • (For Core/Capability) I have included/updated the relevant JSON schemas.
  • I have regenerated Python Pydantic models by running generate_models.sh under python_sdk.

Screenshots / Logs (if applicable)

N/A — schema/documentation change only.

Some promotions grant a free or discounted line item (buy-one-get-one,
free-gift-with-purchase) rather than reducing an existing item's price.
Today applied_discount can only describe an amount-based effect, so a
gifted line item has no structured link back to the discount that
produced it -- a platform sees a mystery zero-price item with no
attribution.

Adds an optional gift_line_item_id field to applied_discount, pointing
at the line_items[] entry the discount granted. Omitted for ordinary
amount-based discounts, so this is backward compatible.
@prateek-ct
prateek-ct requested review from a team as code owners July 10, 2026 13:01
@prateek-ct
prateek-ct requested review from archrao and mmohades July 10, 2026 13:01
Comment thread source/schemas/shopping/discount.json Outdated
},
"description": "Breakdown of where this discount was allocated. Sum of allocation amounts equals total amount."
},
"gift_line_item_id": {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some ecommerce systems allow for multiple gifts or bonus products being triggered from a single promotion. i.e. buy x, get gift y. Buy a 2nd x, get a 2nd gift y.

Could we make this an array?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch - done. Changed gift_line_item_id to gift_line_item_ids (array of strings) so promotions that grant multiple gift units from a single discount (e.g. "buy 2, get 2 free" scaling with quantity) can be represented. Added a second example showing the multi-gift case with the latest commit.

{
"code": "FREEGIFT",
"title": "Free Socks with Shoe Purchase",
"amount": 1200,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe naive question, but what's the rationale of keeping the amount here? shouldn't it be automatically computed based on "gift_line_item_id" to avoid discrepancies?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not naive at all , your question made me rethink this properly and it turned up a real mistake in my example- the gift line item's price was zeroed out. Fixed now , it shows its actual price like any other line item with the discount as its own items_discount entry in that line item's totals, so the total nets to 0 (or less, for partial-price gifts). Pushed in a recent commit, happy to have you take another look.

amount on the discount itself is still required though, even with the line item priced correctly. Nothing stops a second, unrelated discount from also touching that same line item , its totals[] can carry more than one items_discount entry with no way to tell which came from which discount. amount is what lets you attribute the value to this specific discount, same as for every other discount type.

Per review feedback: some promotions grant more than one gift unit from
a single discount (e.g. "buy 2, get 2 free" scaling with quantity), which
a single gift_line_item_id string couldn't represent.

- gift_line_item_id (string) -> gift_line_item_ids (array of strings)
- Added a multi-gift example
- Clarified why applied_discount.amount stays required even when
  gift_line_item_ids is present: a gift line item's serialized price is
  already zeroed by the discount, and there's no separate "regular price"
  field to recover its value from, so amount is the only place that value
  is communicated
The gift examples zeroed item.price to reflect the discount, which
contradicts how every other discount in this spec already works:
item.price stays at the regular value and the discount shows up as a
separate items_discount entry in that line item's totals[]. Fixed both
examples to follow that convention, and added allocations so the
gift-granting applied_discount entries are consistent with how
amount-based discounts already report their breakdown.

Also corrects the "why amount stays required" rationale: it's not
because the value is otherwise unrecoverable (it now is, from the line
item's own price/totals) -- it's required for the same reason every
other discount keeps it explicit, so a platform doesn't have to
cross-reference line_items[] or sum allocations[] just to read what a
discount was worth.
The previous wording justified amount as "consistency" with ordinary
discounts, which is a style preference, not a real constraint -- easy
to read as "not actually needed." Replace it with the concrete reason:
allocations is optional (nothing to sum in the simple case), and even
when a gift line item's own totals are correct, a second, unrelated
discount can add its own items_discount entry to the same line item
with no way to tell which entry came from which discount. amount is
the only value always attributable to this specific discount.
@damaz91 damaz91 added status:needs-triage Signal that the PR is ready for human triage and removed status:needs-triage Signal that the PR is ready for human triage labels Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants