新版本
This commit is contained in:
@@ -285,6 +285,16 @@ class UnitConverter:
|
||||
|
||||
logger.debug(f"解析规格: {spec}")
|
||||
|
||||
# 新增:处理“1件=12桶/袋/盒...”等等式规格,统一为1*12
|
||||
eq_match = re.match(r'(\d+(?:\.\d+)?)\s*(?:件|箱|提|盒)\s*[==]\s*(\d+)\s*(?:瓶|桶|盒|支|个|袋|罐|包|卷)', spec)
|
||||
if eq_match:
|
||||
try:
|
||||
level2 = int(eq_match.group(2))
|
||||
logger.info(f"解析等式规格: {spec} -> 1*{level2}")
|
||||
return 1, level2, None
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# 处理三级包装,如1*5*12
|
||||
three_level_match = re.match(r'(\d+)[*](\d+)[*](\d+)', spec)
|
||||
if three_level_match:
|
||||
@@ -522,4 +532,4 @@ class UnitConverter:
|
||||
更新是否成功
|
||||
"""
|
||||
self.special_barcodes = new_mappings
|
||||
return self.save_barcode_mappings(new_mappings)
|
||||
return self.save_barcode_mappings(new_mappings)
|
||||
|
||||
@@ -11,7 +11,7 @@ import numpy as np
|
||||
import xlrd
|
||||
import xlwt
|
||||
from xlutils.copy import copy as xlcopy
|
||||
from typing import Dict, List, Optional, Tuple, Union, Any
|
||||
from typing import Dict, List, Optional, Tuple, Union, Any, Callable
|
||||
from datetime import datetime
|
||||
|
||||
from ...config.settings import ConfigManager
|
||||
@@ -414,7 +414,7 @@ class PurchaseOrderMerger:
|
||||
logger.error(f"创建合并采购单时出错: {e}")
|
||||
return None
|
||||
|
||||
def process(self, file_paths: Optional[List[str]] = None) -> Optional[str]:
|
||||
def process(self, file_paths: Optional[List[str]] = None, progress_cb: Optional[Callable[[int], None]] = None) -> Optional[str]:
|
||||
"""
|
||||
处理采购单合并
|
||||
|
||||
@@ -427,6 +427,11 @@ class PurchaseOrderMerger:
|
||||
# 如果未指定文件路径,则获取所有采购单文件
|
||||
if file_paths is None:
|
||||
file_paths = self.get_purchase_orders()
|
||||
try:
|
||||
if progress_cb:
|
||||
progress_cb(97)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 检查是否有文件需要合并
|
||||
if not file_paths:
|
||||
@@ -438,16 +443,26 @@ class PurchaseOrderMerger:
|
||||
if merged_df is None:
|
||||
logger.error("合并采购单失败")
|
||||
return None
|
||||
try:
|
||||
if progress_cb:
|
||||
progress_cb(98)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 创建合并的采购单文件
|
||||
output_file = self.create_merged_purchase_order(merged_df)
|
||||
if output_file is None:
|
||||
logger.error("创建合并采购单文件失败")
|
||||
return None
|
||||
try:
|
||||
if progress_cb:
|
||||
progress_cb(100)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 记录已合并文件
|
||||
for file_path in file_paths:
|
||||
self.merged_files[file_path] = output_file
|
||||
self._save_merged_files()
|
||||
|
||||
return output_file
|
||||
return output_file
|
||||
|
||||
@@ -11,7 +11,7 @@ import numpy as np
|
||||
import xlrd
|
||||
import xlwt
|
||||
from xlutils.copy import copy as xlcopy
|
||||
from typing import Dict, List, Optional, Tuple, Union, Any
|
||||
from typing import Dict, List, Optional, Tuple, Union, Any, Callable
|
||||
from datetime import datetime
|
||||
|
||||
from ...config.settings import ConfigManager
|
||||
@@ -281,6 +281,36 @@ class ExcelProcessor:
|
||||
product['amount'] = row['小计']
|
||||
elif column_mapping.get('amount') and not pd.isna(row[column_mapping['amount']]):
|
||||
product['amount'] = row[column_mapping['amount']]
|
||||
# 根据金额判断赠品:金额为0、为空、或为o/O
|
||||
amt = product.get('amount', None)
|
||||
try:
|
||||
is_amt_gift = False
|
||||
if amt is None:
|
||||
is_amt_gift = True
|
||||
elif isinstance(amt, str):
|
||||
s = amt.strip()
|
||||
if s == '' or s.lower() == 'o' or s == '0' or s == '○':
|
||||
is_amt_gift = True
|
||||
else:
|
||||
amt_clean = re.sub(r'[^\d\.,]', '', s)
|
||||
if ',' in amt_clean and '.' not in amt_clean:
|
||||
amt_clean = amt_clean.replace(',', '.')
|
||||
elif ',' in amt_clean and '.' in amt_clean:
|
||||
amt_clean = amt_clean.replace(',', '')
|
||||
if amt_clean:
|
||||
try:
|
||||
is_amt_gift = float(amt_clean) == 0.0
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
is_amt_gift = float(amt) == 0.0
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if is_amt_gift:
|
||||
product['is_gift'] = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 提取数量
|
||||
if '数量' in df.columns and not pd.isna(row['数量']):
|
||||
@@ -472,7 +502,7 @@ class ExcelProcessor:
|
||||
logger.warning(f"通过金额和单价计算数量失败: {e}")
|
||||
|
||||
# 判断是否为赠品(价格为0)
|
||||
is_gift = price == 0
|
||||
is_gift = bool(product.get('is_gift', False)) or (price == 0)
|
||||
|
||||
logger.info(f"处理商品: 条码={barcode}, 数量={quantity}, 单价={price}, 是否赠品={is_gift}")
|
||||
|
||||
@@ -631,7 +661,7 @@ class ExcelProcessor:
|
||||
logger.warning("无法识别表头行")
|
||||
return None
|
||||
|
||||
def process_specific_file(self, file_path: str) -> Optional[str]:
|
||||
def process_specific_file(self, file_path: str, progress_cb: Optional[Callable[[int], None]] = None) -> Optional[str]:
|
||||
"""
|
||||
处理指定的Excel文件
|
||||
|
||||
@@ -649,6 +679,11 @@ class ExcelProcessor:
|
||||
|
||||
try:
|
||||
# 读取Excel文件时不立即指定表头
|
||||
if progress_cb:
|
||||
try:
|
||||
progress_cb(92)
|
||||
except Exception:
|
||||
pass
|
||||
df = pd.read_excel(file_path, header=None)
|
||||
logger.info(f"成功读取Excel文件: {file_path}, 共 {len(df)} 行")
|
||||
|
||||
@@ -661,10 +696,20 @@ class ExcelProcessor:
|
||||
logger.info(f"识别到表头在第 {header_row+1} 行")
|
||||
|
||||
# 重新读取Excel,正确指定表头行
|
||||
if progress_cb:
|
||||
try:
|
||||
progress_cb(94)
|
||||
except Exception:
|
||||
pass
|
||||
df = pd.read_excel(file_path, header=header_row)
|
||||
logger.info(f"使用表头行重新读取数据,共 {len(df)} 行有效数据")
|
||||
|
||||
# 提取商品信息
|
||||
if progress_cb:
|
||||
try:
|
||||
progress_cb(96)
|
||||
except Exception:
|
||||
pass
|
||||
products = self.extract_product_info(df)
|
||||
|
||||
if not products:
|
||||
@@ -685,6 +730,11 @@ class ExcelProcessor:
|
||||
|
||||
# 不再自动打开输出目录
|
||||
logger.info(f"采购单已保存到: {output_file}")
|
||||
if progress_cb:
|
||||
try:
|
||||
progress_cb(100)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return output_file
|
||||
|
||||
@@ -694,7 +744,7 @@ class ExcelProcessor:
|
||||
logger.error(f"处理Excel文件时出错: {file_path}, 错误: {e}")
|
||||
return None
|
||||
|
||||
def process_latest_file(self) -> Optional[str]:
|
||||
def process_latest_file(self, progress_cb: Optional[Callable[[int], None]] = None) -> Optional[str]:
|
||||
"""
|
||||
处理最新的Excel文件
|
||||
|
||||
@@ -708,7 +758,7 @@ class ExcelProcessor:
|
||||
return None
|
||||
|
||||
# 处理文件
|
||||
return self.process_specific_file(latest_file)
|
||||
return self.process_specific_file(latest_file, progress_cb=progress_cb)
|
||||
|
||||
def _detect_column_mapping(self, df: pd.DataFrame) -> Dict[str, str]:
|
||||
"""
|
||||
@@ -889,6 +939,11 @@ class ExcelProcessor:
|
||||
|
||||
logger.debug(f"清理后的规格字符串: {spec_str}")
|
||||
|
||||
# 新增:匹配“1件=12桶/袋/盒…”等等式规格,取右侧数量作为包装数量
|
||||
eq_match = re.search(r'(\d+(?:\.\d+)?)\s*(?:件|箱|提|盒)\s*[==]\s*(\d+)\s*(?:瓶|桶|盒|支|个|袋|罐|包|卷)', spec_str)
|
||||
if eq_match:
|
||||
return int(eq_match.group(2))
|
||||
|
||||
# 匹配带单位的格式,如"5kg*6"、"450g*15"、"450ml*15"
|
||||
weight_pattern = r'(\d+(?:\.\d+)?)\s*(?:kg|KG|千克|公斤)[*×](\d+)'
|
||||
match = re.search(weight_pattern, spec_str)
|
||||
@@ -946,4 +1001,4 @@ class ExcelProcessor:
|
||||
except Exception as e:
|
||||
logger.warning(f"解析规格'{spec_str}'时出错: {e}")
|
||||
|
||||
return None
|
||||
return None
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
"""
|
||||
单位转换器测试模块
|
||||
---------------
|
||||
测试单位转换和条码映射逻辑
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from typing import Dict, Any
|
||||
|
||||
# 添加项目根目录到Python路径
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
|
||||
|
||||
from app.core.excel.converter import UnitConverter
|
||||
from app.core.excel.validators import ProductValidator
|
||||
|
||||
|
||||
class TestUnitConverter(unittest.TestCase):
|
||||
"""
|
||||
测试单位转换器功能
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
测试前的准备工作
|
||||
"""
|
||||
self.converter = UnitConverter()
|
||||
|
||||
def test_jian_unit_conversion(self):
|
||||
"""
|
||||
测试"件"单位的转换
|
||||
"""
|
||||
# 准备测试数据
|
||||
product = {
|
||||
'barcode': '6954767400129',
|
||||
'name': '美汁源果粒橙1.8L*8瓶',
|
||||
'specification': '1.8L*8',
|
||||
'quantity': 1.0,
|
||||
'unit': '件',
|
||||
'price': 65.0
|
||||
}
|
||||
|
||||
# 执行转换
|
||||
result = self.converter.process_unit_conversion(product)
|
||||
|
||||
# 验证结果
|
||||
self.assertEqual(result['quantity'], 8.0)
|
||||
self.assertEqual(result['price'], 8.125)
|
||||
self.assertEqual(result['unit'], '瓶')
|
||||
|
||||
def test_box_unit_conversion(self):
|
||||
"""
|
||||
测试"箱"单位的转换
|
||||
"""
|
||||
# 准备测试数据
|
||||
product = {
|
||||
'barcode': '6925303721244',
|
||||
'name': '统一鲜橙多2L*6瓶',
|
||||
'specification': '2L*6',
|
||||
'quantity': 1.0,
|
||||
'unit': '箱',
|
||||
'price': 43.0
|
||||
}
|
||||
|
||||
# 执行转换
|
||||
result = self.converter.process_unit_conversion(product)
|
||||
|
||||
# 验证结果
|
||||
self.assertEqual(result['quantity'], 6.0)
|
||||
self.assertEqual(result['price'], 7.1666666666666667)
|
||||
self.assertEqual(result['unit'], '瓶')
|
||||
|
||||
def test_tihe_unit_conversion_level3(self):
|
||||
"""
|
||||
测试"提"单位的转换(三级规格)
|
||||
"""
|
||||
# 准备测试数据(三级规格:1*6*4,表示1排6提,每提4瓶)
|
||||
product = {
|
||||
'barcode': '6921168509347',
|
||||
'name': '农夫山泉550ml*24瓶',
|
||||
'specification': '1*6*4',
|
||||
'quantity': 2.0,
|
||||
'unit': '提',
|
||||
'price': 16.0
|
||||
}
|
||||
|
||||
# 执行转换
|
||||
result = self.converter.process_unit_conversion(product)
|
||||
|
||||
# 验证结果:三级规格,提单位特殊处理,数量*最后一级
|
||||
self.assertEqual(result['quantity'], 8.0) # 2提 * 4瓶/提
|
||||
self.assertEqual(result['price'], 4.0) # 16元/提 ÷ 4瓶/提
|
||||
self.assertEqual(result['unit'], '瓶')
|
||||
|
||||
def test_tihe_unit_conversion_level2(self):
|
||||
"""
|
||||
测试"提"单位的转换(二级规格)
|
||||
"""
|
||||
# 准备测试数据(二级规格:1*4,表示每件4提)
|
||||
product = {
|
||||
'barcode': '6921168509347',
|
||||
'name': '农夫山泉550ml*4瓶',
|
||||
'specification': '1*4',
|
||||
'quantity': 5.0,
|
||||
'unit': '提',
|
||||
'price': 10.0
|
||||
}
|
||||
|
||||
# 执行转换
|
||||
result = self.converter.process_unit_conversion(product)
|
||||
|
||||
# 验证结果:二级规格,提单位保持不变
|
||||
self.assertEqual(result['quantity'], 5.0)
|
||||
self.assertEqual(result['price'], 10.0)
|
||||
self.assertEqual(result['unit'], '提')
|
||||
|
||||
def test_barcode_mapping(self):
|
||||
"""
|
||||
测试条码映射
|
||||
"""
|
||||
# 准备测试数据(使用需要被映射的条码)
|
||||
product = {
|
||||
'barcode': '6920584471055', # 这个条码应映射到6920584471017
|
||||
'name': '测试映射条码商品',
|
||||
'specification': '1*12',
|
||||
'quantity': 1.0,
|
||||
'unit': '件',
|
||||
'price': 60.0
|
||||
}
|
||||
|
||||
# 执行转换
|
||||
result = self.converter.process_unit_conversion(product)
|
||||
|
||||
# 验证结果:条码应该被映射
|
||||
self.assertEqual(result['barcode'], '6920584471017')
|
||||
self.assertEqual(result['quantity'], 12.0) # 同时处理件单位转换
|
||||
self.assertEqual(result['price'], 5.0) # 60元/件 ÷ 12瓶/件
|
||||
self.assertEqual(result['unit'], '瓶')
|
||||
|
||||
def test_special_barcode_multiplier(self):
|
||||
"""
|
||||
测试特殊条码的倍数处理
|
||||
"""
|
||||
# 准备测试数据(使用特殊条码)
|
||||
product = {
|
||||
'barcode': '6925019900087', # 特殊条码:数量*10,单位转瓶
|
||||
'name': '特殊条码商品',
|
||||
'specification': '1*10',
|
||||
'quantity': 2.0,
|
||||
'unit': '箱',
|
||||
'price': 100.0
|
||||
}
|
||||
|
||||
# 执行转换
|
||||
result = self.converter.process_unit_conversion(product)
|
||||
|
||||
# 验证结果:特殊条码乘数应该生效
|
||||
self.assertEqual(result['quantity'], 20.0) # 2箱 * 10倍数
|
||||
self.assertEqual(result['price'], 5.0) # 100元/箱 ÷ 10倍数/箱
|
||||
self.assertEqual(result['unit'], '瓶')
|
||||
|
||||
|
||||
class TestProductValidator(unittest.TestCase):
|
||||
"""
|
||||
测试商品数据验证器功能
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
测试前的准备工作
|
||||
"""
|
||||
self.validator = ProductValidator()
|
||||
|
||||
def test_validate_barcode(self):
|
||||
"""
|
||||
测试条码验证
|
||||
"""
|
||||
# 测试有效条码
|
||||
is_valid, barcode, error = self.validator.validate_barcode('6925303721244')
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(barcode, '6925303721244')
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试包含非数字字符的条码
|
||||
is_valid, barcode, error = self.validator.validate_barcode('6925303-721244')
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(barcode, '6925303721244')
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试5开头的条码修正
|
||||
is_valid, barcode, error = self.validator.validate_barcode('5925303721244')
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(barcode, '6925303721244')
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试过短的条码
|
||||
is_valid, barcode, error = self.validator.validate_barcode('12345')
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(barcode, '12345')
|
||||
self.assertIn("条码长度异常", error)
|
||||
|
||||
# 测试仓库标识
|
||||
is_valid, barcode, error = self.validator.validate_barcode('仓库')
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(barcode, '仓库')
|
||||
self.assertEqual(error, "条码为仓库标识")
|
||||
|
||||
# 测试空值
|
||||
is_valid, barcode, error = self.validator.validate_barcode(None)
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(barcode, "")
|
||||
self.assertEqual(error, "条码为空")
|
||||
|
||||
def test_validate_quantity(self):
|
||||
"""
|
||||
测试数量验证
|
||||
"""
|
||||
# 测试有效数量
|
||||
is_valid, quantity, error = self.validator.validate_quantity(10)
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(quantity, 10.0)
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试字符串数量
|
||||
is_valid, quantity, error = self.validator.validate_quantity("25.5")
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(quantity, 25.5)
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试带单位的数量
|
||||
is_valid, quantity, error = self.validator.validate_quantity("30瓶")
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(quantity, 30.0)
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试零数量
|
||||
is_valid, quantity, error = self.validator.validate_quantity(0)
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(quantity, 0.0)
|
||||
self.assertIn("数量必须大于0", error)
|
||||
|
||||
# 测试负数量
|
||||
is_valid, quantity, error = self.validator.validate_quantity(-5)
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(quantity, 0.0)
|
||||
self.assertIn("数量必须大于0", error)
|
||||
|
||||
# 测试非数字
|
||||
is_valid, quantity, error = self.validator.validate_quantity("abc")
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(quantity, 0.0)
|
||||
self.assertIn("数量不包含数字", error)
|
||||
|
||||
# 测试空值
|
||||
is_valid, quantity, error = self.validator.validate_quantity(None)
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(quantity, 0.0)
|
||||
self.assertEqual(error, "数量为空")
|
||||
|
||||
def test_validate_price(self):
|
||||
"""
|
||||
测试单价验证
|
||||
"""
|
||||
# 测试有效单价
|
||||
is_valid, price, is_gift, error = self.validator.validate_price(12.5)
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(price, 12.5)
|
||||
self.assertFalse(is_gift)
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试字符串单价
|
||||
is_valid, price, is_gift, error = self.validator.validate_price("8.0")
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(price, 8.0)
|
||||
self.assertFalse(is_gift)
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试零单价(赠品)
|
||||
is_valid, price, is_gift, error = self.validator.validate_price(0)
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(price, 0.0)
|
||||
self.assertTrue(is_gift)
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试"赠品"标记
|
||||
is_valid, price, is_gift, error = self.validator.validate_price("赠品")
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(price, 0.0)
|
||||
self.assertTrue(is_gift)
|
||||
self.assertIsNone(error)
|
||||
|
||||
# 测试负单价
|
||||
is_valid, price, is_gift, error = self.validator.validate_price(-5)
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(price, 0.0)
|
||||
self.assertTrue(is_gift)
|
||||
self.assertIn("单价不能为负数", error)
|
||||
|
||||
# 测试空值
|
||||
is_valid, price, is_gift, error = self.validator.validate_price(None)
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(price, 0.0)
|
||||
self.assertTrue(is_gift)
|
||||
self.assertEqual(error, "单价为空,视为赠品")
|
||||
|
||||
def test_validate_product(self):
|
||||
"""
|
||||
测试商品数据验证
|
||||
"""
|
||||
# 准备测试数据(有效商品)
|
||||
product = {
|
||||
'barcode': '6954767400129',
|
||||
'name': '测试商品',
|
||||
'specification': '1*12',
|
||||
'quantity': 3.0,
|
||||
'price': 36.0,
|
||||
'unit': '件',
|
||||
'is_gift': False
|
||||
}
|
||||
|
||||
# 验证有效商品
|
||||
result = self.validator.validate_product(product)
|
||||
self.assertEqual(result['barcode'], '6954767400129')
|
||||
self.assertEqual(result['quantity'], 3.0)
|
||||
self.assertEqual(result['price'], 36.0)
|
||||
self.assertFalse(result['is_gift'])
|
||||
|
||||
# 验证赠品商品
|
||||
gift_product = product.copy()
|
||||
gift_product['price'] = 0
|
||||
result = self.validator.validate_product(gift_product)
|
||||
self.assertEqual(result['price'], 0.0)
|
||||
self.assertTrue(result['is_gift'])
|
||||
|
||||
# 验证需要修复的商品
|
||||
invalid_product = {
|
||||
'barcode': '5954767-400129', # 需要修复前缀和移除非数字
|
||||
'name': '测试商品',
|
||||
'specification': '1*12',
|
||||
'quantity': '2件', # 需要提取数字
|
||||
'price': '赠品', # 赠品标记
|
||||
'unit': '件',
|
||||
'is_gift': False
|
||||
}
|
||||
|
||||
result = self.validator.validate_product(invalid_product)
|
||||
self.assertEqual(result['barcode'], '6954767400129') # 5->6,移除 '-'
|
||||
self.assertEqual(result['quantity'], 2.0) # 提取数字
|
||||
self.assertEqual(result['price'], 0.0) # 赠品价格为0
|
||||
self.assertTrue(result['is_gift']) # 标记为赠品
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -225,6 +225,36 @@ class ProductValidator:
|
||||
validated_product['is_gift'] = True
|
||||
if error_msg:
|
||||
logger.info(error_msg)
|
||||
|
||||
amount = product.get('amount', None)
|
||||
try:
|
||||
is_amount_gift = False
|
||||
if amount is None:
|
||||
is_amount_gift = True
|
||||
elif isinstance(amount, str):
|
||||
s = amount.strip()
|
||||
if s == '' or s.lower() == 'o' or s == '0':
|
||||
is_amount_gift = True
|
||||
else:
|
||||
amt_clean = re.sub(r'[^\d\.,]', '', s)
|
||||
if ',' in amt_clean and '.' not in amt_clean:
|
||||
amt_clean = amt_clean.replace(',', '.')
|
||||
elif ',' in amt_clean and '.' in amt_clean:
|
||||
amt_clean = amt_clean.replace(',', '')
|
||||
if amt_clean:
|
||||
try:
|
||||
is_amount_gift = float(amt_clean) == 0.0
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
is_amount_gift = float(amount) == 0.0
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if is_amount_gift:
|
||||
validated_product['is_gift'] = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 验证数量
|
||||
quantity = product.get('quantity', None)
|
||||
@@ -268,4 +298,4 @@ class ProductValidator:
|
||||
logger.warning(f"数量验证失败: {error_msg}")
|
||||
validated_product['quantity'] = 0.0
|
||||
|
||||
return validated_product
|
||||
return validated_product
|
||||
|
||||
Reference in New Issue
Block a user