import logging from typing import Any, Dict, List from celery import shared_task from business import services as business_services logger = logging.getLogger(__name__) @shared_task(bind=True) def create_purchase_order_stock_entries( self, *, purchase_order_id: int, warehouse_id: int, items: List[Dict[str, Any]], created_by_id: int | None = None, ) -> Dict[str, Any]: payload = business_services.create_purchase_order_stock_entries_sync( purchase_order_id=purchase_order_id, warehouse_id=warehouse_id, items=items, created_by_id=created_by_id, ) payload['task_id'] = self.request.id return payload @shared_task(bind=True) def sync_external_customer_finance_scheduled(self) -> Dict[str, Any]: """ 定时任务:同步指定客户列表的外部财务(对账)数据。 每天凌晨 5 点由 celery beat 触发,逐个客户调用 sync_customer_finance()。 单个客户失败时跳过并记录错误,不影响其他客户。 """ from django.conf import settings from basic_info.models import Employee from business.external_finance_sync import sync_customer_finance customer_names = getattr(settings, 'FINANCE_SYNC_CUSTOMER_NAMES', []) operator_id = getattr(settings, 'HAOBUYE_FINANCE_SYNC_OPERATOR_ID', 0) if not customer_names: logger.warning('[finance_sync_scheduled] FINANCE_SYNC_CUSTOMER_NAMES 为空,跳过') return {'status': 'skipped', 'reason': 'empty_customer_list'} if not operator_id: logger.error('[finance_sync_scheduled] HAOBUYE_FINANCE_SYNC_OPERATOR_ID 未配置') return {'status': 'error', 'reason': 'missing_operator_id'} try: operator = Employee.objects.select_related('merchant').get(id=operator_id) except Employee.DoesNotExist: logger.error('[finance_sync_scheduled] 经办人不存在: id=%s', operator_id) return {'status': 'error', 'reason': f'operator_not_found:{operator_id}'} results: List[Dict[str, Any]] = [] errors: List[Dict[str, str]] = [] for customer_name in customer_names: try: logger.info('[finance_sync_scheduled] 开始同步客户: %s', customer_name) result = sync_customer_finance( customer_name=customer_name, operator=operator, allow_create_customer=True, force_update=True, ) results.append({ 'customer_name': customer_name, 'created_count': result.get('created_count', 0), 'skipped_existing_count': result.get('skipped_existing_count', 0), 'external_business_created_count': result.get('external_business_created_count', 0), }) logger.info( '[finance_sync_scheduled] 客户 %s 同步完成: created=%s, skipped=%s', customer_name, result.get('created_count', 0), result.get('skipped_existing_count', 0), ) except Exception as exc: logger.exception('[finance_sync_scheduled] 客户 %s 同步失败', customer_name) errors.append({'customer_name': customer_name, 'error': str(exc)}) summary = { 'status': 'completed', 'total_customers': len(customer_names), 'success_count': len(results), 'error_count': len(errors), 'results': results, 'errors': errors, } logger.info( '[finance_sync_scheduled] 全部完成: success=%s, errors=%s', len(results), len(errors), ) return summary @shared_task(bind=True) def create_sales_order_stock_entries( self, *, sales_order_id: int, warehouse_id: int, items: List[Dict[str, Any]], created_by_id: int | None = None, ) -> Dict[str, Any]: payload = business_services.create_sales_order_stock_entries_sync( sales_order_id=sales_order_id, warehouse_id=warehouse_id, items=items, created_by_id=created_by_id, ) payload['task_id'] = self.request.id return payload @shared_task(bind=True) def create_purchase_return_order_stock_entries( self, *, purchase_return_order_id: int, warehouse_id: int, items: List[Dict[str, Any]], created_by_id: int | None = None, ) -> Dict[str, Any]: payload = business_services.create_purchase_return_order_stock_entries_sync( purchase_return_order_id=purchase_return_order_id, warehouse_id=warehouse_id, items=items, created_by_id=created_by_id, ) payload['task_id'] = self.request.id return payload @shared_task(bind=True) def create_sales_return_order_stock_entries( self, *, sales_return_order_id: int, warehouse_id: int, items: List[Dict[str, Any]], created_by_id: int | None = None, ) -> Dict[str, Any]: payload = business_services.create_sales_return_order_stock_entries_sync( sales_return_order_id=sales_return_order_id, warehouse_id=warehouse_id, items=items, created_by_id=created_by_id, ) payload['task_id'] = self.request.id return payload @shared_task(bind=True) def notify_business_order_speech(self, *, text: str) -> Dict[str, Any]: try: from flower.utils import play_speech response = play_speech(text=text, timeout_seconds=3.0) return { 'status': 'sent', 'task_id': self.request.id, 'status_code': response.status_code, 'raw': response.raw, } except Exception as exc: logger.exception('[business.tasks] 业务单据语音播报失败(已忽略)') return { 'status': 'error', 'task_id': self.request.id, 'error': str(exc), }