Ticket Coupon Revalidation After a Product Coupon Discount

Date: 2026-08-27
Scope: wpos_CheckPCoupon, wpos_TicketCouponUse, wpos_TicketCouponUseSum, and the normal-sales caller

Summary

The ticket coupon is accepted only provisionally when scanned. If a later product coupon with PRODUCTCOUPON.CPN_TYPE = 'C' lowers the eligible product value below the ticket coupon's TCINMASTER.MIN_PURCHASE_AMT, wpos_TicketCouponUseSum re-evaluates the receipt and rejects the ticket coupon for that sale.

The exact outcome is:

The initial premise is therefore partly right: wpos_TicketCouponUseSum uses promotion-adjusted eligible value, but the earlier scan-time check in wpos_TicketCouponUse does not use the same calculation.

Short answer to each question

Question Answer
Does the later C product coupon count against ticket-coupon minimum spend? Yes. Its item discount is included in the FDS deducted from eligible gross product amount.
Is the already-scanned ticket coupon removed? No, not from SCANTC on this path.
Is it still usable? No, not while the recalculated eligible value remains below the minimum. No valid TCIN usage row is produced.
Does wpos_TicketCouponUseSum fail? It fails the business condition, skips the coupon, and returns an “unusable coupon” diagnostic. It does not throw a SQL exception for this condition.

Sequence and state transition

sequenceDiagram
    participant POS as NormalSales
    participant Check as wpos_CheckPCoupon
    participant Use as wpos_TicketCouponUse
    participant Sum as wpos_TicketCouponUseSum
    participant DB as Coupon state

    POS->>Check: Scan ticket coupon first
    Check->>Use: Route ticket coupon
    Use->>Use: Compare SUM(TEMPDLYPTRANS.AMT) to MIN_PURCHASE_AMT
    Use->>DB: Stage accepted scan in SCANTC
    POS->>Check: Add product coupon C
    Check->>DB: Add item discount, DISCID=100
    POS->>Sum: Enter payment/tender processing
    Sum->>Sum: Rebuild eligible amount as gross AMT - FDS
    alt eligible net >= minimum
        Sum->>DB: Insert TCIN usage row
        Sum-->>POS: Ticket coupon is usable
    else eligible net < minimum
        Sum->>Sum: GOTO Ext; skip TCIN insertion
        Note over DB: SCANTC remains; TCIN is absent
        Sum-->>POS: Return coupon-not-usable diagnostic rows
    end

Evidence

1. CPN_TYPE = 'C' is the product-discount case

wpos_CalculatePRCPN creates DISCTRANS rows only for scanned product coupons whose CPN_TYPE='C', calculating the discount as the smaller of coupon value and item amount. It handles CPN_TYPE='P' separately as product-coupon payment.

2. Product coupon entry adds an item discount and does not revalidate scanned tickets

wpos_CheckPCoupon reads the product coupon's CPN_TYPE, calculates the eligible item price after existing discounts, and inserts a new SCANPRODUCT_ITEM row with DISCID=100, DISC=CPN_VAL, and the coupon series in EVNUM.

There is no branch here that revisits existing SCANTC ticket scans. Ticket-coupon input is routed separately to wpos_TicketCouponUse.

3. Ticket scan-time validation is not the final promotion-aware calculation

wpos_TicketCouponUse reads MIN_PURCHASE_AMT, but its early check compares that minimum to SUM(TEMPDLYPTRANS.AMT) for sale-item rows. It does not subtract FDS in this check.

On acceptance, it stages the ticket in SCANTC; this is not yet the final TCIN redemption.

This is why a ticket can pass when scanned first, even though a later discount causes it to fail at payment summary.

4. wpos_TicketCouponUseSum counts the later C discount

The summary procedure aggregates SCANPRODUCT_ITEM.DISC for item-discount rows. It excludes discount IDs 0, 1, and 2, and it excludes coupon discount rows only when their product coupon has CPN_TYPE='P'. A CPN_TYPE='C' row is therefore retained.

It then builds the eligible-product working set with:

For amount-based ticket criteria, it calculates @saleamtpromo = SUM(amt - fds) over the eligible product group.

5. Falling below the minimum skips the actual ticket usage

For each ticket coupon, the summary procedure reloads TCINMASTER.MIN_PURCHASE_AMT, then performs the decisive check:

if @saleamtpromo < @MIN_AMOUNT
begin
    goto Ext
end

Ext advances to the next coupon. It bypasses the success path that inserts the TCIN usage record.

Before recalculation, normal local processing deletes prior TCIN, TCINPRODUCT, and TCTRANS results and rebuilds them. A prior summary result therefore does not “lock in” ticket eligibility.

6. The failed coupon stays scanned but is reported as unusable

At the end, the procedure detects positive-quantity SCANTC records that have no matching TCIN usage. It returns the Thai heading:

*** เลขที่คูปองที่ไม่สามารถใช้ร่วมรายการขายได้ ***

Meaning: “Coupon number(s) that cannot be used with this sale.” It also returns each affected running number and quantity.

This branch does not delete SCANTC and does not raise an error. It does clean up some derived/manual/duplicate/CLM state associated with coupons that did not qualify.

7. The POS runs this during payment processing

NormalSales first inserts payment working data, then calls TicketCouponProcess, which executes wpos_TicketCouponUseSum. The returned rows populate a modal ticket-coupon/payment summary screen.

Example scenario

Assume:

At ticket scan

The early check sees the sale amount as ฿501.00, so the ticket is accepted and staged in SCANTC.

After the product coupon

The product coupon creates a DISCID=100 item discount of ฿2.00.

At payment summary

wpos_TicketCouponUseSum calculates:

eligible gross AMT   = ฿501.00
included discounts  =   ฿2.00
eligible net amount = ฿499.00
required minimum    = ฿500.00

Because ฿499.00 < ฿500.00, it follows goto Ext:

If the basket is raised back to at least ฿500.00 of eligible net product value and the summary is run again, the coupon can qualify again, subject to all other limits and compatibility rules.

Important qualifications

  1. The ฿501 - ฿2 example assumes Product A is included by the ticket rule and not removed by ticket exclusion hierarchy/article/brand rules.
  2. It assumes promotion policy permits the ticket coupon and product coupon together. Policy or duplicate-promotion logic may reject one earlier.
  3. This conclusion describes the normal local EStore path. OCP, CLM, online promotion, combined coupons, value-added coupons, or SPO auto-coupons can add booking/reversal behavior. The MIN_PURCHASE_AMT -> goto Ext path itself still does not delete the local SCANTC row.
  4. “After promotions” is accurate for the later eligible-product computation only for discounts included in FDS. The code explicitly excludes product-coupon type P; it includes type C.

Verdict

Your expected risk is real, but the most accurate wording is:

A ticket coupon scanned before a product coupon is only provisionally accepted. A later CPN_TYPE='C' discount is included when wpos_TicketCouponUseSum recalculates eligible net product value. If that value falls below MIN_PURCHASE_AMT, the ticket remains in SCANTC but is excluded from TCIN, is unusable for the receipt, and is returned to the POS as an unusable coupon—without a SQL exception for this condition.