11import copy
22import datetime
3- import typing
43import uuid
4+ from typing import List , Optional , Union
55
66from marshmallow import Schema
77from marshmallow import fields as schema_fields
88
99from commercetools import schemas , types
10+ from commercetools .testing import utils
1011from commercetools .testing .abstract import BaseModel , ServiceBackend
1112from commercetools .testing .utils import (
1213 create_commercetools_response ,
@@ -22,7 +23,7 @@ class ProductsModel(BaseModel):
2223 _unique_values = ["key" ]
2324
2425 def _create_from_draft (
25- self , draft : types .ProductDraft , id : typing . Optional [str ] = None
26+ self , draft : types .ProductDraft , id : Optional [str ] = None
2627 ) -> types .Product :
2728 object_id = str (uuid .UUID (id ) if id is not None else uuid .uuid4 ())
2829
@@ -63,11 +64,11 @@ def _create_variant_from_draft(
6364 self , draft : types .ProductVariantDraft
6465 ) -> types .ProductVariant :
6566
66- assets : typing . Optional [typing . List [types .Asset ]] = None
67+ assets : Optional [List [types .Asset ]] = None
6768 if draft .assets :
6869 assets = self ._create_assets_from_draft (draft .assets )
6970
70- prices : typing . Optional [typing . List [types .Price ]] = None
71+ prices : Optional [List [types .Price ]] = None
7172 if draft .prices :
7273 prices = self ._create_prices_from_draft (draft .prices )
7374
@@ -87,11 +88,11 @@ def _create_variant_from_draft(
8788 )
8889
8990 def _create_assets_from_draft (
90- self , drafts : typing . List [types .AssetDraft ]
91- ) -> typing . List [types .Asset ]:
92- assets : typing . List [types .Asset ] = []
91+ self , drafts : List [types .AssetDraft ]
92+ ) -> List [types .Asset ]:
93+ assets : List [types .Asset ] = []
9394 for draft in drafts :
94- custom : typing . Optional [types .CustomFields ] = None
95+ custom : Optional [types .CustomFields ] = None
9596 if draft .custom :
9697 custom = custom_fields_from_draft (self ._storage , draft .custom )
9798
@@ -107,9 +108,9 @@ def _create_assets_from_draft(
107108 return assets
108109
109110 def _create_prices_from_draft (
110- self , drafts : typing . List [types .PriceDraft ]
111- ) -> typing . List [types .Price ]:
112- prices : typing . List [types .Price ] = []
111+ self , drafts : List [types .PriceDraft ]
112+ ) -> List [types .Price ]:
113+ prices : List [types .Price ] = []
113114 for draft in drafts :
114115 custom = None
115116 if draft .custom :
@@ -130,8 +131,8 @@ def _create_prices_from_draft(
130131 return prices
131132
132133 def _create_price_from_draft (
133- self , draft : typing . Optional [types .TypedMoneyDraft ]
134- ) -> typing . Optional [types .TypedMoney ]:
134+ self , draft : Union [ types . Money , Optional [types .TypedMoneyDraft ] ]
135+ ) -> Optional [types .TypedMoney ]:
135136 if draft is None :
136137 return None
137138
@@ -163,9 +164,7 @@ def _get_target_obj(obj: dict, staged: bool):
163164 return obj ["masterData" ]["staged" ]
164165
165166
166- def _get_variant (
167- data_object : dict , * , id : str = "" , sku : str = ""
168- ) -> typing .Optional [dict ]:
167+ def _get_variant (data_object : dict , * , id : int = "" , sku : str = "" ) -> Optional [dict ]:
169168 if not data_object :
170169 return None
171170
@@ -251,35 +250,40 @@ def updater(self, obj: dict, action: types.ProductPublishAction):
251250 # not implemented scopes right now.
252251 if new ["masterData" ].get ("staged" ):
253252 new ["masterData" ]["current" ] = new ["masterData" ]["staged" ]
253+ del new ["masterData" ]["staged" ]
254254 new ["masterData" ]["hasStagedChanges" ] = False
255255 new ["masterData" ]["published" ] = True
256256 return new
257257
258258 return updater
259259
260260
261+ def convert_draft_price (
262+ price_draft : types .PriceDraft , price_id : str = None
263+ ) -> types .Price :
264+ tiers : Optional [List [types .PriceTier ]] = None
265+ if price_draft .tiers :
266+ tiers = [utils .create_from_draft (tier ) for tier in price_draft .tiers ]
267+ return types .Price (
268+ id = price_id or str (uuid .uuid4 ()),
269+ country = price_draft .country ,
270+ channel = price_draft .channel ,
271+ value = utils ._money_to_typed (price_draft .value ),
272+ valid_from = price_draft .valid_from ,
273+ valid_until = price_draft .valid_until ,
274+ discounted = price_draft .discounted ,
275+ custom = utils .create_from_draft (price_draft .custom ),
276+ tiers = tiers ,
277+ )
278+
279+
261280def _set_product_prices ():
262281 def updater (self , obj : dict , action : types .ProductSetPricesAction ):
263282 new = copy .deepcopy (obj )
264283 target_obj = _get_target_obj (new , getattr (action , "staged" , True ))
265284 prices = []
266285 for price_draft in action .prices :
267- price = types .Price (
268- id = str (uuid .uuid4 ()),
269- country = price_draft .country ,
270- channel = price_draft .channel ,
271- value = types .TypedMoney (
272- fraction_digits = 2 ,
273- cent_amount = price_draft .value .cent_amount ,
274- currency_code = price_draft .value .currency_code ,
275- type = types .MoneyType .CENT_PRECISION ,
276- ),
277- valid_from = price_draft .valid_from ,
278- valid_until = price_draft .valid_until ,
279- discounted = price_draft .discounted ,
280- custom = price_draft .custom ,
281- tiers = price_draft .tiers ,
282- )
286+ price = convert_draft_price (price_draft )
283287 prices .append (price )
284288
285289 schema = schemas .PriceSchema ()
@@ -293,6 +297,61 @@ def updater(self, obj: dict, action: types.ProductSetPricesAction):
293297 return updater
294298
295299
300+ def _change_price ():
301+ def updater (self , obj : dict , action : types .ProductChangePriceAction ):
302+ new = copy .deepcopy (obj )
303+ staged = action .staged
304+ if staged is None :
305+ staged = True
306+ target_obj = _get_target_obj (new , staged )
307+ changed_price = convert_draft_price (action .price , action .price_id )
308+ schema = schemas .PriceSchema ()
309+
310+ found_price = True
311+ for variant in get_product_variants (target_obj ):
312+ for index , price in enumerate (variant ["prices" ]):
313+ if price ["id" ] == action .price_id :
314+ variant ["prices" ][index ] = schema .dump (changed_price )
315+ found_price = True
316+ break
317+ if not found_price :
318+ raise ValueError ("Could not find price with id %s" % action .price_id )
319+ if staged :
320+ new ["masterData" ]["hasStagedChanges" ] = True
321+ return new
322+
323+ return updater
324+
325+
326+ def _add_price ():
327+ def updater (self , obj : dict , action : types .ProductAddPriceAction ):
328+ new = copy .deepcopy (obj )
329+ staged = action .staged
330+ if staged is None :
331+ staged = True
332+ target_obj = _get_target_obj (new , staged )
333+ new_price = convert_draft_price (action .price )
334+ schema = schemas .PriceSchema ()
335+
336+ found_sku = False
337+ for variant in get_product_variants (target_obj ):
338+ if variant ["sku" ] == action .sku :
339+ if "prices" not in variant :
340+ variant ["prices" ] = []
341+ elif not variant ["prices" ]:
342+ variant ["prices" ] = []
343+ variant ["prices" ].append (schema .dump (new_price ))
344+ found_sku = True
345+ break
346+ if not found_sku :
347+ raise ValueError ("Could not find sku %s" % action .sku )
348+ if staged :
349+ new ["masterData" ]["hasStagedChanges" ] = True
350+ return new
351+
352+ return updater
353+
354+
296355class UploadImageQuerySchema (Schema ):
297356 staged = schema_fields .Bool ()
298357 filename = schema_fields .Field ()
@@ -325,6 +384,8 @@ def urls(self):
325384 "setAttribute" : _set_attribute_action (),
326385 "addVariant" : _add_variant_action (),
327386 "setPrices" : _set_product_prices (),
387+ "changePrice" : _change_price (),
388+ "addPrice" : _add_price (),
328389 "publish" : _publish_product_action (),
329390 }
330391
0 commit comments