1- // frontend/lib/services/products/mutations/update.ts
21import { eq , sql } from 'drizzle-orm' ;
3-
42import {
53 destroyProductImage ,
64 uploadProductImageFromFile ,
@@ -17,6 +15,7 @@ import { mapRowToProduct } from '../mapping';
1715import { normalizeSlug } from '../slug' ;
1816import {
1917 assertMoneyMinorInt ,
18+ assertMergedPricesPolicy ,
2019 enforceSaleBadgeRequiresOriginal ,
2120 normalizePricesFromInput ,
2221 validatePriceRows ,
@@ -56,10 +55,14 @@ export async function updateProduct(
5655
5756 const prices = normalizePricesFromInput ( input ) ;
5857 if ( prices . length ) validatePriceRows ( prices ) ;
59- // Enforce SALE invariant against FINAL state (DB rows + incoming upserts)
58+
6059 const finalBadge = ( input as any ) . badge ?? existing . badge ;
6160
62- if ( finalBadge === 'SALE' ) {
61+ // Enforce merged-state invariants (DB rows + incoming upserts)
62+ // - If prices are patched, validate merged currency policy (e.g. USD must exist)
63+ // - If final badge is SALE, enforce originalPrice for ALL currencies in merged state
64+
65+ if ( prices . length || finalBadge === 'SALE' ) {
6366 const existingPriceRows = await db
6467 . select ( {
6568 currency : productPrices . currency ,
@@ -92,7 +95,18 @@ export async function updateProduct(
9295 merged . set ( p . currency , p ) ;
9396 }
9497
95- enforceSaleBadgeRequiresOriginal ( 'SALE' , Array . from ( merged . values ( ) ) ) ;
98+ const mergedRows = Array . from ( merged . values ( ) ) ;
99+
100+ // Currency-set policy should be enforced on merged state ONLY when prices are patched.
101+ // This keeps PATCH semantics: partial prices payload is allowed, and policy is checked post-merge.
102+ if ( prices . length ) {
103+ assertMergedPricesPolicy ( mergedRows , { productId : id , requireUsd : true } ) ;
104+ }
105+
106+ // SALE invariant must be enforced on merged state when badge is SALE (even if prices are not patched).
107+ if ( finalBadge === 'SALE' ) {
108+ enforceSaleBadgeRequiresOriginal ( 'SALE' , mergedRows ) ;
109+ }
96110 }
97111
98112 // Base fields update
@@ -138,7 +152,7 @@ export async function updateProduct(
138152 }
139153
140154 try {
141- // 1) upsert prices
155+ // 1) upsert prices
142156 if ( prices . length ) {
143157 const upsertRows = prices . map ( p => {
144158 const priceMinor = p . priceMinor ;
0 commit comments