@@ -66,6 +66,21 @@ def _parse_sold_at_iso(value: str | None) -> dt.datetime | None:
6666 ) from exc
6767
6868
69+ def _parse_optional_iso_dt (value : str | None ) -> dt .datetime | None :
70+ """Parse ISO-ish datetime without raising (e.g. wardrobe import)."""
71+ if value is None or not str (value ).strip ():
72+ return None
73+ raw = str (value ).strip ()
74+ try :
75+ return dt .datetime .fromisoformat (raw .replace ("Z" , "+00:00" ))
76+ except ValueError :
77+ pass
78+ try :
79+ return dt .datetime .strptime (raw [:10 ], "%Y-%m-%d" ).replace (tzinfo = dt .UTC )
80+ except ValueError :
81+ return None
82+
83+
6984@router .post ("/bulk-delete" , status_code = status .HTTP_200_OK )
7085def bulk_delete_articles (
7186 body : BulkIdsBody ,
@@ -218,14 +233,16 @@ def list_articles(
218233 return [article_service .article_to_dict (a ) for a in rows ]
219234
220235
221- @router .get ("/{article_id}/vinted-progress" )
222- async def vinted_progress_stream (
236+ @router .get ("/{article_id}/listing-progress" )
237+ @router .get ("/{article_id}/vinted-progress" , include_in_schema = False )
238+ async def article_listing_progress_stream (
223239 article_id : int ,
224240 user : Annotated [User , Depends (get_current_user_from_token_str )],
225241 db : Annotated [Session , Depends (get_db )],
226242) -> StreamingResponse :
227243 """
228244 SSE stream for Vinted and/or eBay publish steps (JWT via ``Authorization`` or ``?token=``).
245+ Legacy path: ``/vinted-progress`` (alias).
229246 """
230247 article = article_service .get_article (db , article_id , user .id )
231248 if article is None :
@@ -267,7 +284,7 @@ def publish_vinted_for_article(
267284) -> dict [str , Any ]:
268285 """
269286 Start Vinted publish for an existing article (images already stored).
270- Follow SSE ``GET /articles/{id}/vinted -progress``.
287+ Follow SSE ``GET /articles/{id}/listing -progress``.
271288 """
272289 article = article_service .get_article (db , article_id , user .id )
273290 if article is None :
@@ -300,7 +317,7 @@ def publish_vinted_for_article(
300317 return {
301318 "vinted" : {
302319 "status" : "running" ,
303- "stream_path" : f"/articles/{ article_id } /vinted -progress" ,
320+ "stream_path" : f"/articles/{ article_id } /listing -progress" ,
304321 },
305322 }
306323
@@ -368,7 +385,7 @@ def publish_ebay_for_article(
368385 return {
369386 "ebay" : {
370387 "status" : "running" ,
371- "stream_path" : f"/articles/{ article_id } /vinted -progress" ,
388+ "stream_path" : f"/articles/{ article_id } /listing -progress" ,
372389 },
373390 }
374391
@@ -387,10 +404,14 @@ async def create_article(
387404 card_number : str | None = Form (None ),
388405 condition : str = Form ("Near Mint" ),
389406 sell_price : str | None = Form (None ),
407+ sold_price : str | None = Form (None ),
408+ sale_source : str | None = Form (None ),
390409 publish_to_vinted : str | None = Form (None ),
391410 publish_to_ebay : str | None = Form (None ),
392411 is_sold : str | None = Form (None ),
393412 sold_at : str | None = Form (None ),
413+ wardrobe_vinted_listed : str | None = Form (None ),
414+ vinted_published_at : str | None = Form (None ),
394415 images : list [UploadFile ] | None = File (None ),
395416) -> dict [str , Any ]:
396417 settings = get_settings ()
@@ -399,6 +420,19 @@ async def create_article(
399420 if sold_flag and sold_at_dt is None :
400421 sold_at_dt = dt .datetime .now (dt .UTC )
401422
423+ sell_dec = _parse_decimal (sell_price )
424+ proceeds_dec = _parse_decimal (sold_price ) if sold_flag and sold_price else None
425+ if sold_flag and proceeds_dec is None :
426+ proceeds_dec = sell_dec
427+
428+ sale_src : str | None = None
429+ if sold_flag :
430+ raw_ss = (sale_source or "" ).strip ().lower ()
431+ if raw_ss in ("vinted" , "ebay" ):
432+ sale_src = raw_ss [:16 ]
433+ elif _form_bool (wardrobe_vinted_listed ):
434+ sale_src = "vinted"
435+
402436 article = Article (
403437 user_id = user .id ,
404438 title = title .strip (),
@@ -408,10 +442,20 @@ async def create_article(
408442 card_number = card_number .strip () if card_number else None ,
409443 condition = condition .strip () or "Near Mint" ,
410444 purchase_price = _parse_decimal_required (purchase_price ),
411- sell_price = _parse_decimal (sell_price ),
445+ sell_price = sell_dec ,
446+ sold_price = proceeds_dec if sold_flag else None ,
447+ sale_source = sale_src ,
412448 is_sold = sold_flag ,
413449 sold_at = sold_at_dt if sold_flag else None ,
414450 )
451+
452+ if _form_bool (wardrobe_vinted_listed ):
453+ article .published_on_vinted = True
454+ vp = _parse_optional_iso_dt (vinted_published_at )
455+ if vp is not None :
456+ article .vinted_published_at = vp
457+ elif not sold_flag :
458+ article .vinted_published_at = dt .datetime .now (dt .UTC )
415459 db .add (article )
416460 db .flush ()
417461
@@ -446,7 +490,7 @@ async def create_article(
446490 want_vinted = raw_want_vinted and ms .vinted_enabled
447491 raw_want_ebay = _form_bool (publish_to_ebay ) and not sold_flag
448492
449- stream_path = f"/articles/{ article .id } /vinted -progress"
493+ stream_path = f"/articles/{ article .id } /listing -progress"
450494 want_both_server = (
451495 want_vinted
452496 and not vinted_local_desktop
0 commit comments