fix: allow filtering products with empty brands
This commit is contained in:
parent
ae0cae8cbf
commit
8c3aac43a9
@ -265,7 +265,9 @@ def products_list():
|
|||||||
if supplier_filter:
|
if supplier_filter:
|
||||||
where.append("(s.supplier_name LIKE %s OR p.supplier_name_match LIKE %s)")
|
where.append("(s.supplier_name LIKE %s OR p.supplier_name_match LIKE %s)")
|
||||||
params.extend([f"%{supplier_filter}%", f"%{supplier_filter}%"])
|
params.extend([f"%{supplier_filter}%", f"%{supplier_filter}%"])
|
||||||
if brand_filter:
|
if brand_filter == '__EMPTY__':
|
||||||
|
where.append("(p.brand IS NULL OR TRIM(p.brand)='')")
|
||||||
|
elif brand_filter:
|
||||||
where.append("TRIM(p.brand)=%s")
|
where.append("TRIM(p.brand)=%s")
|
||||||
params.append(brand_filter)
|
params.append(brand_filter)
|
||||||
if category1_filter:
|
if category1_filter:
|
||||||
@ -405,10 +407,17 @@ def products_filters():
|
|||||||
f"SELECT DISTINCT category1 FROM products WHERE {scope_clause}"
|
f"SELECT DISTINCT category1 FROM products WHERE {scope_clause}"
|
||||||
"category1 IS NOT NULL AND category1 != '' ORDER BY category1"
|
"category1 IS NOT NULL AND category1 != '' ORDER BY category1"
|
||||||
)
|
)
|
||||||
|
empty_brand_count = query(
|
||||||
|
f"SELECT COUNT(*) AS cnt FROM products WHERE {scope_clause}"
|
||||||
|
"(brand IS NULL OR TRIM(brand) = '')"
|
||||||
|
)[0]['cnt']
|
||||||
|
brand_values = [b['brand'] for b in brands]
|
||||||
|
if empty_brand_count:
|
||||||
|
brand_values.insert(0, '__EMPTY__')
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"code": 0,
|
"code": 0,
|
||||||
"data": {
|
"data": {
|
||||||
"brands": [b['brand'] for b in brands],
|
"brands": brand_values,
|
||||||
"categories": [c['category1'] for c in categories]
|
"categories": [c['category1'] for c in categories]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -2086,7 +2095,9 @@ def products_recycle_bin():
|
|||||||
if supplier_filter:
|
if supplier_filter:
|
||||||
where.append("(p.supplier_name_match LIKE %s OR s.supplier_name LIKE %s)")
|
where.append("(p.supplier_name_match LIKE %s OR s.supplier_name LIKE %s)")
|
||||||
params.extend([f"%{supplier_filter}%", f"%{supplier_filter}%"])
|
params.extend([f"%{supplier_filter}%", f"%{supplier_filter}%"])
|
||||||
if brand_filter:
|
if brand_filter == '__EMPTY__':
|
||||||
|
where.append("(p.brand IS NULL OR TRIM(p.brand)='')")
|
||||||
|
elif brand_filter:
|
||||||
where.append("TRIM(p.brand)=%s")
|
where.append("TRIM(p.brand)=%s")
|
||||||
params.append(brand_filter)
|
params.append(brand_filter)
|
||||||
if product_type:
|
if product_type:
|
||||||
|
|||||||
@ -8322,7 +8322,8 @@ function loadProductBrandFilter(tab) {
|
|||||||
if (!document.body.contains(sel) || !res || res.code !== 0) return;
|
if (!document.body.contains(sel) || !res || res.code !== 0) return;
|
||||||
var brands = (res.data && res.data.brands) || [];
|
var brands = (res.data && res.data.brands) || [];
|
||||||
sel.innerHTML = '<option value="">全部品牌</option>' + brands.map(function(brand) {
|
sel.innerHTML = '<option value="">全部品牌</option>' + brands.map(function(brand) {
|
||||||
return '<option value="' + escHtml(brand) + '">' + escHtml(brand) + '</option>';
|
var label = brand === '__EMPTY__' ? '(品牌为空)' : brand;
|
||||||
|
return '<option value="' + escHtml(brand) + '">' + escHtml(label) + '</option>';
|
||||||
}).join('');
|
}).join('');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ class ProductBrandFilterStaticTest(unittest.TestCase):
|
|||||||
self.assertIn("loadProductBrandFilter('成品')", APP_JS)
|
self.assertIn("loadProductBrandFilter('成品')", APP_JS)
|
||||||
self.assertIn('loadProductBrandFilter(tab)', APP_JS)
|
self.assertIn('loadProductBrandFilter(tab)', APP_JS)
|
||||||
self.assertGreaterEqual(APP_JS.count("url += '&brand='"), 2)
|
self.assertGreaterEqual(APP_JS.count("url += '&brand='"), 2)
|
||||||
|
self.assertIn("brand === '__EMPTY__' ? '(品牌为空)' : brand", APP_JS)
|
||||||
|
|
||||||
|
|
||||||
class ProductBrandFilterApiTest(unittest.TestCase):
|
class ProductBrandFilterApiTest(unittest.TestCase):
|
||||||
@ -35,16 +36,43 @@ class ProductBrandFilterApiTest(unittest.TestCase):
|
|||||||
self.assertIn('TRIM(p.brand)=%s', query.call_args_list[0].args[0])
|
self.assertIn('TRIM(p.brand)=%s', query.call_args_list[0].args[0])
|
||||||
self.assertEqual(query.call_args_list[0].args[1], ['无'])
|
self.assertEqual(query.call_args_list[0].args[1], ['无'])
|
||||||
|
|
||||||
|
def test_active_products_filter_empty_brand(self):
|
||||||
|
with mock.patch.object(products, 'query', side_effect=[[], [{'cnt': 0}], []]) as query:
|
||||||
|
response = self.client.get('/api/products/list?brand=__EMPTY__')
|
||||||
|
|
||||||
|
self.assertEqual(response.get_json()['code'], 0)
|
||||||
|
self.assertIn("(p.brand IS NULL OR TRIM(p.brand)='')", query.call_args_list[1].args[0])
|
||||||
|
self.assertEqual(query.call_args_list[1].args[1], [])
|
||||||
|
|
||||||
|
def test_recycle_bin_filters_empty_brand(self):
|
||||||
|
with mock.patch.object(products, 'query', side_effect=[[{'cnt': 0}], []]) as query:
|
||||||
|
response = self.client.get('/api/products/recycle_bin?brand=__EMPTY__')
|
||||||
|
|
||||||
|
self.assertEqual(response.get_json()['code'], 0)
|
||||||
|
self.assertIn("(p.brand IS NULL OR TRIM(p.brand)='')", query.call_args_list[0].args[0])
|
||||||
|
self.assertEqual(query.call_args_list[0].args[1], [])
|
||||||
|
|
||||||
def test_filter_options_can_be_scoped_to_recycle_bin(self):
|
def test_filter_options_can_be_scoped_to_recycle_bin(self):
|
||||||
with mock.patch.object(products, 'query', side_effect=[
|
with mock.patch.object(products, 'query', side_effect=[
|
||||||
[{'brand': '回收品牌'}],
|
[{'brand': '回收品牌'}],
|
||||||
[{'category1': '分类'}],
|
[{'category1': '分类'}],
|
||||||
|
[{'cnt': 2}],
|
||||||
]) as query:
|
]) as query:
|
||||||
response = self.client.get('/api/products/filters?scope=recycle')
|
response = self.client.get('/api/products/filters?scope=recycle')
|
||||||
|
|
||||||
self.assertEqual(response.get_json()['data']['brands'], ['回收品牌'])
|
self.assertEqual(response.get_json()['data']['brands'], ['__EMPTY__', '回收品牌'])
|
||||||
self.assertIn('deleted_at IS NOT NULL', query.call_args_list[0].args[0])
|
self.assertIn('deleted_at IS NOT NULL', query.call_args_list[0].args[0])
|
||||||
|
|
||||||
|
def test_filter_options_omit_empty_brand_when_scope_has_none(self):
|
||||||
|
with mock.patch.object(products, 'query', side_effect=[
|
||||||
|
[{'brand': '正常品牌'}],
|
||||||
|
[],
|
||||||
|
[{'cnt': 0}],
|
||||||
|
]):
|
||||||
|
response = self.client.get('/api/products/filters?scope=active')
|
||||||
|
|
||||||
|
self.assertEqual(response.get_json()['data']['brands'], ['正常品牌'])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user