Skip to content
This repository was archived by the owner on May 28, 2023. It is now read-only.

Commit bff483f

Browse files
author
tkostuch
committed
use productEquals to compare products in o2m
1 parent 7c2cf9b commit bff483f

File tree

3 files changed

+148
-6
lines changed

3 files changed

+148
-6
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const { sha3_224 } = require('js-sha3')
2+
const get = require('lodash/get')
3+
const flow = require('lodash/flow')
4+
const cloneDeep = require('lodash/cloneDeep')
5+
6+
const replaceNumberToString = obj => {
7+
Object.keys(obj).forEach(key => {
8+
if (obj[key] !== null && typeof obj[key] === 'object') {
9+
return replaceNumberToString(obj[key]);
10+
} else if (typeof obj[key] === 'number') {
11+
obj[key] = String(obj[key]);
12+
}
13+
});
14+
return obj;
15+
}
16+
17+
const transformToArray = value => Array.isArray(value) ? value : Object.values(value)
18+
19+
const getProductOptions = (product, optionsName) => {
20+
return flow([
21+
get,
22+
cloneDeep,
23+
transformToArray,
24+
replaceNumberToString
25+
])(product, `product_option.extension_attributes.${optionsName}`, [])
26+
}
27+
28+
const getDataToHash = (product) => {
29+
if (!product.product_option) {
30+
return null
31+
}
32+
33+
const supportedProductOptions = ['bundle_options', 'custom_options', 'configurable_item_options']
34+
35+
// returns first options that has array with options
36+
for (let optionName of supportedProductOptions) {
37+
const options = getProductOptions(product, optionName)
38+
if (options.length) {
39+
return options
40+
}
41+
}
42+
43+
// if there are options that are not supported then just return all options
44+
return product.product_option
45+
}
46+
47+
const productChecksum = (product) => sha3_224(JSON.stringify(getDataToHash(product)))
48+
49+
module.exports = {
50+
getProductOptions,
51+
productChecksum
52+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
const { getProductOptions, productChecksum } = require('./productChecksum');
2+
3+
// 'id' check
4+
const getServerItemId = (product) =>
5+
product.server_item_id || product.item_id
6+
const isServerIdsEquals = (product1, product2) => {
7+
const product1ItemId = getServerItemId(product1)
8+
const product2ItemId = getServerItemId(product2)
9+
10+
const areItemIdsDefined = product1ItemId !== undefined && product2ItemId !== undefined
11+
12+
return areItemIdsDefined && product1ItemId === product2ItemId
13+
}
14+
15+
// 'checksum' check
16+
const getChecksum = (product) => {
17+
if (product.checksum) {
18+
return product.checksum
19+
}
20+
return productChecksum(product)
21+
}
22+
const isChecksumEquals = (product1, product2) =>
23+
getChecksum(product1) === getChecksum(product2)
24+
25+
// 'sku' check
26+
const isSkuEqual = (product1, product2) =>
27+
String(product1.sku) === String(product2.sku)
28+
29+
/**
30+
* Returns product equality check function
31+
* @param checkName - determines what type of check we want to do
32+
*/
33+
const getCheckFn = (checkName) => {
34+
switch (checkName) {
35+
case 'id': {
36+
return isServerIdsEquals
37+
}
38+
case 'checksum': {
39+
return isChecksumEquals
40+
}
41+
case 'sku': {
42+
return isSkuEqual
43+
}
44+
default: {
45+
return isSkuEqual
46+
}
47+
}
48+
}
49+
50+
/**
51+
* It passes all types of checks and returns the first passed. The order of checks matters!
52+
*/
53+
const makeCheck = (product1, product2, checks) => {
54+
for (let checkName of checks) {
55+
const fn = getCheckFn(checkName)
56+
if (fn(product1, product2)) {
57+
return true
58+
}
59+
}
60+
return false
61+
}
62+
63+
const productsEquals = (product1, product2) => {
64+
if (!product1 || !product2) {
65+
return false
66+
}
67+
68+
const check = makeCheck.bind(null, product1, product2)
69+
70+
if (getProductOptions(product1, 'bundle_options').length || getProductOptions(product2, 'bundle_options').length) {
71+
// bundle options skus are merged into one sku so we can't rely on 'sku'
72+
// by default we want to check server_item_id ('id'), we can also use 'checksum'
73+
return check(['id', 'checksum'])
74+
}
75+
76+
if (getProductOptions(product1, 'custom_options').length || getProductOptions(product2, 'custom_options').length) {
77+
// in admin panel we can add different sku for specific custom option so we can't rely on 'sku'
78+
// by default we want to check server_item_id ('id'), we can also use 'checksum'
79+
return check(['id', 'checksum'])
80+
}
81+
82+
if (getProductOptions(product1, 'configurable_item_options').length || getProductOptions(product2, 'configurable_item_options').length) {
83+
// 'sku' should be uniq for configurable products
84+
// we can't check 'id' because it is the same when user edit product in microcart, so it can give wrong result
85+
return check(['sku'])
86+
}
87+
88+
// by default we want to check if server_item_id is equal and check sku as fallback
89+
// this is for 'simple' and 'group' products
90+
return check(['id', 'sku'])
91+
}
92+
93+
module.exports = productsEquals

src/platform/magento2/o2m.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ if (fs.existsSync('../../models/order.schema.extension.json')) {
1515
orderSchemaExtension = require('../../models/order.schema.extension.json')
1616
}
1717
const validate = ajv.compile(merge(orderSchema, orderSchemaExtension));
18+
const productsEquals = require('./helpers/productsEquals')
1819

1920
function isNumeric (val) {
2021
return Number(parseFloat(val)).toString() === val;
@@ -107,9 +108,7 @@ function processSingleOrder (orderData, config, job, done, logger = console) {
107108
logger.info(THREAD_ID + '> ... and serverItems', serverItems)
108109

109110
for (const clientItem of clientItems) {
110-
const serverItem = serverItems.find((itm) => {
111-
return itm.sku === clientItem.sku || itm.sku.indexOf(clientItem.sku + '-') >= 0 /* bundle products */
112-
})
111+
const serverItem = serverItems.find(itm => productsEquals(itm, clientItem))
113112
if (!serverItem) {
114113
logger.info(THREAD_ID + '< No server item for ' + clientItem.sku)
115114
syncPromises.push(api.cart.update(null, cartId, { // use magento API
@@ -134,9 +133,7 @@ function processSingleOrder (orderData, config, job, done, logger = console) {
134133

135134
for (const serverItem of serverItems) {
136135
if (serverItem) {
137-
const clientItem = clientItems.find((itm) => {
138-
return itm.sku === serverItem.sku || serverItem.sku.indexOf(itm.sku + '-') >= 0 /* bundle products */
139-
})
136+
const clientItem = clientItems.find(itm => productsEquals(itm, serverItem))
140137
if (!clientItem) {
141138
logger.info(THREAD_ID + '< No client item for ' + serverItem.sku + ', removing from server cart') // use magento API
142139
syncPromises.push(api.cart.delete(null, cartId, { // delete server side item if not present if client's cart

0 commit comments

Comments
 (0)