diff --git a/.cursor/debug.log b/.cursor/debug.log new file mode 100644 index 0000000..5651951 --- /dev/null +++ b/.cursor/debug.log @@ -0,0 +1,72 @@ +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"39326","categories":["Carnes > Vacuna"],"sellUnit":"kg"},"timestamp":1768774472072,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/39326","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774472088,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Carnes > Vacuna"],"normalizedNames":["carnes","vacuna"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774472886,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Carnes > Vacuna"],"foundCount":2,"found":[{"id":109,"name":"Carnes"},{"id":115,"name":"Vacuna"}]},"timestamp":1768774472887,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":109},{"id":115}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774472888,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"39326","categories":[],"sellUnit":"kg"},"timestamp":1768774473232,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/39326","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774473235,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774473236,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"39326","responseCategories":[{"id":109,"name":"Carnes"},{"id":115,"name":"Vacuna"}],"status":"success"},"timestamp":1768774473657,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"39326","responseCategories":[{"id":109,"name":"Carnes"},{"id":115,"name":"Vacuna"}],"status":"success"},"timestamp":1768774473983,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"39326","categories":[],"sellUnit":"kg"},"timestamp":1768774474525,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/39326","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774474527,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774474527,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"39326","responseCategories":[{"id":109,"name":"Carnes"},{"id":115,"name":"Vacuna"}],"status":"success"},"timestamp":1768774475208,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"39326","categories":[],"sellUnit":"kg"},"timestamp":1768774481413,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/39326","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774481415,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774481415,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"39326","responseCategories":[{"id":109,"name":"Carnes"},{"id":115,"name":"Vacuna"}],"status":"success"},"timestamp":1768774482123,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"3253","categories":["Quesos","Carnes > Achuras"],"sellUnit":"kg"},"timestamp":1768774662226,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/3253","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774662231,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Quesos","Carnes > Achuras"],"normalizedNames":["quesos","carnes","achuras"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774663022,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Quesos","Carnes > Achuras"],"foundCount":3,"found":[{"id":110,"name":"Achuras"},{"id":109,"name":"Carnes"},{"id":120,"name":"Quesos"}]},"timestamp":1768774663023,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":110},{"id":109},{"id":120}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774663023,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"3253","responseCategories":[{"id":109,"name":"Carnes"},{"id":110,"name":"Achuras"},{"id":120,"name":"Quesos"}],"status":"success"},"timestamp":1768774663803,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"3256","categories":["Quesos","Carnes > Achuras"],"sellUnit":"kg"},"timestamp":1768774663825,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/3256","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774663827,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Quesos","Carnes > Achuras"],"normalizedNames":["quesos","carnes","achuras"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774664616,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Quesos","Carnes > Achuras"],"foundCount":3,"found":[{"id":110,"name":"Achuras"},{"id":109,"name":"Carnes"},{"id":120,"name":"Quesos"}]},"timestamp":1768774664616,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":110},{"id":109},{"id":120}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774664617,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"3256","responseCategories":[{"id":109,"name":"Carnes"},{"id":110,"name":"Achuras"},{"id":120,"name":"Quesos"}],"status":"success"},"timestamp":1768774665336,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"3251","categories":["Fiambreria y Charcuteria de autor","Quesos","Carnes > Achuras"],"sellUnit":"kg"},"timestamp":1768774665354,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/3251","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774665357,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Fiambreria y Charcuteria de autor","Quesos","Carnes > Achuras"],"normalizedNames":["fiambreria y charcuteria de autor","quesos","carnes","achuras"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774666044,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Fiambreria y Charcuteria de autor","Quesos","Carnes > Achuras"],"foundCount":4,"found":[{"id":110,"name":"Achuras"},{"id":109,"name":"Carnes"},{"id":117,"name":"Fiambreria y Charcuteria de autor"},{"id":120,"name":"Quesos"}]},"timestamp":1768774666045,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":110},{"id":109},{"id":117},{"id":120}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774666045,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"3251","responseCategories":[{"id":109,"name":"Carnes"},{"id":110,"name":"Achuras"},{"id":117,"name":"Fiambreria y Charcuteria de autor"},{"id":120,"name":"Quesos"}],"status":"success"},"timestamp":1768774666798,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"3253","categories":["Quesos"],"sellUnit":"kg"},"timestamp":1768774718170,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/3253","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774718173,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Quesos"],"normalizedNames":["quesos"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774718887,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Quesos"],"foundCount":1,"found":[{"id":120,"name":"Quesos"}]},"timestamp":1768774718887,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":120}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774718887,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"3253","responseCategories":[{"id":120,"name":"Quesos"}],"status":"success"},"timestamp":1768774719630,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"3256","categories":["Quesos"],"sellUnit":"kg"},"timestamp":1768774722058,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/3256","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774722061,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Quesos"],"normalizedNames":["quesos"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774722779,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Quesos"],"foundCount":1,"found":[{"id":120,"name":"Quesos"}]},"timestamp":1768774722780,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":120}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774722780,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"3256","categories":["Quesos"],"sellUnit":"kg"},"timestamp":1768774723054,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/3256","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774723055,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"3256","responseCategories":[{"id":120,"name":"Quesos"}],"status":"success"},"timestamp":1768774723469,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Quesos"],"normalizedNames":["quesos"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774723821,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Quesos"],"foundCount":1,"found":[{"id":120,"name":"Quesos"}]},"timestamp":1768774723822,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":120}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774723822,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"3256","responseCategories":[{"id":120,"name":"Quesos"}],"status":"success"},"timestamp":1768774724564,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"3253","categories":["Quesos"],"sellUnit":"kg"},"timestamp":1768774728111,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/3253","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774728113,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Quesos"],"normalizedNames":["quesos"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774728877,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Quesos"],"foundCount":1,"found":[{"id":120,"name":"Quesos"}]},"timestamp":1768774728877,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":120}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774728877,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"3253","responseCategories":[{"id":120,"name":"Quesos"}],"status":"success"},"timestamp":1768774729591,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"3251","categories":["Fiambreria y Charcuteria de autor","Quesos"],"sellUnit":"kg"},"timestamp":1768774733211,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/3251","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774733215,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Fiambreria y Charcuteria de autor","Quesos"],"normalizedNames":["fiambreria y charcuteria de autor","quesos"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774733981,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Fiambreria y Charcuteria de autor","Quesos"],"foundCount":2,"found":[{"id":117,"name":"Fiambreria y Charcuteria de autor"},{"id":120,"name":"Quesos"}]},"timestamp":1768774733981,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":117},{"id":120}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774733981,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"3251","responseCategories":[{"id":117,"name":"Fiambreria y Charcuteria de autor"},{"id":120,"name":"Quesos"}],"status":"success"},"timestamp":1768774734709,"sessionId":"debug-session","hypothesisId":"D,E"} +{"location":"wooSnapshot.js:pushProductToWoo:entry","message":"Push entrada","data":{"wooProductId":"3251","categories":["Fiambreria y Charcuteria de autor","Quesos"],"sellUnit":"kg"},"timestamp":1768774735556,"sessionId":"debug-session","hypothesisId":"A,B"} +{"location":"wooSnapshot.js:pushProductToWoo:url","message":"URL construida","data":{"url":"https://piaf.floda.dev/wp-json/wc/v3/products/3251","base":"https://piaf.floda.dev/wp-json/wc/v3"},"timestamp":1768774735558,"sessionId":"debug-session","hypothesisId":"A"} +{"location":"wooSnapshot.js:fetchWooCategoriesByNames","message":"Categorias en Woo","data":{"searchingFor":["Fiambreria y Charcuteria de autor","Quesos"],"normalizedNames":["fiambreria y charcuteria de autor","quesos"],"wooCategoriesCount":50,"wooCategories":[{"id":96,"name":"Aceites acetos y vinagres","slug":"aceites-acetos-y-vinagres"},{"id":99,"name":"Aceitesacetos y vinagres","slug":"aceitesacetos-y-vinagres"},{"id":95,"name":"Aceitunas","slug":"aceitunas"},{"id":110,"name":"Achuras","slug":"achuras"},{"id":85,"name":"Almacén","slug":"almacen"},{"id":87,"name":"Asado","slug":"asado"},{"id":97,"name":"Bebidas","slug":"bebidas"},{"id":138,"name":"Cabernet","slug":"cabernet"},{"id":134,"name":"Calchaquies","slug":"calchaquies"},{"id":109,"name":"Carnes","slug":"carnes"},{"id":111,"name":"Cerdo","slug":"cerdo"},{"id":126,"name":"Cerveza","slug":"cerveza"},{"id":98,"name":"Con alcohol","slug":"con-alcohol"},{"id":90,"name":"Condimentos","slug":"condimentos"},{"id":93,"name":"Conservas","slug":"conservas"},{"id":112,"name":"Cordero","slug":"cordero"},{"id":92,"name":"Delicatessen","slug":"delicatessen"},{"id":105,"name":"Dips","slug":"dips"},{"id":104,"name":"Dulces","slug":"dulces"},{"id":118,"name":"Embutidos","slug":"embutidos-rebozados"},{"id":113,"name":"Exóticas","slug":"exoticas"},{"id":117,"name":"Fiambreria y Charcuteria de autor","slug":"charcuteria"},{"id":121,"name":"Frutos secos","slug":"frutos-secos"},{"id":116,"name":"Hamburguesas","slug":"hamburguesas"},{"id":86,"name":"Huevos","slug":"huevos"},{"id":135,"name":"Low cost","slug":"low-cost"},{"id":127,"name":"Madurada","slug":"madurada"},{"id":136,"name":"Malbec","slug":"malbec"},{"id":132,"name":"Mendoza","slug":"mendoza"},{"id":103,"name":"Mermeladas","slug":"mermeladas"},{"id":100,"name":"Miel","slug":"miel"},{"id":88,"name":"Panes y Harinas","slug":"panes-y-harinas"},{"id":101,"name":"Pastas y arroces","slug":"pastas-y-arroces"},{"id":131,"name":"Patagonia","slug":"patagonia"},{"id":94,"name":"Pepinos","slug":"pepinos"},{"id":119,"name":"Pescados","slug":"pescados"},{"id":137,"name":"Pinot Noir","slug":"pinot-noir"},{"id":114,"name":"Pollo","slug":"pollo"},{"id":84,"name":"Proveeduría","slug":"proveeduria"},{"id":120,"name":"Quesos","slug":"quesos"},{"id":15,"name":"Rebozados","slug":"uncategorized"},{"id":89,"name":"Sal pimienta y especias","slug":"sal-pimienta-y-especias"},{"id":91,"name":"Salsas","slug":"salsas"},{"id":130,"name":"Selección Malbec","slug":"seleccion-malbec"},{"id":133,"name":"Selección Pinot","slug":"seleccion-pinot"},{"id":129,"name":"Seleccionados","slug":"seleccionados"},{"id":108,"name":"Sin alcohol","slug":"sin-alcohol"},{"id":102,"name":"Snacks","slug":"snacks"},{"id":115,"name":"Vacuna","slug":"vacuna"},{"id":125,"name":"Vinos","slug":"vinos"}]},"timestamp":1768774736395,"sessionId":"debug-session","hypothesisId":"B"} +{"location":"wooSnapshot.js:pushProductToWoo:cats","message":"Categorias mapeadas","data":{"requested":["Fiambreria y Charcuteria de autor","Quesos"],"foundCount":2,"found":[{"id":117,"name":"Fiambreria y Charcuteria de autor"},{"id":120,"name":"Quesos"}]},"timestamp":1768774736395,"sessionId":"debug-session","hypothesisId":"B,C"} +{"location":"wooSnapshot.js:pushProductToWoo:payload","message":"Payload final","data":{"updatePayload":{"categories":[{"id":117},{"id":120}],"meta_data":[{"key":"_sell_unit_override","value":"kg"}]},"isEmpty":false},"timestamp":1768774736395,"sessionId":"debug-session","hypothesisId":"C"} +{"location":"wooSnapshot.js:pushProductToWoo:response","message":"Respuesta Woo","data":{"wooProductId":"3251","responseCategories":[{"id":117,"name":"Fiambreria y Charcuteria de autor"},{"id":120,"name":"Quesos"}],"status":"success"},"timestamp":1768774737141,"sessionId":"debug-session","hypothesisId":"D,E"} diff --git a/public/app.js b/public/app.js index 2dc6958..daabd70 100644 --- a/public/app.js +++ b/public/app.js @@ -8,6 +8,8 @@ import "./components/products-crud.js"; import "./components/aliases-crud.js"; import "./components/recommendations-crud.js"; import "./components/quantities-crud.js"; +import "./components/orders-crud.js"; +import "./components/test-panel.js"; import { connectSSE } from "./lib/sse.js"; connectSSE(); diff --git a/public/components/ops-shell.js b/public/components/ops-shell.js index 0f9aa6d..f18e0b7 100644 --- a/public/components/ops-shell.js +++ b/public/components/ops-shell.js @@ -45,6 +45,8 @@ class OpsShell extends HTMLElement { + +
SSE: connecting…
@@ -93,6 +95,18 @@ class OpsShell extends HTMLElement { + +
+
+ +
+
+ +
+
+ +
+
`; } diff --git a/public/components/orders-crud.js b/public/components/orders-crud.js new file mode 100644 index 0000000..e594571 --- /dev/null +++ b/public/components/orders-crud.js @@ -0,0 +1,468 @@ +import { api } from "../lib/api.js"; + +function formatDate(dateStr) { + if (!dateStr) return "—"; + const d = new Date(dateStr); + return d.toLocaleString("es-AR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" }); +} + +function statusLabel(status) { + const map = { + pending: "Pendiente", + processing: "Procesando", + "on-hold": "En espera", + completed: "Completado", + cancelled: "Cancelado", + refunded: "Reembolsado", + failed: "Fallido", + }; + return map[status] || status; +} + +function statusColor(status) { + const map = { + pending: "#f59e0b", + processing: "#3b82f6", + "on-hold": "#8b5cf6", + completed: "#22c55e", + cancelled: "#6b7280", + refunded: "#ec4899", + failed: "#ef4444", + }; + return map[status] || "#8aa0b5"; +} + +class OrdersCrud extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this.orders = []; + this.selectedOrder = null; + this.loading = false; + + this.shadowRoot.innerHTML = ` + + +
+
+
+ Pedidos de WooCommerce + +
+
+
Cargando pedidos...
+
+
+ +
+
Detalle del Pedido
+
+
Seleccioná un pedido para ver los detalles
+
+
+
+ `; + } + + connectedCallback() { + this.shadowRoot.getElementById("btnRefresh").onclick = () => this.loadOrders(); + this.loadOrders(); + } + + async loadOrders() { + const container = this.shadowRoot.getElementById("ordersTable"); + container.innerHTML = `
Cargando pedidos...
`; + + try { + const result = await api.listRecentOrders({ limit: 50 }); + this.orders = result.items || []; + this.renderTable(); + } catch (e) { + console.error("[orders-crud] Error loading orders:", e); + container.innerHTML = `
Error cargando pedidos: ${e.message}
`; + } + } + + renderTable() { + const container = this.shadowRoot.getElementById("ordersTable"); + + if (this.orders.length === 0) { + container.innerHTML = `
No hay pedidos
`; + return; + } + + container.innerHTML = ` + + + + + + + + + + + + + + + ${this.orders.map(order => { + const isSelected = this.selectedOrder?.id === order.id; + const customerName = [order.billing.first_name, order.billing.last_name].filter(Boolean).join(" ") || order.billing.phone || "—"; + + return ` + + + + + + + + + + + `; + }).join("")} + +
#TipoEstadoEnvíoPagoClienteTotalFecha
${order.id} +
+ ${order.is_test ? 'TEST' : 'REAL'} + ${order.source === "whatsapp" ? 'WA' : 'WEB'} +
+
${statusLabel(order.status)}${order.is_delivery ? 'DEL' : 'RET'} +
+ ${order.is_cash ? '$' : 'MP'} + ${order.is_paid ? '✓' : '✗'} +
+
${customerName}$${Number(order.total || 0).toLocaleString("es-AR")}${formatDate(order.date_created)}
+ `; + + container.querySelectorAll("tr[data-order-id]").forEach(row => { + row.onclick = () => { + const orderId = parseInt(row.dataset.orderId); + const order = this.orders.find(o => o.id === orderId); + if (order) { + this.selectOrder(order); + } + }; + }); + } + + selectOrder(order) { + this.selectedOrder = order; + this.renderTable(); + this.renderDetail(); + } + + renderDetail() { + const container = this.shadowRoot.getElementById("orderDetail"); + + if (!this.selectedOrder) { + container.innerHTML = `
Seleccioná un pedido para ver los detalles
`; + return; + } + + const order = this.selectedOrder; + const customerName = [order.billing.first_name, order.billing.last_name].filter(Boolean).join(" ") || "—"; + + // Construir dirección de envío + const shippingAddr = [ + order.shipping?.address_1, + order.shipping?.address_2, + order.shipping?.city, + order.shipping?.state, + order.shipping?.postcode + ].filter(Boolean).join(", "); + + const billingAddr = [ + order.billing?.address_1, + order.billing?.address_2, + order.billing?.city, + order.billing?.state, + order.billing?.postcode + ].filter(Boolean).join(", "); + + const address = shippingAddr || billingAddr || "—"; + + container.innerHTML = ` +
+
Información General
+
+ Pedido # + ${order.id} +
+
+ Estado + ${statusLabel(order.status)} +
+
+ Tipo + ${order.is_test ? "Test" : "Real"} • ${order.source === "whatsapp" ? "WhatsApp" : "Web"} +
+
+ Fecha + ${formatDate(order.date_created)} +
+
+ Total + $${Number(order.total || 0).toLocaleString("es-AR")} ${order.currency || ""} +
+
+ +
+
Envío
+
+ Método + + + ${order.is_delivery ? 'DELIVERY' : 'RETIRO'} + + ${order.shipping_method ? `${order.shipping_method}` : ''} + +
+ ${order.is_delivery && address !== "—" ? ` +
+ Dirección + ${address} +
+ ` : ''} +
+ +
+
Pago
+
+ Método + + + ${order.is_cash ? 'EFECTIVO' : 'LINK'} + + ${order.payment_method_title ? `${order.payment_method_title}` : ''} + +
+
+ Estado + + + ${order.is_paid ? 'PAGADO' : 'PENDIENTE'} + + ${order.date_paid ? `${formatDate(order.date_paid)}` : ''} + +
+
+ +
+
Cliente
+
+ Nombre + ${customerName} +
+
+ Teléfono + ${order.billing.phone || "—"} +
+
+ Email + ${order.billing.email || "—"} +
+
+ +
+
Productos (${order.line_items.length})
+
+ ${order.line_items.length === 0 ? '
Sin productos
' : + order.line_items.map(item => ` +
+ ${item.name} + x${item.quantity} + $${Number(item.total || 0).toLocaleString("es-AR")} +
+ `).join("") + } +
+
+ + ${order.run_id ? ` +
+
Metadata
+
+ Run ID + ${order.run_id} +
+
+ ` : ""} + `; + } +} + +customElements.define("orders-crud", OrdersCrud); diff --git a/public/components/test-panel.js b/public/components/test-panel.js new file mode 100644 index 0000000..baa77ac --- /dev/null +++ b/public/components/test-panel.js @@ -0,0 +1,533 @@ +import { api } from "../lib/api.js"; + +// Datos aleatorios para generar usuarios de prueba +const NOMBRES = ["Juan", "María", "Carlos", "Ana", "Pedro", "Laura", "Diego", "Sofía"]; +const APELLIDOS = ["García", "Rodríguez", "Martínez", "López", "González", "Fernández", "Pérez"]; +const CALLES = ["Av. Corrientes", "Av. Santa Fe", "Calle Florida", "Av. Rivadavia", "Av. Cabildo", "Av. Libertador"]; + +function randomItem(arr) { + return arr[Math.floor(Math.random() * arr.length)]; +} + +function randomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function generateTestUser() { + const randomPhone = `549${randomInt(1000000000, 9999999999)}`; + const wa_chat_id = `${randomPhone}@s.whatsapp.net`; + const nombre = randomItem(NOMBRES); + const apellido = randomItem(APELLIDOS); + + return { + wa_chat_id, + phone: randomPhone, + name: `${nombre} ${apellido}`, + address: { + first_name: nombre, + last_name: apellido, + address_1: `${randomItem(CALLES)} ${randomInt(100, 9000)}`, + city: "CABA", + state: "Buenos Aires", + postcode: `${randomInt(1000, 1999)}`, + country: "AR", + phone: randomPhone, + email: `${randomPhone}@no-email.local`, + }, + }; +} + +class TestPanel extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this.products = []; + this.selectedProducts = []; + this.testUser = null; + this.lastOrder = null; + this.lastPaymentLink = null; + this.loading = false; + + this.shadowRoot.innerHTML = ` + + +
+
+
1. Generar Orden de Prueba
+ +
+
+ + +
+
+ +
+
Productos seleccionados
+
+
Click "Generar Orden Aleatoria" para comenzar
+
+
+ +
+
Datos del usuario
+ +
+ +
+ +
+ + +
+ +
+
2. Link de Pago (MercadoPago)
+ +
+
Monto
+
+ + +
+
+ + + +
3. Simular Pago Exitoso
+ +
+

+ Simula el webhook de MercadoPago con status "approved". + Esto actualiza la orden en WooCommerce a "processing". +

+ +
+ + +
+
+ `; + } + + connectedCallback() { + this.shadowRoot.getElementById("btnGenerate").onclick = () => this.generateRandomOrder(); + this.shadowRoot.getElementById("btnClear").onclick = () => this.clearAll(); + this.shadowRoot.getElementById("btnCreateOrder").onclick = () => this.createOrder(); + this.shadowRoot.getElementById("btnPaymentLink").onclick = () => this.createPaymentLink(); + this.shadowRoot.getElementById("btnSimulateWebhook").onclick = () => this.simulateWebhook(); + + this.loadProducts(); + } + + async loadProducts() { + try { + const result = await api.getProductsWithStock(); + this.products = result.items || []; + console.log(`[test-panel] Loaded ${this.products.length} products with stock`); + } catch (e) { + console.error("[test-panel] Error loading products:", e); + } + } + + async generateRandomOrder() { + if (this.products.length === 0) { + await this.loadProducts(); + } + + if (this.products.length === 0) { + alert("No hay productos con stock disponible"); + return; + } + + // Generar usuario aleatorio + this.testUser = generateTestUser(); + + // Seleccionar 1-3 productos aleatorios + const numProducts = randomInt(1, Math.min(3, this.products.length)); + const shuffled = [...this.products].sort(() => Math.random() - 0.5); + this.selectedProducts = []; + + for (let i = 0; i < numProducts; i++) { + const product = shuffled[i]; + const isKg = product.sell_unit === "kg"; + const quantity = isKg ? randomInt(200, 2000) : randomInt(1, 5); + + this.selectedProducts.push({ + product_id: product.woo_product_id, + name: product.name, + quantity, + unit: isKg ? "kg" : "unit", + price: product.price, + }); + } + + this.renderProductList(); + this.renderUserInfo(); + this.updateButtonStates(); + } + + renderProductList() { + const container = this.shadowRoot.getElementById("productList"); + + if (this.selectedProducts.length === 0) { + container.innerHTML = `
Click "Generar Orden Aleatoria" para comenzar
`; + return; + } + + container.innerHTML = this.selectedProducts.map((p, i) => ` +
+
${p.name}
+
${p.unit === "kg" ? `${p.quantity}g` : `${p.quantity}u`}
+
$${Number(p.price || 0).toFixed(0)}
+ +
+ `).join(""); + + container.querySelectorAll(".remove-btn").forEach(btn => { + btn.onclick = (e) => { + const idx = parseInt(e.target.dataset.index); + this.selectedProducts.splice(idx, 1); + this.renderProductList(); + this.updateButtonStates(); + }; + }); + } + + renderUserInfo() { + const container = this.shadowRoot.getElementById("userInfo"); + + if (!this.testUser) { + container.innerHTML = `
Se generarán automáticamente
`; + return; + } + + const addr = this.testUser.address; + container.innerHTML = ` +
Nombre: ${addr.first_name} ${addr.last_name}
+
Dirección: ${addr.address_1}
+
Ciudad: ${addr.city}, ${addr.state}
+
Teléfono: ${addr.phone}
+
Email: ${addr.email}
+ `; + } + + updateButtonStates() { + const hasProducts = this.selectedProducts.length > 0; + const hasOrder = this.lastOrder?.woo_order_id; + const hasPaymentLink = this.lastPaymentLink?.init_point; + + this.shadowRoot.getElementById("btnCreateOrder").disabled = !hasProducts; + this.shadowRoot.getElementById("btnPaymentLink").disabled = !hasOrder; + this.shadowRoot.getElementById("btnSimulateWebhook").disabled = !hasOrder; + + if (hasOrder) { + this.shadowRoot.getElementById("inputAmount").value = this.lastOrder.total || ""; + } + } + + async createOrder() { + if (this.loading) return; + this.loading = true; + + const btn = this.shadowRoot.getElementById("btnCreateOrder"); + btn.disabled = true; + btn.textContent = "Creando..."; + + try { + const basket = { + items: this.selectedProducts.map(p => ({ + product_id: p.product_id, + quantity: p.quantity, + unit: p.unit, + })), + }; + + const result = await api.createTestOrder({ + basket, + address: this.testUser?.address || null, + wa_chat_id: this.testUser?.wa_chat_id || null, + }); + + if (result.ok) { + this.lastOrder = result; + this.shadowRoot.getElementById("orderIdValue").textContent = `#${result.woo_order_id}`; + this.shadowRoot.getElementById("orderTotalValue").textContent = `$${Number(result.total || 0).toFixed(2)}`; + this.shadowRoot.getElementById("orderResult").style.display = "block"; + this.shadowRoot.getElementById("inputAmount").value = result.total || ""; + } else { + alert("Error: " + (result.error || "Error desconocido")); + } + } catch (e) { + console.error("[test-panel] createOrder error:", e); + alert("Error creando orden: " + e.message); + } finally { + this.loading = false; + btn.textContent = "Crear Orden en WooCommerce"; + this.updateButtonStates(); + } + } + + async createPaymentLink() { + if (this.loading) return; + if (!this.lastOrder?.woo_order_id) { + alert("Primero creá una orden"); + return; + } + + const amount = parseFloat(this.shadowRoot.getElementById("inputAmount").value); + if (!amount || amount <= 0) { + alert("Ingresá un monto válido"); + return; + } + + this.loading = true; + const btn = this.shadowRoot.getElementById("btnPaymentLink"); + btn.disabled = true; + btn.textContent = "Generando..."; + + try { + const result = await api.createPaymentLink({ + woo_order_id: this.lastOrder.woo_order_id, + amount, + }); + + if (result.ok) { + this.lastPaymentLink = result; + const linkEl = this.shadowRoot.getElementById("paymentLinkValue"); + linkEl.href = result.init_point || result.sandbox_init_point || "#"; + linkEl.textContent = result.init_point || result.sandbox_init_point || "—"; + this.shadowRoot.getElementById("preferenceIdValue").textContent = result.preference_id || "—"; + this.shadowRoot.getElementById("paymentResult").style.display = "block"; + } else { + alert("Error: " + (result.error || "Error desconocido")); + } + } catch (e) { + console.error("[test-panel] createPaymentLink error:", e); + alert("Error generando link: " + e.message); + } finally { + this.loading = false; + btn.textContent = "Generar Link de Pago"; + this.updateButtonStates(); + } + } + + async simulateWebhook() { + if (this.loading) return; + if (!this.lastOrder?.woo_order_id) { + alert("Primero creá una orden"); + return; + } + + this.loading = true; + const btn = this.shadowRoot.getElementById("btnSimulateWebhook"); + btn.disabled = true; + btn.textContent = "Simulando..."; + + try { + const amount = parseFloat(this.shadowRoot.getElementById("inputAmount").value) || this.lastOrder.total || 0; + + const result = await api.simulateMpWebhook({ + woo_order_id: this.lastOrder.woo_order_id, + amount, + }); + + if (result.ok) { + this.shadowRoot.getElementById("webhookStatusValue").textContent = `Payment ${result.payment_id} - ${result.status}`; + this.shadowRoot.getElementById("webhookOrderStatusValue").textContent = result.order_status || "processing"; + this.shadowRoot.getElementById("webhookResult").style.display = "block"; + } else { + alert("Error: " + (result.error || "Error desconocido")); + } + } catch (e) { + console.error("[test-panel] simulateWebhook error:", e); + alert("Error simulando webhook: " + e.message); + } finally { + this.loading = false; + btn.textContent = "Simular Pago Exitoso"; + this.updateButtonStates(); + } + } + + clearAll() { + this.selectedProducts = []; + this.testUser = null; + this.lastOrder = null; + this.lastPaymentLink = null; + + this.renderProductList(); + this.renderUserInfo(); + this.updateButtonStates(); + + this.shadowRoot.getElementById("orderResult").style.display = "none"; + this.shadowRoot.getElementById("paymentResult").style.display = "none"; + this.shadowRoot.getElementById("webhookResult").style.display = "none"; + this.shadowRoot.getElementById("inputAmount").value = ""; + } +} + +customElements.define("test-panel", TestPanel); diff --git a/public/lib/api.js b/public/lib/api.js index 2c69e67..121b6bc 100644 --- a/public/lib/api.js +++ b/public/lib/api.js @@ -184,4 +184,39 @@ export const api = { async syncFromWoo() { return fetch("/products/sync-from-woo", { method: "POST" }).then(r => r.json()); }, + + // --- Testing --- + async listRecentOrders({ limit = 20 } = {}) { + const u = new URL("/test/orders", location.origin); + u.searchParams.set("limit", String(limit)); + return fetch(u).then(r => r.json()); + }, + + async getProductsWithStock() { + return fetch("/test/products-with-stock").then(r => r.json()); + }, + + async createTestOrder({ basket, address, wa_chat_id }) { + return fetch("/test/order", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ basket, address, wa_chat_id }), + }).then(r => r.json()); + }, + + async createPaymentLink({ woo_order_id, amount }) { + return fetch("/test/payment-link", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ woo_order_id, amount }), + }).then(r => r.json()); + }, + + async simulateMpWebhook({ woo_order_id, amount }) { + return fetch("/test/simulate-webhook", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ woo_order_id, amount }), + }).then(r => r.json()); + }, }; diff --git a/src/modules/0-ui/controllers/testing.js b/src/modules/0-ui/controllers/testing.js new file mode 100644 index 0000000..50484d2 --- /dev/null +++ b/src/modules/0-ui/controllers/testing.js @@ -0,0 +1,92 @@ +import { + handleListRecentOrders, + handleGetProductsWithStock, + handleCreateTestOrder, + handleCreatePaymentLink, + handleSimulateMpWebhook, +} from "../handlers/testing.js"; + +export const makeListRecentOrders = (tenantIdOrFn) => async (req, res) => { + try { + const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn; + const limit = parseInt(req.query.limit) || 20; + const result = await handleListRecentOrders({ tenantId, limit }); + res.json(result); + } catch (err) { + console.error("[testing] listRecentOrders error:", err); + res.status(500).json({ ok: false, error: err.message || "internal_error" }); + } +}; + +export const makeGetProductsWithStock = (tenantIdOrFn) => async (req, res) => { + try { + const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn; + const result = await handleGetProductsWithStock({ tenantId }); + res.json(result); + } catch (err) { + console.error("[testing] getProductsWithStock error:", err); + res.status(500).json({ ok: false, error: err.message || "internal_error" }); + } +}; + +export const makeCreateTestOrder = (tenantIdOrFn) => async (req, res) => { + try { + const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn; + const { basket, address, wa_chat_id } = req.body || {}; + + if (!basket?.items?.length) { + return res.status(400).json({ ok: false, error: "basket_required" }); + } + + const result = await handleCreateTestOrder({ tenantId, basket, address, wa_chat_id }); + res.json(result); + } catch (err) { + console.error("[testing] createTestOrder error:", err); + res.status(500).json({ ok: false, error: err.message || "internal_error" }); + } +}; + +export const makeCreatePaymentLink = (tenantIdOrFn) => async (req, res) => { + try { + const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn; + const { woo_order_id, amount } = req.body || {}; + + if (!woo_order_id) { + return res.status(400).json({ ok: false, error: "woo_order_id_required" }); + } + if (!amount || Number(amount) <= 0) { + return res.status(400).json({ ok: false, error: "amount_required" }); + } + + const result = await handleCreatePaymentLink({ + tenantId, + wooOrderId: woo_order_id, + amount: Number(amount) + }); + res.json(result); + } catch (err) { + console.error("[testing] createPaymentLink error:", err); + res.status(500).json({ ok: false, error: err.message || "internal_error" }); + } +}; + +export const makeSimulateMpWebhook = (tenantIdOrFn) => async (req, res) => { + try { + const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn; + const { woo_order_id, amount } = req.body || {}; + + if (!woo_order_id) { + return res.status(400).json({ ok: false, error: "woo_order_id_required" }); + } + + const result = await handleSimulateMpWebhook({ + tenantId, + wooOrderId: woo_order_id, + amount: Number(amount) || 0 + }); + res.json(result); + } catch (err) { + console.error("[testing] simulateMpWebhook error:", err); + res.status(500).json({ ok: false, error: err.message || "internal_error" }); + } +}; diff --git a/src/modules/0-ui/handlers/testing.js b/src/modules/0-ui/handlers/testing.js new file mode 100644 index 0000000..d433690 --- /dev/null +++ b/src/modules/0-ui/handlers/testing.js @@ -0,0 +1,131 @@ +import { createOrder, listRecentOrders } from "../../4-woo-orders/wooOrders.js"; +import { createPreference, reconcilePayment } from "../../6-mercadopago/mercadoPago.js"; +import { listProducts } from "../db/repo.js"; + +/** + * Lista pedidos recientes de WooCommerce + */ +export async function handleListRecentOrders({ tenantId, limit = 20 }) { + const orders = await listRecentOrders({ tenantId, limit }); + return { items: orders }; +} + +/** + * Obtiene productos con stock para testing + */ +export async function handleGetProductsWithStock({ tenantId }) { + const allProducts = await listProducts({ tenantId, limit: 500 }); + const withStock = allProducts.filter(p => + p.stock_status === "instock" && + p.price && + Number(p.price) > 0 + ); + return { items: withStock }; +} + +/** + * Crea una orden de prueba en WooCommerce + */ +export async function handleCreateTestOrder({ tenantId, basket, address, wa_chat_id }) { + if (!basket?.items?.length) { + throw new Error("basket_empty"); + } + + const order = await createOrder({ + tenantId, + wooCustomerId: null, // Sin customer de Woo para testing + basket, + address, + run_id: `test-${Date.now()}`, + }); + + // Calcular total desde line_items + let total = 0; + if (order?.raw?.line_items) { + for (const item of order.raw.line_items) { + total += Number(item.total) || 0; + } + } else if (order?.raw?.total) { + total = Number(order.raw.total) || 0; + } + + return { + ok: true, + woo_order_id: order?.id || null, + total, + line_items: order?.line_items || [], + raw: order?.raw || null, + }; +} + +/** + * Crea un link de pago de MercadoPago + */ +export async function handleCreatePaymentLink({ tenantId, wooOrderId, amount }) { + if (!wooOrderId) { + throw new Error("missing_woo_order_id"); + } + if (!amount || Number(amount) <= 0) { + throw new Error("invalid_amount"); + } + + const pref = await createPreference({ + tenantId, + wooOrderId, + amount: Number(amount), + }); + + return { + ok: true, + preference_id: pref?.preference_id || null, + init_point: pref?.init_point || null, + sandbox_init_point: pref?.sandbox_init_point || null, + }; +} + +/** + * Simula un webhook de MercadoPago con pago exitoso + * No pasa por el endpoint real (requiere firma HMAC) + * Crea un payment mock y llama a reconcilePayment directamente + */ +export async function handleSimulateMpWebhook({ tenantId, wooOrderId, amount }) { + if (!wooOrderId) { + throw new Error("missing_woo_order_id"); + } + + // Crear payment mock con status approved + const mockPaymentId = `test-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; + const mockPayment = { + id: mockPaymentId, + status: "approved", + status_detail: "accredited", + external_reference: `${tenantId}|${wooOrderId}`, + transaction_amount: Number(amount) || 0, + currency_id: "ARS", + date_approved: new Date().toISOString(), + date_created: new Date().toISOString(), + payment_method_id: "test", + payment_type_id: "credit_card", + payer: { + email: "test@test.com", + }, + order: { + id: `pref-test-${wooOrderId}`, + }, + }; + + // Reconciliar el pago (actualiza mp_payments y cambia status de orden a processing) + const result = await reconcilePayment({ + tenantId, + payment: mockPayment, + }); + + return { + ok: true, + payment_id: mockPaymentId, + woo_order_id: result?.woo_order_id || wooOrderId, + status: "approved", + order_status: "processing", + reconciled: result?.payment || null, + }; +} diff --git a/src/modules/1-intake/routes/simulator.js b/src/modules/1-intake/routes/simulator.js index 21049d2..0f8e5dd 100644 --- a/src/modules/1-intake/routes/simulator.js +++ b/src/modules/1-intake/routes/simulator.js @@ -11,6 +11,7 @@ import { makeListAliases, makeCreateAlias, makeUpdateAlias, makeDeleteAlias } fr import { makeListRecommendations, makeGetRecommendation, makeCreateRecommendation, makeUpdateRecommendation, makeDeleteRecommendation } from "../../0-ui/controllers/recommendations.js"; import { makeListProductQtyRules, makeGetProductQtyRules, makeSaveProductQtyRules } from "../../0-ui/controllers/quantities.js"; import { makeDeleteConversation, makeDeleteUser, makeListUsers, makeRetryLast } from "../../0-ui/controllers/admin.js"; +import { makeListRecentOrders, makeGetProductsWithStock, makeCreateTestOrder, makeCreatePaymentLink, makeSimulateMpWebhook } from "../../0-ui/controllers/testing.js"; function nowIso() { return new Date().toISOString(); @@ -82,6 +83,13 @@ export function createSimulatorRouter({ tenantId }) { router.get("/runs", makeListRuns(getTenantId)); router.get("/runs/:run_id", makeGetRunById(getTenantId)); + // --- Testing routes --- + router.get("/test/orders", makeListRecentOrders(getTenantId)); + router.get("/test/products-with-stock", makeGetProductsWithStock(getTenantId)); + router.post("/test/order", makeCreateTestOrder(getTenantId)); + router.post("/test/payment-link", makeCreatePaymentLink(getTenantId)); + router.post("/test/simulate-webhook", makeSimulateMpWebhook(getTenantId)); + return router; } diff --git a/src/modules/4-woo-orders/wooOrders.js b/src/modules/4-woo-orders/wooOrders.js index 9aba889..86e1096 100644 --- a/src/modules/4-woo-orders/wooOrders.js +++ b/src/modules/4-woo-orders/wooOrders.js @@ -89,7 +89,7 @@ async function getWooClient({ tenantId }) { return { base, authHeader: { Authorization: `Basic ${auth}` }, - timeout: Math.max(cfg.timeout_ms ?? 20000, 20000), + timeout: Math.max(cfg.timeout_ms ?? 60000, 60000), }; } @@ -134,7 +134,7 @@ async function buildLineItems({ tenantId, basket }) { const total = pricePerKg != null ? toMoney(pricePerKg * qty) : null; lineItems.push({ product_id: productId, - variation_id: it.variation_id ?? null, + ...(it.variation_id ? { variation_id: it.variation_id } : {}), quantity: Math.round(qty), ...(total ? { subtotal: total, total } : {}), meta_data: [ @@ -150,7 +150,7 @@ async function buildLineItems({ tenantId, basket }) { const total = pricePerKg != null ? toMoney(pricePerKg * kilos) : null; lineItems.push({ product_id: productId, - variation_id: it.variation_id ?? null, + ...(it.variation_id ? { variation_id: it.variation_id } : {}), quantity: 1, ...(total ? { subtotal: total, total } : {}), meta_data: [ @@ -224,6 +224,33 @@ export async function updateOrder({ tenantId, wooOrderId, basket, address, run_i }); } +function isRetryableNetworkError(err) { + const e0 = err; + const e1 = err?.cause; + const e2 = err?.cause?.cause; + const candidates = [e0, e1, e2].filter(Boolean); + const codes = new Set(candidates.map((e) => e.code).filter(Boolean)); + const names = new Set(candidates.map((e) => e.name).filter(Boolean)); + const messages = candidates.map((e) => String(e.message || "")).join(" | ").toLowerCase(); + + const aborted = + names.has("AbortError") || + messages.includes("aborted") || + messages.includes("timeout") || + messages.includes("timed out"); + + const retryCodes = new Set(["ECONNRESET", "ETIMEDOUT", "UND_ERR_CONNECT_TIMEOUT", "UND_ERR_SOCKET"]); + const byCode = [...codes].some((c) => retryCodes.has(c)); + + return aborted || byCode; +} + +async function getOrderStatus({ client, wooOrderId }) { + const url = `${client.base}/orders/${encodeURIComponent(wooOrderId)}`; + const data = await fetchWoo({ url, method: "GET", timeout: client.timeout, headers: client.authHeader }); + return { id: data?.id, status: data?.status, raw: data }; +} + export async function updateOrderStatus({ tenantId, wooOrderId, status }) { if (!wooOrderId) throw new Error("missing_woo_order_id"); const lockKey = `${tenantId}:order:${wooOrderId}:status`; @@ -231,8 +258,115 @@ export async function updateOrderStatus({ tenantId, wooOrderId, status }) { const client = await getWooClient({ tenantId }); const payload = { status }; const url = `${client.base}/orders/${encodeURIComponent(wooOrderId)}`; - const data = await fetchWoo({ url, method: "PUT", body: payload, timeout: client.timeout, headers: client.authHeader }); - return { id: data?.id || wooOrderId, raw: data }; + + // Timeout corto para el PUT (Woo procesa pero tarda en responder) + const putTimeout = 3000; + + try { + const data = await fetchWoo({ url, method: "PUT", body: payload, timeout: putTimeout, headers: client.authHeader }); + return { id: data?.id || wooOrderId, raw: data }; + } catch (err) { + // Si es timeout, verificar si el status cambió con un GET + if (isRetryableNetworkError(err)) { + if (dbg.wooHttp) console.log("[wooOrders] updateOrderStatus timeout, checking with GET..."); + + try { + const current = await getOrderStatus({ client, wooOrderId }); + // Si el status ya es el deseado, la operación fue exitosa + if (current.status === status) { + if (dbg.wooHttp) console.log("[wooOrders] updateOrderStatus confirmed via GET", { wooOrderId, status }); + return { id: current.id || wooOrderId, raw: current.raw, recovered: true }; + } + } catch (getErr) { + // Si falla el GET también, propagar el error original + if (dbg.wooHttp) console.log("[wooOrders] updateOrderStatus GET also failed:", getErr.message); + } + } + throw err; + } }); } +export async function listRecentOrders({ tenantId, limit = 20 }) { + const client = await getWooClient({ tenantId }); + const url = `${client.base}/orders?per_page=${limit}&orderby=date&order=desc`; + const data = await fetchWoo({ url, method: "GET", timeout: client.timeout, headers: client.authHeader }); + + if (!Array.isArray(data)) return []; + + // Mapear a formato simplificado + return data.map(order => { + // Detectar si es orden de test (run_id empieza con "test-") + const runIdMeta = order.meta_data?.find(m => m.key === "run_id"); + const runId = runIdMeta?.value || null; + const isTest = runId?.startsWith("test-") || false; + const sourceMeta = order.meta_data?.find(m => m.key === "source"); + const source = sourceMeta?.value || "web"; + + // Método de envío (shipping) + const shippingLines = order.shipping_lines || []; + const shippingMethod = shippingLines[0]?.method_title || shippingLines[0]?.method_id || null; + const isDelivery = shippingMethod ? + !shippingMethod.toLowerCase().includes("retiro") && + !shippingMethod.toLowerCase().includes("pickup") && + !shippingMethod.toLowerCase().includes("local") : false; + + // Método de pago + const paymentMethod = order.payment_method || null; + const paymentMethodTitle = order.payment_method_title || null; + const isCash = paymentMethod === "cod" || + paymentMethodTitle?.toLowerCase().includes("efectivo") || + paymentMethodTitle?.toLowerCase().includes("cash"); + + // Estado de pago (basado en status de la orden) + // pending = no pago, processing/completed = pago + const isPaid = ["processing", "completed", "on-hold"].includes(order.status); + const datePaid = order.date_paid || null; + + return { + id: order.id, + status: order.status, + total: order.total, + currency: order.currency, + date_created: order.date_created, + date_paid: datePaid, + billing: { + first_name: order.billing?.first_name || "", + last_name: order.billing?.last_name || "", + phone: order.billing?.phone || "", + email: order.billing?.email || "", + address_1: order.billing?.address_1 || "", + address_2: order.billing?.address_2 || "", + city: order.billing?.city || "", + state: order.billing?.state || "", + postcode: order.billing?.postcode || "", + }, + shipping: { + first_name: order.shipping?.first_name || "", + last_name: order.shipping?.last_name || "", + address_1: order.shipping?.address_1 || "", + address_2: order.shipping?.address_2 || "", + city: order.shipping?.city || "", + state: order.shipping?.state || "", + postcode: order.shipping?.postcode || "", + }, + line_items: (order.line_items || []).map(li => ({ + id: li.id, + name: li.name, + quantity: li.quantity, + total: li.total, + })), + source, + run_id: runId, + is_test: isTest, + // Shipping info + shipping_method: shippingMethod, + is_delivery: isDelivery, + // Payment info + payment_method: paymentMethod, + payment_method_title: paymentMethodTitle, + is_cash: isCash, + is_paid: isPaid, + }; + }); +} diff --git a/src/modules/shared/wooSnapshot.js b/src/modules/shared/wooSnapshot.js index 41a0da0..73a2fd1 100644 --- a/src/modules/shared/wooSnapshot.js +++ b/src/modules/shared/wooSnapshot.js @@ -363,6 +363,7 @@ export async function pushProductToWoo({ tenantId, wooProductId, categories, sel if (categories && Array.isArray(categories) && categories.length > 0) { // Primero obtener las categorías existentes en Woo para mapear nombres a IDs const existingCats = await fetchWooCategoriesByNames({ tenantId, names: categories }); + if (existingCats.length > 0) { updatePayload.categories = existingCats.map(c => ({ id: c.id })); } @@ -424,8 +425,28 @@ async function fetchWooCategoriesByNames({ tenantId, names }) { page++; } + // Normalizar nombres - soportar formato jerárquico "Parent > Child" + // Extraer TODAS las partes del path para asignar la jerarquía completa + const normalizedNames = []; + for (const n of names) { + const full = String(n).toLowerCase().trim(); + // Si tiene formato jerárquico "Parent > Child > Grandchild", extraer cada parte + if (full.includes(' > ')) { + const parts = full.split(' > ').map(p => p.trim()).filter(Boolean); + for (const part of parts) { + if (!normalizedNames.includes(part)) { + normalizedNames.push(part); + } + } + } else { + // Categoría simple (sin jerarquía) + if (!normalizedNames.includes(full)) { + normalizedNames.push(full); + } + } + } + // Filtrar las que coinciden con los nombres buscados - const normalizedNames = names.map(n => String(n).toLowerCase().trim()); return allCategories.filter(c => normalizedNames.includes(String(c.name).toLowerCase().trim()) || normalizedNames.includes(String(c.slug).toLowerCase().trim())