PageRenderTime 44ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

/connector/src/main/java/net/magja/service/product/ProductRemoteServiceImpl.java

http://github.com/magja/magja
Java | 1128 lines | 834 code | 152 blank | 142 comment | 226 complexity | fb7446661104ee5a31c0487f73a2f235 MD5 | raw file
  1. package net.magja.service.product;
  2. import com.google.common.base.Function;
  3. import com.google.common.base.Optional;
  4. import com.google.common.cache.Cache;
  5. import com.google.common.cache.CacheBuilder;
  6. import com.google.common.collect.*;
  7. import net.magja.magento.ResourcePath;
  8. import net.magja.model.category.Category;
  9. import net.magja.model.product.*;
  10. import net.magja.service.GeneralServiceImpl;
  11. import net.magja.service.RemoteServiceFactory;
  12. import net.magja.service.ServiceException;
  13. import net.magja.soap.SoapClient;
  14. import org.apache.axis2.AxisFault;
  15. import org.apache.commons.lang3.BooleanUtils;
  16. import org.apache.commons.lang3.math.NumberUtils;
  17. import org.slf4j.Logger;
  18. import org.slf4j.LoggerFactory;
  19. import java.security.NoSuchAlgorithmException;
  20. import java.text.SimpleDateFormat;
  21. import java.util.*;
  22. import java.util.concurrent.TimeUnit;
  23. /**
  24. * Product service implementation.
  25. *
  26. * @author andre
  27. * @author Simon Zambrovski
  28. */
  29. public class ProductRemoteServiceImpl extends GeneralServiceImpl<Product> implements ProductRemoteService {
  30. private static final long serialVersionUID = -3943518467672208326L;
  31. private transient final Logger log = LoggerFactory.getLogger(ProductRemoteServiceImpl.class);
  32. private RemoteServiceFactory serviceFactory;
  33. public ProductRemoteServiceImpl(SoapClient soapClient, RemoteServiceFactory serviceFactory) {
  34. super(soapClient);
  35. this.serviceFactory = serviceFactory;
  36. }
  37. /**
  38. * Cache that caches all objects for 10 minutes after last access
  39. */
  40. private Cache<String, ProductAttributeSet> cache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build();
  41. /**
  42. * Create a object product with basic fields from the attribute map
  43. *
  44. * @param mpp
  45. * - the attribute map
  46. * @return Product
  47. */
  48. private Product buildProductBasic(Map<String, Object> mpp) {
  49. Product product = new Product();
  50. // populate the basic fields
  51. for (Map.Entry<String, Object> attribute : mpp.entrySet())
  52. product.set(attribute.getKey(), attribute.getValue());
  53. return product;
  54. }
  55. private Product buildProduct(Map<String, Object> mpp, Set<String> attributes, boolean dependencies) throws ServiceException {
  56. if (dependencies) {
  57. return buildProduct(mpp, attributes,
  58. ImmutableSet.of(Dependency.CATEGORIES, Dependency.MEDIAS, Dependency.LINKS, Dependency.TYPES, Dependency.ATTRIBUTE_SET, Dependency.INVENTORY));
  59. } else {
  60. return buildProduct(mpp, attributes, ImmutableSet.<Dependency> of());
  61. }
  62. }
  63. private Product buildProductWithCategories(Map<String, Object> mpp) throws ServiceException {
  64. return buildProduct(mpp, ImmutableSet.<String> of(), ImmutableSet.of(Dependency.CATEGORIES));
  65. }
  66. /**
  67. * Build the object Product with your dependencies, for the queries
  68. *
  69. * @param mpp
  70. * @return Product
  71. * @throws ServiceException
  72. */
  73. // boolean loadCategories,
  74. // boolean loadMedia, boolean loadLinks, boolean loadTypes, boolean
  75. // loadAttributeSet,
  76. // boolean loadInventory
  77. private Product buildProduct(Map<String, Object> mpp, Set<String> attributes, Set<Dependency> dependencies) throws ServiceException {
  78. Product product = buildProductBasic(mpp);
  79. // attribute values
  80. Map<String, Object> attributeValues = new HashMap<String, Object>();
  81. for (String attrCode : attributes) {
  82. Object attrValue = mpp.get(attrCode);
  83. attributeValues.put(attrCode, attrValue);
  84. }
  85. product.setAttributes(attributeValues);
  86. // product visibility
  87. if (mpp.get("visibility") != null) {
  88. Integer visi = new Integer(mpp.get("visibility").toString());
  89. switch (visi) {
  90. case 1:
  91. product.setVisibility(Visibility.NOT_VISIBLE_INDIVIDUALLY);
  92. break;
  93. case 2:
  94. product.setVisibility(Visibility.CATALOG);
  95. break;
  96. case 3:
  97. product.setVisibility(Visibility.SEARCH);
  98. break;
  99. case 4:
  100. product.setVisibility(Visibility.CATALOG_SEARCH);
  101. break;
  102. default:
  103. product.setVisibility(Visibility.CATALOG_SEARCH);
  104. break;
  105. }
  106. }
  107. // set product type
  108. if (mpp.get("type") != null) {
  109. ProductType type = ProductType.getType((String) mpp.get("type"));
  110. if (type == null && dependencies.contains(Dependency.TYPES)) {
  111. /*
  112. * means its a type not covered by the enum, so we have to look in
  113. * magento api to get this type
  114. */
  115. // FIXME: this is a potential bug.
  116. List<ProductType> types = listAllProductTypes();
  117. for (ProductType productType : types) {
  118. type = productType.getType((String) mpp.get("type"));
  119. break;
  120. }
  121. }
  122. if (type != null)
  123. product.setType(type);
  124. }
  125. // set full attributeSet if loadAttributeSet is requested
  126. if (mpp.get("set") != null && dependencies.contains(Dependency.ATTRIBUTE_SET)) {
  127. product.setAttributeSet(getAttributeSet((String) mpp.get("set")));
  128. } else {
  129. // if loadAttributeSet is not requested, only provide the attribute set ID
  130. final ProductAttributeSet attributeSet = new ProductAttributeSet(Integer.valueOf((String) mpp.get("set")), null);
  131. product.setAttributeSet(attributeSet);
  132. }
  133. // categories - dont get the full tree, only basic info of categories
  134. if (mpp.get("categories") != null)
  135. {
  136. if (dependencies.contains(Dependency.CATEGORIES)) {
  137. product.getCategories().addAll(getCategoriesBasicInfo((List<Object>) mpp.get("categories")));
  138. } else {
  139. List<Category> categories = new ArrayList<Category>();
  140. for (Object obj : (List<Object>) mpp.get("categories")) {
  141. Integer id = Integer.parseInt((String) obj);
  142. categories.add(new Category(id));
  143. }
  144. product.setCategories(categories);
  145. }
  146. }
  147. // Inventory
  148. if (dependencies.contains(Dependency.INVENTORY)) {
  149. Set<Product> products = new HashSet<Product>();
  150. products.add(product);
  151. getInventoryInfo(products);
  152. }
  153. // medias
  154. if (dependencies.contains(Dependency.MEDIAS))
  155. product.setMedias(serviceFactory.getProductMediaRemoteService().listByProduct(product));
  156. // product links
  157. if (dependencies.contains(Dependency.LINKS))
  158. product.setLinks(serviceFactory.getProductLinkRemoteService().list(product));
  159. return product;
  160. }
  161. /**
  162. * @param ids
  163. * @return list of categories with specified ids, just the basic info
  164. * @throws ServiceException
  165. */
  166. private List<Category> getCategoriesBasicInfo(List<Object> ids) throws ServiceException {
  167. log.info("getCategoriesBasicInfo {}", ids);
  168. List<Category> categories = new ArrayList<Category>();
  169. for (Object obj : ids) {
  170. Integer id = Integer.parseInt((String) obj);
  171. Category category = serviceFactory.getCategoryRemoteService().getByIdClean(id);
  172. categories.add(category);
  173. }
  174. return categories;
  175. }
  176. /**
  177. * @param id
  178. * @return the ProductAttributeSet with the specified id
  179. * @throws ServiceException
  180. */
  181. // TODO: this is called multiple times by ProductService.listAll(), please
  182. // cache this
  183. private ProductAttributeSet getAttributeSet(String id) throws ServiceException {
  184. String cacheKey = "attributeSet." + id;
  185. ProductAttributeSet prdAttSet = cache.getIfPresent(cacheKey);
  186. if (prdAttSet != null) {
  187. log.trace("Returning cached ProductAttributeSet {}: {}", id, prdAttSet);
  188. } else {
  189. prdAttSet = new ProductAttributeSet();
  190. Integer set_id = Integer.parseInt(id);
  191. // if are the default attribute set, that not list on the api, so we
  192. // have to set manually
  193. if (set_id.equals(soapClient.getConfig().getDefaultAttributeSetId())) {
  194. prdAttSet.setId(set_id);
  195. prdAttSet.setName("Default");
  196. } else {
  197. List<Map<String, Object>> setList;
  198. try {
  199. setList = (List<Map<String, Object>>) soapClient.callSingle(ResourcePath.ProductAttributeSetList, "");
  200. log.debug("{} returned {}", ResourcePath.ProductAttributeSetList.getPath(), setList);
  201. } catch (AxisFault e) {
  202. log.error("Error getting attribute set " + id, e);
  203. if (debug)
  204. e.printStackTrace();
  205. throw new ServiceException(e.getMessage());
  206. }
  207. if (setList != null) {
  208. for (Map<String, Object> set : setList) {
  209. // Workaround: Some Magento version (or the magja-catalog-ext
  210. // extension?) return "set_id",
  211. // while the other returns "attribute_set_id". Let's accept both.
  212. String setId = (String) (set.containsKey("set_id") ? set.get("set_id") : set.get("attribute_set_id"));
  213. String setName = (String) (set.containsKey("set_name") ? set.get("set_name") : set.get("attribute_set_name"));
  214. if (setId.equals(set_id.toString())) {
  215. prdAttSet.setId(Integer.valueOf(setId));
  216. prdAttSet.setName(setName);
  217. for (Map.Entry<String, Object> att : set.entrySet())
  218. prdAttSet.set(att.getKey(), att.getValue());
  219. break;
  220. }
  221. }
  222. }
  223. }
  224. log.trace("Caching ProductAttributeSet {} as {}: {}", new Object[] { id, cacheKey, prdAttSet });
  225. cache.put(cacheKey, prdAttSet);
  226. }
  227. return prdAttSet;
  228. }
  229. /**
  230. * Delete a product by your id (prefered) or your sku
  231. *
  232. * @param id
  233. * @param sku
  234. * @throws ServiceException
  235. */
  236. private void delete(Integer id, String sku) throws ServiceException {
  237. Boolean success = false;
  238. try {
  239. if (id != null) {
  240. success = soapClient.callSingle(ResourcePath.ProductDelete, id);
  241. } else if (sku != null) {
  242. success = soapClient.callSingle(ResourcePath.ProductDelete, sku);
  243. }
  244. } catch (AxisFault e) {
  245. if (debug)
  246. e.printStackTrace();
  247. throw new ServiceException(e.getMessage());
  248. }
  249. if (!success)
  250. throw new ServiceException("Not success deleting product.");
  251. }
  252. /**
  253. * Delete a product by sku and category if empty
  254. *
  255. * @param sku
  256. * @throws ServiceException
  257. */
  258. @Override
  259. public void deleteWithEmptyCategory(String sku) throws ServiceException {
  260. Product product = getBySku(sku);
  261. List<Category> categories = product.getCategories();
  262. delete(sku);
  263. if (categories != null) {
  264. for (Category category : categories) {
  265. serviceFactory.getCategoryRemoteService().deleteEmptyRecursive(category);
  266. }
  267. }
  268. }
  269. /**
  270. * List the products, if dependencies is true, the products will be populated
  271. * with all your dependencies, otherwise, no.
  272. *
  273. * @param dependencies
  274. * @return List<Product>
  275. * @throws ServiceException
  276. */
  277. private List<Product> list(boolean dependencies) throws ServiceException {
  278. List<Product> products = new ArrayList<Product>();
  279. List<Map<String, Object>> productList;
  280. try {
  281. productList = soapClient.callNoArgs(ResourcePath.ProductList);
  282. } catch (AxisFault e) {
  283. if (debug)
  284. e.printStackTrace();
  285. throw new ServiceException(e.getMessage());
  286. }
  287. if (productList == null) {
  288. return products;
  289. }
  290. for (Map<String, Object> mpp : productList) {
  291. products.add(buildProduct(mpp, ImmutableSet.<String> of(), dependencies));
  292. }
  293. return products;
  294. }
  295. @Override
  296. public Product getBySku(String sku) throws ServiceException {
  297. return getBySku(sku, false);
  298. }
  299. @Override
  300. public Product getBySku(String sku, Set<String> attributes) throws ServiceException {
  301. return getBySku(sku, attributes, false);
  302. }
  303. @Override
  304. public Product getBySkuWithCategories(String sku) throws ServiceException {
  305. Map<String, Object> mpp = loadBaseProduct(sku, ImmutableSet.<String> of());
  306. if (mpp == null) {
  307. return null;
  308. } else {
  309. return buildProductWithCategories(mpp);
  310. }
  311. }
  312. @Override
  313. public Product getBySku(String sku, boolean dependencies) throws ServiceException {
  314. return getBySku(sku, ImmutableSet.<String> of(), dependencies);
  315. }
  316. public Product getBySku(String sku, Set<String> attributes, Set<Dependency> dependencies) throws ServiceException {
  317. Map<String, Object> mpp = loadBaseProduct(sku, attributes);
  318. if (mpp == null) {
  319. return null;
  320. } else {
  321. return buildProduct(mpp, attributes, dependencies);
  322. }
  323. }
  324. @Override
  325. public Product getBySku(String sku, Set<String> attributes, boolean dependencies) throws ServiceException {
  326. if (dependencies) {
  327. return getBySku(sku, attributes,
  328. ImmutableSet.of(Dependency.CATEGORIES, Dependency.MEDIAS, Dependency.LINKS, Dependency.TYPES, Dependency.ATTRIBUTE_SET, Dependency.INVENTORY));
  329. } else {
  330. return getBySku(sku, attributes, ImmutableSet.<Dependency> of());
  331. }
  332. }
  333. private Map<String, Object> loadBaseProduct(String sku, Set<String> attributes) throws ServiceException {
  334. Map<String, Object> mpp;
  335. try {
  336. // There's a bug in Magento not interpreting properly numeric SKUs.
  337. // Implementing proposed workaround on:
  338. // http://stackoverflow.com/questions/6748142/magento-1-5-numeric-skus-and-productidentifiertype/10915276#10915276
  339. // Consisting on adding a whitespace at the end that will be finally
  340. // trimmed with Magento.
  341. if (NumberUtils.isNumber(sku))
  342. sku = sku + " ";
  343. mpp = soapClient.callArgs(ResourcePath.ProductInfo, new Object[] { sku, null, attributes });
  344. } catch (AxisFault e) {
  345. log.error("Error calling product.info with sku=" + sku + ", attributes=" + attributes, e);
  346. if (e.getMessage().indexOf("Product not exists") >= 0) {
  347. mpp = null;
  348. } else {
  349. if (debug)
  350. e.printStackTrace();
  351. throw new ServiceException("Error calling product.info with sku=" + sku + ", attributes=" + attributes, e);
  352. }
  353. }
  354. return mpp;
  355. }
  356. @Override
  357. public void updatePrice(List<ProductUpdatePrice> products) throws ServiceException {
  358. log.info("Updating products price for {}.", products);
  359. try {
  360. soapClient.callSingle(ResourcePath.ProductUpdatePrice, products);
  361. } catch (AxisFault e) {
  362. log.error("Cannot call product.ProductUpdatePrice for " + products, e);
  363. if (debug)
  364. e.printStackTrace();
  365. throw new ServiceException("Cannot call product.ProductUpdatePrice for " + products, e);
  366. }
  367. }
  368. @Override
  369. public Map<String, Map<String, String>> getRefsMap(List<String> skus) throws ServiceException {
  370. Map<String, Map<String, String>> mpp;
  371. try {
  372. mpp = soapClient.callSingle(ResourcePath.ProductGetRefs, skus);
  373. } catch (AxisFault e) {
  374. log.error("Cannot call product.get_refs for " + skus, e);
  375. if (debug)
  376. e.printStackTrace();
  377. throw new ServiceException("Cannot call product.get_refs for " + skus, e);
  378. }
  379. return mpp;
  380. }
  381. @Override
  382. public Map<String, ProductRefMagja> getRefs(List<String> skus) throws ServiceException {
  383. Map<String, Map<String, String>> refsMap = getRefsMap(skus);
  384. Map<String, ProductRefMagja> refs = Maps.transformEntries(refsMap, new Maps.EntryTransformer<String, Map<String, String>, ProductRefMagja>() {
  385. @Override
  386. public ProductRefMagja transformEntry(String key, Map<String, String> value) {
  387. return new ProductRefMagja(key, value.get("url_path"), value.get("name"), value.get("image_50x50"), value.get("shop_id"));
  388. }
  389. });
  390. return refs;
  391. }
  392. @Override
  393. public Product getById(Integer id) throws ServiceException {
  394. return getById(id, ImmutableSet.<String> of());
  395. }
  396. @Override
  397. public Product getById(Integer id, Set<String> attributes) throws ServiceException {
  398. Map<String, Object> mpp;
  399. try {
  400. mpp = soapClient.callArgs(ResourcePath.ProductInfo, new Object[] { id, null, attributes });
  401. } catch (AxisFault e) {
  402. log.error("Error calling product.info with id=" + id + ", attributes=" + attributes, e);
  403. if (debug)
  404. e.printStackTrace();
  405. throw new ServiceException("Error calling product.info with id=" + id + ", attributes=" + attributes, e);
  406. }
  407. if (mpp == null) {
  408. return null;
  409. } else {
  410. return buildProduct(mpp, attributes, true);
  411. }
  412. }
  413. @Override
  414. public List<Product> listAll() throws ServiceException {
  415. return list(true);
  416. }
  417. @Override
  418. public List<Product> listAllNoDep() throws ServiceException {
  419. return list(false);
  420. }
  421. public List<Product> listAllPlus(final Set<String> attributesToSelect) throws ServiceException {
  422. try {
  423. final List<Map<String, Object>> productListPlusResult = soapClient.callArgs(ResourcePath.ProductListPlus,
  424. new Object[] { null, null, attributesToSelect.toArray(new String[] {}) });
  425. List<Map<String, Object>> productMapList = Optional.fromNullable(productListPlusResult).or(new ArrayList<Map<String, Object>>());
  426. List<Product> products = Lists.transform(productMapList, new Function<Map<String, Object>, Product>() {
  427. @Override
  428. public Product apply(Map<String, Object> mpp) {
  429. try {
  430. Product product = buildProduct(mpp, attributesToSelect, false);
  431. return product;
  432. } catch (ServiceException e) {
  433. log.error("Cannot map to Product: " + mpp, e);
  434. throw new RuntimeException("Cannot map to Product: " + mpp, e);
  435. }
  436. }
  437. });
  438. return products;
  439. } catch (AxisFault e) {
  440. if (debug) {
  441. e.printStackTrace();
  442. }
  443. log.error("listAllPlus error " + attributesToSelect, e);
  444. throw new ServiceException("listAllPlus error " + attributesToSelect, e);
  445. }
  446. }
  447. @Override
  448. @Deprecated
  449. public void save(Product product, Product existingProduct) throws ServiceException, NoSuchAlgorithmException {
  450. save(product, existingProduct, "");
  451. }
  452. @Override
  453. @Deprecated
  454. public void save(Product product, Product existingProduct, String storeView) throws ServiceException, NoSuchAlgorithmException {
  455. if (product.getId() != null && product.getId() > 0) {
  456. // means its a existing product
  457. update(product, existingProduct, storeView);
  458. } else {
  459. // means its a new product
  460. add(product, storeView);
  461. }
  462. }
  463. private void assignCategories(Product product, Product existingProduct) throws ServiceException {
  464. doAssignCategories(product, existingProduct != null ? existingProduct.getCategories() : ImmutableList.<Category> of());
  465. List<Category> toBeDeleted = new ArrayList<Category>();
  466. if (existingProduct != null) {
  467. for (Category category : existingProduct.getCategories()) {
  468. boolean found = false;
  469. for (Category newCategory : product.getCategories()) {
  470. if (newCategory.getId().equals(category.getId())) {
  471. found = true;
  472. break;
  473. }
  474. }
  475. if (!found) {
  476. toBeDeleted.add(category);
  477. }
  478. }
  479. }
  480. if (toBeDeleted.size() > 0) {
  481. for (Category category : toBeDeleted) {
  482. log.info("Removing '" + product.getSku() + " from category " + category.getName());
  483. serviceFactory.getCategoryRemoteService().removeProduct(category, product);
  484. }
  485. }
  486. }
  487. /**
  488. * @param product
  489. * @param existingProduct
  490. * @throws ServiceException
  491. */
  492. protected void doAssignCategories(Product product, List<Category> existingCategories) throws ServiceException {
  493. for (Category cat : product.getCategories()) {
  494. boolean found = false;
  495. if (existingCategories != null) {
  496. for (Category category : existingCategories) {
  497. if (cat.getId().equals(category.getId())) {
  498. found = true;
  499. break;
  500. }
  501. }
  502. }
  503. if (!found) {
  504. log.debug("Adding '{}' to category #{} ({}) with position {}", new Object[] { product.getSku(), cat.getId(), cat.getName(), cat.getPosition() });
  505. serviceFactory.getCategoryRemoteService().assignProductWithPosition(cat, product, cat.getPosition());
  506. } else {
  507. log.debug("Updating '{}' to category #{} ({}) with position {}", new Object[] { product.getSku(), cat.getId(), cat.getName(), cat.getPosition() });
  508. serviceFactory.getCategoryRemoteService().assignProductWithPosition(cat, product, cat.getPosition());
  509. }
  510. }
  511. }
  512. private void assignProductMedias(Product product) throws ServiceException, NoSuchAlgorithmException {
  513. // if have media, create it too
  514. List<String> mediaFound = new ArrayList<String>();
  515. List<ProductMedia> toBeDeleted = new ArrayList<ProductMedia>();
  516. List<ProductMedia> existingMedias = serviceFactory.getProductMediaRemoteService().listByProduct(product);
  517. doAssignProductMedias(product, existingMedias);
  518. for (ProductMedia existingMedia : existingMedias) {
  519. boolean found = false;
  520. if (product.getMedias() != null) {
  521. for (ProductMedia media : product.getMedias()) {
  522. if (existingMedia.getLabel().equals(media.getLabel())) {
  523. found = true;
  524. break;
  525. }
  526. }
  527. }
  528. if (!found) {
  529. toBeDeleted.add(existingMedia);
  530. } else {
  531. if (mediaFound.contains(existingMedia.getLabel())) {
  532. toBeDeleted.add(existingMedia);
  533. } else {
  534. mediaFound.add(existingMedia.getLabel());
  535. }
  536. }
  537. }
  538. for (ProductMedia existingMedia : toBeDeleted) {
  539. log.info("Deleting media '{}' from product #{} ({}) ", new Object[] { existingMedia.getLabel(), product.getId(), product.getSku() });
  540. serviceFactory.getProductMediaRemoteService().delete(existingMedia);
  541. }
  542. }
  543. /**
  544. * @param product
  545. * @param found
  546. * @param existingMedias
  547. * @throws ServiceException
  548. */
  549. protected void doAssignProductMedias(Product product, List<ProductMedia> existingMedias) throws ServiceException {
  550. if (product.getMedias() != null) {
  551. if (!product.getMedias().isEmpty()) {
  552. for (ProductMedia media : product.getMedias()) {
  553. if (media.getImage() == null) {
  554. log.debug("Skipping media '{}' from product #{} ({}) because image is empty", new Object[] { media.getLabel(), product.getId(), product.getSku() });
  555. continue;
  556. }
  557. boolean found = false;
  558. for (ProductMedia existingMedia : existingMedias) {
  559. if (existingMedia.getLabel().equals(media.getLabel())) {
  560. found = true;
  561. existingMedia.setTypes(media.getTypes());
  562. existingMedia.setImage(media.getImage());
  563. log.info("Updating media '{}' in product #{} ({}) ", new Object[] { existingMedia.getLabel(), product.getId(), product.getSku() });
  564. serviceFactory.getProductMediaRemoteService().update(existingMedia);
  565. break;
  566. }
  567. }
  568. if (!found) {
  569. if (media.getImage() != null && media.getImage().getData() != null)
  570. log.info("Adding media '{}' to product #{} ({}) ", new Object[] { media.getLabel(), product.getId(), product.getSku() });
  571. serviceFactory.getProductMediaRemoteService().create(media);
  572. }
  573. }
  574. }
  575. }
  576. }
  577. protected void assignProductLinks(Product product) throws ServiceException {
  578. List<ProductLink> linksToBeDeleted = new ArrayList<ProductLink>();
  579. Set<ProductLink> existingLinks = serviceFactory.getProductLinkRemoteService().list(product);
  580. doAssignProductLinks(product, existingLinks);
  581. for (ProductLink existingLink : existingLinks) {
  582. boolean found = false;
  583. if (product.getLinks() != null) {
  584. for (ProductLink link : product.getLinks()) {
  585. if (existingLink.getSku().equals(link.getSku()) && existingLink.getLinkType().equals(link.getLinkType())) {
  586. found = true;
  587. break;
  588. }
  589. }
  590. }
  591. if (!found) {
  592. linksToBeDeleted.add(existingLink);
  593. }
  594. }
  595. for (ProductLink link : linksToBeDeleted) {
  596. log.info("Removing " + link.getLinkType() + " Link with product : " + link.getSku());
  597. serviceFactory.getProductLinkRemoteService().remove(product, link);
  598. }
  599. }
  600. /**
  601. * @param product
  602. * @param existingLinks
  603. * @throws ServiceException
  604. */
  605. protected void doAssignProductLinks(Product product, Set<ProductLink> existingLinks) throws ServiceException {
  606. if (product.getLinks() != null) {
  607. if (!product.getLinks().isEmpty()) {
  608. for (ProductLink link : product.getLinks()) {
  609. boolean found = false;
  610. for (ProductLink existingLink : existingLinks) {
  611. if (existingLink.getSku().equals(link.getSku()) && existingLink.getLinkType().equals(link.getLinkType())) {
  612. found = true;
  613. break;
  614. }
  615. }
  616. if (!found) {
  617. if (link.getLinkType() != null && (link.getId() != null || link.getSku() != null))
  618. log.info("Assigning " + link.getLinkType() + " Link with product : " + link.getSku());
  619. serviceFactory.getProductLinkRemoteService().assign(product, link);
  620. }
  621. }
  622. }
  623. }
  624. }
  625. /**
  626. * Method using Magja extension.
  627. *
  628. * @param productSku
  629. * @param attributeNames
  630. * @throws ServiceException
  631. */
  632. @Override
  633. public void setConfigurableAttributes(String productSku, Map<String, String> attributeNames) throws ServiceException {
  634. try {
  635. String results = soapClient.callArgs(ResourcePath.ProductConfigurableAttributes, new Object[] { productSku, attributeNames });
  636. } catch (AxisFault e) {
  637. e.printStackTrace();
  638. throw new ServiceException(e.getMessage());
  639. }
  640. }
  641. @Override
  642. public void setAssociatedProducts(String productSku, Map<String, String> childProducts) throws ServiceException {
  643. try {
  644. String results = soapClient.callArgs(ResourcePath.ProductAssociateChildren, new Object[] { productSku, childProducts });
  645. log.debug("setAssociated products {} returned {}", productSku, results);
  646. } catch (AxisFault e) {
  647. if (debug)
  648. e.printStackTrace();
  649. throw new ServiceException(e.getMessage());
  650. }
  651. }
  652. /*
  653. * Handle configurable products just for insert new products
  654. */
  655. private void handleConfigurableForNewProducts(Product product) throws ServiceException, NoSuchAlgorithmException {
  656. // if isn't a configurable product, stop the execution
  657. if (!product.getType().equals(ProductType.CONFIGURABLE)) {
  658. return;
  659. }
  660. if (product.getConfigurableAttributesData() != null) {
  661. final Map<String, Object> confAttrDataMap = new HashMap<String, Object>();
  662. Integer i = 0;
  663. for (ConfigurableAttributeData configAttr : product.getConfigurableAttributesData()) {
  664. confAttrDataMap.put(i.toString(), configAttr.serializeToApi());
  665. i++;
  666. }
  667. product.set("configurable_attributes_data", confAttrDataMap);
  668. }
  669. if (product.getConfigurableSubProducts() != null) {
  670. if (product.getConfigurableProductsData() == null) {
  671. product.setConfigurableProductsData(new HashMap<String, Map<String, Object>>());
  672. }
  673. for (ConfigurableProductData childProductData : product.getConfigurableSubProducts()) {
  674. final Product child = childProductData.getProduct();
  675. if (child.getType().equals(ProductType.SIMPLE)) {
  676. if (child.getSku() != null && child.getId() != null) {
  677. // product is already pre-loaded, skip loading it.
  678. } else {
  679. Product existingProduct = getBySku(child.getSku(), false);
  680. if (existingProduct != null) {
  681. child.setId(existingProduct.getId());
  682. childProductData.setExistingProduct(existingProduct);
  683. }
  684. // update the child. this is required if the child has been created
  685. // on the fly.
  686. this.update(child, existingProduct);
  687. }
  688. } else {
  689. log.info("Bogus configuraion, a subproduct point to a configurable product instead of a simple.");
  690. }
  691. product.getConfigurableProductsData().put(child.getId().toString(), childProductData.serializeToApi());
  692. }
  693. }
  694. }
  695. @Override
  696. public List<ProductType> listAllProductTypes() throws ServiceException {
  697. List<ProductType> resultList = new ArrayList<ProductType>();
  698. List<Map<String, Object>> productTypes;
  699. try {
  700. productTypes = soapClient.callNoArgs(ResourcePath.ProductTypeList);
  701. } catch (AxisFault e) {
  702. if (debug)
  703. e.printStackTrace();
  704. throw new ServiceException(e.getMessage());
  705. }
  706. if (productTypes == null)
  707. return resultList;
  708. ProductType productType;
  709. for (Map<String, Object> type : productTypes) {
  710. for (Map.Entry<String, Object> attribute : type.entrySet()) {
  711. productType = new ProductType((String) attribute.getValue(), attribute.getKey());
  712. resultList.add(productType);
  713. }
  714. }
  715. return resultList;
  716. }
  717. @Override
  718. public void delete(Integer id) throws ServiceException {
  719. delete(id, null);
  720. }
  721. @Override
  722. public void delete(String sku) throws ServiceException {
  723. delete(null, sku);
  724. }
  725. @Override
  726. public void deleteAll() throws ServiceException {
  727. List<Product> products = listAllNoDep();
  728. for (Product product : products) {
  729. delete(product.getId());
  730. }
  731. }
  732. @Override
  733. public void getInventoryInfo(Set<Product> products) throws ServiceException {
  734. List<Integer> productIds = ImmutableList.copyOf(Iterables.transform(products, new Function<Product, Integer>() {
  735. @Override
  736. public Integer apply(Product input) {
  737. return input.getId();
  738. }
  739. }));
  740. List<Map<String, Object>> resultList = null;
  741. try {
  742. resultList = soapClient.callSingle(ResourcePath.ProductStockList, productIds);
  743. } catch (AxisFault e) {
  744. if (debug)
  745. e.printStackTrace();
  746. throw new ServiceException(e.getMessage());
  747. }
  748. for (Map<String, Object> iv : resultList) {
  749. for (Product product : products) {
  750. if (product.getId().equals(Integer.parseInt((String) iv.get("product_id")))) {
  751. if (iv.get("qty") != null || !"".equals(iv.get("qty")))
  752. product.setQty(Double.parseDouble((String) iv.get("qty")));
  753. if (iv.get("is_in_stock") != null || !"".equals(iv.get("is_in_stock"))) {
  754. if (iv.get("is_in_stock").toString().equals("0") || iv.get("is_in_stock").toString().equals("false"))
  755. product.setInStock(false);
  756. else
  757. product.setInStock(true);
  758. }
  759. if (iv.get("manage_stock") != null)
  760. product.setManageStock(iv.get("manage_stock").toString().equals("1"));
  761. else
  762. product.setManageStock(null);
  763. if (iv.get("use_config_manage_stock") != null)
  764. product.setUseConfigManageStock(iv.get("use_config_manage_stock").toString().equals("1"));
  765. else
  766. product.setUseConfigManageStock(null);
  767. }
  768. }
  769. }
  770. }
  771. @Override
  772. public void updateInventory(Product product) throws ServiceException {
  773. updateInventory(product, new HashMap<String, Object>());
  774. }
  775. @Override
  776. public void updateInventory(final Product product, final Map<String, Object> properties) throws ServiceException {
  777. if (product.getId() == null && product.getSku() == null) {
  778. throw new ServiceException("The product must have the id or the sku seted for update inventory");
  779. }
  780. if (product.getInStock() == null && product.getQty() != null) {
  781. product.setInStock(product.getQty() > 0);
  782. }
  783. if (product.getQty() != null) {
  784. properties.put("qty", product.getQty());
  785. }
  786. properties.put("is_in_stock", BooleanUtils.toBoolean(product.getInStock()) ? "1" : "0");
  787. if (product.getManageStock() != null) {
  788. properties.put("manage_stock", BooleanUtils.toBoolean(product.getManageStock()) ? "1" : "0");
  789. }
  790. if (product.getUseConfigManageStock() != null) {
  791. properties.put("use_config_manage_stock", BooleanUtils.toBoolean(product.getUseConfigManageStock()) ? "1" : "0");
  792. }
  793. try {
  794. soapClient.callArgs(ResourcePath.ProductStockUpdate, new Object[] { product.getId() != null ? product.getId() : product.getSku(), properties });
  795. } catch (final AxisFault e) {
  796. log.error("Cannot update inventory for product " + product.getId(), e);
  797. if (debug) {
  798. e.printStackTrace();
  799. }
  800. throw new ServiceException("Cannot update inventory for product " + product.getId(), e);
  801. }
  802. }
  803. @Override
  804. public void setManageStock(Product product) throws ServiceException {
  805. setManageStock(product, false, true);
  806. }
  807. @Override
  808. public void unsetManageStock(Product product) throws ServiceException {
  809. setManageStock(product, true, true);
  810. }
  811. @Override
  812. public void setManageStock(final Product product, final boolean manageStock) throws ServiceException {
  813. setManageStock(product, false, manageStock);
  814. }
  815. /**
  816. * Controls the parameter manage stock for given product.
  817. *
  818. * @param product
  819. * product with SKU or ID set.
  820. * @param useDefaultManageStock
  821. * controls if the store default is used or not.
  822. * @param manageStock
  823. * if store default is not used, controls the value of the property
  824. * manage stock.
  825. * @throws ServiceException
  826. * on any error.
  827. */
  828. public void setManageStock(final Product product, final boolean useDefaultManageStock, final boolean manageStock) throws ServiceException {
  829. if (product.getId() == null && product.getSku() == null) {
  830. throw new ServiceException("The product must have the id or the sku seted for update inventory");
  831. }
  832. final Map<String, Object> properties = new HashMap<String, Object>();
  833. if (useDefaultManageStock) {
  834. properties.put("use_config_manage_stock", "1");
  835. } else {
  836. properties.put("use_config_manage_stock", "0");
  837. if (manageStock) {
  838. properties.put("manage_stock", "1");
  839. } else {
  840. properties.put("manage_stock", "0");
  841. }
  842. }
  843. try {
  844. soapClient.callArgs(ResourcePath.ProductStockUpdate, new Object[] { product.getId() != null ? product.getId() : product.getSku(), properties });
  845. } catch (AxisFault e) {
  846. log.error("Cannot set 'Manage Stock' for product " + product.getId(), e);
  847. if (debug) {
  848. e.printStackTrace();
  849. }
  850. throw new ServiceException("Cannot set 'Manage Stock' for product " + product.getId(), e);
  851. }
  852. }
  853. /**
  854. * Get products without category
  855. *
  856. * @return List<Product>
  857. * @throws ServiceException
  858. */
  859. @Override
  860. public List<Product> getWithoutCategory() throws ServiceException {
  861. List<Product> withoutCategory = new ArrayList<Product>();
  862. List<Product> products = listAllNoDep();
  863. for (Product product : products) {
  864. if (product.getCategories().isEmpty()) {
  865. withoutCategory.add(product);
  866. }
  867. }
  868. return withoutCategory;
  869. }
  870. @Override
  871. public List<Product> listUpdatedBetween(Date from, Date to) throws ServiceException {
  872. Map<String, Object> param = new HashMap<String, Object>();
  873. List<Product> products = new ArrayList<Product>();
  874. Map<String, String> values = new HashMap<String, String>();
  875. values.put("from", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(from));
  876. values.put("to", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(to));
  877. param.put("updated_at", values);
  878. try {
  879. @SuppressWarnings("unchecked")
  880. List<Map<String, Object>> productList = (List<Map<String, Object>>) soapClient.callSingle(ResourcePath.ProductList, param);
  881. if (productList != null) {
  882. for (Map<String, Object> mpp : productList)
  883. products.add(buildProduct(mpp, ImmutableSet.<String> of(), false));
  884. }
  885. } catch (AxisFault e) {
  886. if (debug)
  887. e.printStackTrace();
  888. throw new ServiceException(e.getMessage());
  889. }
  890. return products;
  891. }
  892. @Override
  893. public void add(Product product) throws ServiceException, NoSuchAlgorithmException {
  894. add(product, "");
  895. }
  896. @Override
  897. public void add(Product product, String storeView) throws ServiceException, NoSuchAlgorithmException {
  898. // if is a configurable product, call the proper handle
  899. if (product.getType().equals(ProductType.CONFIGURABLE)) {
  900. handleConfigurableForNewProducts(product);
  901. }
  902. try {
  903. Object[] newProductArgs = product.serializeToApi();
  904. log.info("Creating '" + product.getSku() + "'");
  905. int id = Integer.parseInt((String) soapClient.callArgs(ResourcePath.ProductCreate, newProductArgs));
  906. log.debug("{} {} returned {}", new Object[] { ResourcePath.ProductCreate, product.getSku(), id });
  907. if (id > 0) {
  908. product.setId(id);
  909. } else {
  910. throw new ServiceException("Error adding Product " + product.getSku() + ": " + product.getName() + ", returned Product ID is empty");
  911. }
  912. } catch (Exception e) {
  913. log.error("Error adding Product " + product.getSku() + ": " + product.getName() + " cause: " + e.getCause(), e);
  914. if (debug) {
  915. e.printStackTrace();
  916. }
  917. throw new ServiceException("Error adding Product " + product.getSku() + ": " + product.getName() + " cause: " + e.getCause(), e);
  918. }
  919. // inventory
  920. if (product.getQty() != null) {
  921. updateInventory(product);
  922. }
  923. doAssignProductMedias(product, ImmutableList.<ProductMedia> of());
  924. doAssignProductLinks(product, ImmutableSet.<ProductLink> of());
  925. doAssignCategories(product, ImmutableList.<Category> of());
  926. }
  927. @Override
  928. public void update(Product product, Product existingProduct) throws ServiceException, NoSuchAlgorithmException {
  929. update(product, existingProduct, "");
  930. }
  931. @Override
  932. public void update(Product product, Product existingProduct, String storeView) throws ServiceException, NoSuchAlgorithmException {
  933. update(product, existingProduct, storeView, ImmutableSet.of(Dependency.INVENTORY, Dependency.MEDIAS, Dependency.LINKS, Dependency.CATEGORIES));
  934. }
  935. @Override
  936. public void update(Product product, Product existingProduct, String storeView, Set<Dependency> dependencies)
  937. throws ServiceException, NoSuchAlgorithmException {
  938. try {
  939. Map<String, Object> productData = new HashMap<String, Object>();
  940. // add custom attributes
  941. productData.putAll(product.getAttributes());
  942. // add/override static attribute values
  943. productData.putAll(product.getAllProperties());
  944. if (product.getVisibility() != null) {
  945. productData.put("visibility", product.getVisibility().getValue());
  946. }
  947. Object[] newProductArgs = new Object[] { product.getId(), productData, !storeView.isEmpty() ? storeView : null };
  948. log.info("Updating '{}' with data {}", product.getSku(), productData);
  949. Object callResult = soapClient.callArgs(ResourcePath.ProductUpdate, newProductArgs);
  950. log.debug("{} {} returned {}", new Object[] { ResourcePath.ProductUpdate, product.getId(), callResult });
  951. if (product.getType().equals(ProductType.CONFIGURABLE)) {
  952. handleConfigurableForNewProducts(product);
  953. }
  954. } catch (Exception e) {
  955. log.error("Error updating Product " + product.getId() + " cause: " + e.getCause(), e);
  956. if (debug) {
  957. e.printStackTrace();
  958. }
  959. throw new ServiceException("Error updating Product " + product.getId() + " cause: " + e.getCause(), e);
  960. }
  961. // inventory
  962. if (dependencies.contains(Dependency.INVENTORY)) {
  963. if (product.getQty() != null)
  964. updateInventory(product);
  965. }
  966. if (dependencies.contains(Dependency.MEDIAS)) {
  967. assignProductMedias(product);
  968. }
  969. if (dependencies.contains(Dependency.LINKS)) {
  970. assignProductLinks(product);
  971. }
  972. if (dependencies.contains(Dependency.CATEGORIES)) {
  973. assignCategories(product, existingProduct);
  974. }
  975. }
  976. }