Subversion Repositories SmartDukaan

Rev

Rev 35777 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
35503 vikas 1
package com.spice.profitmandi.dao.service.shopify;
2
 
3
import com.fasterxml.jackson.databind.JsonNode;
4
import com.fasterxml.jackson.databind.ObjectMapper;
5
import com.spice.profitmandi.common.enumuration.SchemeType;
6
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
7
import com.spice.profitmandi.common.web.client.RestClient;
8
import com.spice.profitmandi.dao.entity.dtr.WebOffer;
9
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
10
import com.spice.profitmandi.dao.repository.catalog.ItemRepository;
11
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
12
import com.spice.profitmandi.dao.repository.dtr.WebOfferRepository;
13
import com.spice.profitmandi.dao.repository.fofo.CurrentInventorySnapshotRepository;
14
import com.spice.profitmandi.service.inventory.FofoAvailabilityInfo;
15
import com.spice.profitmandi.service.inventory.FofoCatalogResponse;
16
import com.spice.profitmandi.service.inventory.SaholicInventoryService;
17
import org.apache.commons.lang.StringUtils;
18
import org.apache.logging.log4j.LogManager;
19
import org.apache.logging.log4j.Logger;
20
import org.json.JSONArray;
21
import org.json.JSONObject;
22
import org.springframework.beans.factory.annotation.Autowired;
23
import org.springframework.beans.factory.annotation.Value;
24
import org.springframework.stereotype.Service;
25
 
26
import java.util.*;
27
import java.util.stream.Collectors;
28
 
29
@Service
30
public class ShopifyService {
31
 
32
    private static final Logger logger = LogManager.getLogger(ShopifyService.class);
33
 
34
    @Value("${new.solr.url}")
35
    private String solrUrl;
36
 
37
    @Autowired
38
    ObjectMapper objectMapper = new ObjectMapper();
39
    @Autowired
40
    ShopifyGraphQLService shopifyGraphQLService;
41
    @Autowired
42
    RestClient restClient;
43
    @Autowired
44
    FofoStoreRepository fofoStoreRepository;
45
    @Autowired
46
    ItemRepository itemRepository;
47
    @Autowired
48
    WebOfferRepository webOfferRepository;
49
    @Autowired
50
    CurrentInventorySnapshotRepository currentInventorySnapshotRepository;
51
    @Autowired
52
    SaholicInventoryService saholicInventoryService;
53
 
54
    // -------------------------------
55
    // ✅ 1. Create Product (with duplicate check)
56
    // -------------------------------
57
    public JsonNode createProduct(Map<String, Object> productInput) {
58
        try {
59
            return shopifyGraphQLService.productCreate(productInput);
60
        } catch (Exception e) {
61
            logger.error("createProduct Error: {}", e.getMessage());
62
            return objectMapper.createObjectNode();
63
        }
64
    }
65
 
66
    // -------------------------------
67
    // ✅ 2. Get All Products
68
    // -------------------------------
69
    public JsonNode getAllProducts() {
70
        try {
71
            String query = "query { products(first: 50) { edges { node { id title handle } } } }";
72
            return shopifyGraphQLService.execute(query, null);
73
        } catch (Exception e) {
74
            logger.error("getAllProducts Error: {}", e.getMessage());
75
            return objectMapper.createObjectNode();
76
        }
77
    }
78
 
79
    // -------------------------------
80
    // ✅ 3. Get Product by ID
81
    // -------------------------------
82
    public JsonNode getProductById(String productId) {
83
        try {
84
            String query = "query($id: ID!) { product(id: $id) { id title handle } }";
85
            Map<String, Object> vars = new HashMap<String, Object>();
86
            vars.put("id", productId);
87
            return shopifyGraphQLService.execute(query, vars);
88
        } catch (Exception e) {
89
            logger.error("getProductById Error: {}", e.getMessage());
90
            return objectMapper.createObjectNode();
91
        }
92
    }
93
 
94
    // -------------------------------
95
    // ✅ 4. Update Product by ID
96
    // -------------------------------
97
    public JsonNode updateProductById(String productId, Map<String, Object> params) {
98
        try {
99
            params.put("id", productId);
100
            String mutation = "mutation($input: ProductInput!) { productUpdate(input: $input) { product { id title } userErrors { field message } } }";
101
            Map<String, Object> vars = new HashMap<String, Object>();
102
            vars.put("input", params);
103
            return shopifyGraphQLService.execute(mutation, vars);
104
        } catch (Exception e) {
105
            logger.error("updateProductById Error: {}", e.getMessage());
106
            return objectMapper.createObjectNode();
107
        }
108
    }
109
 
110
    // -------------------------------
111
    // ✅ 5. Delete Product by ID
112
    // -------------------------------
113
    public JsonNode deleteProductById(String productId) {
114
        try {
115
            String mutation = "mutation($input: ProductDeleteInput!) { productDelete(input: $input) { deletedProductId userErrors { field message } } }";
116
            Map<String, Object> vars = new HashMap<String, Object>();
117
            Map<String, Object> input = new HashMap<String, Object>();
118
            input.put("id", productId);
119
            vars.put("input", input);
120
            return shopifyGraphQLService.execute(mutation, vars);
121
        } catch (Exception e) {
122
            logger.error("deleteProductById Error: {}", e.getMessage());
123
            return objectMapper.createObjectNode();
124
        }
125
    }
126
 
127
    public JsonNode getAllLocations() {
128
        try {
129
            String query = "query { locations(first: 10) { edges { node { id name } } } }";
130
            return shopifyGraphQLService.execute(query, null);
131
        } catch (Exception e) {
132
            logger.error("getAllLocations Error: {}", e.getMessage());
133
            return objectMapper.createObjectNode();
134
        }
135
    }
136
 
137
    public String getPrimaryLocationId() {
138
        try {
139
            JsonNode locationsResponse = getAllLocations();
140
            JsonNode edges = locationsResponse
141
                    .path("locations")
142
                    .path("edges");
143
 
35791 vikas 144
            if (edges.isArray() && edges.size() > 0) {
35503 vikas 145
                String locationId = edges.get(0).path("node").path("id").asText();
146
                logger.info("✅ Using Shopify Location ID: {}", locationId);
147
                return locationId;
148
            } else {
149
                logger.warn("⚠️ No Shopify locations found.");
150
                return null;
151
            }
152
        } catch (Exception e) {
153
            logger.error("getPrimaryLocationId Error: {}", e.getMessage());
154
            return null;
155
        }
156
    }
157
 
158
    public List<Map<String, Object>> buildAllVariantsBulk(List<FofoAvailabilityInfo> items, List<Map<String, String>> variantAttrs) {
159
 
160
        List<Map<String, Object>> list = new ArrayList<>();
161
 
162
        for (FofoAvailabilityInfo item : items) {
163
            String color = item.getColor().trim();
164
            String price = String.valueOf(item.getSellingPrice());
165
            String compareAt = String.valueOf(item.getMrp());
166
 
167
            for (Map<String, String> attr : variantAttrs) {
168
                String storageValue = attr.get("variantName").trim();
169
                String sku = String.valueOf(attr.get("catalogId"));
170
                list.add(buildVariant(color, storageValue, price, compareAt, sku));
171
            }
172
        }
173
 
174
        return list;
175
    }
176
 
177
    public Map<String, JsonNode> fetchVariantMapByOptions(String productId) throws Exception {
178
        String query =
179
            "query getVariants($id: ID!) { " +
180
                "  product(id: $id) { " +
181
                "    variants(first: 100) { " +
182
                "      edges { " +
183
                "        node { " +
184
                "          id " +
185
                "          title " +
186
                "          sku " +
187
                "          inventoryItem { id } " +
188
                "          selectedOptions { name value } " +
189
                "          image { originalSrc } " +
190
                "        } " +
191
                "      } " +
192
                "    } " +
193
                "  } " +
194
                "}";
195
 
196
        Map<String,Object> vars = new HashMap<>();
197
        vars.put("id", productId);
198
 
199
        JsonNode data = shopifyGraphQLService.execute(query, vars);
200
        JsonNode edges = data.path("product").path("variants").path("edges");
201
 
202
        Map<String, JsonNode> map = new HashMap<>();
203
 
204
        for (JsonNode edge : edges) {
205
            JsonNode node = edge.path("node");
206
 
35777 vikas 207
            String color = "", variant = "", insurance = "";
35503 vikas 208
 
209
            for (JsonNode opt : node.path("selectedOptions")) {
210
                String name = opt.path("name").asText();
211
                String value = opt.path("value").asText();
212
                if ("Color".equalsIgnoreCase(name)) color = value;
35777 vikas 213
                if ("Variant".equalsIgnoreCase(name)) variant = value;
214
                if ("Insurance".equalsIgnoreCase(name)) insurance = value;
35503 vikas 215
            }
216
 
35777 vikas 217
            String key = color + "|" + variant + "|" + insurance;
35503 vikas 218
            map.put(key.trim(), node);
219
        }
220
 
221
        return map;
222
    }
223
 
224
    public List<JsonNode> getAllOrders() {
225
        List<JsonNode> allOrders = new ArrayList<>();
226
        try {
227
            String cursor = null;
228
            while (true) {
229
                JsonNode page = shopifyGraphQLService.getOrders(cursor);
230
                JsonNode ordersNode = page.path("orders");
231
                JsonNode edges = ordersNode.path("edges");
232
                for (JsonNode edge : edges) {
233
                    allOrders.add(edge.path("node"));
234
                }
235
                boolean hasNext = ordersNode.path("pageInfo").path("hasNextPage").asBoolean();
236
                if (!hasNext) break;
237
                cursor = ordersNode.path("pageInfo").path("endCursor").asText();
238
            }
239
        } catch (Exception e) {
240
            logger.error("getAllOrders Error: {}", e.getMessage());
241
        }
242
        return allOrders;
243
    }
244
 
245
    public JsonNode getOrderDetailById(String id) {
246
        String graphQLId = "gid://shopify/Order/" + id;
247
        JsonNode order = null;
248
        try {
249
            JsonNode orderNode = shopifyGraphQLService.getOrderById(graphQLId);
250
            order = orderNode.path("order");
251
        } catch (Exception e) {
252
            logger.error("getAllOrders Error: {}", e.getMessage());
253
        }
254
 
255
        return order;
256
    }
257
 
258
    public List<FofoCatalogResponse> getAllDocs(FofoStore fofoStore) throws Exception {
259
 
260
        Map<String, String> params = new HashMap<String, String>();
261
        List<String> mandatoryQ = new ArrayList<String>();
262
        mandatoryQ.add("+active_b:true AND show_default_b:true");
263
 
264
        params.put("q", StringUtils.join(mandatoryQ, " "));
265
        params.put("fl", "*, [child parentFilter=id:catalog* childFilter=active_b:true ]");
266
        params.put("wt", "json");
267
        params.put("rows", "10");
268
 
269
        String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
270
        JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
271
        JSONArray docs = solrResponseJSONObj.getJSONArray("docs");
272
 
273
        return getCatalogResponse(docs, fofoStore.getId());
274
 
275
    }
276
 
35777 vikas 277
    public List<FofoCatalogResponse> getAllDocsPaginated(FofoStore fofoStore, int batchSize) throws Exception {
278
        List<FofoCatalogResponse> allProducts = new ArrayList<>();
279
        int start = 0;
280
 
281
        while (true) {
282
            Map<String, String> params = new HashMap<>();
283
            params.put("q", "+active_b:true AND show_default_b:true");
284
            params.put("fl", "*, [child parentFilter=id:catalog* childFilter=active_b:true ]");
285
            params.put("wt", "json");
286
            params.put("rows", String.valueOf(batchSize));
287
            params.put("start", String.valueOf(start));
288
 
289
            String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
290
            JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
291
            int numFound = solrResponseJSONObj.getInt("numFound");
292
            JSONArray docs = solrResponseJSONObj.getJSONArray("docs");
293
 
294
            if (docs.length() == 0) break;
295
 
296
            List<FofoCatalogResponse> batch = getCatalogResponse(docs, fofoStore.getId());
297
            allProducts.addAll(batch);
298
 
299
            start += batchSize;
300
            if (start >= numFound) break;
301
 
302
            logger.info("Fetched {} / {} products from Solr", Math.min(start, numFound), numFound);
303
        }
304
 
305
        logger.info("Total products fetched from Solr: {}", allProducts.size());
306
        return allProducts;
307
    }
308
 
309
    public List<FofoCatalogResponse> getAllDocsPaginatedForWarehouse(int batchSize) throws Exception {
310
        List<FofoCatalogResponse> allProducts = new ArrayList<>();
311
        int start = 0;
312
 
313
        while (true) {
314
            Map<String, String> params = new HashMap<>();
315
            params.put("q", "+active_b:true AND show_default_b:true");
316
            params.put("fl", "*, [child parentFilter=id:catalog* childFilter=active_b:true ]");
317
            params.put("wt", "json");
318
            params.put("rows", String.valueOf(batchSize));
319
            params.put("start", String.valueOf(start));
320
 
321
            String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
322
            JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
323
            int numFound = solrResponseJSONObj.getInt("numFound");
324
            JSONArray docs = solrResponseJSONObj.getJSONArray("docs");
325
 
326
            if (docs.length() == 0) break;
327
 
328
            List<FofoCatalogResponse> batch = getCatalogResponse(docs, 0);
329
            allProducts.addAll(batch);
330
 
331
            start += batchSize;
332
            if (start >= numFound) break;
333
 
334
            logger.info("Fetched {} / {} products from Solr (warehouse)", Math.min(start, numFound), numFound);
335
        }
336
 
337
        logger.info("Total products fetched from Solr (warehouse): {}", allProducts.size());
338
        return allProducts;
339
    }
340
 
35503 vikas 341
    private List<FofoCatalogResponse> getCatalogResponse(JSONArray docs, int fofoId) throws ProfitMandiBusinessException {
342
        Map<Integer, Integer> ourItemAvailabilityMap = null;
35777 vikas 343
        Map<Integer, Integer> partnerStockAvailabilityMap = new HashMap<>();
35503 vikas 344
        List<FofoCatalogResponse> dealResponse = new ArrayList<>();
345
        if (docs.length() > 0) {
346
            HashSet<Integer> itemsSet = new HashSet<>();
347
            for (int i = 0; i < docs.length(); i++) {
348
                JSONObject doc = docs.getJSONObject(i);
349
                if (doc.has("_childDocuments_")) {
350
                    for (int j = 0; j < doc.getJSONArray("_childDocuments_").length(); j++) {
351
                        JSONObject childItem = doc.getJSONArray("_childDocuments_").getJSONObject(j);
352
                        int itemId = childItem.getInt("itemId_i");
353
                        itemsSet.add(itemId);
354
                    }
355
                }
356
            }
357
            if (itemsSet.isEmpty()) {
358
                return dealResponse;
359
            }
360
            if (fofoId > 0) {
361
                partnerStockAvailabilityMap = currentInventorySnapshotRepository.selectItemsStock(fofoId).stream().collect(Collectors.toMap(x -> x.getItemId(), x -> x.getAvailability()));
362
            }
363
            ourItemAvailabilityMap = saholicInventoryService.getTotalAvailabilityByItemIds(new ArrayList<>(itemsSet));
364
        }
365
 
366
        for (int i = 0; i < docs.length(); i++) {
367
            Map<Integer, FofoAvailabilityInfo> fofoAvailabilityInfoMap = new HashMap<>();
368
            JSONObject doc = docs.getJSONObject(i);
369
            FofoCatalogResponse ffdr = new FofoCatalogResponse();
370
            ffdr.setCatalogId(doc.getInt("catalogId_i"));
371
            ffdr.setImageUrl(doc.getString("imageUrl_s"));
372
            ffdr.setTitle(doc.getString("title_s"));
373
            ffdr.setSuperCatalogTitle(doc.getString("superCatalog_s"));
374
            ffdr.setSuperCatalogId(doc.getInt("superCatalog_i"));
375
            ffdr.setSuperCatalogVariants(doc.getString("superCatalogVariants_s"));
376
            List<WebOffer> webOffers = webOfferRepository.selectAllActiveOffers().get(ffdr.getCatalogId());
377
            if (webOffers != null && webOffers.size() > 0) {
378
                ffdr.setOffers(webOffers.stream().map(x -> x.getTitle()).collect(Collectors.toList()));
379
            }
380
            try {
381
                ffdr.setFeature(doc.getString("feature_s"));
382
            } catch (Exception e) {
383
                ffdr.setFeature(null);
384
            }
385
            ffdr.setBrand(doc.getJSONArray("brand_ss").getString(0));
386
 
387
            if (doc.has("_childDocuments_")) {
388
                for (int j = 0; j < doc.getJSONArray("_childDocuments_").length(); j++) {
35777 vikas 389
                    try {
390
                        JSONObject childItem = doc.getJSONArray("_childDocuments_").getJSONObject(j);
391
                        int itemId = childItem.getInt("itemId_i");
392
                        ffdr.setIsSmartPhone(itemRepository.selectById(itemId).isSmartPhone());
393
                        float sellingPrice = (float) childItem.getDouble("sellingPrice_f");
394
                        if (fofoAvailabilityInfoMap.containsKey(itemId)) {
395
                            if (fofoAvailabilityInfoMap.get(itemId).getSellingPrice() > sellingPrice) {
396
                                fofoAvailabilityInfoMap.get(itemId).setSellingPrice(sellingPrice);
397
                                fofoAvailabilityInfoMap.get(itemId).setMop((float) childItem.getDouble("mop_f"));
398
                            }
399
                        } else {
400
                            FofoAvailabilityInfo fdi = new FofoAvailabilityInfo();
401
                            fdi.setSellingPrice(sellingPrice);
402
                            fdi.setMrp(childItem.getDouble("mrp_f"));
403
                            fdi.setMop((float) childItem.getDouble("mop_f"));
404
                            fdi.setColor(childItem.has("color_s") ? childItem.getString("color_s") : "");
405
                            fdi.setTagId(childItem.getInt("tagId_i"));
406
                            fdi.setItem_id(itemId);
407
                            fdi.setCatalog_id(doc.getInt("catalogId_i"));
408
                            fdi.setCashback(0);
409
                            fdi.setMinBuyQuantity(1);
410
                            int partnerAvailability = partnerStockAvailabilityMap.get(itemId) == null ? 0 : partnerStockAvailabilityMap.get(itemId);
411
                            int ourStockAvailability = ourItemAvailabilityMap.get(itemId) == null ? 0 : Math.max(0, ourItemAvailabilityMap.get(itemId));
412
                            fdi.setActive(partnerAvailability > 0);
413
                            fdi.setPartnerAvailability(partnerAvailability);
414
                            fdi.setAvailability(Math.min(5, ourStockAvailability + partnerAvailability));
415
                            fdi.setQuantityStep(1);
416
                            fdi.setMaxQuantity(fdi.getAvailability());
417
                            fofoAvailabilityInfoMap.put(itemId, fdi);
35503 vikas 418
                        }
35777 vikas 419
                    } catch (Exception e) {
420
                        logger.warn("Skipping stale item in Solr doc catalogId={}: {}", doc.getInt("catalogId_i"), e.getMessage());
35503 vikas 421
                    }
422
                }
423
            }
424
            if (fofoAvailabilityInfoMap.values().size() > 0) {
425
                ffdr.setItems(fofoAvailabilityInfoMap.values().stream().sorted(Comparator.comparing(FofoAvailabilityInfo::isActive, Comparator.reverseOrder()).thenComparingInt(y -> -y.getAvailability())).collect(Collectors.toList()));
426
                dealResponse.add(ffdr);
427
            }
428
        }
429
        return dealResponse.stream().sorted(Comparator.comparing(FofoCatalogResponse::getItems, (s1, s2) -> (s2.get(0).isActive() ? 1 : 0) - (s1.get(0).isActive() ? 1 : 0)).thenComparing(FofoCatalogResponse::getItems, (x, y) -> y.get(0).getAvailability() - x.get(0).getAvailability())).collect(Collectors.toList());
430
    }
431
 
432
    private List<Map<String, String>> buildSelectedOptions(String color, String storage) {
433
 
434
        List<Map<String, String>> selectedOptions = new ArrayList<>();
435
 
436
        Map<String, String> c = new HashMap<>();
35777 vikas 437
        c.put("name", "Colour");
35503 vikas 438
        c.put("value", color);
439
        selectedOptions.add(c);
440
 
441
        Map<String, String> s = new HashMap<>();
442
        s.put("name", "Storage");
443
        s.put("value", storage);
444
        selectedOptions.add(s);
445
 
446
        return selectedOptions;
447
    }
448
 
449
    private Map<String, Object> buildVariant(String color, String storage, String price, String compareAt, String sku) {
450
        Map<String, Object> v = new HashMap<>();
451
 
452
        v.put("selectedOptions", this.buildSelectedOptions(color, storage));
453
        v.put("price", price);
454
        v.put("compareAtPrice", compareAt);
455
        v.put("sku", sku);
456
 
457
        return v;
458
    }
459
}