"""Shared validation for sampling confirmation edits and imports.""" from datetime import date, datetime, timedelta REASONS = ('价格高', '质感差', '资质不全', '同质化严重') SAMPLE_STATUSES = ('未付款', '付款中', '样品到货中', '样品已到货', '不授权', '厂家无货', '停产', '其他') # Keep existing imported values editable without silently changing historical records. LEGACY_SAMPLE_STATUSES = ('不做', '产品停产', '厂家无现货', '已到货', '待寄出') TEXT_LIMITS = { 'sample_status': 50, 'oa_approval_no': 100, 'express_tracking_no': 100, 'remark': 65535, 'product_code': 100, 'final_product_name': 300, 'platform': 200, 'sample_confirmed': 50, 'sample_confirmation_reason': 65535, 'barcode': 100, 'product_unit_price': 1000, } EDITABLE = frozenset((*TEXT_LIMITS, 'product_unit_price', 'arrival_date')) def validate_confirmation_values(data): values = {} for key in EDITABLE & data.keys(): raw = data[key] value = str(raw).strip() if raw is not None else '' if key == 'arrival_date': try: values[key] = date.fromisoformat(value) if value else None except ValueError: raise ValueError('到样日期格式错误,请填写YYYY-MM-DD') else: size = len(value.encode('utf-8')) if TEXT_LIMITS[key] == 65535 else len(value) if size > TEXT_LIMITS[key]: raise ValueError('字段内容过长') if key == 'sample_confirmed' and value not in ('', '做', '不做'): raise ValueError('样品确认请选择做或不做') if key == 'sample_status' and value not in ('', *SAMPLE_STATUSES, *LEGACY_SAMPLE_STATUSES): raise ValueError('请选择有效的样品状态') if key == 'sample_confirmation_reason' and value: if value == '其他' or value.startswith('其他:'): other = str(data.get('other_reason') or '').strip() if value == '其他' else value[3:].strip() if not other: raise ValueError('选择其他时,请填写具体原因') if any(reason in other for reason in REASONS): raise ValueError('其他原因不能包含价格高、质感差、资质不全、同质化严重') value = '其他:' + other if len(value.encode('utf-8')) > 65535: raise ValueError('其他原因内容过长') elif value not in (*REASONS, '无'): raise ValueError('请选择有效的样品确认原因') values[key] = value if str(data.get('sample_confirmed') or '').strip() == '做' and not str( data.get('sample_confirmation_reason') or '').strip(): values['sample_confirmation_reason'] = '无' return values def confirmation_reminder_state(arrival_date, sample_confirmed, as_of=None): """Return the derived 10-day confirmation reminder state without sending messages.""" if not arrival_date: return {'due_date': None, 'pending': False} if isinstance(arrival_date, str): try: arrival_date = date.fromisoformat(arrival_date[:10]) except ValueError: return {'due_date': None, 'pending': False} if isinstance(arrival_date, datetime): arrival_date = arrival_date.date() due_date = arrival_date + timedelta(days=10) today = as_of or date.today() pending = not str(sample_confirmed or '').strip() and today >= due_date return {'due_date': due_date.isoformat(), 'pending': pending}