From f743ace6af76ce0e2e532b3b07dfaca42a002f43 Mon Sep 17 00:00:00 2001 From: seb Date: Mon, 12 Oct 2026 19:48:04 +0200 Subject: [PATCH] u --- artikeltabelle.csv | 1066 ----------------- docs/api/README.md | 6 + docs/api/routes-all.md | 329 ++--- docs/api/routes.md | 287 +++-- docs/api/routes/order/_id/shipment.get.md | 71 ++ docs/api/routes/order/_id/shipment.post.md | 108 ++ docs/api/routes/order/_id/shipping.get.md | 2 + docs/api/routes/order/_id/shipping.post.md | 2 + docs/api/routes/order/create.md | 48 +- docs/api/routes/order/get.md | 48 +- docs/api/routes/order/list.md | 48 +- docs/api/routes/order/update.md | 48 +- docs/api/routes/shipment/_id.delete.md | 32 + docs/api/routes/shipment/_id.get.md | 69 ++ docs/api/routes/shipment/_id/shipping.get.md | 45 + docs/api/routes/shipment/_id/shipping.post.md | 66 + docs/api/routes/shipping/_id.patch.md | 6 + img2.jpg | Bin 282 -> 0 bytes img3.jpg | Bin 282 -> 0 bytes src/api/openapi.js | 63 + src/api/openapiSpec.js | 2 + src/api/routes/shipment.js | 386 ++++++ src/api/routes/shipping.js | 185 ++- src/db/order.js | 76 +- src/db/shipment.js | 21 + src/services/companyImport/orderImporter.js | 55 +- 26 files changed, 1694 insertions(+), 1375 deletions(-) delete mode 100644 artikeltabelle.csv create mode 100644 docs/api/routes/order/_id/shipment.get.md create mode 100644 docs/api/routes/order/_id/shipment.post.md create mode 100644 docs/api/routes/shipment/_id.delete.md create mode 100644 docs/api/routes/shipment/_id.get.md create mode 100644 docs/api/routes/shipment/_id/shipping.get.md create mode 100644 docs/api/routes/shipment/_id/shipping.post.md delete mode 100644 img2.jpg delete mode 100644 img3.jpg create mode 100644 src/api/routes/shipment.js create mode 100644 src/db/shipment.js diff --git a/artikeltabelle.csv b/artikeltabelle.csv deleted file mode 100644 index 9014d3b..0000000 --- a/artikeltabelle.csv +++ /dev/null @@ -1,1066 +0,0 @@ -="ArtNr";"Name";Preis;USt;Pfand;"Hersteller";"Kategorie";="Barcode";"V/K" -="42000573";" Steel Pipe Screen 12mm";2,00;19;;;"Zubehör";; -="42001190";"420Strainz";0,00;19;;;"Flowers | CBN";;"V" -="42001190-1";"420Strainz 1g";11,00;19;;;"Flowers | CBN";;"K" -="42001190-2";"420Strainz 2g";21,00;19;;;"Flowers | CBN";;"K" -="42001190-3";"420Strainz 5g";50,00;19;;;"Flowers | CBN";;"K" -="42000002";"420z Leaf Blueberry";9,95;19;;;"Tabak";="4270001588593"; -="42000004";"420z Leaf Classic";9,95;19;;;"Tabak";="4270001588579"; -="42000005";"420z Leaf Lemon";9,95;19;;;"Tabak";="4270001588586"; -="42000003";"420z Leaf Rainbow";9,95;19;;;"Tabak";="4270000769634"; -="42001319";"50 Pre Roll Xtra";24,90;19;;;"Smoke | Papes";="4260748413760"; -="42001299";"Agro Perlit 4 l";5,00;19;;;"Grow | Erde";="8592542017506"; -="42001275";"Agro Perlit 8 l";8,00;19;;;"Grow | Erde";="8594200871161"; -="42001289";"Airontek Clip Fan 15W 15 cm";22,00;19;;;"Grow | Zubehör";="8595722026664"; -="42000006";"Ak420 1";7,50;19;;;"Seeds | Seedstockers";; -="42000007";"Ak420 3";15,00;19;;;"Seeds | Seedstockers";; -="42000008";"Ak420 5";22,00;19;;;"Seeds | Seedstockers";; -="42000009";"Aktivkohletasche XS Schwarz";9,90;19;;;"Zubehör";; -="42000011";"Amaretto Tarmac Auto 1";17,00;19;;;"Seeds | Seedstockers";; -="42000012";"Amaretto Tarmac Auto 3";34,00;19;;;"Seeds | Seedstockers";; -="42000013";"Amaretto Tarmac Auto 5";51,00;19;;;"Seeds | Seedstockers";; -="42000015";"American Spirit Blue";6,50;19;;;"Tabak";="4032800056860"; -="42000014";"American Spirit Gelb";6,50;19;;;"Tabak";="4032800056884"; -="42001135";"American Spirit Master Blend Orange";7,00;19;;;"Tabak";="4032800056907"; -="42000016";"American Spirit Selected Red";6,30;19;;;"Tabak";="4032800056754"; -="42001224";"Amsterdam | Glass Bowl - SG:18.8mm with Small Hole";8,00;19;;;"Bong/Glasfilter";="616695664580"; -="42001235";"Amsterdam Glass Bong- H:28cm- Ø:40mm SG:18.8mm ";35,00;19;;;"Bong/Glasfilter";="616695666508"; -="42000017";"Anheuser 0,33L";1,42;19;0,08;;"Getränke";="4100130022081"; -="42000018";"Apollo Black Cherry Auto 1";24,00;19;;;"Seeds | Seedstockers";; -="42000019";"Apollo Black Cherry Auto 3";48,00;19;;;"Seeds | Seedstockers";; -="42000020";"Apollo Black Cherry Auto 5";72,00;19;;;"Seeds | Seedstockers";; -="42001276";"Aqua Master Tools pH meter P50 PRO 2.0 ";65,00;19;;;"Grow | Messgeräte";="8721249602009"; -="42000673-1";"Aqua Panna Tuscani";1,25;19;0,15;;"Getränke";="8002270018848";"K" -="42000021";"Arizona";2,25;19;0,25;;"Getränke";; -="42001182";"Astra";0,00;19;;;"Getränke";;"V" -="42001182-2";"Astra Kiezmische";1,72;19;0,08;;"Getränke";="42278177";"K" -="42001182-1";"Astra Standard";1,72;19;0,08;;"Getränke";;"K" -="42001301";"Athena Trial Kit Liquid Blended Starter kit";95,00;19;;;"Grow | Dünger";="860011221456"; -="42000024";"Augustiner";2,02;19;0,08;;"Getränke";="4105250022003"; -="42001260";"Autopot Airdome";7,00;19;;;"Grow | AutoPot";; -="42001252";"Autopot Tray 2 Grow";87,90;19;;;"Grow | AutoPot";="5060151875488"; -="42000029";"Banana Jealousy x Guava";35,00;19;;;"Seeds | Seedstockers";; -="42000025";"BCN Power Plant Auto 1";11,50;19;;;"Seeds | Seedstockers";; -="42000026";"BCN Power Plant Auto 3";23,00;19;;;"Seeds | Seedstockers";; -="42000027";"BCN Power Plant Auto 5";34,50;19;;;"Seeds | Seedstockers";; -="42000030";"Becks ";1,52;19;0,08;;"Getränke";;"V" -="42000030-3";"Becks ICE";1,92;19;0,08;;"Getränke";="42150022";"K" -="42000030-1";"Becks 0,33l";1,52;19;0,08;;"Getränke";="41001301";"K" -="42000030-2";"Becks 0,5l";1,92;19;0,08;;"Getränke";="41001318";"K" -="42001234";"Beer Bottle Glass Bong- H:26cm- SG:14.5mm";20,00;19;;;"Bong/Glasfilter";="616695664405"; -="42001209";"Berries";0,00;19;;;"CBN_";;"V" -="42001216";"Berries";0,00;19;;;"CBD_";;"V" -="42001209-1";"Berries 1g";0,00;19;;;"CBN_";="4262557690455";"K" -="42001216-1";"Berries 1g";0,00;19;;;"CBD_";;"K" -="42001216-2";"Berries 2g";0,00;19;;;"CBD_";;"K" -="42001209-2";"Berries 2g";0,00;19;;;"CBN_";="4262557690462";"K" -="42001209-3";"Berries 5g";0,00;19;;;"CBN_";="4262557690479";"K" -="42001117";"Berries CBD";0,00;19;;;"Flowers | CBD";;"V" -="42001117-1";"Berries CBD 1g";5,00;19;;;"Flowers | CBD";="4262557690066";"K" -="42001117-2";"Berries CBD 2g";9,00;19;;;"Flowers | CBD";="4262557690073";"K" -="42001117-3";"Berries CBD 5g";22,50;19;;;"Flowers | CBD";="4262557690080";"K" -="42001246";"BIC Mini PUN";1,60;19;;;"Zubehör";; -="42001318";"BioBizz All Pack Indoor";54,90;19;;;"Grow | Dünger";; -="42001274";"Biobizz Try Pack Indoor 750 ml";20,00;19;;;"Grow | Dünger";="8718403232127"; -="42001320";"BioBizz TryPack Outdoor";15,50;19;;;"Grow | Dünger";; -="42000090";"Biodor Moschus";14,90;19;;;"Zubehör";; -="42000087";"Biodor Neutral";14,90;19;;;"Zubehör";; -="42000091";"Biodor Spray";16,90;19;;;"Zubehör";; -="42001298";"BioNova Open Top Coco Bag 12 l ";6,00;19;;;"Grow | Erde";="8595722002910"; -="42001214";"Black Caviar";0,00;19;;;"CBD_";;"V" -="42001214-1";"Black Caviar 1g";0,00;19;;;"CBD_";;"K" -="42001214-2";"Black Caviar 2g";0,00;19;;;"CBD_";;"K" -="42001118";"Black Caviar CBD";0,00;19;;;"Flowers | CBD";;"V" -="42001118-1";"Black Caviar CBD 1g";5,00;19;;;"Flowers | CBD";="4262557690035";"K" -="42001118-2";"Black Caviar CBD 2g";9,00;19;;;"Flowers | CBD";="4262557690042";"K" -="42001118-3";"Black Caviar CBD 5g";22,50;19;;;"Flowers | CBD";="4262557690059";"K" -="42001211";"Black Cherry";0,00;19;;;"CBN_";;"V" -="42001217";"Black Cherry";0,00;19;;;"CBD_";;"V" -="42001217-1";"Black Cherry 1g";0,00;19;;;"CBD_";="4262557690127";"K" -="42001211-1";"Black Cherry 1g";0,00;19;;;"CBN_";="4262557690516";"K" -="42001211-2";"Black Cherry 2g";0,00;19;;;"CBN_";="4262557690523";"K" -="42001217-2";"Black Cherry 2g";0,00;19;;;"CBD_";;"K" -="42001211-3";"Black Cherry 5g";0,00;19;;;"CBN_";="4262557691384";"K" -="42000098";"Black Rainbow Auto 1";14,50;19;;;"Seeds | Seedstockers";; -="42000099";"Black Rainbow Auto 3";29,00;19;;;"Seeds | Seedstockers";; -="42000100";"Black Rainbow Auto 5";43,50;19;;;"Seeds | Seedstockers";; -="42000101";"Blue Moby FEM 1";14,50;19;;;"Seeds | Seedstockers";; -="42000102";"Blue Moby FEM 3";29,00;19;;;"Seeds | Seedstockers";; -="42000103";"Blue Moby FEM 5";43,50;19;;;"Seeds | Seedstockers";; -="42001120";"Boarding Pass CBD";0,00;19;;;"Flowers | CBD";;"V" -="42001120-1";"Boarding Pass CBD 1g";10,00;19;;;"Flowers | CBD";="4262557690967";"K" -="42001120-2";"Boarding Pass CBD 2g";19,00;19;;;"Flowers | CBD";="4262557690974";"K" -="42001120-3";"Boarding Pass CBD 5g";45,00;19;;;"Flowers | CBD";="4262557690981";"K" -="42001106";"Boarding Pass CBN";0,00;19;;;"Flowers | CBN";;"V" -="42001106-6";"Boarding Pass CBN 10g";60,00;19;;;"Flowers | CBN";;"K" -="42001106-1";"Boarding Pass CBN 15g";82,50;19;;;"Flowers | CBN";;"K" -="42001106-9";"Boarding Pass CBN 1g";7,50;19;;;"Flowers | CBN";="4262557690882";"K" -="42001106-5";"Boarding Pass CBN 20g";105,00;19;;;"Flowers | CBN";;"K" -="42001106-4";"Boarding Pass CBN 25g";125,00;19;;;"Flowers | CBN";;"K" -="42001106-8";"Boarding Pass CBN 2g";14,00;19;;;"Flowers | CBN";="4262557690899";"K" -="42001106-2";"Boarding Pass CBN 30g";142,50;19;;;"Flowers | CBN";;"K" -="42001106-3";"Boarding Pass CBN 3g";20,00;19;;;"Flowers | CBN";;"K" -="42001106-7";"Boarding Pass CBN 5g";32,50;19;;;"Flowers | CBN";="4262557690905";"K" -="42000114";"Bong Atomic 9mm";49,90;19;;;"Bong/Glasfilter";; -="42000116";"Bong Atomic Acryl groß";24,90;19;;;"Bong/Glasfilter";; -="42000115";"Bong Atomic Acryl klein";19,90;19;;;"Bong/Glasfilter";; -="42000113";"Bong Standard";1,00;19;;;"Bong/Glasfilter";; -="42001138";"Brass Screens 5";0,50;19;;;"Bong/Glasfilter";="000420011389"; -="42001121";"Bubblegum CBD";0,00;19;;;"Flowers | CBD";;"V" -="42001121-4";"Bubblegum CBD 10g";85,00;19;;;"Flowers | CBD";;"K" -="42001121-1";"Bubblegum CBD 1g";10,00;19;;;"Flowers | CBD";="4262557690936";"K" -="42001121-2";"Bubblegum CBD 2g";19,00;19;;;"Flowers | CBD";="4262557690943";"K" -="42001121-3";"Bubblegum CBD 5g";45,00;19;;;"Flowers | CBD";="4262557690950";"K" -="42001115";"Bubblegum CBN";0,00;19;;;"Flowers | CBN";;"V" -="42001115-2";"Bubblegum CBN 1g";11,00;19;;;"Flowers | CBN";="4262557690851";"K" -="42001115-3";"Bubblegum CBN 2g";21,00;19;;;"Flowers | CBN";="4262557690868";"K" -="42001115-1";"Bubblegum CBN 5g";50,00;19;;;"Flowers | CBN";="4262557690875";"K" -="42000136";"Budweiser";2,02;19;0,08;;"Getränke";="8594403110111"; -="42000143";"Candy Dawg Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000144";"Candy Dawg Auto 3";26,00;19;;;"Seeds | Seedstockers";; -="42000145";"Candy Dawg Auto 5";39,00;19;;;"Seeds | Seedstockers";; -="42001313";"Canna Smile Live Resin CBD";0,00;19;;;"Vapes";;"V" -="42001313-4";"Canna Smile Live Resin CBD Banana Runtz";24,90;19;;;"Vapes";="9824204201022";"K" -="42001313-3";"Canna Smile Live Resin CBD Bubba Kush";24,90;19;;;"Vapes";="9824204201015";"K" -="42001313-2";"Canna Smile Live Resin CBD Lemon Cherry Gelato";24,90;19;;;"Vapes";="9824204201039";"K" -="42001313-5";"Canna Smile Live Resin CBD London Pound Cake";24,90;19;;;"Vapes";="9824204201046";"K" -="42001313-1";"Canna Smile Live Resin CBD Super Jack";24,90;19;;;"Vapes";="9824204201053";"K" -="42001166";"Cannasseur Cooling Case";25,00;19;;;"Cannasseur";="657039784908"; -="42001165";"Cannasseur Lupe";15,00;19;;;"Cannasseur";="657039784892"; -="42000159";"Canuma Blau";5,90;19;;;"Tabak";="42477518"; -="42000160";"Canuma Grün";5,90;19;;;"Tabak";="42477495"; -="42001185";"Capri Sonne";0,00;19;;;"Getränke";;"V" -="42001185-2";"Capri Sonne Groß";1,80;19;;;"Getränke";;"K" -="42001185-3";"Capri Sonne Monster";1,20;19;;;"Getränke";;"K" -="42001185-4";"Capri Sonne Mystic";1,20;19;;;"Getränke";;"K" -="42001185-1";"Capri Sonne Orange";1,20;19;;;"Getränke";="4000177032732";"K" -="42001249";"Carlsberg 0,5l";1,92;19;0,08;;"Getränke";="5740600514143"; -="42001212";"Caviar";0,00;19;;;"CBN_";;"V" -="42001212-1";"Caviar 1g";0,00;19;;;"CBN_";="4262557690424";"K" -="42001212-2";"Caviar 2g";0,00;19;;;"CBN_";="4262557690431";"K" -="42001212-3";"Caviar 5g";0,00;19;;;"CBN_";="4262557690448";"K" -="42000140";"CBD 1:1 Silver Lime Haze Auto 1";17,00;19;;;"Seeds | Seedstockers";; -="42000141";"CBD 1:1 Silver Lime Haze Auto 3";34,00;19;;;"Seeds | Seedstockers";; -="42000142";"CBD 1:1 Silver Lime Haze Auto 5";51,00;19;;;"Seeds | Seedstockers";; -="42001149";"CBD Trim";0,00;19;;;"Flowers | CBD";;"V" -="42001149-1";"CBD Trim 20g";20,00;19;;;"Flowers | CBD";="4262557691490";"K" -="42001149-2";"CBD Trim 5g";10,00;19;;;"Flowers | CBD";="4262557691483";"K" -="42001148";"CBN Trim ";45,00;19;;;"Flowers | CBN";="4262557691506";"V" -="42001148-1";"CBN Trim 20g";45,00;19;;;"Flowers | CBN";="4262557691506";"K" -="42001314";"CCell M4";9,90;19;;;"Vapes";="6943498626670"; -="42000167";"Chocomel";5,00;19;;;"Getränke";; -="42001247";"Clipper 420 Fairytale";2,00;19;;;"Zubehör";; -="42001245";"Clipper THC Train";2,50;19;;;"Zubehör";; -="42000169";"Club Mate 0,5";2,05;19;0,15;;"Getränke";;"V" -="42000169-2";"Club Mate 0,5 Eistee";2,05;19;0,15;;"Getränke";="4029764001814";"K" -="42000169-1";"Club Mate 0,5 Original";2,05;19;0,15;;"Getränke";="4029764001807";"K" -="42000170";"Coca Cola 0,33L";2,02;19;0,08;;"Getränke";;"V" -="42000170-1";"Coca Cola 0,33L Cherry";2,02;19;0,08;;"Getränke";="54035546";"K" -="42000170-2";"Coca Cola 0,33L Original";2,02;19;0,08;;"Getränke";="87126853";"K" -="42000172";"Deckel Humidor L";18,90;19;;;"Cannasseur";; -="42000171";"Deckel Humidor M";17,90;19;;;"Cannasseur";; -="42000173";"Deckel Humidor XL";19,90;19;;;"Cannasseur";; -="42000174";"Desperados 0,33L";2,22;19;0,08;;"Getränke";; -="42001102";"Devils Milk";0,00;19;;;"Flowers | CBN";;"V" -="42001132-3";"Devils Milk 10g";45,00;19;;;"Flowers | CBD";;"K" -="42001102-3";"Devils Milk 10g";65,00;19;;;"Flowers | CBN";;"K" -="42001102-1";"Devils Milk 15g";90,00;19;;;"Flowers | CBN";;"K" -="42001132-4";"Devils Milk 15g";62,50;19;;;"Flowers | CBD";;"K" -="42001132-2";"Devils Milk 1g";6,00;19;;;"Flowers | CBD";="4262557691452";"K" -="42001102-2";"Devils Milk 1g";8,00;19;;;"Flowers | CBN";="4262557691100";"K" -="42001102-5";"Devils Milk 20g";115,00;19;;;"Flowers | CBN";;"K" -="42001132-6";"Devils Milk 20g";80,00;19;;;"Flowers | CBD";;"K" -="42001132-7";"Devils Milk 25g";95,00;19;;;"Flowers | CBD";;"K" -="42001102-6";"Devils Milk 25g";137,50;19;;;"Flowers | CBN";;"K" -="42001102-4";"Devils Milk 2g";15,00;19;;;"Flowers | CBN";="4262557691094";"K" -="42001132-5";"Devils Milk 2g";10,00;19;;;"Flowers | CBD";="4262557691469";"K" -="42001132-8";"Devils Milk 30g";105,00;19;;;"Flowers | CBD";;"K" -="42001102-8";"Devils Milk 30g";157,50;19;;;"Flowers | CBN";;"K" -="42001102-7";"Devils Milk 3g";22,50;19;;;"Flowers | CBN";;"K" -="42001102-9";"Devils Milk 5g";35,00;19;;;"Flowers | CBN";="4262557691087";"K" -="42001132-1";"Devils Milk 5g";25,00;19;;;"Flowers | CBD";="4262557691476";"K" -="42001132";"Devils Milk CBD";0,00;19;;;"Flowers | CBD";;"V" -="42000194";"Drizl Pack Auto 1";19,50;19;;;"Seeds | Seedstockers";; -="42000195";"Drizl Pack Auto 3";39,00;19;;;"Seeds | Seedstockers";; -="42000196";"Drizl Pack Auto 5";58,50;19;;;"Seeds | Seedstockers";; -="42000197";"Drizl Pack FEM 1";19,50;19;;;"Seeds | Seedstockers";; -="42000198";"Drizl Pack FEM 3";39,00;19;;;"Seeds | Seedstockers";; -="42000199";"Drizl Pack FEM 5";58,50;19;;;"Seeds | Seedstockers";; -="42001103";"Dry Sift";0,00;19;;;"Flowers | CBN";;"V" -="42001103-9";"Dry Sift 10g";85,00;19;;;"Flowers | CBN";;"K" -="42001103-8";"Dry Sift 15g";120,00;19;;;"Flowers | CBN";;"K" -="42001103-1";"Dry Sift 1g";10,00;19;;;"Flowers | CBN";="4262557691544";"K" -="42001103-6";"Dry Sift 20g";155,00;19;;;"Flowers | CBN";;"K" -="42001103-5";"Dry Sift 25g";185,00;19;;;"Flowers | CBN";;"K" -="42001103-7";"Dry Sift 2g";19,00;19;;;"Flowers | CBN";="4262557691551";"K" -="42001103-3";"Dry Sift 30g";210,00;19;;;"Flowers | CBN";;"K" -="42001103-4";"Dry Sift 3g";28,50;19;;;"Flowers | CBN";;"K" -="42001103-2";"Dry Sift 5g";45,00;19;;;"Flowers | CBN";="4262557691568";"K" -="42001266";"Drybag";0,00;19;;;"Zubehör";;"V" -="42001266-3";"Drybag Groß";4,00;19;;;"Zubehör";;"K" -="42001266-1";"Drybag Klein";2,00;19;;;"Zubehör";;"K" -="42001266-2";"Drybag Mittel";3,00;19;;;"Zubehör";;"K" -="42001186";"Effect";0,00;19;;;"Getränke";;"V" -="42001186-4";"Effect Black Acai";2,25;19;0,25;;"Getränke";="4025127090235";"K" -="42001186-1";"Effect Cocos Blaubeere";2,25;19;0,25;;"Getränke";="4025127088126";"K" -="42001186-5";"Effect Habibi Berry";2,25;19;0,25;;"Getränke";="4025127085460";"K" -="42001186-2";"Effect Original";2,25;19;0,25;;"Getränke";="4025127020997";"K" -="42001186-3";"Effect Pink Grapefruit";2,25;19;0,25;;"Getränke";="4025127085187";"K" -="42001186-6";"Effect Strawberry Apricot";2,25;19;0,25;;"Getränke";="4025127085767";"K" -="42000223";"Erdinger Weißbier";2,02;19;0,08;;"Getränke";="4002103000013"; -="42001238";"Erdinger Weißbier 0,33l";1,62;19;0,08;;"Getränke";="4002103000105"; -="42000224";"Extract Cooling Case";39,90;19;;;"Cannasseur";; -="42000225";"F.U.H Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000226";"F.U.H Auto 3";26,00;19;;;"Seeds | Seedstockers";; -="42000227";"F.U.H Auto 5";39,00;19;;;"Seeds | Seedstockers";; -="42001310";"Fanta";0,00;19;;;"CBN_";;"V" -="42001310-1";"Fanta 1g";0,00;19;;;"CBN_";="4262557691650";"K" -="42001310-2";"Fanta 2g";0,00;19;;;"CBN_";="4262557691643";"K" -="42001310-3";"Fanta 5g";0,00;19;;;"CBN_";="4262557691636";"K" -="42001177";"Fanta Orange 0,33l";1,92;19;0,08;;"Getränke";="87126860"; -="42000228";"FastBuds 5er Mix Pack Auto";55,00;19;;;"Seeds | FastBuds";; -="42000229";"FastBuds Afghan Kush Auto 1";9,00;19;;;"Seeds | FastBuds";; -="42000230";"FastBuds Afghan Kush Auto 3";24,00;19;;;"Seeds | FastBuds";; -="42000231";"FastBuds Afghan Kush Auto 5";35,00;19;;;"Seeds | FastBuds";; -="42001097";"FastBuds Amnesia Z Auto";0,00;19;;;"Seeds | FastBuds";;"V" -="42001097-1";"FastBuds Amnesia Z Auto 1g";13,00;19;;;"Seeds | FastBuds";;"K" -="42001097-2";"FastBuds Amnesia Z Auto 3g";36,00;19;;;"Seeds | FastBuds";;"K" -="42001097-3";"FastBuds Amnesia Z Auto 5g";55,00;19;;;"Seeds | FastBuds";;"K" -="42001098";"FastBuds Apple Strudel Auto";0,00;19;;;"Seeds | FastBuds";;"V" -="42001098-1";"FastBuds Apple Strudel Auto 1g";13,00;19;;;"Seeds | FastBuds";;"K" -="42001098-2";"FastBuds Apple Strudel Auto 3g";36,00;19;;;"Seeds | FastBuds";;"K" -="42001098-3";"FastBuds Apple Strudel Auto 5g";55,00;19;;;"Seeds | FastBuds";;"K" -="42000238";"FastBuds Banana Purple Punch Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000239";"FastBuds Banana Purple Punch Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000240";"FastBuds Banana Purple Punch Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000241";"FastBuds Biscotti Gelato 1";22,00;19;;;"Seeds | FastBuds";; -="42000242";"FastBuds Biscotti Gelato 3";40,00;19;;;"Seeds | FastBuds";; -="42000243";"FastBuds Biscotti Gelato 5";65,00;19;;;"Seeds | FastBuds";; -="42000244";"FastBuds Blueberry Auto 1";9,00;19;;;"Seeds | FastBuds";; -="42000245";"FastBuds Blueberry Auto 3";24,00;19;;;"Seeds | FastBuds";; -="42000246";"FastBuds Blueberry Auto 5";35,00;19;;;"Seeds | FastBuds";; -="42000247";"FastBuds Bruce Banner Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000248";"FastBuds Bruce Banner Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000249";"FastBuds Bruce Banner Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000250";"FastBuds BubbleGum Auto 1";9,00;19;;;"Seeds | FastBuds";; -="42000251";"FastBuds BubbleGum Auto 3";24,00;19;;;"Seeds | FastBuds";; -="42000252";"FastBuds BubbleGum Auto 5";35,00;19;;;"Seeds | FastBuds";; -="42000253";"FastBuds CBD Auto 1:1 (CBD Crack) 1";9,00;19;;;"Seeds | FastBuds";; -="42000254";"FastBuds CBD Auto 1:1 (CBD Crack) 3";24,00;19;;;"Seeds | FastBuds";; -="42000255";"FastBuds CBD Auto 1:1 (CBD Crack) 5";35,00;19;;;"Seeds | FastBuds";; -="42000256";"FastBuds Cherry Cola Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000257";"FastBuds Cherry Cola Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000258";"FastBuds Cherry Cola Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000259";"FastBuds Crystal Meth Auto 1";9,00;19;;;"Seeds | FastBuds";; -="42000260";"FastBuds Crystal Meth Auto 3";24,00;19;;;"Seeds | FastBuds";; -="42000261";"FastBuds Crystal Meth Auto 5";35,00;19;;;"Seeds | FastBuds";; -="42000262";"FastBuds Forbidden Runtz Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000263";"FastBuds Forbidden Runtz Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000264";"FastBuds Forbidden Runtz Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000265";"FastBuds Frostbanger Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000266";"FastBuds Frostbanger Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000267";"FastBuds Frostbanger Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000268";"FastBuds GG4 Sherbet FF 1";13,00;19;;;"Seeds | FastBuds";; -="42000269";"FastBuds GG4 Sherbet FF 3";36,00;19;;;"Seeds | FastBuds";; -="42000270";"FastBuds GG4 Sherbet FF 5";55,00;19;;;"Seeds | FastBuds";; -="42000271";"FastBuds Gorilla Cookies FF 1";13,00;19;;;"Seeds | FastBuds";; -="42000272";"FastBuds Gorilla Cookies FF 3";36,00;19;;;"Seeds | FastBuds";; -="42000273";"FastBuds Gorilla Cookies FF 5";55,00;19;;;"Seeds | FastBuds";; -="42000274";"FastBuds Gorilla Melon Feminisiert 1";13,00;19;;;"Seeds | FastBuds";; -="42000275";"FastBuds Gorilla Melon Feminisiert 3";36,00;19;;;"Seeds | FastBuds";; -="42000276";"FastBuds Gorilla Melon Feminisiert 5";55,00;19;;;"Seeds | FastBuds";; -="42000277";"FastBuds Gorilla Z Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000278";"FastBuds Gorilla Z Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000279";"FastBuds Gorilla Z Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000280";"FastBuds Guava Auto RF3 1";16,00;19;;;"Seeds | FastBuds";; -="42000281";"FastBuds Guava Auto RF3 3";42,00;19;;;"Seeds | FastBuds";; -="42000282";"FastBuds Guava Auto RF3 5";65,00;19;;;"Seeds | FastBuds";; -="42000286";"FastBuds Lemon Cherry Cookies Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000287";"FastBuds Lemon Cherry Cookies Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000288";"FastBuds Lemon Cherry Cookies Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000289";"FastBuds Lemon Cherry Runtz 1";22,00;19;;;"Seeds | FastBuds";; -="42000290";"FastBuds Lemon Cherry Runtz 3";40,00;19;;;"Seeds | FastBuds";; -="42000291";"FastBuds Lemon Cherry Runtz 5";65,00;19;;;"Seeds | FastBuds";; -="42000292";"FastBuds Lemonpaya Feminisiert 1";13,00;19;;;"Seeds | FastBuds";; -="42000293";"FastBuds Lemonpaya Feminisiert 3";36,00;19;;;"Seeds | FastBuds";; -="42000294";"FastBuds Lemonpaya Feminisiert 5";55,00;19;;;"Seeds | FastBuds";; -="42000283";"FastBuds LSD-25 Auto 1";9,00;19;;;"Seeds | FastBuds";; -="42000284";"FastBuds LSD-25 Auto 3";24,00;19;;;"Seeds | FastBuds";; -="42000285";"FastBuds LSD-25 Auto 5";35,00;19;;;"Seeds | FastBuds";; -="42000295";"FastBuds Moby Dick Auto 1";9,00;19;;;"Seeds | FastBuds";; -="42000296";"FastBuds Moby Dick Auto 3";24,00;19;;;"Seeds | FastBuds";; -="42000297";"FastBuds Moby Dick Auto 5";35,00;19;;;"Seeds | FastBuds";; -="42000298";"FastBuds Orange President Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000299";"FastBuds Orange President Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000300";"FastBuds Orange President Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000301";"FastBuds Orange Sherbet FF 1";13,00;19;;;"Seeds | FastBuds";; -="42000302";"FastBuds Orange Sherbet FF 3";36,00;19;;;"Seeds | FastBuds";; -="42000303";"FastBuds Orange Sherbet FF 5";55,00;19;;;"Seeds | FastBuds";; -="42000304";"FastBuds Papaya Cookies Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000305";"FastBuds Papaya Cookies Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000306";"FastBuds Papaya Cookies Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000307";"FastBuds Papaya Sherbet Feminisiert 1";13,00;19;;;"Seeds | FastBuds";; -="42000308";"FastBuds Papaya Sherbet Feminisiert 3";36,00;19;;;"Seeds | FastBuds";; -="42000309";"FastBuds Papaya Sherbet Feminisiert 5";55,00;19;;;"Seeds | FastBuds";; -="42000310";"FastBuds Pound Cake Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000311";"FastBuds Pound Cake Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000312";"FastBuds Pound Cake Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000313";"FastBuds Purple Haze Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000314";"FastBuds Purple Haze Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000315";"FastBuds Purple Haze Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000316";"FastBuds Purple Lemonade Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000317";"FastBuds Purple Lemonade Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000318";"FastBuds Purple Lemonade Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000319";"FastBuds Purple Lemonade FF 1";13,00;19;;;"Seeds | FastBuds";; -="42000320";"FastBuds Purple Lemonade FF 3";36,00;19;;;"Seeds | FastBuds";; -="42000321";"FastBuds Purple Lemonade FF 5";55,00;19;;;"Seeds | FastBuds";; -="42000322";"FastBuds Smoothie Auto 1";9,00;19;;;"Seeds | FastBuds";; -="42000323";"FastBuds Smoothie Auto 3";24,00;19;;;"Seeds | FastBuds";; -="42000324";"FastBuds Smoothie Auto 5";35,00;19;;;"Seeds | FastBuds";; -="42000325";"FastBuds Sour Jealousy Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000326";"FastBuds Sour Jealousy Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000327";"FastBuds Sour Jealousy Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000328";"FastBuds Strawberry Banana Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000329";"FastBuds Strawberry Banana Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000330";"FastBuds Strawberry Banana Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000331";"FastBuds Strawberry Pie Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000332";"FastBuds Strawberry Pie Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000333";"FastBuds Strawberry Pie Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000334";"FastBuds Super Boof Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000335";"FastBuds Super Boof Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000336";"FastBuds Super Boof Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000337";"FastBuds Tropicana Cookies Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000338";"FastBuds Tropicana Cookies Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000339";"FastBuds Tropicana Cookies Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000340";"FastBuds Tropicana Cookies FF 1";13,00;19;;;"Seeds | FastBuds";; -="42000341";"FastBuds Tropicana Cookies FF 3";36,00;19;;;"Seeds | FastBuds";; -="42000342";"FastBuds Tropicana Cookies FF 5";55,00;19;;;"Seeds | FastBuds";; -="42000343";"FastBuds Wedding Cheesecake FF 1";13,00;19;;;"Seeds | FastBuds";; -="42000344";"FastBuds Wedding Cheesecake FF 3";36,00;19;;;"Seeds | FastBuds";; -="42000345";"FastBuds Wedding Cheesecake FF 5";55,00;19;;;"Seeds | FastBuds";; -="42000349";"FastBuds Ztrawberriez Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000350";"FastBuds Ztrawberriez Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000351";"FastBuds Ztrawberriez Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42000346";"FastBuds Z-Up Auto 1";13,00;19;;;"Seeds | FastBuds";; -="42000347";"FastBuds Z-Up Auto 3";36,00;19;;;"Seeds | FastBuds";; -="42000348";"FastBuds Z-Up Auto 5";55,00;19;;;"Seeds | FastBuds";; -="42001180";"Feldschlößchen Radler";0,00;19;;;"Getränke";;"V" -="42001180-1";"Feldschlößchen Radler Grapefruit";1,72;19;0,08;;"Getränke";;"K" -="42001180-2";"Feldschlößchen Radler Zitrone";1,72;19;0,08;;"Getränke";="4250594205695";"K" -="42000354";"Feuerzeug BIC";1,90;19;;;"Zubehör";; -="42000356";"Feuerzeug Brenner";3,00;19;;;"Zubehör";; -="42000357";"Feuerzeug Clicker";2,50;19;;;"Zubehör";; -="42000355";"Feuerzeug Clipper";2,00;19;;;"Zubehör";; -="42000358";"Feuerzeug Feuerstein Clipper";1,00;19;;;"Zubehör";; -="42000359";"Fishy Zoap Auto 1";17,00;19;;;"Seeds | Seedstockers";; -="42000360";"Fishy Zoap Auto 3";34,00;19;;;"Seeds | Seedstockers";; -="42000361";"Fishy Zoap Auto 5";51,00;19;;;"Seeds | Seedstockers";; -="42000362";"Fishy Zoap FEM 1";17,00;19;;;"Seeds | Seedstockers";; -="42000363";"Fishy Zoap FEM 3";34,00;19;;;"Seeds | Seedstockers";; -="42000364";"Fishy Zoap FEM 5";51,00;19;;;"Seeds | Seedstockers";; -="42000373";"Forbidden Mochi Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000374";"Forbidden Mochi Auto 3";26,00;19;;;"Seeds | Seedstockers";; -="42000375";"Forbidden Mochi Auto 5";39,00;19;;;"Seeds | Seedstockers";; -="42000376";"Fred Jaune";6,70;19;;;"Tabak";="7640119424125"; -="42000377";"Fred Rose";6,95;19;;;"Tabak";="7640119424156"; -="42000378";"Fritz 0,33";1,92;19;0,08;;"Getränke";="4260107220015"; -="42000379";"Frosty Tooth FEM 1";17,00;19;;;"Seeds | Seedstockers";; -="42000380";"Frosty Tooth FEM 3";34,00;19;;;"Seeds | Seedstockers";; -="42000381";"Frosty Tooth FEM 5";51,00;19;;;"Seeds | Seedstockers";; -="42000382";"Fuze Tea";1,85;19;0,25;;"Getränke";; -="42000394";"Gelato 41 Auto 1";17,00;19;;;"Seeds | Seedstockers";; -="42000395";"Gelato 41 Auto 3";34,00;19;;;"Seeds | Seedstockers";; -="42000396";"Gelato 41 Auto 5";51,00;19;;;"Seeds | Seedstockers";; -="42000383";"GG4 Auto 1";17,00;19;;;"Seeds | Seedstockers";; -="42000384";"GG4 Auto 3";34,00;19;;;"Seeds | Seedstockers";; -="42000385";"GG4 Auto 5";51,00;19;;;"Seeds | Seedstockers";; -="42000397";"Girl Scout Cookies Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000398";"Girl Scout Cookies Auto 3";26,00;19;;;"Seeds | Seedstockers";; -="42000399";"Girl Scout Cookies Auto 5";39,00;19;;;"Seeds | Seedstockers";; -="42000407";"Glasfilter 10mm Herb Shuttle";7,90;19;;;"Bong/Glasfilter";; -="42000406";"Glasfilter 12mm Herb Shuttle";9,90;19;;;"Bong/Glasfilter";; -="42000408";"Glasfilter Purize 9mm";9,90;19;;;"Bong/Glasfilter";; -="42001223";"Glass Kawum- L:10cm";3,00;19;;;"Bong/Glasfilter";="616695669509"; -="42001170";"Glucks 0,33l";2,00;19;0,08;;"Getränke";;"V" -="42001170-3";"Glucks 0,33l Helles";2,00;19;;;"Getränke";="4270000189241";"K" -="42001170-2";"Glucks 0,33l Pils";2,00;19;;;"Getränke";="4270000189227";"K" -="42001170-1";"Glucks 0,33l Radler";2,00;19;;;"Getränke";="4270000189265";"K" -="42001187";"Goldberg Soda";0,00;19;;;"Getränke";;"V" -="42001187-2";"Goldberg Soda Pink Grapefruit";1,85;19;0,15;;"Getränke";="4025127088768";"K" -="42001187-3";"Goldberg Soda Tonic Water";1,85;19;0,15;;"Getränke";="4025127027972";"K" -="42001187-1";"Goldberg Soda Wild Berry";1,85;19;0,15;;"Getränke";="4025127087471";"K" -="42000410";"Gorilla Cookies Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000411";"Gorilla Cookies Auto 3";39,00;19;;;"Seeds | Seedstockers";; -="42000412";"Gorilla Cookies Auto 5";52,00;19;;;"Seeds | Seedstockers";; -="42000424";"Gösser Radler 0,5L";1,72;19;0,08;;"Getränke";="90288104"; -="42001227";"Grace Glass | Green Palm Tree – H:27cm – :50mm – SG:18.8mm";40,00;19;;;"Bong/Glasfilter";="616695688623"; -="42000414";"Green Crack Auto 1";17,50;19;;;"Seeds | Seedstockers";; -="42000415";"Green Crack Auto 3";29,00;19;;;"Seeds | Seedstockers";; -="42000416";"Green Crack Auto 5";43,50;19;;;"Seeds | Seedstockers";; -="42000419";"Grinder Champ High";17,50;19;;;"Grinder";; -="42000417";"Grinder Plaste";3,00;19;;;"Grinder";; -="42000420";"Grinder Purize Plaste";5,00;19;;;"Grinder";; -="42000423";"Grinder RAW Rot";28,90;19;;;"Grinder";="716165304104"; -="42000422";"Grinder RAW Schwarz";35,90;19;;;"Grinder";="716165304043"; -="42000418";"Grinder Würfel";5,00;19;;;"Grinder";; -="42000421";"Grinderkarte Woodzl";5,00;19;;;"Zubehör";; -="42000425";"Happy Amsterdam Amnesia Haze";34,50;19;;;"Vapes";="4262499460031"; -="42000426";"Happy Amsterdam Cali Exoticz";34,50;19;;;"Vapes";="4262499460000"; -="42000430";"Happy Amsterdam Cherry Kush";34,50;19;;;"Vapes";="4262499460062"; -="42000434";"Happy Amsterdam Forbidden Fruit";34,50;19;;;"Vapes";="4262499460079"; -="42000432";"Happy Amsterdam Golden OG Kush";34,50;19;;;"Vapes";; -="42000427";"Happy Amsterdam Mango Safari";34,50;19;;;"Vapes";="4262499460147"; -="42000429";"Happy Amsterdam Moroccan Hash";34,50;19;;;"Vapes";="4262499460017"; -="42000431";"Happy Amsterdam Purple Punch";34,50;19;;;"Vapes";="4262499460086"; -="42000428";"Happy Amsterdam Super Lemon Haze";34,50;19;;;"Vapes";="4262499460048"; -="42000433";"Happy Amsterdam Super Silver Haze";34,50;19;;;"Vapes";="4262499460024"; -="42000436";"Heineken 0,33L";1,82;19;0,08;;"Getränke";="8712000900663"; -="42001240";"Heineken 0,4l";1,92;19;0,08;;"Getränke";="8712000039370"; -="42001151";"Homebox Ambient Q60+ 60x60x160cm";95,00;19;;;"Grow | Zelte";; -="42001152";"Homebox Ambient Q80+ 80x80x180cm";120,00;19;;;"Grow | Zelte";="4260220922025"; -="42001283";"Homebox DryNet 60 cm";10,00;19;;;"Grow | Zubehör";="4260220921127"; -="42000438";"Humidor L";59,90;19;;;"Cannasseur";; -="42000437";"Humidor M";49,90;19;;;"Cannasseur";; -="42000439";"Humidor XL";69,90;19;;;"Cannasseur";; -="42000440";"Humidor XLA";69,90;19;;;"Cannasseur";; -="42000441";"Jack Herer Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000442";"Jack Herer Auto 3";26,00;19;;;"Seeds | Seedstockers";; -="42000443";"Jack Herer Auto 5";39,00;19;;;"Seeds | Seedstockers";; -="42000444";"Jarritos 0,33l";2,45;19;0,15;;"Getränke";; -="42000445";"Javaanese Jongens";8,50;19;;;"Tabak";="42504436"; -="42001183";"Jever";0,00;19;;;"Getränke";;"V" -="42001183-1";"Jever Fun 0,0%";1,52;19;0,08;;"Getränke";;"K" -="42001183-2";"Jever Pilsner";1,52;19;0,08;;"Getränke";;"K" -="42000452";"Jointhülse";1,00;19;;;"Zubehör";; -="42000453";"Kamala OG 1";13,00;19;;;"Seeds | FastBuds";; -="42000454";"Kamala OG 3";36,00;19;;;"Seeds | FastBuds";; -="42000455";"Kamala OG 5";55,00;19;;;"Seeds | FastBuds";; -="42001317";"King Palm Mini Funnel";4,50;19;;;"Zubehör";="810089617156"; -="42001142";"King Palm Variety Flavors";24,90;19;;;"Smoke | King Palm";="810089616739"; -="42001176";"Kräuterbraut";1,92;19;0,08;;"Getränke";;"V" -="42001176-1";"Kräuterbraut Bergkräuter";1,92;19;0,08;;"Getränke";="4002846036812";"K" -="42001176-2";"Kräuterbraut Brombeere Rosmarin";1,92;19;0,08;;"Getränke";="4002846036850";"K" -="42001176-3";"Kräuterbraut Zitrone Minze";1,92;19;0,08;;"Getränke";="4002846036836";"K" -="42001250";"Krombacher Natur Radler";1,82;19;0,08;;"Getränke";="4008287940947"; -="42001219";"Kush Mintz";0,00;19;;;"Flowers | CBN";;"V" -="42001219-1";"Kush Mintz 1g";10,00;19;;;"Flowers | CBN";="4262557691575";"K" -="42001219-2";"Kush Mintz 2g";19,00;19;;;"Flowers | CBN";="4262557691582";"K" -="42001219-3";"Kush Mintz 5g";45,00;19;;;"Flowers | CBN";="4262557691599";"K" -="42001309";"LA Coast";0,00;19;;;"CBN_";;"V" -="42001309-1";"LA Coast 1g";0,00;19;;;"CBN_";="4262557691629";"K" -="42001309-2";"LA Coast 2g";0,00;19;;;"CBN_";="4262557691612";"K" -="42001309-3";"LA Coast 5g";0,00;19;;;"CBN_";="4262557691605";"K" -="42001236";"Lichtenauer PUR 0,5l";1,45;19;0,25;;"Getränke";="4013256642074"; -="42000469";"Lipton Pfirsich";1,65;19;0,25;;"Getränke";="4062139025039"; -="42000470";"Lipton Zitrone";1,65;19;0,25;;"Getränke";="4062139025015"; -="42000472";"Lößnitz Pils";1,52;19;0,08;;"Getränke";; -="42000471";"Lupe";17,90;19;;;"Cannasseur";; -="42000474";"Mack and Crack Auto 1";19,50;19;;;"Seeds | Seedstockers";; -="42000475";"Mack and Crack Auto 3";39,00;19;;;"Seeds | Seedstockers";; -="42000476";"Mack and Crack Auto 5";58,50;19;;;"Seeds | Seedstockers";; -="42000477";"Mandarin Panties Auto 1";17,00;19;;;"Seeds | Seedstockers";; -="42000478";"Mandarin Panties Auto 3";34,00;19;;;"Seeds | Seedstockers";; -="42000479";"Mandarin Panties Auto 5";51,00;19;;;"Seeds | Seedstockers";; -="42000482";"Manitou Grün";7,00;19;;;"Tabak";="4006396103789"; -="42000481";"Manitou Orange";7,00;19;;;"Tabak";="4006396103819"; -="42001297";"Maxibright VARIDRIVE 360W LED";155,00;19;;;"Grow | Lampen";="5060403594518"; -="42001251";"Membran Kompressor";0,00;19;;;"Grow | AutoPot";;"V" -="42001251-1";"Membran Kompressor MK-0502";26,90;19;;;"Grow | AutoPot";="4250247608194";"K" -="42001251-2";"Membran Kompressor MK-9510";47,90;19;;;"Grow | AutoPot";="4250247608200";"K" -="42001122";"Milk Cookie CBD";0,00;19;;;"Flowers | CBD";;"V" -="42001122-1";"Milk Cookie CBD 1g";5,00;19;;;"Flowers | CBD";="4262557690158";"K" -="42001122-2";"Milk Cookie CBD 2g";9,00;19;;;"Flowers | CBD";="4262557690165";"K" -="42001122-3";"Milk Cookie CBD 5g";22,50;19;;;"Flowers | CBD";="4262557690172";"K" -="42001205";"Milk&Cookie";0,00;19;;;"CBN_";;"V" -="42001205-1";"Milk&Cookie 1g";0,00;19;;;"CBN_";="4262557690547";"K" -="42001205-2";"Milk&Cookie 2g";0,00;19;;;"CBN_";="4262557690554";"K" -="42001205-3";"Milk&Cookie 5g";0,00;19;;;"CBN_";="4262557690561";"K" -="42001218";"Milk/Cookie";0,00;19;;;"CBD_";;"V" -="42001218-1";"Milk/Cookie 1g";0,00;19;;;"CBD_";;"K" -="42001218-2";"Milk/Cookie 2g";0,00;19;;;"CBD_";;"K" -="42001168";"Milwaukee Ph600";25,00;19;;;"Grow | Messgeräte";="5999567670007"; -="42001179";"Mio Mio";0,00;19;;;"Getränke";;"V" -="42001179-4";"Mio Mio Banane";1,95;19;0,15;;"Getränke";="4002846034559";"K" -="42001179-2";"Mio Mio Cola";1,95;19;0,15;;"Getränke";="4002846034306";"K" -="42001179-3";"Mio Mio Lemon + Koffein";1,95;19;0,15;;"Getränke";="4002846034894";"K" -="42001179-1";"Mio Mio Mate 0,33l";1,95;19;0,15;;"Getränke";="4002846034443";"K" -="42001179-5";"Mio Mio Mate 0,5l";1,95;19;0,15;;"Getränke";="4002846034504";"K" -="42000510";"Mixery Dose";2,25;19;0,25;;"Getränke";="4002631028114"; -="42000511";"Mole";1,92;19;0,08;;"Getränke";; -="42000516";"Muza Clementine";8,95;19;;;"Tabak";="7290019822103"; -="42001136";"Muza Hemp Original";8,95;19;;;"Tabak";="7290019822097"; -="42000513";"Muza Indica";11,95;19;;;"Tabak";="7290019822004"; -="42001237";"Muza Lemon Haze";8,95;19;;;"Tabak";="7290019822639"; -="42000514";"Muza Neutral";11,95;19;;;"Tabak";="7290019822028"; -="42000512";"Muza Sativa";11,95;19;;;"Tabak";="7290019822011"; -="42000515";"Muza Super Kush";8,95;19;;;"Tabak";="7290019822493"; -="42000517";"Mystery Pack";20,00;19;;;"Flowers | CBD";; -="42000518";"Natur of Spirit Watermelon";12,00;19;;;"Tabak";; -="42001233";"NBoost | Cane Glass Bong - H:38cm -Ø:50mm - Socket:18.8mm- WT:5mm";38,00;19;;;"Bong/Glasfilter";="616695665631"; -="42001262";"Neu";0,00;19;;;"Grow | AutoPot";; -="42001267";"NV Glasfilter";0,00;19;;;"Bong/Glasfilter";;"V" -="42001267-4";"NV Glasfilter 10mm";15,90;19;;;"Bong/Glasfilter";;"K" -="42001267-1";"NV Glasfilter 7mm";12,90;19;;;"Bong/Glasfilter";;"K" -="42001267-2";"NV Glasfilter 8mm";13,90;19;;;"Bong/Glasfilter";;"K" -="42001267-3";"NV Glasfilter 9mm";14,90;19;;;"Bong/Glasfilter";;"K" -="42001178";"Oettinger";1,52;19;0,08;;"Getränke";;"V" -="42001178-1";"Oettinger Eistte Pfirsich";1,52;19;0,08;;"Getränke";="4014086011801";"K" -="42001178-2";"Oettinger Eistte Zitrone";1,52;19;0,08;;"Getränke";="4014086011818";"K" -="42001178-3";"Oettinger Fassbrause";1,52;19;0,08;;"Getränke";="4014086011573";"K" -="42001188";"Oppacher Holunderblüte";1,72;19;0,08;;"Getränke";="4043533006626"; -="42000526";"Orangina 0,33L";1,65;19;0,25;;"Getränke";; -="42000527";"Panty Punch Auto 1";19,50;19;;;"Seeds | Seedstockers";; -="42000528";"Panty Punch Auto 3";39,00;19;;;"Seeds | Seedstockers";; -="42000529";"Panty Punch Auto 5";58,50;19;;;"Seeds | Seedstockers";; -="42000530";"PaperBank";4,00;19;;;"Zubehör";; -="42001311";"Paris Lemon";0,00;19;;;"CBN_";;"V" -="42001311-1";"Paris Lemon 1g";0,00;19;;;"CBN_";="4262557691667";"K" -="42001311-2";"Paris Lemon 2g";0,00;19;;;"CBN_";="4262557691308";"K" -="42001311-3";"Paris Lemon 5g";0,00;19;;;"CBN_";="4262557691315";"K" -="42000531";"Paulaner Spezi 0,5L";1,72;19;0,08;;"Getränke";="4066600603405"; -="42001104-3";"Peach 10g";75,00;19;;;"Flowers | CBN";;"K" -="42001104-4";"Peach 15g";108,50;19;;;"Flowers | CBN";;"K" -="42001104-2";"Peach 1g";9,00;19;;;"Flowers | CBN";="4262557691117";"K" -="42001104-6";"Peach 20g";140,00;19;;;"Flowers | CBN";;"K" -="42001104-7";"Peach 25g";168,50;19;;;"Flowers | CBN";;"K" -="42001104-5";"Peach 2g";17,00;19;;;"Flowers | CBN";="4262557691124";"K" -="42001104-1";"Peach 30g";195,00;19;;;"Flowers | CBN";;"K" -="42001104-8";"Peach 3g";25,00;19;;;"Flowers | CBN";;"K" -="42001104-9";"Peach 5g";40,00;19;;;"Flowers | CBN";="4262557691131";"K" -="42001131";"Peach CBD";0,00;19;;;"Flowers | CBD";;"V" -="42001131-7";"Peach CBD 10g";55,00;19;;;"Flowers | CBD";;"K" -="42001131-6";"Peach CBD 15g";75,00;19;;;"Flowers | CBD";;"K" -="42001131-8";"Peach CBD 1g";7,50;19;;;"Flowers | CBD";="4262557691421";"K" -="42001131-5";"Peach CBD 20g";95,00;19;;;"Flowers | CBD";;"K" -="42001131-4";"Peach CBD 25g";110,00;19;;;"Flowers | CBD";;"K" -="42001131-1";"Peach CBD 2g";14,00;19;;;"Flowers | CBD";="4262557691438";"K" -="42001131-3";"Peach CBD 30g";125,00;19;;;"Flowers | CBD";;"K" -="42001131-2";"Peach CBD 5g";30,00;19;;;"Flowers | CBD";="4262557691445";"K" -="42001104";"Peach CBN";0,00;19;;;"Flowers | CBN";;"V" -="42001111";"Peach Cotton Candy";0,00;19;;;"Flowers | CBN";;"V" -="42001111-9";"Peach Cotton Candy 10g";90,00;19;;;"Flowers | CBN";;"K" -="42001111-8";"Peach Cotton Candy 15g";125,00;19;;;"Flowers | CBN";;"K" -="42001111-1";"Peach Cotton Candy 1g";11,00;19;;;"Flowers | CBN";="4262557691261";"K" -="42001111-6";"Peach Cotton Candy 20g";160,00;19;;;"Flowers | CBN";;"K" -="42001111-5";"Peach Cotton Candy 25g";194,00;19;;;"Flowers | CBN";;"K" -="42001111-7";"Peach Cotton Candy 2g";21,00;19;;;"Flowers | CBN";="4262557691278";"K" -="42001111-3";"Peach Cotton Candy 30g";225,00;19;;;"Flowers | CBN";;"K" -="42001111-4";"Peach Cotton Candy 3g";31,50;19;;;"Flowers | CBN";;"K" -="42001111-2";"Peach Cotton Candy 5g";50,00;19;;;"Flowers | CBN";="4262557691285";"K" -="42000559";"Pepe Bright Green";6,30;19;;;"Tabak";="4006396103635"; -="42000558";"Pepe Dark Green";5,90;19;;;"Tabak";="4006396068484"; -="42000560";"Pepe Fine Green";6,30;19;;;"Tabak";="4006396103666"; -="42001134";"Pepe Rich Green";6,30;19;;;"Tabak";="4006396103604"; -="42000561";"Pfeife klein/groß";5,00;19;;;"Zubehör";; -="42001141";"Pfeiffe groß mit 5 Sieben";5,00;19;;;"Bong/Glasfilter";="4031872340693"; -="42001140";"Pfeiffe klein mit 5 Sieben";5,00;19;;;"Bong/Glasfilter";="4031872340631"; -="42001101";"Piatella";0,00;19;;;"Flowers | CBN";;"V" -="42001101-3";"Piatella 10g";105,00;19;;;"Flowers | CBN";;"K" -="42001101-4";"Piatella 15g";150,00;19;;;"Flowers | CBN";;"K" -="42001101-2";"Piatella 1g";12,50;19;;;"Flowers | CBN";="4262557691056";"K" -="42001101-6";"Piatella 20g";190,00;19;;;"Flowers | CBN";;"K" -="42001101-7";"Piatella 25g";230,00;19;;;"Flowers | CBN";;"K" -="42001101-5";"Piatella 2g";24,00;19;;;"Flowers | CBN";="4262557691063";"K" -="42001101-9";"Piatella 30g";265,00;19;;;"Flowers | CBN";;"K" -="42001101-8";"Piatella 3g";36,00;19;;;"Flowers | CBN";;"K" -="42001101-1";"Piatella 5g";55,00;19;;;"Flowers | CBN";="4262557691070";"K" -="42001300";"Plagron Perlite 10 l";8,50;19;;;"Grow | Erde";="8718104121553"; -="42001189";"Pound Cake";0,00;19;;;"Flowers | CBN";;"V" -="42001189-1";"Pound Cake 1g";11,00;19;;;"Flowers | CBN";="4262557691230";"K" -="42001189-2";"Pound Cake 2g";21,00;19;;;"Flowers | CBN";="4262557691247";"K" -="42001189-3";"Pound Cake 5g";50,00;19;;;"Flowers | CBN";="4262557691254";"K" -="42001291";"Prima Klima PK-Kombo 125-1S, 125 mm, 360 m3/h";145,00;19;;;"Grow | Abluft";="8594171430466"; -="42001290";"Prima Klima PK-Kombo 125-TC, 125 mm, 400 m3/h";199,00;19;;;"Grow | Abluft";="8594171430459"; -="42001206";"Pudding";0,00;19;;;"Flowers | CBN";;"V" -="42001206-1";"Pudding 2g";0,00;19;;;"Flowers | CBN";="4262557690493";"K" -="42000594";"Pueblo Beige";6,75;19;;;"Tabak";="42487661"; -="42000595";"Pueblo Blau";6,75;19;;;"Tabak";="42487647"; -="42000597";"Pueblo Braun";6,50;19;;;"Tabak";="42464730"; -="42000596";"Pueblo Pink";6,75;19;;;"Tabak";="42487845"; -="42000655";"Pülleken 0,33";1,82;19;0,08;;"Getränke";="4005249061702"; -="42000651";"Purple Punch Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000652";"Purple Punch Auto 3";26,00;19;;;"Seeds | Seedstockers";; -="42000653";"Purple Punch Auto 5";39,00;19;;;"Seeds | Seedstockers";; -="42001239";"Radeberger 0,33l";1,52;19;0,08;;"Getränke";="4014388001012"; -="42001288";"RAM 400 ml/h, 5 l";65,00;19;;;"Grow | Zubehör";="5034517802180"; -="42001215";"Ramen";0,00;19;;;"CBD_";;"V" -="42001215-1";"Ramen 1g";0,00;19;;;"CBD_";="4262557690004";"K" -="42001215-2";"Ramen 2g";0,00;19;;;"CBD_";;"K" -="42001184";"RedBull";0,00;19;;;"Getränke";;"V" -="42001184-2";"RedBull Cola";2,25;19;0,25;;"Getränke";;"K" -="42001184-3";"RedBull Organic Orange";2,25;19;0,25;;"Getränke";="9002490278960";"K" -="42001184-4";"RedBull Pfirsich";2,25;19;0,25;;"Getränke";="90474903";"K" -="42001184-1";"RedBull Spring Edition";2,25;19;0,25;;"Getränke";="90486883";"K" -="42000666";"RedBull Winter Edition";2,25;19;0,25;;"Getränke";; -="42000673";"S.Pellegrino 0,25L";1,25;19;0,15;;"Getränke";;"V" -="42000673-2";"S.Pellegrino 0,25L Sprudel";1,25;19;0,15;;"Getränke";="8002270011030";"K" -="42000690";"Salitos";2,22;19;0,08;;"Getränke";;"V" -="42000690-1";"Salitos Mojito";2,22;19;;;"Getränke";="4025127085354";"K" -="42000690-2";"Salitos Pink";2,22;19;;;"Getränke";="4025127085071";"K" -="42000691";"Santa Marta Haze Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000692";"Santa Marta Haze Auto 3";26,00;19;;;"Seeds | Seedstockers";; -="42000693";"Santa Marta Haze Auto 5";39,00;19;;;"Seeds | Seedstockers";; -="42000694";"Sherbet Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000695";"Sherbet Auto 3";39,00;19;;;"Seeds | Seedstockers";; -="42000696";"Sherbet Auto 5";52,00;19;;;"Seeds | Seedstockers";; -="42001312";"Shinys Grape";0,00;19;;;"CBN_";;"V" -="42001312-1";"Shinys Grape 1g";0,00;19;;;"CBN_";="4262557691353";"K" -="42001312-2";"Shinys Grape 2g";0,00;19;;;"CBN_";="4262557691360";"K" -="42001312-3";"Shinys Grape 5g";0,00;19;;;"CBN_";="4262557691377";"K" -="42000689";"SHS";34,90;19;;;"Cannasseur";; -="42001220";"Skruf";0,00;19;;;"Tabak";;"V" -="42001220-1";"Skruf 2";7,50;19;;;"Tabak";="4030500025216";"K" -="42001220-2";"Skruf 3";7,50;19;;;"Tabak";="4030500025247";"K" -="42001220-3";"Skruf 4";7,50;19;;;"Tabak";="4030500025278";"K" -="42001210";"Sour Diesel";0,00;19;;;"CBN_";;"V" -="42001210-1";"Sour Diesel 1g";0,00;19;;;"CBN_";="4262557690608";"K" -="42001210-2";"Sour Diesel 2g";0,00;19;;;"CBN_";="4262557690615";"K" -="42001210-3";"Sour Diesel 5g";0,00;19;;;"CBN_";="4262557690622";"K" -="42000715";"Sour Diesel Auto 1";7,50;19;;;"Seeds | Seedstockers";; -="42000716";"Sour Diesel Auto 3";23,00;19;;;"Seeds | Seedstockers";; -="42000717";"Sour Diesel Auto 5";34,50;19;;;"Seeds | Seedstockers";; -="42001124";"Sour Diesel CBD";0,00;19;;;"Flowers | CBD";;"V" -="42001124-1";"Sour Diesel CBD 1g";10,00;19;;;"Flowers | CBD";="4262557690219";"K" -="42001124-2";"Sour Diesel CBD 2g";19,00;19;;;"Flowers | CBD";="4262557690226";"K" -="42001124-3";"Sour Diesel CBD 5g";45,00;19;;;"Flowers | CBD";="4262557690233";"K" -="42000718";"Spaten 0,5l";2,02;19;0,08;;"Getränke";="4072700005315"; -="42001248";"Sprite 0,5l";2,12;19;0,08;;"Getränke";="54491182"; -="42000719";"Stardawg Auto 1";4,25;19;;;"Seeds | Seedstockers";; -="42000720";"Stardawg Auto 3";8,50;19;;;"Seeds | Seedstockers";; -="42000721";"Stardawg Auto 5";12,75;19;;;"Seeds | Seedstockers";; -="42000574";"Steel Pipe Screen 15mm";2,00;19;;;"Zubehör";="4250853502060"; -="42000572";"Steel Pipe Screen 9mm";2,00;19;;;"Zubehör";="4250853502046"; -="42000722";"Sternburg 0,5L";1,52;19;0,08;;"Getränke";="41057759"; -="42000724";"Sticky Fingers Auto 1";14,50;19;;;"Seeds | Seedstockers";; -="42000725";"Sticky Fingers Auto 3";29,00;19;;;"Seeds | Seedstockers";; -="42000726";"Sticky Fingers Auto 5";43,50;19;;;"Seeds | Seedstockers";; -="42000728";"Super Boof Auto 1";11,50;19;;;"Seeds | Seedstockers";; -="42000729";"Super Boof Auto 3";23,00;19;;;"Seeds | Seedstockers";; -="42000730";"Super Boof Auto 5";34,50;19;;;"Seeds | Seedstockers";; -="42000731";"Super Skunk Auto 1";9,50;19;;;"Seeds | Seedstockers";; -="42000732";"Super Skunk Auto 3";19,00;19;;;"Seeds | Seedstockers";; -="42000733";"Super Skunk Auto 5";28,50;19;;;"Seeds | Seedstockers";; -="42001162";"Taifun Flat Fan";28,00;19;;;"Grow | Abluft";="1400010000011"; -="42001160";"Taifun TT100 Flansch 100mm 180 m^3 ";40,00;19;;;"Grow | Abluft";="4260430860674"; -="42001161";"Taifun TT125 Flansch 125mm 280 m^3";60,00;19;;;"Grow | Abluft";="4260430860681"; -="42001213";"Tante Emma";0,00;19;;;"CBN_";;"V" -="42001130-3";"Tante Emma 10g";85,00;19;;;"Flowers | CBD";;"K" -="42001130-4";"Tante Emma 15g";123,50;19;;;"Flowers | CBD";;"K" -="42001130-2";"Tante Emma 1g";10,00;19;;;"Flowers | CBD";="4262557691391";"K" -="42001213-1";"Tante Emma 1g";0,00;19;;;"CBN_";="4262557691179";"K" -="42001130-6";"Tante Emma 20g";160,00;19;;;"Flowers | CBD";;"K" -="42001130-7";"Tante Emma 25g";194,00;19;;;"Flowers | CBD";;"K" -="42001130-5";"Tante Emma 2g";19,00;19;;;"Flowers | CBD";="4262557691407";"K" -="42001213-2";"Tante Emma 2g";0,00;19;;;"CBN_";="4262557691186";"K" -="42001130-8";"Tante Emma 30g";225,00;19;;;"Flowers | CBD";;"K" -="42001130-1";"Tante Emma 5g";45,00;19;;;"Flowers | CBD";="4262557691414";"K" -="42001213-3";"Tante Emma 5g";0,00;19;;;"CBN_";="4262557691193";"K" -="42001130";"Tante Emma CBD";0,00;19;;;"Flowers | CBD";;"V" -="42000745";"Terp&Tonic Auto 1";19,50;19;;;"Seeds | Seedstockers";; -="42000746";"Terp&Tonic Auto 3";39,00;19;;;"Seeds | Seedstockers";; -="42000747";"Terp&Tonic Auto 5";58,50;19;;;"Seeds | Seedstockers";; -="42000748";"Thunder Banana Auto 1";14,50;19;;;"Seeds | Seedstockers";; -="42000749";"Thunder Banana Auto 3";29,00;19;;;"Seeds | Seedstockers";; -="42000750";"Thunder Banana Auto 5";43,50;19;;;"Seeds | Seedstockers";; -="42000756";"Tray Elements Glas Groß";9,90;19;;;"Zubehör";; -="42000754";"Tray Elements Glas Klein";4,90;19;;;"Zubehör";; -="42000752";"Tray Liquid";9,90;19;;;"Cannasseur";; -="42000755";"Tray Purize";9,90;19;;;"Zubehör";; -="42000751";"Tray Standard";8,90;19;;;"Cannasseur";; -="42000753";"Tray Trippy";9,90;19;;;"Cannasseur";; -="42001282";"Trim Bin Harvest Cut Tray";80,00;19;;;"Grow | Zubehör";="713757640237"; -="42000758";"Triton Biscotto Lime Auto 1";17,00;19;;;"Seeds | Seedstockers";; -="42000759";"Triton Biscotto Lime Auto 3";34,00;19;;;"Seeds | Seedstockers";; -="42000760";"Triton Biscotto Lime Auto 5";51,00;19;;;"Seeds | Seedstockers";; -="42000761";"Tropical Smoothie FEM 1";19,50;19;;;"Seeds | Seedstockers";; -="42000762";"Tropical Smoothie FEM 3";39,00;19;;;"Seeds | Seedstockers";; -="42000763";"Tropical Smoothie FEM 5";58,50;19;;;"Seeds | Seedstockers";; -="42000764";"Tropical Zmoothie Auto 1";19,50;19;;;"Seeds | Seedstockers";; -="42000765";"Tropical Zmoothie Auto 3";39,00;19;;;"Seeds | Seedstockers";; -="42000766";"Tropical Zmoothie Auto 5";58,50;19;;;"Seeds | Seedstockers";; -="42000767";"Tyski 0,5l";2,02;19;0,08;;"Getränke";="5901359062013"; -="42001287";"U Gro Small Basic 11 l Kokos";5,00;19;;;"Grow | Erde";="609224329999"; -="42001302";"U Gro Small Rhiza 11 l";5,50;19;;;"Grow | Erde";="609224330315"; -="42001271";"Urban Bubble Bags 3 Stk.";35,00;19;;;"Grow | Zubehör";="8594200879716"; -="42001270";"Urban Bubble Bags 5 Stk.";65,00;19;;;"Grow | Zubehör";="8594200879709"; -="42001284";"Urban Mechanical Timer Socket";10,00;19;;;"Grow | Zubehör";="8595722026220"; -="42001099";"Vending Machine";0,00;19;;;"Flowers | CBN";;"V" -="42001099-2";"Vending Machine 10g";85,00;19;;;"Flowers | CBN";;"K" -="42001099-3";"Vending Machine 15g";120,00;19;;;"Flowers | CBN";;"K" -="42001099-1";"Vending Machine 1g";10,00;19;;;"Flowers | CBN";="4262557691162";"K" -="42001099-5";"Vending Machine 20g";155,00;19;;;"Flowers | CBN";;"K" -="42001099-6";"Vending Machine 25g";185,00;19;;;"Flowers | CBN";;"K" -="42001099-4";"Vending Machine 2g";19,00;19;;;"Flowers | CBN";="4262557691155";"K" -="42001099-8";"Vending Machine 30g";210,00;19;;;"Flowers | CBN";;"K" -="42001099-7";"Vending Machine 3g";28,50;19;;;"Flowers | CBN";;"K" -="42001099-9";"Vending Machine 5g";45,00;19;;;"Flowers | CBN";="4262557691148";"K" -="42001207";"White Melon";0,00;19;;;"Flowers | CBN";;"V" -="42001207-1";"White Melon 1g";11,00;19;;;"Flowers | CBN";="4262557691513";"K" -="42001207-2";"White Melon 2g";0,00;19;;;"Flowers | CBN";="4262557691520";"K" -="42001207-3";"White Melon 5g";0,00;19;;;"Flowers | CBN";="4262557691537";"K" -="42000777";"White Widow Auto 1";13,00;19;;;"Seeds | Seedstockers";; -="42000778";"White Widow Auto 3";26,00;19;;;"Seeds | Seedstockers";; -="42000779";"White Widow Auto 5";39,00;19;;;"Seeds | Seedstockers";; -="42001243";"Wildfire Glass Pipe";5,00;19;;;"Bong/Glasfilter";="9010210080298"; -="42000028";"Woodzl Bamboo Guard";8,00;19;;;"Zubehör";; -="42001146";"Zig Zag Slime Size 6mm";1,50;19;;;"Smoke | Tips/Filter";="3057068006984"; -="42001127";"Zushi CBD";0,00;19;;;"Flowers | CBD";;"V" -="42001127-3";"Zushi CBD 10g";85,00;19;;;"Flowers | CBD";;"K" -="42001127-4";"Zushi CBD 15g";120,00;19;;;"Flowers | CBD";;"K" -="42001127-2";"Zushi CBD 1g";10,00;19;;;"Flowers | CBD";="4262557690998";"K" -="42001127-6";"Zushi CBD 20g";155,00;19;;;"Flowers | CBD";;"K" -="42001127-7";"Zushi CBD 25g";185,00;19;;;"Flowers | CBD";;"K" -="42001127-5";"Zushi CBD 2g";19,00;19;;;"Flowers | CBD";="4262557691001";"K" -="42001127-1";"Zushi CBD 30g";210,00;19;;;"Flowers | CBD";;"K" -="42001127-8";"Zushi CBD 3g";28,50;19;;;"Flowers | CBD";;"K" -="42001127-9";"Zushi CBD 5g";45,00;19;;;"Flowers | CBD";="4262557691018";"K" -="42001181";"ZWO";1,92;19;0,08;;"Getränke";;"V" -="42001181-5";"ZWO Cola";1,92;19;0,08;;"Getränke";="4260657791485";"K" -="42001181-1";"ZWO Holunder";1,92;19;0,08;;"Getränke";="4260657791607";"K" -="42001181-2";"ZWO Honigmelone";1,92;19;0,08;;"Getränke";="4260657791621";"K" -="42001181-4";"ZWO Orange";1,92;19;0,08;;"Getränke";="4260657791584";"K" -="42001181-3";"ZWO Zitrone";1,92;19;0,08;;"Getränke";="4260657791560";"K" -="42001261";"1 Pot 15L System inkl. 47L Wasserfass";104,90;19;;"AutoPot";"Grow | AutoPot";; -="42001257";"1 Pot 8L System inkl. 47L Wasserfass";69,90;19;;"AutoPot";"Grow | AutoPot";; -="42001254";"1 Pot XL 25L System inkl. 47L Wasserfass";79,90;19;;"AutoPot";"Grow | AutoPot";; -="42001264";"2 Pot 15L System inkl. 47L Wasserfass";109,90;19;;"AutoPot";"Grow | AutoPot";; -="42001258";"2 Pot 8L System inkl. 47L Wasserfass";99,90;19;;"AutoPot";"Grow | AutoPot";; -="42001255";"2 Pot XL 25L System inkl. 47L Wasserfass";109,90;19;;"AutoPot";"Grow | AutoPot";; -="42001265";"3 Pot 15L System inkl. 47L Wasserfass";149,90;19;;"AutoPot";"Grow | AutoPot";; -="42001259";"3 Pot 8L System inkl. 47L Wasserfass";134,90;19;;"AutoPot";"Grow | AutoPot";; -="42001263";"3 Pot XL 25L System inkl. 47L Wasserfass";139,90;19;;"AutoPot";"Grow | AutoPot";; -="42000984";"AutoPot 15 Liter Topf";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000985";"AutoPot 1Pot Tray und Deckel";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000981";"AutoPot 1Pot XL Tray und Deckel";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000982";"AutoPot 25 Liter Topf für 1Pot XL";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000997";"AutoPot 2Pot System (incl. 47 Liter tank)";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42001008";"AutoPot 2x 8,5Liter + 47L Tank Set ";90,00;19;;"AutoPot";"Grow | AutoPot";; -="42000995";"AutoPot 30L Tank mit 9mm Tülle und Deckel";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42001010";"AutoPot 3x 15Liter + 47L Tank Set";130,00;19;;"AutoPot";"Grow | AutoPot";; -="42001009";"AutoPot 3x 8,5Liter + 47L Tank Set";120,00;19;;"AutoPot";"Grow | AutoPot";; -="42000996";"AutoPot 47L Tank mit 9mm Tülle und Deckel";29,00;19;;"AutoPot";"Grow | AutoPot";; -="42000983";"AutoPot 8,5 Liter Topf";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000990";"AutoPot 9mm Inline Absperrhahn";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000980";"AutoPot 9mm Schlauch (blau)";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000991";"AutoPot 9mm T-Kupplung";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000992";"AutoPot 9mm Tülle";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000993";"AutoPot 9mm Wasserfilter inklusive. Filtermaterial";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000989";"AutoPot AirBase quadratisch für 8,5L Topf und 15L Topf";8,90;19;;"AutoPot";"Grow | AutoPot";="5060151875457"; -="42000988";"AutoPot AirBase Rund für 20L Topf und 25L Topf";9,90;19;;"AutoPot";"Grow | AutoPot";="5060151875464"; -="42000979";"AutoPot AirDome Kit";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000987";"AutoPot Schlauchbürste – 9mm & 16mm Bürstenkopf";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000986";"AutoPot Zubehörpaket für 1Pot Modul – mit AQUAvalve5";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42000994";"AutoPot Zubehörpaket für 1Pot XL – mit AQUAvalve5";0,00;19;;"AutoPot";"Grow | AutoPot";; -="42001191";"BioBizz Acti Vera";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001191-1";"BioBizz Acti Vera 1l";23,00;19;;"BioBizz";"Grow | Dünger";="8718403232523";"K" -="42001191-2";"BioBizz Acti Vera 250ml";9,00;19;;"BioBizz";"Grow | Dünger";="8718403232509";"K" -="42001191-3";"BioBizz Acti Vera 500ml";14,50;19;;"BioBizz";"Grow | Dünger";="8718403232516";"K" -="42001192";"BioBizz Alg A Mic";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001192-2";"BioBizz Alg A Mic 1l";24,00;19;;"BioBizz";"Grow | Dünger";="8718403231236";"K" -="42001192-1";"BioBizz Alg A Mic 250ml";6,50;19;;"BioBizz";"Grow | Dünger";="8718403232196";"K" -="42001192-3";"BioBizz Alg A Mic 500ml";9,50;19;;"BioBizz";"Grow | Dünger";="8718403231250";"K" -="42000042";"BioBizz AllMix 20L";12,50;19;;"BioBizz";"Grow | Erde";; -="42000043";"BioBizz AllMix 50L";15,00;19;;"BioBizz";"Grow | Erde";="8718403231595"; -="42000044";"BioBizz AllMix Peat Free 20L";13,50;19;;"BioBizz";"Grow | Erde";="8718403234763"; -="42000045";"BioBizz AllMix Peat Free 50L";21,50;19;;"BioBizz";"Grow | Erde";="8718403234749"; -="42001193";"BioBizz Bio Heaven";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001193-1";"BioBizz Bio Heaven 250ml";28,00;19;;"BioBizz";"Grow | Dünger";="8718403231564";"K" -="42001193-2";"BioBizz Bio Heaven 500ml";30,00;19;;"BioBizz";"Grow | Dünger";="8718403231540";"K" -="42001195";"BioBizz Bio PH Up";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001195-3";"BioBizz Bio PH Up 1l";20,00;19;;"BioBizz";"Grow | Dünger";="8718403232912";"K" -="42001195-1";"BioBizz Bio PH Up 250ml";7,00;19;;"BioBizz";"Grow | Dünger";="8718403232875";"K" -="42001195-2";"BioBizz Bio PH Up 500ml";12,00;19;;"BioBizz";"Grow | Dünger";="8718403232899";"K" -="42001194";"Biobizz BioBio PH Down";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001194-3";"Biobizz BioBio PH Down 1l";21,00;19;;"BioBizz";"Grow | Dünger";;"K" -="42001194-1";"Biobizz BioBio PH Down 250ml";7,00;19;;"BioBizz";"Grow | Dünger";="8718403232967";"K" -="42001194-2";"Biobizz BioBio PH Down 500ml";12,00;19;;"BioBizz";"Grow | Dünger";="8718403232981";"K" -="42001196";"BioBizz Bloom";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001196-2";"BioBizz Bloom 1l";16,70;19;;"BioBizz";"Grow | Dünger";="000420008938";"K" -="42001196-3";"BioBizz Bloom 250ml";5,00;19;;"BioBizz";"Grow | Dünger";;"K" -="42001196-1";"BioBizz Bloom 500ml";9,00;19;;"BioBizz";"Grow | Dünger";;"K" -="42001198";"BioBizz CalMag";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001198-2";"BioBizz CalMag 1l";20,00;19;;"BioBizz";"Grow | Dünger";="000420008945";"K" -="42001198-3";"BioBizz CalMag 250ml";7,00;19;;"BioBizz";"Grow | Dünger";="000420000536";"K" -="42001198-1";"BioBizz CalMag 500ml";12,00;19;;"BioBizz";"Grow | Dünger";="8718403233377";"K" -="42000054";"BioBizz CocoMix 50l";15,90;19;;"BioBizz";"Grow | Erde";="8718403231670"; -="42001199";"BioBizz FishMix";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001199-2";"BioBizz FishMix 1l";14,00;19;;"BioBizz";"Grow | Dünger";="8718403231298";"K" -="42001199-3";"BioBizz FishMix 250ml";4,50;19;;"BioBizz";"Grow | Dünger";="871403232189";"K" -="42001199-1";"BioBizz FishMix 500ml";8,00;19;;"BioBizz";"Grow | Dünger";="8718403231311";"K" -="42001197";"BioBizz Grow";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001197-1";"BioBizz Grow 1l";15,00;19;;"BioBizz";"Grow | Dünger";="000420008990";"K" -="42001197-2";"BioBizz Grow 250ml";4,50;19;;"BioBizz";"Grow | Dünger";;"K" -="42001197-3";"BioBizz Grow 500ml";8,00;19;;"BioBizz";"Grow | Dünger";="8718403231199";"K" -="42000057";"BioBizz Leaf Coat 500ml";14,90;19;;"BioBizz";"Grow | Dünger";="8718403231489"; -="42000058";"BioBizz Light Mix 20L";8,50;19;;"BioBizz";"Grow | Erde";="8718403231656"; -="42000059";"BioBizz Light Mix 50L";16,50;19;;"BioBizz";"Grow | Erde";="8718403231632"; -="42000872";"BioBizz Light Mix Erde 90L";25,00;19;;"BioBizz";"Grow | Erde";="8718403235180"; -="42000060";"BioBizz Light Mix Peat Free 20L";12,50;19;;"BioBizz";"Grow | Erde";="8718403234770"; -="42000061";"BioBizz Light Mix Peat Free 50L";19,50;19;;"BioBizz";"Grow | Erde";; -="42000898";"Biobizz Microbes, Pulver, 150 g";89,00;19;;"BioBizz";"Grow | Dünger";="8718403233995"; -="42000062";"BioBizz PreMix";16,90;19;;"BioBizz";"Grow | Erde";="8718403231694"; -="42001200";"BioBizz Root Juice";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001200-2";"BioBizz Root Juice 250ml";16,00;19;;"BioBizz";"Grow | Dünger";="8718403231373";"K" -="42001200-1";"BioBizz Root Juice 500ml";27,00;19;;"BioBizz";"Grow | Dünger";="8718403232479";"K" -="42000896";"Biobizz Starters Pack";79,00;19;;"BioBizz";"Grow | Dünger";="8718403231755"; -="42001201";"BioBizz TopMax";0,00;19;;"BioBizz";"Grow | Dünger";;"V" -="42001201-3";"BioBizz TopMax 1l";28,90;19;;"BioBizz";"Grow | Dünger";="8718403231403";"K" -="42001201-1";"BioBizz TopMax 250ml";10,90;19;;"BioBizz";"Grow | Dünger";="8718403231816";"K" -="42001201-2";"BioBizz TopMax 500ml";18,90;19;;"BioBizz";"Grow | Dünger";="8718403231427";"K" -="42000067";"BioBizz TryPack Indoor";14,90;19;;"BioBizz";"Grow | Dünger";; -="42000068";"BioBizz WurmHumus 50l";23,50;19;;"BioBizz";"Grow | Erde";="8718403231731"; -="42000089";"Biodor Citrus";14,90;19;;"BioBizz";"Zubehör";; -="42000088";"Biodor Eukalyptus";14,90;19;;"BioBizz";"Zubehör";; -="42000079";"BioTabs BacTrex 50g";24,50;19;;"BioTabs";"Grow | Dünger";="4260619880158"; -="42000081";"BioTabs Bio PK Compost Tea 2000g";52,50;19;;"BioTabs";"Grow | Dünger";; -="42000080";"BioTabs Bio PK Compost Tea 650g";18,50;19;;"BioTabs";"Grow | Dünger";; -="42000070";"BioTabs BioTabs";14,00;19;;"BioTabs";"Grow | Dünger";;"V" -="42000070-1";"BioTabs BioTabs 10 Stück";14,00;19;;"BioTabs";"Grow | Dünger";="4260619880080";"K" -="42000070-2";"BioTabs BioTabs 100 Stück";95,00;19;;"BioTabs";"Grow | Dünger";="4260619880097";"K" -="42000071";"BioTabs BoomBoom";9,00;19;;"BioTabs";"Grow | Dünger";="1357911161733"; -="42000072";"BioTabs Guerilla Box";35,00;19;;"BioTabs";"Grow | Dünger";="4260619880011"; -="42000069";"BioTabs Guerilla Juice 500ml";14,90;19;;"BioTabs";"Grow | Dünger";="4260619880172"; -="42000082";"BioTabs Mycotrex 100g";34,50;19;;"BioTabs";"Grow | Dünger";; -="42000078";"BioTabs Orgatrex 1000ml";17,00;19;;"BioTabs";"Grow | Dünger";; -="42000077";"BioTabs PK Booster flüssig";29,00;19;;"BioTabs";"Grow | Dünger";; -="42000073";"BioTabs PPP";22,50;19;;"BioTabs";"Grow | Dünger";="000420000734"; -="42000076";"BioTabs Silicium Flash 1,25kg";30,00;19;;"BioTabs";"Grow | Dünger";="4260619880257"; -="42000074";"BioTabs Starterbox";45,00;19;;"BioTabs";"Grow | Dünger";="42606619880004"; -="42000075";"BioTabs Startrex 1,5kg";21,50;19;;"BioTabs";"Grow | Dünger";="4260619880028"; -="42001232";"Boost | Beaker Glass Bong -H:44cm- Ø:50mm- Socket:18.8mm- WT:5mm";38,00;19;;"Boost";"Bong/Glasfilter";="616695665716"; -="42001229";"Boost | Bolt Glass Bong -H:38cm- Ø:50mm - SG:18.8mm- WT:5mm";40,00;19;;"Boost";"Bong/Glasfilter";="616695665747"; -="42001228";"Boost | Bouncer Glass Bong H:32cm Ø:40mm SG:14.5mm WT: 5mm ";38,00;19;;"Boost";"Bong/Glasfilter";="616695666089"; -="42001231";"Boost | Bouncer Glass Bong -H:41cm- Ø:50mm- Socket:18.8mm- WT:5mm ";45,00;19;;"Boost";"Bong/Glasfilter";="616695665709"; -="42001230";"Boost | Cane Glass Bong -H:48cm- Ø:65mm- Socket:18.8mm- WT:5mm ";59,00;19;;"Boost";"Bong/Glasfilter";="616695665686"; -="42001225";"Boost | Fumed Glass Bowl- Black- Ø:18.8mm";9,00;19;;"Boost";"Bong/Glasfilter";="616695665273"; -="42001226";"Boost | Fumed Glass Bowl-Black- Ø:14.5mm";7,50;19;;"Boost";"Bong/Glasfilter";="616695665334"; -="42000037";"Bio Terra+ 50L";21,50;19;;"Canna";"Grow | Erde";="8717524957841"; -="42000150";"Canna CalMag Agent 1L";15,50;19;;"Canna";"Grow | Dünger";="753321681907"; -="42001057";"CANNA Clay Pebbles, Tongranulat, 45 L";27,00;19;;"Canna";"Grow | Erde";="8717524959913"; -="42001204";"Canna Flush";0,00;19;;"Canna";"Grow | Dünger";;"V" -="42001204-2";"Canna Flush 1l";14,00;19;;"Canna";"Grow | Dünger";="8717524957100";"K" -="42001204-1";"Canna Flush 500ml";8,00;19;;"Canna";"Grow | Dünger";="8717524957131";"K" -="42000153";"Canna Home Grow Kit";19,50;19;;"Canna";"Grow | Dünger";; -="42000149";"Canna PK 13/14";11,00;19;;"Canna";"Grow | Dünger";="8717524951832"; -="42000146";"Canna Professionell+";17,50;19;;"Canna";"Grow | Erde";="8717524957780"; -="42000148";"Canna Rhizotonic 250ml";19,50;19;;"Canna";"Grow | Dünger";="753321685790"; -="42000147";"Canna SeedMix 25l";14,90;19;;"Canna";"Grow | Erde";="8717524957797"; -="42001203";"Canna Start";0,00;19;;"Canna";"Grow | Dünger";;"V" -="42001203-1";"Canna Start 1l";16,00;19;;"Canna";"Grow | Dünger";;"K" -="42001203-2";"Canna Start 500ml";13,00;19;;"Canna";"Grow | Dünger";="8717524957162";"K" -="42000137";"CANNA Terra Flores";14,00;19;;"Canna";"Grow | Dünger";="8717524951382"; -="42000138";"CANNA Terra Starterkit";60,00;19;;"Canna";"Grow | Dünger";="7081256839319"; -="42000139";"CANNA Terra Vega";14,00;19;;"Canna";"Grow | Dünger";="8717524951412"; -="42001202";"Canna Zym";0,00;19;;"Canna";"Grow | Dünger";;"V" -="42001202-1";"Canna Zym 1l";19,00;19;;"Canna";"Grow | Dünger";;"K" -="42001202-3";"Canna Zym 250ml";9,00;19;;"Canna";"Grow | Dünger";="8717524951917";"K" -="42001202-2";"Canna Zym 500ml";14,00;19;;"Canna";"Grow | Dünger";;"K" -="42001139";"Champ High Glass Pipe";5,00;19;;"Champ High";"Bong/Glasfilter";="3661075189327"; -="42001157";"Chikamasa Erntescheere gerade mit Feder TP-530S";24,00;19;;"Chikamasa";"Grow | Zubehör";="4967645016000"; -="42001156";"Chikamasa Erntescheere L Form mit Magnet B-500SLF";29,00;19;;"Chikamasa";"Grow | Zubehör";="4967645013535"; -="42001155";"Chikamasa Erntescheere mit Feder T-552";16,00;19;;"Chikamasa";"Grow | Zubehör";="4967645032802"; -="42000867";"COMPO GROW ORGANIC Aussaaterde 5L";6,00;19;;"Compo";"Grow | Erde";="4008398188702"; -="42001144";"DIPSE Exact 50";37,50;19;;"Dipse";"Grow | Zubehör";="4260215941628"; -="42000193";"Dipse Waage 2000g";32,50;19;;"Dipse";"Grow | Zubehör";="4260215940300"; -="42000192";"Dipse Waage 500g";30,00;19;;"Dipse";"Grow | Zubehör";; -="42000963";"Elements Cone Tips";0,50;19;;"Elements";"Smoke | Tips/Filter";="716165177234"; -="42000968";"Elements Connisseur King Size Slim Ultra Thin mit Tips ";1,75;19;;"Elements";"Smoke | Papes";="716165179894"; -="42000966";"Elements King Size Slim";0,00;19;;"Elements";"Smoke | Papes";;"V" -="42001133";"Elements King Size Slim 32 Blatt + Tips";0,00;19;;"Elements";"Smoke | Papes";;"V" -="42001133-1";"Elements King Size Slim 32 Blatt + Tips Green";1,75;19;;"Elements";"Smoke | Papes";="716165301554";"K" -="42001133-2";"Elements King Size Slim 32 Blatt + Tips Pink";1,75;19;;"Elements";"Smoke | Papes";="716165303442";"K" -="42001133-3";"Elements King Size Slim 32 Blatt + Tips Rainbow";1,75;19;;"Elements";"Smoke | Papes";="716165306238";"K" -="42000966-3";"Elements King Size Slim Green";1,00;19;;"Elements";"Smoke | Papes";="716165301523";"K" -="42000966-2";"Elements King Size Slim Pink";1,00;19;;"Elements";"Smoke | Papes";="716165301486";"K" -="42000966-1";"Elements King Size Slim Ultra Thin";1,00;19;;"Elements";"Smoke | Papes";="716165177777";"K" -="42000967";"Elements King Size Ultra Thin Rice";1,00;19;;"Elements";"Smoke | Papes";="716165177814"; -="42000964";"Elements Rainbow Tips";0,50;19;;"Elements";"Smoke | Tips/Filter";="716165307334"; -="42001089";"Elements Single Wide Rolls Papers 5m - Plastic Holder";2,50;19;;"Elements";"Smoke | Papes";="000420010894"; -="42000965";"Elements Wide Tips";0,50;19;;"Elements";"Smoke | Tips/Filter";="716165174196"; -="42001090";"G-Rollz Geschenkbox S - Reggae";39,00;19;;"G-Rollz";"Smoke | Zubehör";; -="42000978";"Gizeh Papes Extra Fine Weiß";1,30;19;;"Gizeh";"Smoke | Papes";="42237792"; -="42000977";"Gizeh Papes Fine Grün";1,30;19;;"Gizeh";"Smoke | Papes";="42227526"; -="42000959";"GIZEH Slim Filter";1,50;19;;"Gizeh";"Smoke | Tips/Filter";="4002604009256"; -="42000960";"GIZEH Slim Filter Aktivkohle";1,50;19;;"Gizeh";"Smoke | Tips/Filter";="4002604029254"; -="42000962";"GIZEH XL Slim Filter";1,50;19;;"Gizeh";"Smoke | Tips/Filter";="4002604481106"; -="42000961";"GIZEH XL Slim Filter unbleached";1,50;19;;"Gizeh";"Smoke | Tips/Filter";="4002604179256"; -="42001153";"Homebox Ambient Q100 100x100x200cm";180,00;19;;"Homebox";"Grow | Zelte";="4260220921714"; -="42001154";"HomeBox Ambient R120S 120x60x180cm";200,00;19;;"Homebox";"Grow | Zelte";; -="42000973";"King Palm 1 1/4 Papes mit Flavored Tips";0,00;19;;"King Palm";"Smoke | Papes";;"V" -="42000973-2";"King Palm 1 1/4 Papes mit Flavored Tips Grape-HD";3,50;19;;"King Palm";"Smoke | Papes";="810089613899";"K" -="42000973-1";"King Palm 1 1/4 Papes mit Flavored Tips Lemon Haze";3,50;19;;"King Palm";"Smoke | Papes";="810089614063";"K" -="42001019";"King Palm Flavored Tips";1,00;19;;"King Palm";"Smoke | King Palm";;"V" -="42001019-2";"King Palm Flavored Tips Banana Cream";1,00;19;;"King Palm";"Smoke | King Palm";="850018987066";"K" -="42001019-1";"King Palm Flavored Tips Magic Mint";1,00;19;;"King Palm";"Smoke | King Palm";="850018987073";"K" -="42001019-3";"King Palm Flavored Tips Pine Drip";1,00;19;;"King Palm";"Smoke | King Palm";="850018987493";"K" -="42001018";"King Palm Guava the Great";2,00;19;;"King Palm";"Smoke | King Palm";="850018987622"; -="42001014";"King Palm Leaf Cones 3 Natural";3,45;19;;"King Palm";"Smoke | King Palm";="810089612373"; -="42001011";"King Palm Leaf Rolls 2";2,95;19;;"King Palm";"Smoke | King Palm";;"V" -="42001011-3";"King Palm Leaf Rolls 2 Banana Cream";2,95;19;;"King Palm";"Smoke | King Palm";="850018987059";"K" -="42001011-4";"King Palm Leaf Rolls 2 Berry Terps";2,95;19;;"King Palm";"Smoke | King Palm";="854029008734";"K" -="42001011-6";"King Palm Leaf Rolls 2 Blue Grape";2,95;19;;"King Palm";"Smoke | King Palm";="810089610515";"K" -="42001011-2";"King Palm Leaf Rolls 2 Honey Mango";2,95;19;;"King Palm";"Smoke | King Palm";="810089611970";"K" -="42001011-5";"King Palm Leaf Rolls 2 Honey Orange";2,95;19;;"King Palm";"Smoke | King Palm";="810089610331";"K" -="42001011-1";"King Palm Leaf Rolls 2 Mini";2,95;19;;"King Palm";"Smoke | King Palm";="810078745167";"K" -="42001011-7";"King Palm Leaf Rolls 2 Strawberry Shortcake";2,95;19;;"King Palm";"Smoke | King Palm";="810089610904";"K" -="42001016";"King Palm Leaf Rolls 5 Grape HD";5,50;19;;"King Palm";"Smoke | King Palm";="810089616388"; -="42001017";"King Palm Leaf Rolls Variety Pack";9,00;19;;"King Palm";"Smoke | King Palm";="850018987387"; -="42001015";"King Palm Leaf Tubes Don Pomegranate";3,00;19;;"King Palm";"Smoke | King Palm";="810089613707"; -="42001021";"King Palm Mars Grinder";0,00;19;;"King Palm";"Grinder";;"V" -="42001021-3";"King Palm Mars Grinder The Arrival";25,00;19;;"King Palm";"Grinder";="850018987462";"K" -="42001021-2";"King Palm Mars Grinder The Colony";25,00;19;;"King Palm";"Grinder";="810078743996";"K" -="42001021-4";"King Palm Mars Grinder The Departure";25,00;19;;"King Palm";"Grinder";="810078743514";"K" -="42001021-5";"King Palm Mars Grinder The Gift";25,00;19;;"King Palm";"Grinder";="810078743330";"K" -="42001021-1";"King Palm Mars Grinder The Session";25,00;19;;"King Palm";"Grinder";="810078743453";"K" -="42001012";"King Palm Mini Leaf Rolls 2";1,95;19;;"King Palm";"Smoke | King Palm";;"V" -="42001012-1";"King Palm Mini Leaf Rolls 2 Cherry Charm";1,95;19;;"King Palm";"Smoke | King Palm";="810089610294";"K" -="42001012-2";"King Palm Mini Leaf Rolls 2 Perfect Pear";1,95;19;;"King Palm";"Smoke | King Palm";="810089610324";"K" -="42001013";"King Palm Wraps 2";2,95;19;;"King Palm";"Smoke | King Palm";;"V" -="42001013-2";"King Palm Wraps 2 Cherry Bomb";2,95;19;;"King Palm";"Smoke | King Palm";="850018987158";"K" -="42001013-1";"King Palm Wraps 2 Golden Mango";2,95;19;;"King Palm";"Smoke | King Palm";="810078740148";"K" -="42000955";"Medusa Filter 6mm 10 Stück";0,00;19;;"Medusa";"Smoke | Tips/Filter";;"V" -="42000955-1";"Medusa Filter 6mm 10 Stück Rosé";2,90;19;;"Medusa";"Smoke | Tips/Filter";="4262408940326";"K" -="42000955-2";"Medusa Filter 6mm 10 Stück Violet";2,90;19;;"Medusa";"Smoke | Tips/Filter";="4262408940302";"K" -="42000956";"Medusa Filter 6mm Mixed Tüte á 32";7,00;19;;"Medusa";"Smoke | Tips/Filter";="4262408941545"; -="42000957";"Medusa Filter 6mm Tüte á 100 ";0,00;19;;"Medusa";"Smoke | Tips/Filter";;"V" -="42000957-3";"Medusa Filter 6mm Tüte á 100 Mixed";18,90;19;;"Medusa";"Smoke | Tips/Filter";="4270003233545";"K" -="42000957-1";"Medusa Filter 6mm Tüte á 100 Sunset";18,90;19;;"Medusa";"Smoke | Tips/Filter";="4262408940135";"K" -="42000957-2";"Medusa Filter 6mm Tüte á 100 Vioket";18,90;19;;"Medusa";"Smoke | Tips/Filter";="4270003233569";"K" -="42000942";"Medusa Filter 6mm Tüte á 50 Stück";0,00;19;;"Medusa";"Smoke | Tips/Filter";;"V" -="42000942-3";"Medusa Filter 6mm Tüte á 50 Stück Organic";9,90;19;;"Medusa";"Smoke | Tips/Filter";="4270002129542";"K" -="42000942-2";"Medusa Filter 6mm Tüte á 50 Stück Rosé";9,90;19;;"Medusa";"Smoke | Tips/Filter";="4262408940128";"K" -="42000942-4";"Medusa Filter 6mm Tüte á 50 Stück Sunset";9,90;19;;"Medusa";"Smoke | Tips/Filter";="4262408940098";"K" -="42000942-1";"Medusa Filter 6mm Tüte á 50 Stück Violet";9,90;19;;"Medusa";"Smoke | Tips/Filter";="4270002129511";"K" -="42000871";"Milwaukee pH Messgerät pH600";25,00;19;;"Milwaukee";"Grow | Messgeräte";; -="42001159";"Neudorff Gelb Sticker 10 Stück";9,00;19;;"Neudorff";"Grow | Zubehör";; -="42001158";"Neudorff Gelbtafeln 7 Tafeln";9,00;19;;"Neudorff";"Grow | Zubehör";="4005240003220"; -="42000870";"Pflanzenjojo";1,60;19;;"Niecorol";"Grow | Zubehör";; -="42000976";"OCB Papes Double Premium";1,30;19;;"OCB";"Smoke | Papes";="30062948"; -="42000974";"OCB Papes Double Blue";1,30;19;;"OCB";"Smoke | Papes";="30034440"; -="42000975";"OCB Papes No4";1,30;19;;"OCB";"Smoke | Papes";="30004832"; -="42000958";"OCB Slim Filter Natur";1,50;19;;"OCB";"Smoke | Tips/Filter";; -="42000575";"Plagron All Mix 25L";12,00;19;;"Plagron";"Grow | Erde";; -="42000576";"Plagron All Mix 50L";19,50;19;;"Plagron";"Grow | Erde";; -="42000577";"Plagron Grow Mix 25L";13,50;19;;"Plagron";"Grow | Erde";; -="42000578";"Plagron Grow Mix 50L";17,00;19;;"Plagron";"Grow | Erde";; -="42000579";"Plagron Ligth Mix 25L";11,50;19;;"Plagron";"Grow | Erde";="8718104121362"; -="42000580";"Plagron Ligth Mix 50L";15,00;19;;"Plagron";"Grow | Erde";; -="42000582";"Plagron Pro Mix 50L";19,50;19;;"Plagron";"Grow | Erde";="8718104121300"; -="42000909";"PURIZE Aktivkohlefilter Big Size 14mm (3 Stück)";8,90;19;;"Purize";"Smoke | Tips/Filter";; -="42000910";"PURIZE Aktivkohlefilter Big Size 14mm (7 Stück)";22,90;19;;"Purize";"Smoke | Tips/Filter";="4260748413159"; -="42001078";"PURIZE Aktivkohlefilter Super Slim Size 5mm (500 Stück)";80,00;19;;"Purize";"Smoke | Tips/Filter";="4260748416501"; -="42000010";"Purize Aktivkohletasche XS Pink";9,90;19;;"Purize";"Zubehör";="4260748410745"; -="42000908";"PURIZE Aufblasbarer Jibbit Large 1,20 m";9,90;19;;"Purize";"Smoke | Zubehör";="4260748411735"; -="42000939";"PURIZE Big Size Tüte á 7 Stück";20,00;19;;"Purize";"Smoke | Tips/Filter";="4260748413159"; -="42001174";"Purize Blazy Susan 9mm Lila";9,90;19;;"Purize";"Smoke | Tips/Filter";="810134925526"; -="42001175";"Purize Blazy Susan 9mm Pink";9,90;19;;"Purize";"Smoke | Tips/Filter";="810134925519"; -="42000941";"PURIZE First Aid Kit";3,00;19;;"Purize";"Smoke | Papes";="4260748414804"; -="42000919";"PURIZE King Size Slim";0,00;19;;"Purize";"Smoke | Papes";;"V" -="42000919-6";"PURIZE King Size Slim 420 Papers";8,90;19;;"Purize";"Smoke | Papes";="4260748412992";"K" -="42000919-3";"PURIZE King Size Slim Braun";1,50;19;;"Purize";"Smoke | Papes";="4260748410103";"K" -="42000919-4";"PURIZE King Size Slim Inside Out";1,50;19;;"Purize";"Smoke | Papes";="4260748413104";"K" -="42000919-1";"PURIZE King Size Slim Pink";1,50;19;;"Purize";"Smoke | Papes";="4260748410134";"K" -="42000919-5";"PURIZE King Size Slim Plus Paper Tips";0,00;19;;"Purize";"Smoke | Papes";;"K" -="42000919-2";"PURIZE King Size Slim Rainbow Spezial";1,50;19;;"Purize";"Smoke | Papes";="4260748410011";"K" -="42000921";"PURIZE King Size Ultra Slim";1,50;19;;"Purize";"Smoke | Papes";="4260748410165"; -="42000920";"PURIZE King Size Wide";2,00;19;;"Purize";"Smoke | Papes";="4260748410301"; -="42000923";"PURIZE Long Size Slim";0,00;19;;"Purize";"Smoke | Papes";; -="42000922";"PURIZE Long Size Wide";0,00;19;;"Purize";"Smoke | Papes";; -="42001080";"PURIZE Pink Papes'n'Tips Pink";4,90;19;;"Purize";"Smoke | Papes";="4260748410400"; -="42000953";"Purize Prerolled Cones 6mm 3 Stück";2,00;19;;"Purize";"Smoke | Papes";="4260748413036"; -="42000952";"Purize Prerolled Cones 9mm 3 Stück";2,00;19;;"Purize";"Smoke | Papes";="4260748413302"; -="42000649";"Purize Pullover";59,90;19;;"Purize";"Zubehör";; -="42000645";"PURIZE Queen Size Slim";1,50;19;;"Purize";"Smoke | Papes";="4260748414750"; -="42000938";"PURIZE Regular Size Pack á 10 Stück";2,00;19;;"Purize";"Smoke | Tips/Filter";="4260748410936"; -="42000929";"PURIZE Regular Size Short Tüte á 50 Stück";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748415214"; -="42000933";"PURIZE Regular Size Tüte á 100 Stück";0,00;19;;"Purize";"Smoke | Tips/Filter";;"V" -="42000933-1";"PURIZE Regular Size Tüte á 100 Stück Lila";16,90;19;;"Purize";"Smoke | Tips/Filter";="4260748413753";"K" -="42000933-3";"PURIZE Regular Size Tüte á 100 Stück Organic";16,90;19;;"Purize";"Smoke | Tips/Filter";="4260748411353";"K" -="42000933-4";"PURIZE Regular Size Tüte á 100 Stück Pink";16,90;19;;"Purize";"Smoke | Tips/Filter";="4260748411360";"K" -="42000933-5";"PURIZE Regular Size Tüte á 100 Stück Spy";16,90;19;;"Purize";"Smoke | Tips/Filter";;"K" -="42000933-2";"PURIZE Regular Size Tüte á 100 Stück Weiß";16,90;19;;"Purize";"Smoke | Tips/Filter";="4260748410752";"K" -="42000934";"PURIZE Regular Size Tüte á 250 Stück";0,00;19;;"Purize";"Smoke | Tips/Filter";;"V" -="42000934-1";"PURIZE Regular Size Tüte á 250 Stück Allcolor";38,90;19;;"Purize";"Smoke | Tips/Filter";="4260748415740";"K" -="42000934-3";"PURIZE Regular Size Tüte á 250 Stück Organic";38,90;19;;"Purize";"Smoke | Tips/Filter";="4260748415733";"K" -="42000934-2";"PURIZE Regular Size Tüte á 250 Stück Pink";38,90;19;;"Purize";"Smoke | Tips/Filter";="4260748416839";"K" -="42000934-4";"PURIZE Regular Size Tüte á 250 Stück Weiß";38,90;19;;"Purize";"Smoke | Tips/Filter";="4260748410448";"K" -="42000930";"PURIZE Regular Size Tüte á 50 Stück";0,00;19;;"Purize";"Smoke | Tips/Filter";;"V" -="42000930-1";"PURIZE Regular Size Tüte á 50 Stück Allcolor";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748411377";"K" -="42000930-4";"PURIZE Regular Size Tüte á 50 Stück Organic";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748411384";"K" -="42000930-2";"PURIZE Regular Size Tüte á 50 Stück Pink";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748411391";"K" -="42000930-3";"PURIZE Regular Size Tüte á 50 Stück White";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748410653";"K" -="42000644";"PURIZE Roll Wide";2,50;19;;"Purize";"Smoke | Papes";="4260748414286"; -="42000648";"PURIZE Rolls Pink";2,00;19;;"Purize";"Smoke | Papes";="4260748410196"; -="42000643";"PURIZE Rools Braun";2,00;19;;"Purize";"Smoke | Papes";="4260748410226"; -="42000931";"PURIZE Slim Size Air Tüte á 50 Stück";9,00;19;;"Purize";"Smoke | Tips/Filter";="4260748415801"; -="42000936";"PURIZE Slim Size Tüte á 50 Stück";9,50;19;;"Purize";"Smoke | Tips/Filter";="4260748414422"; -="42000928";"PURIZE Super Slim Size Tüte á 111 Stück";17,90;19;;"Purize";"Smoke | Tips/Filter";="4260748417690"; -="42000940";"PURIZE Super Slim Size Tüte á 50 Stück";9,50;19;;"Purize";"Smoke | Tips/Filter";="4260748413173"; -="42000650";"Purize TShirt";35,00;19;;"Purize";"Zubehör";; -="42000932";"PURIZE Variety Bag";3,00;19;;"Purize";"Smoke | Tips/Filter";="4260748415894"; -="42000621";"PURIZE XTRA Slim 6mm Glas á 100 Filter";16,90;19;;"Purize";"Smoke | Tips/Filter";;"V" -="42000621-9";"PURIZE XTRA Slim 6mm Glas á 100 Filter Blau";16,90;19;;"Purize";"Smoke | Tips/Filter";;"K" -="42000621-8";"PURIZE XTRA Slim 6mm Glas á 100 Filter Gelb";16,90;19;;"Purize";"Smoke | Tips/Filter";;"K" -="42000621-7";"PURIZE XTRA Slim 6mm Glas á 100 Filter Grün";16,90;19;;"Purize";"Smoke | Tips/Filter";="4260748411469";"K" -="42000621-1";"PURIZE XTRA Slim 6mm Glas á 100 Filter Lila";16,90;19;;"Purize";"Smoke | Tips/Filter";="4260748412534";"K" -="42000621-6";"PURIZE XTRA Slim 6mm Glas á 100 Filter Organic";16,90;19;;"Purize";"Smoke | Tips/Filter";="4260748411513";"K" -="42000621-5";"PURIZE XTRA Slim 6mm Glas á 100 Filter Pink";16,90;19;;"Purize";"Smoke | Tips/Filter";="4260748411537";"K" -="42000621-4";"PURIZE XTRA Slim 6mm Glas á 100 Filter Rainbow";16,90;19;;"Purize";"Smoke | Tips/Filter";;"K" -="42000621-3";"PURIZE XTRA Slim 6mm Glas á 100 Filter Spy";16,90;19;;"Purize";"Smoke | Tips/Filter";;"K" -="42000621-2";"PURIZE XTRA Slim 6mm Glas á 100 Filter Weiß";16,90;19;;"Purize";"Smoke | Tips/Filter";="4260748410066";"K" -="42000935";"PURIZE Xtra Slim Size Pack á 10 Stück";0,00;19;;"Purize";"Smoke | Tips/Filter";;"V" -="42000935-1";"PURIZE Xtra Slim Size Pack á 10 Stück Rainbow";2,00;19;;"Purize";"Smoke | Tips/Filter";="4260748414446";"K" -="42000935-2";"PURIZE Xtra Slim Size Pack á 10 Stück White";2,00;19;;"Purize";"Smoke | Tips/Filter";="4260748410851";"K" -="42000926";"PURIZE Xtra Slim Size Tüte á 250 Stück";0,00;19;;"Purize";"Smoke | Tips/Filter";;"V" -="42000926-5";"PURIZE Xtra Slim Size Tüte á 250 Stück Blau";36,90;19;;"Purize";"Smoke | Tips/Filter";;"K" -="42000926-1";"PURIZE Xtra Slim Size Tüte á 250 Stück Gelb";36,90;19;;"Purize";"Smoke | Tips/Filter";="4260748412237";"K" -="42000926-4";"PURIZE Xtra Slim Size Tüte á 250 Stück Lila";36,90;19;;"Purize";"Smoke | Tips/Filter";="4260748413777";"K" -="42000926-2";"PURIZE Xtra Slim Size Tüte á 250 Stück Pink";36,90;19;;"Purize";"Smoke | Tips/Filter";="4260748412206";"K" -="42000926-3";"PURIZE Xtra Slim Size Tüte á 250 Stück Rainbow";36,90;19;;"Purize";"Smoke | Tips/Filter";="4260748412213";"K" -="42000926-6";"PURIZE Xtra Slim Size Tüte á 250 Stück Weiß";36,90;19;;"Purize";"Smoke | Tips/Filter";;"K" -="42000927";"PURIZE Xtra Slim Size Tüte á 33 Stück";5,90;19;;"Purize";"Smoke | Tips/Filter";="4260748415702";"V" -="42000927-2";"PURIZE Xtra Slim Size Tüte á 33 Stück Bunt";5,90;19;;"Purize";"Smoke | Tips/Filter";="4260748413135";"K" -="42000927-1";"PURIZE Xtra Slim Size Tüte á 33 Stück Frankreich";5,90;19;;"Purize";"Smoke | Tips/Filter";="4260748415702";"K" -="42000925";"PURIZE Xtra Slim Size Tüte á 50 Stück";0,00;19;;"Purize";"Smoke | Tips/Filter";;"V" -="42000925-5";"PURIZE Xtra Slim Size Tüte á 50 Stück Blazy Susan Pink";9,90;19;;"Purize";"Smoke | Tips/Filter";="810134925496";"K" -="42000925-6";"PURIZE Xtra Slim Size Tüte á 50 Stück Blazy Susan Purple";9,90;19;;"Purize";"Smoke | Tips/Filter";="810134925502";"K" -="42000925-1";"PURIZE Xtra Slim Size Tüte á 50 Stück Gelb";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748412299";"K" -="42000925-3";"PURIZE Xtra Slim Size Tüte á 50 Stück Grün";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748412268";"K" -="42000925-2";"PURIZE Xtra Slim Size Tüte á 50 Stück Lila";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748413807";"K" -="42000925-4";"PURIZE Xtra Slim Size Tüte á 50 Stück Organic";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748412251";"K" -="42000925-9";"PURIZE Xtra Slim Size Tüte á 50 Stück Pink";8,90;19;;"Purize";"Smoke | Tips/Filter";="4260748412275";"K" -="42000925-8";"PURIZE Xtra Slim Size Tüte á 50 Stück Rainbow";8,90;19;;"Purize";"Smoke | Tips/Filter";;"K" -="42000925-7";"PURIZE Xtra Slim Size Tüte á 50 Stück Spy";8,90;19;;"Purize";"Smoke | Tips/Filter";;"K" -="42000937";"PURIZE Xtra Slim Size Tüte á 500 Stück";68,90;19;;"Purize";"Smoke | Tips/Filter";="4260748414316"; -="42000657";"RAW Aschenbecher";12,90;19;;"RAW";"Zubehör";; -="42000662";"RAW Aschenbecher Glas";24,90;19;;"RAW";"Zubehör";; -="42001268";"RAW Black + Tips";2,00;19;;"RAW";"Smoke | Papes";="716165284888"; -="42001172";"RAW Donut Tray";14,90;19;;"RAW";"Zubehör";="716165285182"; -="42001082";"RAW Ethereal Papers King Size Slim";2,20;19;;"RAW";"Smoke | Papes";; -="42001163";"RAW Frisbee";12,00;19;;"RAW";"Zubehör";="716165295372"; -="42001169";"RAW Gold Papes";2,20;19;;"RAW";"Smoke | Papes";="716165307563"; -="42001269";"RAW Hemp Wick";1,50;19;;"RAW";"Smoke | Papes";="716165280521"; -="42000659";"RAW Kartenspiel";4,20;19;;"RAW";"Zubehör";; -="42001242";"Raw Kerze";10,90;19;;"RAW";"Zubehör";="716165298083"; -="42000943";"RAW King Size Slim";1,50;19;;"RAW";"Smoke | Papes";="716165177357"; -="42000944";"RAW King Size Slim Black";1,50;19;;"RAW";"Smoke | Papes";="716165280972"; -="42000946";"RAW King Size Slim mit Tips";2,00;19;;"RAW";"Smoke | Papes";="716165174011"; -="42000945";"RAW King Size Slim Organic";1,50;19;;"RAW";"Smoke | Papes";="716165174219"; -="42000947";"RAW King Size Slim Organic mit Tips";2,00;19;;"RAW";"Smoke | Papes";="716165177579"; -="42001137";"RAW Lil Prep Bowl";5,00;19;;"RAW";"Smoke | Zubehör";="716165308270"; -="42000660";"RAW Munchies Box";14,90;19;;"RAW";"Zubehör";; -="42000948";"RAW Rolls 3m";2,00;19;;"RAW";"Smoke | Papes";="716165177371"; -="42001091";"RAW Summer Rolling Tray Small 27,5 x 17,5 cm";7,47;19;;"RAW";"Smoke | Zubehör";="7161653038178"; -="42001171";"RAW Summer Tray";9,90;19;;"RAW";"Zubehör";="716165303817"; -="42000658";"RAW Tabakbefeuchter";1,90;19;;"RAW";"Zubehör";; -="42000656";"RAW Taschenaschenbecher";2,90;19;;"RAW";"Zubehör";="716165158370"; -="42000915";"RAW Tasse Wake up & Bake up";16,90;19;;"RAW";"Smoke | Zubehör";="716165294955"; -="42000949";"RAW Tips";0,50;19;;"RAW";"Smoke | Tips/Filter";="716165177395"; -="42000950";"RAW Tips Black";0,50;19;;"RAW";"Smoke | Tips/Filter";="716165286707"; -="42000951";"RAW Tips Wide";0,50;19;;"RAW";"Smoke | Tips/Filter";="716165177555"; -="42000869";"Sonnenerde Bio Hanferde 20L";23,00;19;;"Sonnenerde";"Grow | Erde";="9120009100202"; -="42000969";"Wirklichrich 66 Papes mit Tips";3,00;19;;"Wirklichrich";"Smoke | Papes";; -="42000972";"Woodzl 1 1/4 Natural mit Tips";1,50;19;;"Woodzl";"Smoke | Papes";="702754956458"; -="42000970";"Woodzl Natural King Size Slim";1,50;19;;"Woodzl";"Smoke | Papes";="096224945852"; -="42000971";"Woodzl Natural King Size Slim mit Tips";2,00;19;;"Woodzl";"Smoke | Papes";="702754956403"; -="42000954";"doobiewood kss + paper tips";2,00;19;;"doobiewood";"Smoke | Papes";="4260748412930"; \ No newline at end of file diff --git a/docs/api/README.md b/docs/api/README.md index 61aa911..a716984 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -171,6 +171,8 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route | [`routes/order/update.md`](routes/order/update.md) | `PATCH /order/{id}` | | [`routes/order/_id/payment.get.md`](routes/order/_id/payment.get.md) | `GET /order/{id}/payment` | | [`routes/order/_id/payment.post.md`](routes/order/_id/payment.post.md) | `POST /order/{id}/payment` | +| [`routes/order/_id/shipment.get.md`](routes/order/_id/shipment.get.md) | `GET /order/{id}/shipment` | +| [`routes/order/_id/shipment.post.md`](routes/order/_id/shipment.post.md) | `POST /order/{id}/shipment` | | [`routes/order/_id/shipping.get.md`](routes/order/_id/shipping.get.md) | `GET /order/{id}/shipping` | | [`routes/order/_id/shipping.post.md`](routes/order/_id/shipping.post.md) | `POST /order/{id}/shipping` | | [`routes/package/_id.md`](routes/package/_id.md) | `DELETE /package/{id}` | @@ -206,6 +208,10 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route | [`routes/quote/update.md`](routes/quote/update.md) | `PATCH /quote/{id}` | | [`routes/serper/image.md`](routes/serper/image.md) | `POST /serper/image` | | [`routes/serper/images.md`](routes/serper/images.md) | `POST /serper/images` | +| [`routes/shipment/_id.delete.md`](routes/shipment/_id.delete.md) | `DELETE /shipment/{id}` | +| [`routes/shipment/_id.get.md`](routes/shipment/_id.get.md) | `GET /shipment/{id}` | +| [`routes/shipment/_id/shipping.get.md`](routes/shipment/_id/shipping.get.md) | `GET /shipment/{id}/shipping` | +| [`routes/shipment/_id/shipping.post.md`](routes/shipment/_id/shipping.post.md) | `POST /shipment/{id}/shipping` | | [`routes/shipping/_id.delete.md`](routes/shipping/_id.delete.md) | `DELETE /shipping/{id}` | | [`routes/shipping/_id.patch.md`](routes/shipping/_id.patch.md) | `PATCH /shipping/{id}` | | [`routes/shipping/_id/package.get.md`](routes/shipping/_id/package.get.md) | `GET /shipping/{id}/package` | diff --git a/docs/api/routes-all.md b/docs/api/routes-all.md index 966166b..db0ce4c 100644 --- a/docs/api/routes-all.md +++ b/docs/api/routes-all.md @@ -1,6 +1,6 @@ # Dash API Routes Overview (All) -Overview of all 210 API endpoints across all modules. +Overview of all 216 API endpoints across all modules. ### Notation @@ -9,54 +9,56 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). ### Reusable types -- `T1` = `{ article: T2 }` -- `T2` = `{ id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T33, brand: T31|null, categoryIds: int[], photos: T29[], eans: str[], prices: T40[], suppliers: T13[] }` -- `T3` = `{ order: T4 }` -- `T4` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T24[], createdAt?: str, shipping?: T39|null, shippings?: T39[], payment?: T36|null, payments?: T36[], paid?: bool, delivered?: bool }` +- `T1` = `{ order: T2 }` +- `T2` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T25[], createdAt?: str, shipping?: T33|null, shippings?: T33[], payment?: T39|null, payments?: T39[], paid?: bool, shipped?: bool, delivered?: bool, orderTotal?: num, paidTotal?: num, orderedQuantity?: num, shippedQuantity?: num, shipments?: T7[] }` +- `T3` = `{ article: T4 }` +- `T4` = `{ id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T36, brand: T34|null, categoryIds: int[], photos: T31[], eans: str[], prices: T42[], suppliers: T14[] }` - `T5` = `{ delivery: T6 }` -- `T6` = `{ id: int, number: str, type: str, supplierId: int|null, date: str, lines: T24[], files: T10[], externalNumber?: str|null, createdAt?: str, ledger?: T44 }` -- `T7` = `{ labeltemplate: T8 }` -- `T8` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T20, thumb?: str|null }` -- `T9` = `{ quote: T12 }` -- `T10` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, thumbavif?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }` -- `T11` = `{ id: int, name: str, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }` -- `T12` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T24[], createdAt?: str }` -- `T13` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T46|null }` -- `T14` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` -- `T15` = `{ ledger: T16[] }` -- `T16` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash?: str, sku?: str|null, created_at?: str }` -- `T17` = `{ id: int, name: str, sort: int, exclusive: bool, values: T34[], articleCount?: int }` -- `T18` = `{ id: int, companyuser_id: int, provider: str, model: str, created_at: str, updated_at: str, state: str, started_by?: integer,null, first_message?: string,null }` -- `T19` = `{ id: int, name: str, rootCategoryId: int|null, languageId: int|null, iso639: str|null, taxrate: num, reducedtaxrate: num, taxfreerate: num }` -- `T20` = `{ objects: T25[] }` +- `T6` = `{ id: int, number: str, type: str, supplierId: int|null, date: str, lines: T25[], files: T11[], externalNumber?: str|null, createdAt?: str, ledger?: T46 }` +- `T7` = `{ id: int, number: str, type: str, date: str, lines: T25[], shippings: T33[], entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, lineCount?: int, packageCount?: int }` +- `T8` = `{ labeltemplate: T9 }` +- `T9` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T21, thumb?: str|null }` +- `T10` = `{ quote: T13 }` +- `T11` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, thumbavif?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }` +- `T12` = `{ id: int, name: str, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }` +- `T13` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T25[], createdAt?: str }` +- `T14` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T48|null }` +- `T15` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` +- `T16` = `{ ledger: T17[] }` +- `T17` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash?: str, sku?: str|null, created_at?: str }` +- `T18` = `{ id: int, name: str, sort: int, exclusive: bool, values: T37[], articleCount?: int }` +- `T19` = `{ id: int, companyuser_id: int, provider: str, model: str, created_at: str, updated_at: str, state: str, started_by?: integer,null, first_message?: string,null }` +- `T20` = `{ id: int, name: str, rootCategoryId: int|null, languageId: int|null, iso639: str|null, taxrate: num, reducedtaxrate: num, taxfreerate: num }` - `T21` = `{ objects: T26[] }` -- `T22` = `{ printer: T27 }` -- `T23` = `{ entity: T30 }` -- `T24` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }` -- `T25` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` +- `T22` = `{ objects: T27[] }` +- `T23` = `{ printer: T29 }` +- `T24` = `{ entity: T32 }` +- `T25` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }` - `T26` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` -- `T27` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }` -- `T28` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }` -- `T29` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }` -- `T30` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }` -- `T31` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }` -- `T32` = `{ article: T37 }` -- `T33` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }` -- `T34` = `{ id: int, attributeId: int, value: str, sort: int, articleCount?: int }` -- `T35` = `{ id: int, name: str, sort: int, exclusive: bool, articleCount?: int }` -- `T36` = `{ id: int, accountId: int, manifestId: int, type: str, amount: num }` -- `T37` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }` -- `T38` = `{ id: int, email: str, selectedCompanyId: int|null, picture?: str }` -- `T39` = `{ id: int, manifestId: int, number: str, type: str, state: str }` -- `T40` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }` -- `T41` = `{ fullHash: str, thumbW: int, thumbH: int, thumbHash: str }` -- `T42` = `{ id: int, entityId: int, contactType: str, contact: str }` -- `T43` = `{ id: int, name: str, createdAt: str, updatedAt: str }` -- `T44` = `{ totalPositions: int, completelyOnLedgerPositions: int }` -- `T45` = `{ warehouse: T47 }` -- `T46` = `{ net: num, taxId: int, taxRate?: num|null }` -- `T47` = `{ id: int, name: str, sort: int }` -- `T48` = `{ id: int, categoryIds: int[] }` +- `T27` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` +- `T28` = `{ shipping: T33 }` +- `T29` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }` +- `T30` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }` +- `T31` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }` +- `T32` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }` +- `T33` = `{ id: int, manifestId: int, number: str, type: str, state: str, trackingCode?: str|null, carrier?: str|null }` +- `T34` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }` +- `T35` = `{ article: T40 }` +- `T36` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }` +- `T37` = `{ id: int, attributeId: int, value: str, sort: int, articleCount?: int }` +- `T38` = `{ id: int, name: str, sort: int, exclusive: bool, articleCount?: int }` +- `T39` = `{ id: int, accountId: int, manifestId: int, type: str, amount: num }` +- `T40` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }` +- `T41` = `{ id: int, email: str, selectedCompanyId: int|null, picture?: str }` +- `T42` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }` +- `T43` = `{ fullHash: str, thumbW: int, thumbH: int, thumbHash: str }` +- `T44` = `{ id: int, entityId: int, contactType: str, contact: str }` +- `T45` = `{ id: int, name: str, createdAt: str, updatedAt: str }` +- `T46` = `{ totalPositions: int, completelyOnLedgerPositions: int }` +- `T47` = `{ warehouse: T49 }` +- `T48` = `{ net: num, taxId: int, taxRate?: num|null }` +- `T49` = `{ id: int, name: str, sort: int }` +- `T50` = `{ id: int, categoryIds: int[] }` ### Routes @@ -77,7 +79,7 @@ _none_ #### Create article in category — `POST /article` - Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }` -- Returns: `201 T32` +- Returns: `201 T35` #### Export articles as ZIP — `GET /article/export.zip` - Params: `query: categoryId?: int` - Returns: `200 zip` @@ -86,10 +88,10 @@ _none_ - Returns: `204 void` #### Get article by SKU — `GET /article/{sku}` - Params: `sku: str` -- Returns: `200 T1` +- Returns: `200 T3` #### Update article by SKU — `PATCH /article/{sku}` - Params: `sku: str, body: { name?: str, desc?: str, seo?: str, unitamount?: num, unitSi?: str, track_stock?: bool }` -- Returns: `200 T1` +- Returns: `200 T3` #### Get Stückliste (bundle items) for an article — `GET /article/{sku}/articlelist` - Params: `sku: str` - Returns: `200 { items: { id: int, articleId: int, sku: str, name: str, count: int, sort: int }[] }` @@ -104,25 +106,25 @@ _none_ - Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, articleId?: int, exclusive?: bool }[] }` #### Set or clear article brand by SKU (null to clear) — `PUT /article/{sku}/brand` - Params: `sku: str, body: { brandId: int|null }` -- Returns: `200 T1` +- Returns: `200 T3` #### Replace article categories by SKU — `PUT /article/{sku}/categories` - Params: `sku: str, body: { categoryIds: int[] }` -- Returns: `200 T1` +- Returns: `200 T3` #### List child articles of a parent article — `GET /article/{sku}/children` - Params: `sku: str` - Returns: `200 { children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` #### Create and attach a new child article to a parent article — `POST /article/{sku}/children` - Params: `sku: str, body: { attributeValueId?: int, value?: str, name?: str }` -- Returns: `201 T32` +- Returns: `201 T35` #### Attach an existing article as a child to a parent article — `PUT /article/{sku}/children` - Params: `sku: str, body: { sku: str, attributeValueId?: int, value?: str, reparent?: bool }` -- Returns: `200 { article: T37, children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` +- Returns: `200 { article: T40, children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` #### Remove a child article from a parent article — `DELETE /article/{sku}/children/{childSku}` - Params: `sku: str, childSku: str, query: deleteArticle?: bool` - Returns: `200 { success: bool, deleted: bool }` #### Replace article EANs by SKU — `PUT /article/{sku}/eans` - Params: `sku: str, body: { eans: str[] }` -- Returns: `200 T1` +- Returns: `200 T3` #### Get article history by SKU — `GET /article/{sku}/history` - Params: `sku: str, query: limit?: int` - Returns: `200 { history: { id: int, date: str, text: str }[], truncated: bool }` @@ -143,10 +145,10 @@ _none_ - Returns: `200 { options: { id: int, articleId: int, sku: str, name: str, count: int, sort: int, mandated: bool }[] }` #### Toggle parent mode for an article — `POST /article/{sku}/parent` - Params: `sku: str, body: { isParent: bool, attributeNameId?: int }` -- Returns: `200 T32` +- Returns: `200 T35` #### Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) — `PUT /article/{sku}/prices` - Params: `sku: str, body: { prices: { net?: num, gross?: num, price?: num, taxRate?: num, tax?: num, minmultiplier?: num }[] }` -- Returns: `200 T1` +- Returns: `200 T3` #### Print article label — `POST /article/{sku}/print` - Params: `sku: str, body: { templateId: int, printerId: int, quantity: int }` - Returns: `200 { success: bool }` @@ -161,10 +163,10 @@ _none_ - Returns: `200 { ok: bool }` #### List article suppliers — `GET /article/{sku}/supplier` - Params: `sku: str` -- Returns: `200 { suppliers: T13[] }` +- Returns: `200 { suppliers: T14[] }` #### Assign supplier to article — `POST /article/{sku}/supplier` - Params: `sku: str, body: { entityId: int, sku?: str|null, pu?: num, stock?: num|null, sort?: int|null, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }` -- Returns: `201 { supplier: T13 }` +- Returns: `201 { supplier: T14 }` #### Reorder article suppliers — `PUT /article/{sku}/supplier/order` - Params: `sku: str, body: { ids: int[] }` - Returns: `200 { ids: int[] }` @@ -173,31 +175,31 @@ _none_ - Returns: `204 void` #### Update article supplier assignment — `PATCH /article/{sku}/supplier/{id}` - Params: `sku: str, id: int, body: { sku?: str|null, pu?: num, stock?: num|null, sort?: int, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }` -- Returns: `200 { supplier: T13 }` +- Returns: `200 { supplier: T14 }` #### Set article track_stock flag by SKU — `PUT /article/{sku}/track_stock` - Params: `sku: str, body: { track_stock: bool }` -- Returns: `200 T1` +- Returns: `200 T3` #### Set article weight by SKU — `PUT /article/{sku}/weight` - Params: `sku: str, body: { weight: num }` -- Returns: `200 T1` +- Returns: `200 T3` ### Section: /attributename #### List attribute names — `GET /attributename` _none_ -- Returns: `200 { attributenames: T17[] }` +- Returns: `200 { attributenames: T18[] }` #### Create attribute name — `POST /attributename` - Params: `body: { name: str, sort?: int, exclusive?: bool }` -- Returns: `201 { attributename: T17 }` +- Returns: `201 { attributename: T18 }` #### Reorder attribute names — `PUT /attributename/order` - Params: `body: { attributenameIds: int[] }` - Returns: `200 { ok: bool }` #### List values for an attribute — `GET /attributename/{attributeId}/value` - Params: `attributeId: int` -- Returns: `200 { values: T34[] }` +- Returns: `200 { values: T37[] }` #### Create value for an attribute — `POST /attributename/{attributeId}/value` - Params: `attributeId: int, body: { value: str, sort?: int }` -- Returns: `201 { attributevalue: T34 }` +- Returns: `201 { attributevalue: T37 }` #### Reorder values of an attribute — `PUT /attributename/{attributeId}/value/order` - Params: `attributeId: int, body: { attributevalueIds: int[] }` - Returns: `200 { ok: bool }` @@ -206,7 +208,7 @@ _none_ - Returns: `200 { ok: bool }` #### Update attribute name — `PATCH /attributename/{id}` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` -- Returns: `200 { attributename: T17 }` +- Returns: `200 { attributename: T18 }` ### Section: /attributevalue @@ -215,28 +217,28 @@ _none_ - Returns: `200 { ok: bool }` #### Update attribute value — `PATCH /attributevalue/{id}` - Params: `id: int, body: { value?: str, sort?: int }` -- Returns: `200 { attributevalue: T34 }` +- Returns: `200 { attributevalue: T37 }` #### Delete attribute value image — `DELETE /attributevalue/{id}/image` - Params: `id: int` -- Returns: `200 { attributevalue: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T41[] } }` +- Returns: `200 { attributevalue: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T43[] } }` #### Add or replace attribute value image — `POST /attributevalue/{id}/image` - Params: `id: int, image/avif` -- Returns: `201 { attributevalue: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T41[] } }` +- Returns: `201 { attributevalue: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T43[] } }` ### Section: /auth #### Login with Google ID token — `POST /auth/login` - Params: `body: { idToken: str }` -- Returns: `200 { message: str, user: T38 }` +- Returns: `200 { message: str, user: T41 }` #### Logout — `POST /auth/logout` _none_ - Returns: `200 { message: str }` #### Current user — `GET /auth/me` _none_ -- Returns: `200 { user: T38, googleClientId: str }` +- Returns: `200 { user: T41, googleClientId: str }` #### Set selected company — `PUT /auth/selected-company` - Params: `body: { companyId: int }` -- Returns: `200 { user: T38 }` +- Returns: `200 { user: T41 }` ### Section: /brand @@ -245,7 +247,7 @@ _none_ - Returns: `200 { brands: { id: int, name: str, articleCount: int, pics: { fullHash: str, thumbW: int, thumbH: int, thumbHash: str, smallThumbW?: int|null, smallThumbH?: int|null, smallThumbBinary?: str|null }[], gsprpinfo?: str|null }[], total: int, offset: int, limit: int }` #### Create brand — `POST /brand` - Params: `body: { name: str, gsprpinfo?: str }` -- Returns: `201 { brand: { id: int, name: str, pics: T41[], gsprpinfo?: str|null } }` +- Returns: `201 { brand: { id: int, name: str, pics: T43[], gsprpinfo?: str|null } }` #### Delete brand — `DELETE /brand/{id}` - Params: `id: int` - Returns: `204 void` @@ -254,10 +256,10 @@ _none_ - Returns: `200 { brand: { id: int, name: str, touchedArticleCount: int, gsprpinfo?: str|null } }` #### Delete brand image — `DELETE /brand/{id}/image` - Params: `id: int` -- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T41[] } }` +- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T43[] } }` #### Add or replace brand image — `POST /brand/{id}/image` - Params: `id: int, image/avif` -- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T41[] } }` +- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T43[] } }` ### Section: /category @@ -281,7 +283,7 @@ _none_ - Returns: `200 { category: { id: int, name: str, slug: str, parentId: int, sort: int } }` #### Articles in category — `GET /category/{id}/articles` - Params: `id: int` -- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T31|null }[] }` +- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T34|null }[] }` #### List category attribute values — `GET /category/{id}/attributevalue` - Params: `id: int` - Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }` @@ -308,10 +310,10 @@ _none_ #### List companies — `GET /company` _none_ -- Returns: `200 { companies: T19[] }` +- Returns: `200 { companies: T20[] }` #### Create company — `POST /company` - Params: `body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }` -- Returns: `201 { company: T19, user: T38 }` +- Returns: `201 { company: T20, user: T41 }` #### Get company history — `GET /company/history` - Params: `query: limit?: int` - Returns: `200 { history: { id: int, date: str, text: str, companyuserId?: int|null }[], truncated: bool }` @@ -335,7 +337,7 @@ _none_ - Returns: `204 void` #### Update company — `PUT /company/{id}` - Params: `id: int, body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }` -- Returns: `200 { company: T19 }` +- Returns: `200 { company: T20 }` ### Section: /customergroup @@ -344,7 +346,7 @@ _none_ - Returns: `200 { customergroups: { id: int, name: str, sort: int, editable: bool }[] }` #### Create customer group — `POST /customergroup` - Params: `body: { name: str }` -- Returns: `201 { customergroup: T47 }` +- Returns: `201 { customergroup: T49 }` #### Reorder customer groups — `PUT /customergroup/order` - Params: `body: { customergroupIds: int[] }` - Returns: `200 { customergroupIds: int[] }` @@ -353,15 +355,15 @@ _none_ - Returns: `204 void` #### Update customer group — `PATCH /customergroup/{id}` - Params: `id: int, body: { name: str }` -- Returns: `200 { customergroup: T47 }` +- Returns: `200 { customergroup: T49 }` ### Section: /delivery #### List deliveries — `GET /delivery` _none_ -- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int|null, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T44 }[] }` +- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int|null, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T46 }[] }` #### Create delivery — `POST /delivery` -- Params: `body: { externalNumber: str, date: str, lines: T28[], supplierId?: int|null, files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` +- Params: `body: { externalNumber: str, date: str, lines: T30[], supplierId?: int|null, files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` - Returns: `201 T5` #### Parse document (SSE) — `POST /delivery/parse` - Params: `form: { file: str }` @@ -382,7 +384,7 @@ _none_ - Params: `id: int` - Returns: `200 T5` #### Update delivery — `PATCH /delivery/{id}` -- Params: `id: int, body: { externalNumber?: str|null, date?: str, supplierId?: int|null, lines?: T28[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` +- Params: `id: int, body: { externalNumber?: str|null, date?: str, supplierId?: int|null, lines?: T30[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` - Returns: `200 T5` #### Get delivery attachment file content — `GET /delivery/{id}/file/{fileId}` - Params: `id: int, fileId: int, query: download?: bool` @@ -392,46 +394,46 @@ _none_ #### List entities — `GET /entity` - Params: `query: customergroupId?: int, role?: str` -- Returns: `200 { entities: T30[] }` +- Returns: `200 { entities: T32[] }` #### Create entity — `POST /entity` - Params: `body: { customergroupId: int, name: str, firstName?: str|null, number?: str }` -- Returns: `201 T23` +- Returns: `201 T24` #### Peek next customer number — `GET /entity/next-number` _none_ - Returns: `200 { number: str }` #### List entity addresses — `GET /entity/{entityId}/address` - Params: `entityId: int` -- Returns: `200 { addresses: T14[] }` +- Returns: `200 { addresses: T15[] }` #### Create entity address — `POST /entity/{entityId}/address` - Params: `entityId: int, body: { addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` -- Returns: `201 { address: T14 }` +- Returns: `201 { address: T15 }` #### Delete entity address — `DELETE /entity/{entityId}/address/{id}` - Params: `entityId: int, id: int` - Returns: `204 void` #### Update entity address — `PATCH /entity/{entityId}/address/{id}` - Params: `entityId: int, id: int, body: { addressType?: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` -- Returns: `200 { address: T14 }` +- Returns: `200 { address: T15 }` #### List entity contacts — `GET /entity/{entityId}/contact` - Params: `entityId: int` -- Returns: `200 { contacts: T42[] }` +- Returns: `200 { contacts: T44[] }` #### Create entity contact — `POST /entity/{entityId}/contact` - Params: `entityId: int, body: { contactType: str, contact: str }` -- Returns: `201 { contact: T42 }` +- Returns: `201 { contact: T44 }` #### Delete entity contact — `DELETE /entity/{entityId}/contact/{id}` - Params: `entityId: int, id: int` - Returns: `204 void` #### Update entity contact — `PATCH /entity/{entityId}/contact/{id}` - Params: `entityId: int, id: int, body: { contactType?: str, contact?: str }` -- Returns: `200 { contact: T42 }` +- Returns: `200 { contact: T44 }` #### Delete entity — `DELETE /entity/{id}` - Params: `id: int` - Returns: `204 void` #### Get entity — `GET /entity/{id}` - Params: `id: int` -- Returns: `200 T23` +- Returns: `200 T24` #### Update entity — `PATCH /entity/{id}` - Params: `id: int, body: { name?: str, firstName?: str|null, number?: str|null }` -- Returns: `200 T23` +- Returns: `200 T24` ### Section: /events @@ -473,50 +475,50 @@ _none_ _none_ - Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb?: str|null }[] }` #### Create label template — `POST /labeltemplate` -- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: T21 }` -- Returns: `201 T7` +- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: T22 }` +- Returns: `201 T8` #### Delete label template — `DELETE /labeltemplate/{id}` - Params: `id: int` - Returns: `204 void` #### Get label template by ID — `GET /labeltemplate/{id}` - Params: `id: int` -- Returns: `200 T7` +- Returns: `200 T8` #### Update label template (partial) — `PATCH /labeltemplate/{id}` -- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T21 }` -- Returns: `200 T7` +- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T22 }` +- Returns: `200 T8` #### Update label template — `PUT /labeltemplate/{id}` -- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T21 }` -- Returns: `200 T7` +- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T22 }` +- Returns: `200 T8` ### Section: /ledger #### Query ledger by manifest article ID or SKU — `GET /ledger` - Params: `query: manifestarticle_id?: int, sku?: str, transaction_type?: str` -- Returns: `200 T15` +- Returns: `200 T16` #### Add ledger entry — `POST /ledger` - Params: `body: { manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str }` -- Returns: `201 { ledger: T16 }` +- Returns: `201 { ledger: T17 }` #### Query ledger by manifest article ID — `GET /ledger/manifestarticle/{manifestarticleId}` - Params: `manifestarticleId: int, query: transaction_type?: str` -- Returns: `200 T15` +- Returns: `200 T16` #### Query ledger by article SKU — `GET /ledger/sku/{sku}` - Params: `sku: str, query: transaction_type?: str` -- Returns: `200 T15` +- Returns: `200 T16` ### Section: /modelconversation #### List conversations — `GET /modelconversation` _none_ -- Returns: `200 { conversations: T18[] }` +- Returns: `200 { conversations: T19[] }` #### Create conversation — `POST /modelconversation` - Params: `body: { provider: str, model: str }` -- Returns: `201 { conversation: T18 }` +- Returns: `201 { conversation: T19 }` #### Delete conversation — `DELETE /modelconversation/{id}` - Params: `id: int` - Returns: `204 void` #### Get conversation — `GET /modelconversation/{id}` - Params: `id: int` -- Returns: `200 { conversation: T18 }` +- Returns: `200 { conversation: T19 }` #### List conversation messages — `GET /modelconversation/{id}/message` - Params: `id: int` - Returns: `200 { messages: { id: int, conversation_id: int, created_at: str, role: str, message: str }[] }` @@ -540,31 +542,37 @@ _none_ #### List orders — `GET /order` - Params: `query: offset?: int, limit?: int` -- Returns: `200 { orders: T4[], total: int, offset: int, limit: int }` +- Returns: `200 { orders: T2[], total: int, offset: int, limit: int }` #### Create order — `POST /order` -- Params: `body: { number: str, date: str, lines: T28[], entityId?: int|null }` -- Returns: `201 T3` +- Params: `body: { number: str, date: str, lines: T30[], entityId?: int|null }` +- Returns: `201 T1` #### Delete order — `DELETE /order/{id}` - Params: `id: int` - Returns: `204 void` #### Get order — `GET /order/{id}` - Params: `id: int` -- Returns: `200 T3` +- Returns: `200 T1` #### Update order — `PATCH /order/{id}` -- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T28[] }` -- Returns: `200 T3` +- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T30[] }` +- Returns: `200 T1` #### List payments of an order — `GET /order/{id}/payment` - Params: `id: int` -- Returns: `200 { payments: T36[] }` +- Returns: `200 { payments: T39[] }` #### Add payment to an order — `POST /order/{id}/payment` - Params: `id: int, body: { accountId: int, type: str, amount: num }` -- Returns: `201 { payment: T36 }` +- Returns: `201 { payment: T39 }` +#### List shipments of an order — `GET /order/{id}/shipment` +- Params: `id: int` +- Returns: `200 { shipments: T7[] }` +#### Create shipment for an order — `POST /order/{id}/shipment` +- Params: `id: int, body: { lines: { sku: str, amount: num, name?: str, pu?: num, net?: num, rebate?: num, taxrate?: num, comment?: str }[], date?: str, externalNumber?: str }` +- Returns: `201 { shipment: T7 }` #### List shippings of an order — `GET /order/{id}/shipping` - Params: `id: int` -- Returns: `200 { shippings: T39[] }` +- Returns: `200 { shippings: T33[] }` #### Create shipping for an order — `POST /order/{id}/shipping` - Params: `id: int, body: { type: str, state: str, warehouseId?: int }` -- Returns: `201 { shipping: T39 }` +- Returns: `201 T28` ### Section: /package @@ -582,37 +590,37 @@ _none_ #### List POS terminals — `GET /posterminal` _none_ -- Returns: `200 { posterminals: T11[] }` +- Returns: `200 { posterminals: T12[] }` #### Create POS terminal — `PUT /posterminal` - Params: `body: { name: str, rootCategoryId?: int|null, syncRootArticles?: bool }` -- Returns: `200 { posterminal: T11 }` +- Returns: `200 { posterminal: T12 }` #### Delete POS terminal — `DELETE /posterminal/{id}` - Params: `id: int` - Returns: `204 void` #### Update POS terminal — `PATCH /posterminal/{id}` - Params: `id: int, body: { name?: str, rootCategoryId?: int|null, syncRootArticles?: bool, pairingpin?: str|null }` -- Returns: `200 { posterminal: T11 }` +- Returns: `200 { posterminal: T12 }` #### Disable all categories on terminal — `DELETE /posterminal/{id}/all` - Params: `id: int` -- Returns: `200 T48` +- Returns: `200 T50` #### Enable all categories on terminal — `PUT /posterminal/{id}/all` - Params: `id: int` -- Returns: `200 T48` +- Returns: `200 T50` #### Disable category on terminal — `DELETE /posterminal/{id}/category/{categoryId}` - Params: `id: int, categoryId: int` -- Returns: `200 T48` +- Returns: `200 T50` #### Enable category on terminal — `PUT /posterminal/{id}/category/{categoryId}` - Params: `id: int, categoryId: int` -- Returns: `200 T48` +- Returns: `200 T50` ### Section: /printer #### List printers — `GET /printer` _none_ -- Returns: `200 { printers: T27[] }` +- Returns: `200 { printers: T29[] }` #### Create printer — `POST /printer` - Params: `body: { address: str, type: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` -- Returns: `201 T22` +- Returns: `201 T23` #### Reorder printers — `PUT /printer/order` - Params: `body: { printerIds: int[] }` - Returns: `200 { printerIds: int[] }` @@ -621,22 +629,22 @@ _none_ - Returns: `204 void` #### Get printer by ID — `GET /printer/{id}` - Params: `id: int` -- Returns: `200 T22` +- Returns: `200 T23` #### Update printer (partial) — `PATCH /printer/{id}` - Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` -- Returns: `200 T22` +- Returns: `200 T23` #### Update printer — `PUT /printer/{id}` - Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` -- Returns: `200 T22` +- Returns: `200 T23` ### Section: /propertyname #### List property names — `GET /propertyname` _none_ -- Returns: `200 { propertynames: T35[] }` +- Returns: `200 { propertynames: T38[] }` #### Create property name — `POST /propertyname` - Params: `body: { name: str, sort?: int, exclusive?: bool }` -- Returns: `201 { propertyname: T35 }` +- Returns: `201 { propertyname: T38 }` #### Reorder property names — `PUT /propertyname/order` - Params: `body: { propertynameIds: int[] }` - Returns: `200 { ok: bool }` @@ -645,7 +653,7 @@ _none_ - Returns: `200 { ok: bool }` #### Update property name — `PATCH /propertyname/{id}` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` -- Returns: `200 { propertyname: T35 }` +- Returns: `200 { propertyname: T38 }` ### Section: /public @@ -668,17 +676,17 @@ _none_ _none_ - Returns: `200 { quotes: { id: int, number: str, type: str, entityId: int|null, date: str, createdAt?: str }[] }` #### Create quote — `POST /quote` -- Params: `body: { number: str, date: str, lines: T28[], entityId?: int|null }` -- Returns: `201 T9` +- Params: `body: { number: str, date: str, lines: T30[], entityId?: int|null }` +- Returns: `201 T10` #### Delete quote — `DELETE /quote/{id}` - Params: `id: int` - Returns: `204 void` #### Get quote — `GET /quote/{id}` - Params: `id: int` -- Returns: `200 T9` +- Returns: `200 T10` #### Update quote — `PATCH /quote/{id}` -- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T28[] }` -- Returns: `200 T9` +- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T30[] }` +- Returns: `200 T10` ### Section: /serper @@ -695,14 +703,29 @@ _none_ - `done`: `{ "total": 100 }` - `error`: `{ "error": "…" }` +### Section: /shipment + +#### Delete shipment — `DELETE /shipment/{id}` +- Params: `id: int` +- Returns: `204 void` +#### Get shipment by ID — `GET /shipment/{id}` +- Params: `id: int` +- Returns: `200 { shipment: T7 }` +#### List packages of a shipment — `GET /shipment/{id}/shipping` +- Params: `id: int` +- Returns: `200 { shippings: T33[] }` +#### Create package for a shipment — `POST /shipment/{id}/shipping` +- Params: `id: int, body: { type: str, state: str, trackingCode?: str, carrier?: str, warehouseId?: int }` +- Returns: `201 T28` + ### Section: /shipping #### Delete shipping — `DELETE /shipping/{id}` - Params: `id: int` - Returns: `204 void` #### Update shipping — `PATCH /shipping/{id}` -- Params: `id: int, body: { type?: str, state?: str, warehouseId?: int }` -- Returns: `200 { shipping: T39 }` +- Params: `id: int, body: { type?: str, state?: str, trackingCode?: str, carrier?: str, warehouseId?: int }` +- Returns: `200 T28` #### List packages of a shipping — `GET /shipping/{id}/package` - Params: `id: int` - Returns: `200 { packages: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null }[] }` @@ -720,28 +743,28 @@ _none_ #### Adjust stock manually — `POST /stock/adjust` - Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }` -- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T16[] }` +- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T17[] }` ### Section: /supplier #### List suppliers — `GET /supplier` _none_ -- Returns: `200 { suppliers: T43[] }` +- Returns: `200 { suppliers: T45[] }` #### Create supplier — `POST /supplier` - Params: `body: { name: str }` -- Returns: `201 { supplier: T43 }` +- Returns: `201 { supplier: T45 }` #### Delete supplier — `DELETE /supplier/{id}` - Params: `id: int` - Returns: `204 void` #### Update supplier — `PATCH /supplier/{id}` - Params: `id: int, body: { name: str }` -- Returns: `200 { supplier: T43 }` +- Returns: `200 { supplier: T45 }` #### List supplier articles by SKUs — `GET /supplier/{id}/articles` - Params: `id: int, query: skus?: string` -- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T46|null }[] }` +- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T48|null }[] }` #### Query supplier articles by SKUs (bulk) — `POST /supplier/{id}/articles` - Params: `id: int, body?: { skus?: str[] }` -- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T46|null }[] }` +- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T48|null }[] }` ### Section: /thumb @@ -756,10 +779,10 @@ _none_ - Returns: `204 void` #### List units — `GET /unit` _none_ -- Returns: `200 { units: T33[] }` +- Returns: `200 { units: T36[] }` #### Upsert unit — `PUT /unit` - Params: `body: { si: str, basesi: str, basesifactor: num, symbol: str, name: str }` -- Returns: `200 { unit: T33 }` +- Returns: `200 { unit: T36 }` ### Section: /v1 @@ -810,10 +833,10 @@ _none_ #### List warehouses — `GET /warehouse` _none_ -- Returns: `200 { warehouses: T47[] }` +- Returns: `200 { warehouses: T49[] }` #### Create warehouse — `POST /warehouse` - Params: `body: { name: str, sort?: int }` -- Returns: `201 T45` +- Returns: `201 T47` #### Reorder warehouses — `PUT /warehouse/order` - Params: `body: { warehouseIds: int[] }` - Returns: `200 { warehouseIds: int[] }` @@ -822,10 +845,10 @@ _none_ - Returns: `204 void` #### Get warehouse by ID — `GET /warehouse/{id}` - Params: `id: int` -- Returns: `200 T45` +- Returns: `200 T47` #### Update warehouse (partial) — `PATCH /warehouse/{id}` - Params: `id: int, body: { name?: str, sort?: int }` -- Returns: `200 T45` +- Returns: `200 T47` #### Update warehouse — `PUT /warehouse/{id}` - Params: `id: int, body: { name?: str, sort?: int }` -- Returns: `200 T45` +- Returns: `200 T47` diff --git a/docs/api/routes.md b/docs/api/routes.md index d87d964..476b7aa 100644 --- a/docs/api/routes.md +++ b/docs/api/routes.md @@ -1,6 +1,6 @@ # Dash API Routes Overview -Overview of all 163 API endpoints across all modules. +Overview of all 169 API endpoints across all modules. ### Notation @@ -9,49 +9,51 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). ### Reusable types -- `T1` = `{ article: T2 }` -- `T2` = `{ id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T30, brand: T28|null, categoryIds: int[], photos: T26[], eans: str[], prices: T36[], suppliers: T12[] }` -- `T3` = `{ order: T4 }` -- `T4` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T21[], createdAt?: str, shipping?: T35|null, shippings?: T35[], payment?: T33|null, payments?: T33[], paid?: bool, delivered?: bool }` +- `T1` = `{ order: T2 }` +- `T2` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T22[], createdAt?: str, shipping?: T30|null, shippings?: T30[], payment?: T36|null, payments?: T36[], paid?: bool, shipped?: bool, delivered?: bool, orderTotal?: num, paidTotal?: num, orderedQuantity?: num, shippedQuantity?: num, shipments?: T7[] }` +- `T3` = `{ article: T4 }` +- `T4` = `{ id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T33, brand: T31|null, categoryIds: int[], photos: T28[], eans: str[], prices: T38[], suppliers: T13[] }` - `T5` = `{ delivery: T6 }` -- `T6` = `{ id: int, number: str, type: str, supplierId: int|null, date: str, lines: T21[], files: T10[], externalNumber?: str|null, createdAt?: str, ledger?: T40 }` -- `T7` = `{ labeltemplate: T8 }` -- `T8` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T17, thumb?: str|null }` -- `T9` = `{ quote: T11 }` -- `T10` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, thumbavif?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }` -- `T11` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T21[], createdAt?: str }` -- `T12` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T42|null }` -- `T13` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` -- `T14` = `{ ledger: T15[] }` -- `T15` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash?: str, sku?: str|null, created_at?: str }` -- `T16` = `{ id: int, name: str, sort: int, exclusive: bool, values: T31[], articleCount?: int }` -- `T17` = `{ objects: T22[] }` +- `T6` = `{ id: int, number: str, type: str, supplierId: int|null, date: str, lines: T22[], files: T11[], externalNumber?: str|null, createdAt?: str, ledger?: T42 }` +- `T7` = `{ id: int, number: str, type: str, date: str, lines: T22[], shippings: T30[], entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, lineCount?: int, packageCount?: int }` +- `T8` = `{ labeltemplate: T9 }` +- `T9` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T18, thumb?: str|null }` +- `T10` = `{ quote: T12 }` +- `T11` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, thumbavif?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }` +- `T12` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T22[], createdAt?: str }` +- `T13` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T44|null }` +- `T14` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` +- `T15` = `{ ledger: T16[] }` +- `T16` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash?: str, sku?: str|null, created_at?: str }` +- `T17` = `{ id: int, name: str, sort: int, exclusive: bool, values: T34[], articleCount?: int }` - `T18` = `{ objects: T23[] }` -- `T19` = `{ printer: T24 }` -- `T20` = `{ entity: T27 }` -- `T21` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }` -- `T22` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` +- `T19` = `{ objects: T24[] }` +- `T20` = `{ printer: T26 }` +- `T21` = `{ entity: T29 }` +- `T22` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }` - `T23` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` -- `T24` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }` -- `T25` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }` -- `T26` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }` -- `T27` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }` -- `T28` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }` -- `T29` = `{ article: T34 }` -- `T30` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }` -- `T31` = `{ id: int, attributeId: int, value: str, sort: int, articleCount?: int }` -- `T32` = `{ id: int, name: str, sort: int, exclusive: bool, articleCount?: int }` -- `T33` = `{ id: int, accountId: int, manifestId: int, type: str, amount: num }` -- `T34` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }` -- `T35` = `{ id: int, manifestId: int, number: str, type: str, state: str }` -- `T36` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }` -- `T37` = `{ fullHash: str, thumbW: int, thumbH: int, thumbHash: str }` -- `T38` = `{ id: int, entityId: int, contactType: str, contact: str }` -- `T39` = `{ id: int, name: str, createdAt: str, updatedAt: str }` -- `T40` = `{ totalPositions: int, completelyOnLedgerPositions: int }` -- `T41` = `{ warehouse: T43 }` -- `T42` = `{ net: num, taxId: int, taxRate?: num|null }` -- `T43` = `{ id: int, name: str, sort: int }` +- `T24` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` +- `T25` = `{ shipping: T30 }` +- `T26` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }` +- `T27` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }` +- `T28` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }` +- `T29` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }` +- `T30` = `{ id: int, manifestId: int, number: str, type: str, state: str, trackingCode?: str|null, carrier?: str|null }` +- `T31` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }` +- `T32` = `{ article: T37 }` +- `T33` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }` +- `T34` = `{ id: int, attributeId: int, value: str, sort: int, articleCount?: int }` +- `T35` = `{ id: int, name: str, sort: int, exclusive: bool, articleCount?: int }` +- `T36` = `{ id: int, accountId: int, manifestId: int, type: str, amount: num }` +- `T37` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }` +- `T38` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }` +- `T39` = `{ fullHash: str, thumbW: int, thumbH: int, thumbHash: str }` +- `T40` = `{ id: int, entityId: int, contactType: str, contact: str }` +- `T41` = `{ id: int, name: str, createdAt: str, updatedAt: str }` +- `T42` = `{ totalPositions: int, completelyOnLedgerPositions: int }` +- `T43` = `{ warehouse: T45 }` +- `T44` = `{ net: num, taxId: int, taxRate?: num|null }` +- `T45` = `{ id: int, name: str, sort: int }` ### Routes @@ -72,16 +74,16 @@ _none_ #### Create article in category — `POST /article` - Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }` -- Returns: `201 T29` +- Returns: `201 T32` #### Delete article by SKU — `DELETE /article/{sku}` - Params: `sku: str, query: cascade?: bool` - Returns: `204 void` #### Get article by SKU — `GET /article/{sku}` - Params: `sku: str` -- Returns: `200 T1` +- Returns: `200 T3` #### Update article by SKU — `PATCH /article/{sku}` - Params: `sku: str, body: { name?: str, desc?: str, seo?: str, unitamount?: num, unitSi?: str, track_stock?: bool }` -- Returns: `200 T1` +- Returns: `200 T3` #### Get Stückliste (bundle items) for an article — `GET /article/{sku}/articlelist` - Params: `sku: str` - Returns: `200 { items: { id: int, articleId: int, sku: str, name: str, count: int, sort: int }[] }` @@ -96,25 +98,25 @@ _none_ - Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, articleId?: int, exclusive?: bool }[] }` #### Set or clear article brand by SKU (null to clear) — `PUT /article/{sku}/brand` - Params: `sku: str, body: { brandId: int|null }` -- Returns: `200 T1` +- Returns: `200 T3` #### Replace article categories by SKU — `PUT /article/{sku}/categories` - Params: `sku: str, body: { categoryIds: int[] }` -- Returns: `200 T1` +- Returns: `200 T3` #### List child articles of a parent article — `GET /article/{sku}/children` - Params: `sku: str` - Returns: `200 { children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` #### Create and attach a new child article to a parent article — `POST /article/{sku}/children` - Params: `sku: str, body: { attributeValueId?: int, value?: str, name?: str }` -- Returns: `201 T29` +- Returns: `201 T32` #### Attach an existing article as a child to a parent article — `PUT /article/{sku}/children` - Params: `sku: str, body: { sku: str, attributeValueId?: int, value?: str, reparent?: bool }` -- Returns: `200 { article: T34, children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` +- Returns: `200 { article: T37, children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` #### Remove a child article from a parent article — `DELETE /article/{sku}/children/{childSku}` - Params: `sku: str, childSku: str, query: deleteArticle?: bool` - Returns: `200 { success: bool, deleted: bool }` #### Replace article EANs by SKU — `PUT /article/{sku}/eans` - Params: `sku: str, body: { eans: str[] }` -- Returns: `200 T1` +- Returns: `200 T3` #### Get article history by SKU — `GET /article/{sku}/history` - Params: `sku: str, query: limit?: int` - Returns: `200 { history: { id: int, date: str, text: str }[], truncated: bool }` @@ -132,10 +134,10 @@ _none_ - Returns: `200 { options: { id: int, articleId: int, sku: str, name: str, count: int, sort: int, mandated: bool }[] }` #### Toggle parent mode for an article — `POST /article/{sku}/parent` - Params: `sku: str, body: { isParent: bool, attributeNameId?: int }` -- Returns: `200 T29` +- Returns: `200 T32` #### Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) — `PUT /article/{sku}/prices` - Params: `sku: str, body: { prices: { net?: num, gross?: num, price?: num, taxRate?: num, tax?: num, minmultiplier?: num }[] }` -- Returns: `200 T1` +- Returns: `200 T3` #### Print article label — `POST /article/{sku}/print` - Params: `sku: str, body: { templateId: int, printerId: int, quantity: int }` - Returns: `200 { success: bool }` @@ -150,10 +152,10 @@ _none_ - Returns: `200 { ok: bool }` #### List article suppliers — `GET /article/{sku}/supplier` - Params: `sku: str` -- Returns: `200 { suppliers: T12[] }` +- Returns: `200 { suppliers: T13[] }` #### Assign supplier to article — `POST /article/{sku}/supplier` - Params: `sku: str, body: { entityId: int, sku?: str|null, pu?: num, stock?: num|null, sort?: int|null, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }` -- Returns: `201 { supplier: T12 }` +- Returns: `201 { supplier: T13 }` #### Reorder article suppliers — `PUT /article/{sku}/supplier/order` - Params: `sku: str, body: { ids: int[] }` - Returns: `200 { ids: int[] }` @@ -162,31 +164,31 @@ _none_ - Returns: `204 void` #### Update article supplier assignment — `PATCH /article/{sku}/supplier/{id}` - Params: `sku: str, id: int, body: { sku?: str|null, pu?: num, stock?: num|null, sort?: int, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }` -- Returns: `200 { supplier: T12 }` +- Returns: `200 { supplier: T13 }` #### Set article track_stock flag by SKU — `PUT /article/{sku}/track_stock` - Params: `sku: str, body: { track_stock: bool }` -- Returns: `200 T1` +- Returns: `200 T3` #### Set article weight by SKU — `PUT /article/{sku}/weight` - Params: `sku: str, body: { weight: num }` -- Returns: `200 T1` +- Returns: `200 T3` ### Section: /attributename #### List attribute names — `GET /attributename` _none_ -- Returns: `200 { attributenames: T16[] }` +- Returns: `200 { attributenames: T17[] }` #### Create attribute name — `POST /attributename` - Params: `body: { name: str, sort?: int, exclusive?: bool }` -- Returns: `201 { attributename: T16 }` +- Returns: `201 { attributename: T17 }` #### Reorder attribute names — `PUT /attributename/order` - Params: `body: { attributenameIds: int[] }` - Returns: `200 { ok: bool }` #### List values for an attribute — `GET /attributename/{attributeId}/value` - Params: `attributeId: int` -- Returns: `200 { values: T31[] }` +- Returns: `200 { values: T34[] }` #### Create value for an attribute — `POST /attributename/{attributeId}/value` - Params: `attributeId: int, body: { value: str, sort?: int }` -- Returns: `201 { attributevalue: T31 }` +- Returns: `201 { attributevalue: T34 }` #### Reorder values of an attribute — `PUT /attributename/{attributeId}/value/order` - Params: `attributeId: int, body: { attributevalueIds: int[] }` - Returns: `200 { ok: bool }` @@ -195,7 +197,7 @@ _none_ - Returns: `200 { ok: bool }` #### Update attribute name — `PATCH /attributename/{id}` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` -- Returns: `200 { attributename: T16 }` +- Returns: `200 { attributename: T17 }` ### Section: /attributevalue @@ -204,13 +206,13 @@ _none_ - Returns: `200 { ok: bool }` #### Update attribute value — `PATCH /attributevalue/{id}` - Params: `id: int, body: { value?: str, sort?: int }` -- Returns: `200 { attributevalue: T31 }` +- Returns: `200 { attributevalue: T34 }` #### Delete attribute value image — `DELETE /attributevalue/{id}/image` - Params: `id: int` -- Returns: `200 { attributevalue: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T37[] } }` +- Returns: `200 { attributevalue: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T39[] } }` #### Add or replace attribute value image — `POST /attributevalue/{id}/image` - Params: `id: int, image/avif` -- Returns: `201 { attributevalue: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T37[] } }` +- Returns: `201 { attributevalue: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T39[] } }` ### Section: /auth @@ -228,7 +230,7 @@ _none_ - Returns: `200 { brands: { id: int, name: str, articleCount: int, pics: { fullHash: str, thumbW: int, thumbH: int, thumbHash: str, smallThumbW?: int|null, smallThumbH?: int|null, smallThumbBinary?: str|null }[], gsprpinfo?: str|null }[], total: int, offset: int, limit: int }` #### Create brand — `POST /brand` - Params: `body: { name: str, gsprpinfo?: str }` -- Returns: `201 { brand: { id: int, name: str, pics: T37[], gsprpinfo?: str|null } }` +- Returns: `201 { brand: { id: int, name: str, pics: T39[], gsprpinfo?: str|null } }` #### Delete brand — `DELETE /brand/{id}` - Params: `id: int` - Returns: `204 void` @@ -237,10 +239,10 @@ _none_ - Returns: `200 { brand: { id: int, name: str, touchedArticleCount: int, gsprpinfo?: str|null } }` #### Delete brand image — `DELETE /brand/{id}/image` - Params: `id: int` -- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T37[] } }` +- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T39[] } }` #### Add or replace brand image — `POST /brand/{id}/image` - Params: `id: int, image/avif` -- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T37[] } }` +- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T39[] } }` ### Section: /category @@ -261,7 +263,7 @@ _none_ - Returns: `200 { category: { id: int, name: str, slug: str, parentId: int, sort: int } }` #### Articles in category — `GET /category/{id}/articles` - Params: `id: int` -- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T28|null }[] }` +- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T31|null }[] }` #### List category attribute values — `GET /category/{id}/attributevalue` - Params: `id: int` - Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }` @@ -315,7 +317,7 @@ _none_ - Returns: `200 { customergroups: { id: int, name: str, sort: int, editable: bool }[] }` #### Create customer group — `POST /customergroup` - Params: `body: { name: str }` -- Returns: `201 { customergroup: T43 }` +- Returns: `201 { customergroup: T45 }` #### Reorder customer groups — `PUT /customergroup/order` - Params: `body: { customergroupIds: int[] }` - Returns: `200 { customergroupIds: int[] }` @@ -324,15 +326,15 @@ _none_ - Returns: `204 void` #### Update customer group — `PATCH /customergroup/{id}` - Params: `id: int, body: { name: str }` -- Returns: `200 { customergroup: T43 }` +- Returns: `200 { customergroup: T45 }` ### Section: /delivery #### List deliveries — `GET /delivery` _none_ -- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int|null, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T40 }[] }` +- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int|null, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T42 }[] }` #### Create delivery — `POST /delivery` -- Params: `body: { externalNumber: str, date: str, lines: T25[], supplierId?: int|null, files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` +- Params: `body: { externalNumber: str, date: str, lines: T27[], supplierId?: int|null, files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` - Returns: `201 T5` #### Delete delivery — `DELETE /delivery/{id}` - Params: `id: int` @@ -341,7 +343,7 @@ _none_ - Params: `id: int` - Returns: `200 T5` #### Update delivery — `PATCH /delivery/{id}` -- Params: `id: int, body: { externalNumber?: str|null, date?: str, supplierId?: int|null, lines?: T25[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` +- Params: `id: int, body: { externalNumber?: str|null, date?: str, supplierId?: int|null, lines?: T27[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` - Returns: `200 T5` #### Get delivery attachment file content — `GET /delivery/{id}/file/{fileId}` - Params: `id: int, fileId: int, query: download?: bool` @@ -351,46 +353,46 @@ _none_ #### List entities — `GET /entity` - Params: `query: customergroupId?: int, role?: str` -- Returns: `200 { entities: T27[] }` +- Returns: `200 { entities: T29[] }` #### Create entity — `POST /entity` - Params: `body: { customergroupId: int, name: str, firstName?: str|null, number?: str }` -- Returns: `201 T20` +- Returns: `201 T21` #### Peek next customer number — `GET /entity/next-number` _none_ - Returns: `200 { number: str }` #### List entity addresses — `GET /entity/{entityId}/address` - Params: `entityId: int` -- Returns: `200 { addresses: T13[] }` +- Returns: `200 { addresses: T14[] }` #### Create entity address — `POST /entity/{entityId}/address` - Params: `entityId: int, body: { addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` -- Returns: `201 { address: T13 }` +- Returns: `201 { address: T14 }` #### Delete entity address — `DELETE /entity/{entityId}/address/{id}` - Params: `entityId: int, id: int` - Returns: `204 void` #### Update entity address — `PATCH /entity/{entityId}/address/{id}` - Params: `entityId: int, id: int, body: { addressType?: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` -- Returns: `200 { address: T13 }` +- Returns: `200 { address: T14 }` #### List entity contacts — `GET /entity/{entityId}/contact` - Params: `entityId: int` -- Returns: `200 { contacts: T38[] }` +- Returns: `200 { contacts: T40[] }` #### Create entity contact — `POST /entity/{entityId}/contact` - Params: `entityId: int, body: { contactType: str, contact: str }` -- Returns: `201 { contact: T38 }` +- Returns: `201 { contact: T40 }` #### Delete entity contact — `DELETE /entity/{entityId}/contact/{id}` - Params: `entityId: int, id: int` - Returns: `204 void` #### Update entity contact — `PATCH /entity/{entityId}/contact/{id}` - Params: `entityId: int, id: int, body: { contactType?: str, contact?: str }` -- Returns: `200 { contact: T38 }` +- Returns: `200 { contact: T40 }` #### Delete entity — `DELETE /entity/{id}` - Params: `id: int` - Returns: `204 void` #### Get entity — `GET /entity/{id}` - Params: `id: int` -- Returns: `200 T20` +- Returns: `200 T21` #### Update entity — `PATCH /entity/{id}` - Params: `id: int, body: { name?: str, firstName?: str|null, number?: str|null }` -- Returns: `200 T20` +- Returns: `200 T21` ### Section: /labeltemplate @@ -398,35 +400,35 @@ _none_ _none_ - Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb?: str|null }[] }` #### Create label template — `POST /labeltemplate` -- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: T18 }` -- Returns: `201 T7` +- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: T19 }` +- Returns: `201 T8` #### Delete label template — `DELETE /labeltemplate/{id}` - Params: `id: int` - Returns: `204 void` #### Get label template by ID — `GET /labeltemplate/{id}` - Params: `id: int` -- Returns: `200 T7` +- Returns: `200 T8` #### Update label template (partial) — `PATCH /labeltemplate/{id}` -- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T18 }` -- Returns: `200 T7` +- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T19 }` +- Returns: `200 T8` #### Update label template — `PUT /labeltemplate/{id}` -- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T18 }` -- Returns: `200 T7` +- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T19 }` +- Returns: `200 T8` ### Section: /ledger #### Query ledger by manifest article ID or SKU — `GET /ledger` - Params: `query: manifestarticle_id?: int, sku?: str, transaction_type?: str` -- Returns: `200 T14` +- Returns: `200 T15` #### Add ledger entry — `POST /ledger` - Params: `body: { manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str }` -- Returns: `201 { ledger: T15 }` +- Returns: `201 { ledger: T16 }` #### Query ledger by manifest article ID — `GET /ledger/manifestarticle/{manifestarticleId}` - Params: `manifestarticleId: int, query: transaction_type?: str` -- Returns: `200 T14` +- Returns: `200 T15` #### Query ledger by article SKU — `GET /ledger/sku/{sku}` - Params: `sku: str, query: transaction_type?: str` -- Returns: `200 T14` +- Returns: `200 T15` ### Section: /number @@ -441,31 +443,37 @@ _none_ #### List orders — `GET /order` - Params: `query: offset?: int, limit?: int` -- Returns: `200 { orders: T4[], total: int, offset: int, limit: int }` +- Returns: `200 { orders: T2[], total: int, offset: int, limit: int }` #### Create order — `POST /order` -- Params: `body: { number: str, date: str, lines: T25[], entityId?: int|null }` -- Returns: `201 T3` +- Params: `body: { number: str, date: str, lines: T27[], entityId?: int|null }` +- Returns: `201 T1` #### Delete order — `DELETE /order/{id}` - Params: `id: int` - Returns: `204 void` #### Get order — `GET /order/{id}` - Params: `id: int` -- Returns: `200 T3` +- Returns: `200 T1` #### Update order — `PATCH /order/{id}` -- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T25[] }` -- Returns: `200 T3` +- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T27[] }` +- Returns: `200 T1` #### List payments of an order — `GET /order/{id}/payment` - Params: `id: int` -- Returns: `200 { payments: T33[] }` +- Returns: `200 { payments: T36[] }` #### Add payment to an order — `POST /order/{id}/payment` - Params: `id: int, body: { accountId: int, type: str, amount: num }` -- Returns: `201 { payment: T33 }` +- Returns: `201 { payment: T36 }` +#### List shipments of an order — `GET /order/{id}/shipment` +- Params: `id: int` +- Returns: `200 { shipments: T7[] }` +#### Create shipment for an order — `POST /order/{id}/shipment` +- Params: `id: int, body: { lines: { sku: str, amount: num, name?: str, pu?: num, net?: num, rebate?: num, taxrate?: num, comment?: str }[], date?: str, externalNumber?: str }` +- Returns: `201 { shipment: T7 }` #### List shippings of an order — `GET /order/{id}/shipping` - Params: `id: int` -- Returns: `200 { shippings: T35[] }` +- Returns: `200 { shippings: T30[] }` #### Create shipping for an order — `POST /order/{id}/shipping` - Params: `id: int, body: { type: str, state: str, warehouseId?: int }` -- Returns: `201 { shipping: T35 }` +- Returns: `201 T25` ### Section: /package @@ -483,10 +491,10 @@ _none_ #### List printers — `GET /printer` _none_ -- Returns: `200 { printers: T24[] }` +- Returns: `200 { printers: T26[] }` #### Create printer — `POST /printer` - Params: `body: { address: str, type: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` -- Returns: `201 T19` +- Returns: `201 T20` #### Reorder printers — `PUT /printer/order` - Params: `body: { printerIds: int[] }` - Returns: `200 { printerIds: int[] }` @@ -495,22 +503,22 @@ _none_ - Returns: `204 void` #### Get printer by ID — `GET /printer/{id}` - Params: `id: int` -- Returns: `200 T19` +- Returns: `200 T20` #### Update printer (partial) — `PATCH /printer/{id}` - Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` -- Returns: `200 T19` +- Returns: `200 T20` #### Update printer — `PUT /printer/{id}` - Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` -- Returns: `200 T19` +- Returns: `200 T20` ### Section: /propertyname #### List property names — `GET /propertyname` _none_ -- Returns: `200 { propertynames: T32[] }` +- Returns: `200 { propertynames: T35[] }` #### Create property name — `POST /propertyname` - Params: `body: { name: str, sort?: int, exclusive?: bool }` -- Returns: `201 { propertyname: T32 }` +- Returns: `201 { propertyname: T35 }` #### Reorder property names — `PUT /propertyname/order` - Params: `body: { propertynameIds: int[] }` - Returns: `200 { ok: bool }` @@ -519,7 +527,7 @@ _none_ - Returns: `200 { ok: bool }` #### Update property name — `PATCH /propertyname/{id}` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` -- Returns: `200 { propertyname: T32 }` +- Returns: `200 { propertyname: T35 }` ### Section: /quote @@ -527,17 +535,32 @@ _none_ _none_ - Returns: `200 { quotes: { id: int, number: str, type: str, entityId: int|null, date: str, createdAt?: str }[] }` #### Create quote — `POST /quote` -- Params: `body: { number: str, date: str, lines: T25[], entityId?: int|null }` -- Returns: `201 T9` +- Params: `body: { number: str, date: str, lines: T27[], entityId?: int|null }` +- Returns: `201 T10` #### Delete quote — `DELETE /quote/{id}` - Params: `id: int` - Returns: `204 void` #### Get quote — `GET /quote/{id}` - Params: `id: int` -- Returns: `200 T9` +- Returns: `200 T10` #### Update quote — `PATCH /quote/{id}` -- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T25[] }` -- Returns: `200 T9` +- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T27[] }` +- Returns: `200 T10` + +### Section: /shipment + +#### Delete shipment — `DELETE /shipment/{id}` +- Params: `id: int` +- Returns: `204 void` +#### Get shipment by ID — `GET /shipment/{id}` +- Params: `id: int` +- Returns: `200 { shipment: T7 }` +#### List packages of a shipment — `GET /shipment/{id}/shipping` +- Params: `id: int` +- Returns: `200 { shippings: T30[] }` +#### Create package for a shipment — `POST /shipment/{id}/shipping` +- Params: `id: int, body: { type: str, state: str, trackingCode?: str, carrier?: str, warehouseId?: int }` +- Returns: `201 T25` ### Section: /shipping @@ -545,8 +568,8 @@ _none_ - Params: `id: int` - Returns: `204 void` #### Update shipping — `PATCH /shipping/{id}` -- Params: `id: int, body: { type?: str, state?: str, warehouseId?: int }` -- Returns: `200 { shipping: T35 }` +- Params: `id: int, body: { type?: str, state?: str, trackingCode?: str, carrier?: str, warehouseId?: int }` +- Returns: `200 T25` #### List packages of a shipping — `GET /shipping/{id}/package` - Params: `id: int` - Returns: `200 { packages: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null }[] }` @@ -564,28 +587,28 @@ _none_ #### Adjust stock manually — `POST /stock/adjust` - Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }` -- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T15[] }` +- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T16[] }` ### Section: /supplier #### List suppliers — `GET /supplier` _none_ -- Returns: `200 { suppliers: T39[] }` +- Returns: `200 { suppliers: T41[] }` #### Create supplier — `POST /supplier` - Params: `body: { name: str }` -- Returns: `201 { supplier: T39 }` +- Returns: `201 { supplier: T41 }` #### Delete supplier — `DELETE /supplier/{id}` - Params: `id: int` - Returns: `204 void` #### Update supplier — `PATCH /supplier/{id}` - Params: `id: int, body: { name: str }` -- Returns: `200 { supplier: T39 }` +- Returns: `200 { supplier: T41 }` #### List supplier articles by SKUs — `GET /supplier/{id}/articles` - Params: `id: int, query: skus?: string` -- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T42|null }[] }` +- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T44|null }[] }` #### Query supplier articles by SKUs (bulk) — `POST /supplier/{id}/articles` - Params: `id: int, body?: { skus?: str[] }` -- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T42|null }[] }` +- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T44|null }[] }` ### Section: /unit @@ -594,19 +617,19 @@ _none_ - Returns: `204 void` #### List units — `GET /unit` _none_ -- Returns: `200 { units: T30[] }` +- Returns: `200 { units: T33[] }` #### Upsert unit — `PUT /unit` - Params: `body: { si: str, basesi: str, basesifactor: num, symbol: str, name: str }` -- Returns: `200 { unit: T30 }` +- Returns: `200 { unit: T33 }` ### Section: /warehouse #### List warehouses — `GET /warehouse` _none_ -- Returns: `200 { warehouses: T43[] }` +- Returns: `200 { warehouses: T45[] }` #### Create warehouse — `POST /warehouse` - Params: `body: { name: str, sort?: int }` -- Returns: `201 T41` +- Returns: `201 T43` #### Reorder warehouses — `PUT /warehouse/order` - Params: `body: { warehouseIds: int[] }` - Returns: `200 { warehouseIds: int[] }` @@ -615,13 +638,13 @@ _none_ - Returns: `204 void` #### Get warehouse by ID — `GET /warehouse/{id}` - Params: `id: int` -- Returns: `200 T41` +- Returns: `200 T43` #### Update warehouse (partial) — `PATCH /warehouse/{id}` - Params: `id: int, body: { name?: str, sort?: int }` -- Returns: `200 T41` +- Returns: `200 T43` #### Update warehouse — `PUT /warehouse/{id}` - Params: `id: int, body: { name?: str, sort?: int }` -- Returns: `200 T41` +- Returns: `200 T43` ### Binary & streaming routes (reachable only via `fetch()` in javascript_execute) These routes are NOT in the table above because their responses are binary or long-lived streams, which the `dashback_request` tool cannot return usefully. They ARE reachable from inside `javascript_execute` with `fetch("/route")`, where you can decode the bytes, write them to the VFS and embed them in your reply: diff --git a/docs/api/routes/order/_id/shipment.get.md b/docs/api/routes/order/_id/shipment.get.md new file mode 100644 index 0000000..60f3d02 --- /dev/null +++ b/docs/api/routes/order/_id/shipment.get.md @@ -0,0 +1,71 @@ +# `GET /order/{id}/shipment` + +Protected. Requires a valid session cookie (`credentials: 'include'`). + +List shipments of an order + +Lists all shipment manifests belonging to the specified order. + +## Path parameters + +| Param | Required | Type | Description | +|-------|----------|------|-------------| +| `id` | yes | integer | Order ID | + +## Response `200` + +List of shipments + +```json +{ + "shipments": [ + { + "id": 1, + "number": "…", + "type": "shipment", + "entityId": 1, + "externalNumber": "…", + "date": "…", + "createdAt": "…", + "lineCount": 1, + "packageCount": 1, + "lines": [ + { + "id": 1, + "sku": "…", + "name": "…", + "pu": 1, + "amount": 1, + "net": 1, + "rebate": 1, + "taxrate": 1, + "comment": "…" + } + ], + "shippings": [ + { + "id": 1, + "manifestId": 1, + "number": "…", + "trackingCode": "…", + "carrier": "…", + "type": "…", + "state": "…" + } + ] + } + ] +} +``` + +## Errors + +| Status | Description | +|--------|-------------| +| 400 | Invalid ID or no company selected — `{"error":"…"}` | +| 401 | Unauthorized — `{"error":"…"}` | +| 404 | Order not found — `{"error":"…"}` | + +--- + +_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/order/_id/shipment.post.md b/docs/api/routes/order/_id/shipment.post.md new file mode 100644 index 0000000..5586efb --- /dev/null +++ b/docs/api/routes/order/_id/shipment.post.md @@ -0,0 +1,108 @@ +# `POST /order/{id}/shipment` + +Protected. Requires a valid session cookie (`credentials: 'include'`). + +Create shipment for an order + +Creates a new partial or full shipment manifest for the order. +Validates that article positions belong to the order and cumulative shipped quantities +do not exceed ordered quantities. + + +## Path parameters + +| Param | Required | Type | Description | +|-------|----------|------|-------------| +| `id` | yes | integer | Order ID | + +## Body (JSON) + +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `date` | no | string | | +| `externalNumber` | no | string | | +| `lines` | yes | object[] | | +| `lines[].sku` | yes | string | | +| `lines[].amount` | yes | number | | +| `lines[].name` | no | string | | +| `lines[].pu` | no | number | | +| `lines[].net` | no | number | | +| `lines[].rebate` | no | number | | +| `lines[].taxrate` | no | number | | +| `lines[].comment` | no | string | | + +```json +{ + "date": "2026-08-10", + "externalNumber": "…", + "lines": [ + { + "sku": "…", + "amount": 1, + "name": "…", + "pu": 1, + "net": 1, + "rebate": 1, + "taxrate": 1, + "comment": "…" + } + ] +} +``` + +## Response `201` + +Created shipment + +```json +{ + "shipment": { + "id": 1, + "number": "…", + "type": "shipment", + "entityId": 1, + "externalNumber": "…", + "date": "…", + "createdAt": "…", + "lineCount": 1, + "packageCount": 1, + "lines": [ + { + "id": 1, + "sku": "…", + "name": "…", + "pu": 1, + "amount": 1, + "net": 1, + "rebate": 1, + "taxrate": 1, + "comment": "…" + } + ], + "shippings": [ + { + "id": 1, + "manifestId": 1, + "number": "…", + "trackingCode": "…", + "carrier": "…", + "type": "…", + "state": "…" + } + ] + } +} +``` + +## Errors + +| Status | Description | +|--------|-------------| +| 400 | Validation error or article not in order — `{"error":"…"}` | +| 401 | Unauthorized — `{"error":"…"}` | +| 404 | Order not found — `{"error":"…"}` | +| 409 | Cumulative shipment amount exceeds ordered amount — `{"error":"…"}` | + +--- + +_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/order/_id/shipping.get.md b/docs/api/routes/order/_id/shipping.get.md index dfa3533..1174218 100644 --- a/docs/api/routes/order/_id/shipping.get.md +++ b/docs/api/routes/order/_id/shipping.get.md @@ -23,6 +23,8 @@ List of shippings "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" } diff --git a/docs/api/routes/order/_id/shipping.post.md b/docs/api/routes/order/_id/shipping.post.md index f7c29f9..386e7ce 100644 --- a/docs/api/routes/order/_id/shipping.post.md +++ b/docs/api/routes/order/_id/shipping.post.md @@ -41,6 +41,8 @@ Created shipping "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" } diff --git a/docs/api/routes/order/create.md b/docs/api/routes/order/create.md index 3711f76..a618ce1 100644 --- a/docs/api/routes/order/create.md +++ b/docs/api/routes/order/create.md @@ -67,6 +67,8 @@ Created order "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" }, @@ -75,6 +77,8 @@ Created order "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" } @@ -96,7 +100,49 @@ Created order } ], "paid": true, - "delivered": true + "shipped": true, + "delivered": true, + "orderTotal": 1, + "paidTotal": 1, + "orderedQuantity": 1, + "shippedQuantity": 1, + "shipments": [ + { + "id": 1, + "number": "…", + "type": "shipment", + "entityId": 1, + "externalNumber": "…", + "date": "…", + "createdAt": "…", + "lineCount": 1, + "packageCount": 1, + "lines": [ + { + "id": 1, + "sku": "…", + "name": "…", + "pu": 1, + "amount": 1, + "net": 1, + "rebate": 1, + "taxrate": 1, + "comment": "…" + } + ], + "shippings": [ + { + "id": 1, + "manifestId": 1, + "number": "…", + "trackingCode": "…", + "carrier": "…", + "type": "…", + "state": "…" + } + ] + } + ] } } ``` diff --git a/docs/api/routes/order/get.md b/docs/api/routes/order/get.md index 2365c0d..ac84a6b 100644 --- a/docs/api/routes/order/get.md +++ b/docs/api/routes/order/get.md @@ -42,6 +42,8 @@ Order "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" }, @@ -50,6 +52,8 @@ Order "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" } @@ -71,7 +75,49 @@ Order } ], "paid": true, - "delivered": true + "shipped": true, + "delivered": true, + "orderTotal": 1, + "paidTotal": 1, + "orderedQuantity": 1, + "shippedQuantity": 1, + "shipments": [ + { + "id": 1, + "number": "…", + "type": "shipment", + "entityId": 1, + "externalNumber": "…", + "date": "…", + "createdAt": "…", + "lineCount": 1, + "packageCount": 1, + "lines": [ + { + "id": 1, + "sku": "…", + "name": "…", + "pu": 1, + "amount": 1, + "net": 1, + "rebate": 1, + "taxrate": 1, + "comment": "…" + } + ], + "shippings": [ + { + "id": 1, + "manifestId": 1, + "number": "…", + "trackingCode": "…", + "carrier": "…", + "type": "…", + "state": "…" + } + ] + } + ] } } ``` diff --git a/docs/api/routes/order/list.md b/docs/api/routes/order/list.md index 9a2fb9d..264856a 100644 --- a/docs/api/routes/order/list.md +++ b/docs/api/routes/order/list.md @@ -47,6 +47,8 @@ Order list "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" }, @@ -55,6 +57,8 @@ Order list "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" } @@ -76,7 +80,49 @@ Order list } ], "paid": true, - "delivered": true + "shipped": true, + "delivered": true, + "orderTotal": 1, + "paidTotal": 1, + "orderedQuantity": 1, + "shippedQuantity": 1, + "shipments": [ + { + "id": 1, + "number": "…", + "type": "shipment", + "entityId": 1, + "externalNumber": "…", + "date": "…", + "createdAt": "…", + "lineCount": 1, + "packageCount": 1, + "lines": [ + { + "id": 1, + "sku": "…", + "name": "…", + "pu": 1, + "amount": 1, + "net": 1, + "rebate": 1, + "taxrate": 1, + "comment": "…" + } + ], + "shippings": [ + { + "id": 1, + "manifestId": 1, + "number": "…", + "trackingCode": "…", + "carrier": "…", + "type": "…", + "state": "…" + } + ] + } + ] } ], "total": 1, diff --git a/docs/api/routes/order/update.md b/docs/api/routes/order/update.md index b52f4fd..69b7806 100644 --- a/docs/api/routes/order/update.md +++ b/docs/api/routes/order/update.md @@ -61,6 +61,8 @@ Updated order "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" }, @@ -69,6 +71,8 @@ Updated order "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" } @@ -90,7 +94,49 @@ Updated order } ], "paid": true, - "delivered": true + "shipped": true, + "delivered": true, + "orderTotal": 1, + "paidTotal": 1, + "orderedQuantity": 1, + "shippedQuantity": 1, + "shipments": [ + { + "id": 1, + "number": "…", + "type": "shipment", + "entityId": 1, + "externalNumber": "…", + "date": "…", + "createdAt": "…", + "lineCount": 1, + "packageCount": 1, + "lines": [ + { + "id": 1, + "sku": "…", + "name": "…", + "pu": 1, + "amount": 1, + "net": 1, + "rebate": 1, + "taxrate": 1, + "comment": "…" + } + ], + "shippings": [ + { + "id": 1, + "manifestId": 1, + "number": "…", + "trackingCode": "…", + "carrier": "…", + "type": "…", + "state": "…" + } + ] + } + ] } } ``` diff --git a/docs/api/routes/shipment/_id.delete.md b/docs/api/routes/shipment/_id.delete.md new file mode 100644 index 0000000..a525e32 --- /dev/null +++ b/docs/api/routes/shipment/_id.delete.md @@ -0,0 +1,32 @@ +# `DELETE /shipment/{id}` + +Protected. Requires a valid session cookie (`credentials: 'include'`). + +Delete shipment + +Deletes an unshipped shipment manifest. Shipped shipments cannot be deleted. + +## Path parameters + +| Param | Required | Type | Description | +|-------|----------|------|-------------| +| `id` | yes | integer | Shipment ID | + +## Response `204` + +Shipment deleted + +_No content_ + +## Errors + +| Status | Description | +|--------|-------------| +| 400 | Invalid ID or no company selected — `{"error":"…"}` | +| 401 | Unauthorized — `{"error":"…"}` | +| 404 | Shipment not found — `{"error":"…"}` | +| 409 | Shipped shipment cannot be deleted — `{"error":"…"}` | + +--- + +_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/shipment/_id.get.md b/docs/api/routes/shipment/_id.get.md new file mode 100644 index 0000000..b221703 --- /dev/null +++ b/docs/api/routes/shipment/_id.get.md @@ -0,0 +1,69 @@ +# `GET /shipment/{id}` + +Protected. Requires a valid session cookie (`credentials: 'include'`). + +Get shipment by ID + +Retrieves shipment header, article positions, and attached packages. + +## Path parameters + +| Param | Required | Type | Description | +|-------|----------|------|-------------| +| `id` | yes | integer | Shipment ID | + +## Response `200` + +Shipment details + +```json +{ + "shipment": { + "id": 1, + "number": "…", + "type": "shipment", + "entityId": 1, + "externalNumber": "…", + "date": "…", + "createdAt": "…", + "lineCount": 1, + "packageCount": 1, + "lines": [ + { + "id": 1, + "sku": "…", + "name": "…", + "pu": 1, + "amount": 1, + "net": 1, + "rebate": 1, + "taxrate": 1, + "comment": "…" + } + ], + "shippings": [ + { + "id": 1, + "manifestId": 1, + "number": "…", + "trackingCode": "…", + "carrier": "…", + "type": "…", + "state": "…" + } + ] + } +} +``` + +## Errors + +| Status | Description | +|--------|-------------| +| 400 | Invalid ID or no company selected — `{"error":"…"}` | +| 401 | Unauthorized — `{"error":"…"}` | +| 404 | Shipment not found — `{"error":"…"}` | + +--- + +_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/shipment/_id/shipping.get.md b/docs/api/routes/shipment/_id/shipping.get.md new file mode 100644 index 0000000..d15678f --- /dev/null +++ b/docs/api/routes/shipment/_id/shipping.get.md @@ -0,0 +1,45 @@ +# `GET /shipment/{id}/shipping` + +Protected. Requires a valid session cookie (`credentials: 'include'`). + +List packages of a shipment + +Lists all packages (shippings) belonging to the specified shipment manifest. + +## Path parameters + +| Param | Required | Type | Description | +|-------|----------|------|-------------| +| `id` | yes | integer | Shipment ID | + +## Response `200` + +List of packages + +```json +{ + "shippings": [ + { + "id": 1, + "manifestId": 1, + "number": "…", + "trackingCode": "…", + "carrier": "…", + "type": "…", + "state": "…" + } + ] +} +``` + +## Errors + +| Status | Description | +|--------|-------------| +| 400 | Invalid ID or no company selected — `{"error":"…"}` | +| 401 | Unauthorized — `{"error":"…"}` | +| 404 | Shipment not found — `{"error":"…"}` | + +--- + +_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/shipment/_id/shipping.post.md b/docs/api/routes/shipment/_id/shipping.post.md new file mode 100644 index 0000000..5455d53 --- /dev/null +++ b/docs/api/routes/shipment/_id/shipping.post.md @@ -0,0 +1,66 @@ +# `POST /shipment/{id}/shipping` + +Protected. Requires a valid session cookie (`credentials: 'include'`). + +Create package for a shipment + +Creates a physical package (shipping) for the shipment manifest. +Accepts carrier and parcel tracking code. If state is `shipped`, +deducts stock for the shipment's lines via ledger movements. + + +## Path parameters + +| Param | Required | Type | Description | +|-------|----------|------|-------------| +| `id` | yes | integer | Shipment ID | + +## Body (JSON) + +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `type` | yes | string | | +| `state` | yes | string | | +| `trackingCode` | no | string | Carrier parcel tracking code | +| `carrier` | no | string | Carrier name (e.g. DHL, UPS, DPD) | +| `warehouseId` | no | integer | Optional warehouse ID for stock deduction when state is shipped | + +```json +{ + "type": "…", + "state": "…", + "trackingCode": "…", + "carrier": "…", + "warehouseId": 1 +} +``` + +## Response `201` + +Created package + +```json +{ + "shipping": { + "id": 1, + "manifestId": 1, + "number": "…", + "trackingCode": "…", + "carrier": "…", + "type": "…", + "state": "…" + } +} +``` + +## Errors + +| Status | Description | +|--------|-------------| +| 400 | Validation error, manifest is not a shipment, or no company selected — `{"error":"…"}` | +| 401 | Unauthorized — `{"error":"…"}` | +| 404 | Shipment or warehouse not found — `{"error":"…"}` | + +--- + +_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/shipping/_id.patch.md b/docs/api/routes/shipping/_id.patch.md index d2e525c..72c22ce 100644 --- a/docs/api/routes/shipping/_id.patch.md +++ b/docs/api/routes/shipping/_id.patch.md @@ -20,12 +20,16 @@ via ledger and article history is written. Once shipped, it cannot be undone. |-------|----------|------|-------------| | `type` | no | string | | | `state` | no | string | | +| `trackingCode` | no | string | Carrier parcel tracking code | +| `carrier` | no | string | Carrier name (e.g. DHL, UPS, DPD) | | `warehouseId` | no | integer | Warehouse ID for stock deduction when transitioning to shipped | ```json { "type": "…", "state": "…", + "trackingCode": "…", + "carrier": "…", "warehouseId": 1 } ``` @@ -40,6 +44,8 @@ Updated shipping "id": 1, "manifestId": 1, "number": "…", + "trackingCode": "…", + "carrier": "…", "type": "…", "state": "…" } diff --git a/img2.jpg b/img2.jpg deleted file mode 100644 index 1bc2e4c4d1c6c078c35d129750e88b94ae239b93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282 zcmb7-Ne+TA7)JkEI?xcMp%$zfhy*Q05)NSE!qd3(2HwQ8u<#HLhpE+#ft_#p@(u3p zUT_2&5K00{31y6O5epH9LX9U;oT@BKmDXli` + * type: + * type: string + * enum: [shipment] + * entityId: + * type: integer + * nullable: true + * externalNumber: + * type: string + * nullable: true + * date: + * type: string + * createdAt: + * type: string + * nullable: true + * lineCount: + * type: integer + * packageCount: + * type: integer + * lines: + * type: array + * items: + * $ref: '#/components/schemas/ManifestLine' + * shippings: + * type: array + * items: + * $ref: '#/components/schemas/Shipping' * Package: * type: object * required: [id, shippingId, entityaddressId, number, type, state] diff --git a/src/api/openapiSpec.js b/src/api/openapiSpec.js index f121c74..0afbb23 100644 --- a/src/api/openapiSpec.js +++ b/src/api/openapiSpec.js @@ -36,6 +36,8 @@ export const openApiOptions = { { name: 'Delivery', description: 'Company-scoped goods receipts (Lieferungen)' }, { name: 'Quote', description: 'Company-scoped quotes (Angebote)' }, { name: 'Order', description: 'Company-scoped sales orders (Aufträge)' }, + { name: 'Shipment', description: 'Company-scoped shipment manifests (Lieferscheine)' }, + { name: 'Shipping', description: 'Shipment packages, carriers, and tracking codes' }, { name: 'Customergroup', description: 'Company-scoped customer groups' }, { name: 'Entity', description: 'Company-scoped entities, addresses, and contacts' }, { name: 'Image', description: 'Content-addressed AVIF by fullHash' }, diff --git a/src/api/routes/shipment.js b/src/api/routes/shipment.js new file mode 100644 index 0000000..9feda10 --- /dev/null +++ b/src/api/routes/shipment.js @@ -0,0 +1,386 @@ +import * as db from '../../db/shipment.js'; +import { emit } from '../sse.js'; + +// #region Helpers + +function requireSelectedCompany(req) { + const companyId = req.user?.selectedCompanyId; + if (companyId == null) { + return { error: 'No company selected' }; + } + return { companyId }; +} + +function parsePositiveInt(value, field) { + const id = Number(value); + if (!Number.isInteger(id) || id <= 0) { + return { error: `${field} is required` }; + } + return { id }; +} + +function shipmentErrorStatus(error) { + if (error.message === 'No company selected') return 400; + if (error.message === 'Not authorized') return 403; + if (error.message?.endsWith(' not found')) return 404; + if ( + error.message?.endsWith(' required') || + error.message?.startsWith('Article (') || + error.message?.includes('Invalid lines') + ) { + return 400; + } + if ( + error.message?.startsWith('Shipment amount exceeds') || + error.message?.includes('cannot be deleted') || + error.message?.includes('cannot be undone') || + error.message?.includes('already shipped') + ) { + return 409; + } + return 500; +} + +function emitShipmentEvents(companyId) { + emit('order', { companyId }); + emit('shipment', { companyId }); +} + +// #endregion + +/** + * Registers shipment routes (shipments belong to an order). + * @param {import('express').Application} app + * @param {string} [basePath=''] + */ +export function register(app, basePath = '') { + /** + * @openapi + * /order/{id}/shipment: + * get: + * tags: [Shipment] + * summary: List shipments of an order + * description: Lists all shipment manifests belonging to the specified order. + * security: + * - cookieAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: Order ID + * responses: + * 200: + * description: List of shipments + * content: + * application/json: + * schema: + * type: object + * properties: + * shipments: + * type: array + * items: + * $ref: '#/components/schemas/Shipment' + * 400: + * description: Invalid ID or no company selected + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 401: + * description: Unauthorized + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 404: + * description: Order not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + */ + // #region GET /order/:id/shipment + + app.get(`${basePath}/order/:id/shipment`, async (req, res) => { + const selected = requireSelectedCompany(req); + if (selected.error) { + return res.status(400).json({ error: selected.error }); + } + + const parsedId = parsePositiveInt(req.params.id, 'id'); + if (parsedId.error) { + return res.status(400).json({ error: parsedId.error }); + } + + try { + const shipments = await db.listByOrderId(req.user.id, parsedId.id); + res.status(200).json({ shipments }); + } catch (error) { + const status = shipmentErrorStatus(error); + if (status === 500) console.error('List shipments failed:', error); + res.status(status).json({ error: error.message }); + } + }); + + // #endregion + + /** + * @openapi + * /order/{id}/shipment: + * post: + * tags: [Shipment] + * summary: Create shipment for an order + * description: | + * Creates a new partial or full shipment manifest for the order. + * Validates that article positions belong to the order and cumulative shipped quantities + * do not exceed ordered quantities. + * security: + * - cookieAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: Order ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [lines] + * properties: + * date: + * type: string + * format: date + * externalNumber: + * type: string + * lines: + * type: array + * items: + * type: object + * required: [sku, amount] + * properties: + * sku: + * type: string + * amount: + * type: number + * name: + * type: string + * pu: + * type: number + * net: + * type: number + * rebate: + * type: number + * taxrate: + * type: number + * comment: + * type: string + * responses: + * 201: + * description: Created shipment + * content: + * application/json: + * schema: + * type: object + * properties: + * shipment: + * $ref: '#/components/schemas/Shipment' + * 400: + * description: Validation error or article not in order + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 401: + * description: Unauthorized + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 404: + * description: Order not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 409: + * description: Cumulative shipment amount exceeds ordered amount + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + */ + // #region POST /order/:id/shipment + + app.post(`${basePath}/order/:id/shipment`, async (req, res) => { + const selected = requireSelectedCompany(req); + if (selected.error) { + return res.status(400).json({ error: selected.error }); + } + + const parsedId = parsePositiveInt(req.params.id, 'id'); + if (parsedId.error) { + return res.status(400).json({ error: parsedId.error }); + } + + try { + const shipment = await db.create(req.user.id, { + ...(req.body ?? {}), + orderId: parsedId.id, + }); + emitShipmentEvents(selected.companyId); + res.status(201).json({ shipment }); + } catch (error) { + const status = shipmentErrorStatus(error); + if (status === 500) console.error('Create shipment failed:', error); + res.status(status).json({ error: error.message }); + } + }); + + // #endregion + + /** + * @openapi + * /shipment/{id}: + * get: + * tags: [Shipment] + * summary: Get shipment by ID + * description: Retrieves shipment header, article positions, and attached packages. + * security: + * - cookieAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: Shipment ID + * responses: + * 200: + * description: Shipment details + * content: + * application/json: + * schema: + * type: object + * properties: + * shipment: + * $ref: '#/components/schemas/Shipment' + * 400: + * description: Invalid ID or no company selected + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 401: + * description: Unauthorized + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 404: + * description: Shipment not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + */ + // #region GET /shipment/:id + + app.get(`${basePath}/shipment/:id`, async (req, res) => { + const selected = requireSelectedCompany(req); + if (selected.error) { + return res.status(400).json({ error: selected.error }); + } + + const parsedId = parsePositiveInt(req.params.id, 'id'); + if (parsedId.error) { + return res.status(400).json({ error: parsedId.error }); + } + + try { + const shipment = await db.getById(req.user.id, parsedId.id); + res.status(200).json({ shipment }); + } catch (error) { + const status = shipmentErrorStatus(error); + if (status === 500) console.error('Get shipment failed:', error); + res.status(status).json({ error: error.message }); + } + }); + + // #endregion + + /** + * @openapi + * /shipment/{id}: + * delete: + * tags: [Shipment] + * summary: Delete shipment + * description: Deletes an unshipped shipment manifest. Shipped shipments cannot be deleted. + * security: + * - cookieAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: Shipment ID + * responses: + * 204: + * description: Shipment deleted + * 400: + * description: Invalid ID or no company selected + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 401: + * description: Unauthorized + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 404: + * description: Shipment not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 409: + * description: Shipped shipment cannot be deleted + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + */ + // #region DELETE /shipment/:id + + app.delete(`${basePath}/shipment/:id`, async (req, res) => { + const selected = requireSelectedCompany(req); + if (selected.error) { + return res.status(400).json({ error: selected.error }); + } + + const parsedId = parsePositiveInt(req.params.id, 'id'); + if (parsedId.error) { + return res.status(400).json({ error: parsedId.error }); + } + + try { + await db.remove(req.user.id, parsedId.id); + emitShipmentEvents(selected.companyId); + res.status(204).end(); + } catch (error) { + const status = shipmentErrorStatus(error); + if (status === 500) console.error('Delete shipment failed:', error); + res.status(status).json({ error: error.message }); + } + }); + + // #endregion +} diff --git a/src/api/routes/shipping.js b/src/api/routes/shipping.js index 9f9816d..5a7fa9f 100644 --- a/src/api/routes/shipping.js +++ b/src/api/routes/shipping.js @@ -23,7 +23,12 @@ function shippingErrorStatus(error) { if (error.message === 'No company selected') return 400; if (error.message === 'Not authorized') return 403; if (error.message?.endsWith(' not found')) return 404; - if (error.message?.endsWith(' required')) return 400; + if ( + error.message?.endsWith(' required') || + error.message?.includes('not a shipment') + ) { + return 400; + } if ( error.message?.includes('cannot be deleted') || error.message?.includes('cannot be undone') || @@ -227,6 +232,178 @@ export function register(app, basePath = '') { // #endregion + /** + * @openapi + * /shipment/{id}/shipping: + * get: + * tags: [Shipping] + * summary: List packages of a shipment + * description: Lists all packages (shippings) belonging to the specified shipment manifest. + * security: + * - cookieAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: Shipment ID + * responses: + * 200: + * description: List of packages + * content: + * application/json: + * schema: + * type: object + * properties: + * shippings: + * type: array + * items: + * $ref: '#/components/schemas/Shipping' + * 400: + * description: Invalid ID or no company selected + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 401: + * description: Unauthorized + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 404: + * description: Shipment not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + */ + // #region GET /shipment/:id/shipping + + app.get(`${basePath}/shipment/:id/shipping`, async (req, res) => { + const selected = requireSelectedCompany(req); + if (selected.error) { + return res.status(400).json({ error: selected.error }); + } + + const parsedId = parsePositiveInt(req.params.id, 'id'); + if (parsedId.error) { + return res.status(400).json({ error: parsedId.error }); + } + + try { + const shippings = await db.listByManifestId(req.user.id, parsedId.id); + res.status(200).json({ shippings }); + } catch (error) { + const status = shippingErrorStatus(error); + if (status === 500) console.error('List shipment packages failed:', error); + res.status(status).json({ error: error.message }); + } + }); + + // #endregion + + /** + * @openapi + * /shipment/{id}/shipping: + * post: + * tags: [Shipping] + * summary: Create package for a shipment + * description: | + * Creates a physical package (shipping) for the shipment manifest. + * Accepts carrier and parcel tracking code. If state is `shipped`, + * deducts stock for the shipment's lines via ledger movements. + * security: + * - cookieAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: Shipment ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [type, state] + * properties: + * type: + * type: string + * state: + * type: string + * trackingCode: + * type: string + * description: Carrier parcel tracking code + * carrier: + * type: string + * description: Carrier name (e.g. DHL, UPS, DPD) + * warehouseId: + * type: integer + * description: Optional warehouse ID for stock deduction when state is shipped + * responses: + * 201: + * description: Created package + * content: + * application/json: + * schema: + * type: object + * properties: + * shipping: + * $ref: '#/components/schemas/Shipping' + * 400: + * description: Validation error, manifest is not a shipment, or no company selected + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 401: + * description: Unauthorized + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * 404: + * description: Shipment or warehouse not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + */ + // #region POST /shipment/:id/shipping + + app.post(`${basePath}/shipment/:id/shipping`, async (req, res) => { + const selected = requireSelectedCompany(req); + if (selected.error) { + return res.status(400).json({ error: selected.error }); + } + + const parsedId = parsePositiveInt(req.params.id, 'id'); + if (parsedId.error) { + return res.status(400).json({ error: parsedId.error }); + } + + try { + const shipping = await db.create(req.user.id, { + ...(req.body ?? {}), + manifestId: parsedId.id, + }); + if (String(shipping.state || '').trim().toLowerCase() === 'shipped') { + emitShippingEvents(selected.companyId); + } + res.status(201).json({ shipping }); + } catch (error) { + const status = shippingErrorStatus(error); + if (status === 500) console.error('Create shipment package failed:', error); + res.status(status).json({ error: error.message }); + } + }); + + // #endregion + /** * @openapi * /shipping/{id}: @@ -256,6 +433,12 @@ export function register(app, basePath = '') { * type: string * state: * type: string + * trackingCode: + * type: string + * description: Carrier parcel tracking code + * carrier: + * type: string + * description: Carrier name (e.g. DHL, UPS, DPD) * warehouseId: * type: integer * description: Warehouse ID for stock deduction when transitioning to shipped diff --git a/src/db/order.js b/src/db/order.js index 345126d..12db865 100644 --- a/src/db/order.js +++ b/src/db/order.js @@ -1,22 +1,84 @@ import { getStore } from './store.js'; +function round2(num) { + return Math.round((Number(num) || 0) * 100) / 100; +} + +function calculateOrderGrossTotal(lines = []) { + let total = 0; + for (const line of lines) { + const amount = Number(line.amount || 0); + const net = Number(line.net || 0); + const rebate = Number(line.rebate || 0); + const taxrate = Number(line.taxrate || 0); + const netAfterRebate = net * (1 - rebate / 100); + const gross = netAfterRebate * (1 + taxrate / 100); + total += amount * gross; + } + return round2(total); +} + +function calculatePaidTotal(payments = []) { + let total = 0; + for (const payment of payments) { + total += Number(payment.amount || 0); + } + return round2(total); +} + +function calculateOrderedQuantity(lines = []) { + let qty = 0; + for (const line of lines) { + qty += Number(line.amount || 0); + } + return Math.round(qty * 1000) / 1000; +} + +function calculateShippedQuantity(shipments = []) { + let qty = 0; + for (const shipment of shipments) { + for (const line of shipment.lines || []) { + qty += Number(line.amount || 0); + } + } + return Math.round(qty * 1000) / 1000; +} + async function enrichSingleOrder(store, googleAuthId, order) { if (!order) return order; - const [shippings, payments] = await Promise.all([ + const [shippings, payments, shipments] = await Promise.all([ store.shipping.listByManifestId(googleAuthId, order.id).catch(() => []), store.payment.listByManifestId(googleAuthId, order.id).catch(() => []), + store.shipment.listByOrderId(googleAuthId, order.id).catch(() => []), ]); - const isPaid = Array.isArray(payments) && payments.length > 0; - const isDelivered = Array.isArray(shippings) && shippings.some((s) => s.state === 'shipped' || s.state === 'delivered'); + const orderLines = Array.isArray(order.lines) ? order.lines : []; + const paymentsList = Array.isArray(payments) ? payments : []; + const shipmentsList = Array.isArray(shipments) ? shipments : []; + const shippingsList = Array.isArray(shippings) ? shippings : []; + + const orderTotal = calculateOrderGrossTotal(orderLines); + const paidTotal = calculatePaidTotal(paymentsList); + const orderedQuantity = calculateOrderedQuantity(orderLines); + const shippedQuantity = calculateShippedQuantity(shipmentsList); + + const isPaid = orderTotal > 0 && Math.abs(paidTotal - orderTotal) < 0.01; + const isShipped = orderedQuantity > 0 && Math.abs(shippedQuantity - orderedQuantity) < 0.001; + const isDelivered = isShipped || shippingsList.some((s) => s.state === 'shipped' || s.state === 'delivered'); return { ...order, - shipping: shippings[0] ?? null, - shippings: shippings ?? [], - payment: payments[0] ?? null, - payments: payments ?? [], + shipping: shippingsList[0] ?? (shipmentsList[0]?.shippings?.[0] ?? null), + shippings: shippingsList.length > 0 ? shippingsList : shipmentsList.flatMap((s) => s.shippings || []), + payment: paymentsList[0] ?? null, + payments: paymentsList, + shipments: shipmentsList, + orderTotal, + paidTotal, + orderedQuantity, + shippedQuantity, paid: isPaid, + shipped: isShipped, delivered: isDelivered, }; } diff --git a/src/db/shipment.js b/src/db/shipment.js new file mode 100644 index 0000000..812937d --- /dev/null +++ b/src/db/shipment.js @@ -0,0 +1,21 @@ +import { getStore } from './store.js'; + +export async function create(googleAuthId, data) { + const store = await getStore(); + return store.shipment.create(googleAuthId, data); +} + +export async function getById(googleAuthId, id) { + const store = await getStore(); + return store.shipment.getById(googleAuthId, id); +} + +export async function listByOrderId(googleAuthId, orderId) { + const store = await getStore(); + return store.shipment.listByOrderId(googleAuthId, orderId); +} + +export async function remove(googleAuthId, id) { + const store = await getStore(); + return store.shipment.delete(googleAuthId, id); +} diff --git a/src/services/companyImport/orderImporter.js b/src/services/companyImport/orderImporter.js index 99193be..4abe78c 100644 --- a/src/services/companyImport/orderImporter.js +++ b/src/services/companyImport/orderImporter.js @@ -62,6 +62,19 @@ export async function importOrders({ defaultCustomerGroupId = groups[0]?.id || null; } catch {} + let defaultWarehouseId = null; + try { + const warehouses = await store.warehouse.list(googleAuthId); + if (warehouses && warehouses.length > 0) { + defaultWarehouseId = warehouses[0].id; + } else { + const wh = await store.warehouse.create(googleAuthId, { name: 'Standardlager' }); + defaultWarehouseId = wh.id; + } + } catch (err) { + log.warn('Could not ensure warehouse:', err.message); + } + const entityMap = new Map(); try { const entities = await store.entity.list(googleAuthId); @@ -220,19 +233,37 @@ export async function importOrders({ // Handle Delivery / Shipping const shippingType = cleanValue(m.shippingType); - if (shippingType) { - const isShipped = String(m.shipped ?? '').trim() === '1'; - const shippingState = isShipped ? 'shipped' : 'open'; + const isShipped = String(m.shipped ?? '').trim() === '1'; - try { - await store.shipping.create(googleAuthId, { - manifestId: createdOrder.id, - type: shippingType, - state: shippingState, - }); - shippingsImported++; - } catch (err) { - log.warn(`Failed to create shipping for order ${orderNumber}:`, err.message); + if (isShipped || shippingType) { + const shippableLines = lines + .filter((l) => l.sku) + .map((l) => ({ sku: l.sku, amount: l.amount })); + + if (shippableLines.length > 0) { + try { + const shipment = await store.shipment.create(googleAuthId, { + orderId: createdOrder.id, + date: validDate, + lines: shippableLines, + }); + + const trackingCode = cleanValue(m.trackingCode ?? m.tracking_code ?? m.cTracking); + const carrier = cleanValue(m.carrier ?? m.cCarrier); + const shippingState = isShipped ? 'shipped' : 'draft'; + + await store.shipping.create(googleAuthId, { + manifestId: shipment.id, + type: shippingType || 'standard', + state: shippingState, + trackingCode: trackingCode || undefined, + carrier: carrier || undefined, + warehouseId: defaultWarehouseId || undefined, + }); + shippingsImported++; + } catch (err) { + log.warn(`Failed to create shipment/shipping for order ${orderNumber}:`, err.message); + } } } }