forked from erp-dev/erp
677 lines
24 KiB
Python
677 lines
24 KiB
Python
from __future__ import annotations
|
||
|
||
from decimal import Decimal, InvalidOperation
|
||
from typing import Any, Dict, List, Optional
|
||
|
||
from django.conf import settings
|
||
from django.db import transaction
|
||
from django.core.exceptions import ValidationError
|
||
from django.utils import timezone
|
||
|
||
from basic_info import models as basic_info_models
|
||
from stock import models as stock_models
|
||
|
||
from . import models
|
||
from . import services as business_services
|
||
|
||
|
||
def render_pre_sales_order_created_markdown(
|
||
*,
|
||
pre_sales_order_id: str,
|
||
human_id: str,
|
||
created_at: str,
|
||
sender_label: str,
|
||
customer_name: str,
|
||
warehouse_name: str,
|
||
kind: str,
|
||
items_count: int,
|
||
) -> str:
|
||
followup_url_template = getattr(
|
||
settings,
|
||
"PRE_SALES_ORDER_CREATED_FOLLOWUP_URL_TEMPLATE",
|
||
"",
|
||
)
|
||
followup_url = ""
|
||
if followup_url_template:
|
||
try:
|
||
followup_url = str(followup_url_template).format(
|
||
pre_sales_order_id=str(pre_sales_order_id or "")
|
||
)
|
||
except Exception:
|
||
followup_url = ""
|
||
followup_line = (
|
||
f"- **跟进**:[点击跟进]({followup_url})\n"
|
||
f" {followup_url}\n"
|
||
if followup_url
|
||
else ""
|
||
)
|
||
|
||
template = getattr(
|
||
settings,
|
||
"PRE_SALES_ORDER_CREATED_WECOM_MARKDOWN_TEMPLATE",
|
||
(
|
||
"### 预销售单创建\n"
|
||
"\n"
|
||
"- **预销售单ID**:`{pre_sales_order_id}`\n"
|
||
"- **订单编号**:`{human_id}`\n"
|
||
"- **创建时间**:`{created_at}`\n"
|
||
"- **发送者**:{sender_label}\n"
|
||
"- **客户**:{customer_name}\n"
|
||
"- **仓库**:{warehouse_name}\n"
|
||
"- **类型**:{kind}\n"
|
||
"- **明细条数**:`{items_count}`\n"
|
||
"{followup_line}"
|
||
),
|
||
)
|
||
|
||
created_at_str = (created_at or "").strip()
|
||
if not created_at_str:
|
||
created_at_str = timezone.localtime(timezone.now()).strftime("%Y-%m-%d %H:%M:%S")
|
||
|
||
return template.format(
|
||
pre_sales_order_id=str(pre_sales_order_id or "-") or "-",
|
||
human_id=str(human_id or "-") or "-",
|
||
created_at=str(created_at_str),
|
||
sender_label=str(sender_label or "系统自动发送"),
|
||
customer_name=str(customer_name or "-"),
|
||
warehouse_name=str(warehouse_name or "-"),
|
||
kind=str(kind or "-"),
|
||
items_count=int(items_count) if items_count is not None else 0,
|
||
followup_line=str(followup_line),
|
||
)
|
||
|
||
|
||
def _to_decimal(value: Any, *, field_name: str) -> Decimal:
|
||
if value is None or value == '':
|
||
raise ValueError(f'{field_name} 不能为空')
|
||
try:
|
||
return Decimal(str(value))
|
||
except (InvalidOperation, ValueError) as exc:
|
||
raise ValueError(f'{field_name} 格式不正确') from exc
|
||
|
||
|
||
def _to_optional_int(value: Any) -> Optional[int]:
|
||
if value is None or value == '':
|
||
return None
|
||
try:
|
||
parsed = int(value)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('order_quantity 必须为整数') from exc
|
||
if parsed < 0:
|
||
raise ValueError('order_quantity 不能为负数')
|
||
return parsed
|
||
|
||
|
||
def _to_optional_positive_int(value: Any, *, field_name: str) -> Optional[int]:
|
||
if value is None or value == '':
|
||
return None
|
||
try:
|
||
parsed = int(value)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError(f'{field_name} 必须为整数') from exc
|
||
if parsed <= 0:
|
||
raise ValueError(f'{field_name} 必须大于 0')
|
||
return parsed
|
||
|
||
|
||
def _normalize_pre_sales_items(
|
||
*,
|
||
merchant: basic_info_models.Merchant,
|
||
items: List[Dict[str, Any]],
|
||
) -> List[Dict[str, Any]]:
|
||
if not isinstance(items, list) or not items:
|
||
raise ValueError('items 需要为非空数组')
|
||
|
||
product_ids: List[int] = []
|
||
for item in items:
|
||
if not isinstance(item, dict):
|
||
raise ValueError('items 每一项需要为对象')
|
||
product_id = item.get('product_id') or item.get('product')
|
||
if not product_id:
|
||
raise ValueError('明细缺少 product_id')
|
||
try:
|
||
product_ids.append(int(product_id))
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('product_id 必须为整数') from exc
|
||
|
||
products_by_id = basic_info_models.Product.objects.filter(
|
||
merchant=merchant,
|
||
id__in=product_ids,
|
||
).in_bulk(field_name='id')
|
||
|
||
normalized: List[Dict[str, Any]] = []
|
||
for item in items:
|
||
product_id = int(item.get('product_id') or item.get('product'))
|
||
product = products_by_id.get(product_id)
|
||
if not product:
|
||
raise ValueError(f'产品 {product_id} 不存在')
|
||
|
||
quantity = _to_decimal(item.get('quantity'), field_name='quantity')
|
||
unit = (item.get('unit') or '').strip()
|
||
if not unit:
|
||
unit = str(product.unit)
|
||
if not unit:
|
||
raise ValueError('unit 不能为空')
|
||
|
||
normalized.append(
|
||
{
|
||
'product_id': product_id,
|
||
'product_name': (item.get('product_name') or product.name or '').strip(),
|
||
'color': item.get('color'),
|
||
'quantity': quantity,
|
||
'unit': unit,
|
||
'spec': item.get('spec'),
|
||
'quantity_of_rolls': item.get('quantity_of_rolls'),
|
||
'num_of_rolls': _to_optional_positive_int(item.get('num_of_rolls'), field_name='num_of_rolls'),
|
||
'order_quantity': _to_optional_int(item.get('order_quantity')),
|
||
'remarks': item.get('remarks'),
|
||
}
|
||
)
|
||
|
||
return normalized
|
||
|
||
|
||
def _normalize_pre_purchase_items(
|
||
*,
|
||
merchant: basic_info_models.Merchant,
|
||
items: List[Dict[str, Any]],
|
||
) -> List[Dict[str, Any]]:
|
||
# 与预销售单明细字段保持一致的弱关联产品输入结构
|
||
return _normalize_pre_sales_items(merchant=merchant, items=items)
|
||
|
||
|
||
def list_pre_sales_orders(*, merchant: basic_info_models.Merchant):
|
||
return models.PreSalesOrder.objects.filter(merchant=merchant).select_related(
|
||
'merchant', 'customer', 'warehouse', 'created_by'
|
||
).prefetch_related('items')
|
||
|
||
|
||
def get_pre_sales_order(*, merchant: basic_info_models.Merchant, pre_sales_order_id: int) -> models.PreSalesOrder:
|
||
try:
|
||
return models.PreSalesOrder.objects.select_related(
|
||
'merchant', 'customer', 'warehouse', 'created_by'
|
||
).prefetch_related('items').get(id=pre_sales_order_id, merchant=merchant)
|
||
except models.PreSalesOrder.DoesNotExist as exc:
|
||
raise ValueError('预销售单不存在') from exc
|
||
|
||
|
||
def create_pre_sales_order(
|
||
*,
|
||
merchant: basic_info_models.Merchant,
|
||
customer_id: int | str | None,
|
||
warehouse_id: int | str | None,
|
||
operator: basic_info_models.Employee,
|
||
created_by=None,
|
||
kind: int | str | None = None,
|
||
items: List[Dict[str, Any]],
|
||
remarks: str | None = '',
|
||
) -> models.PreSalesOrder:
|
||
if not customer_id:
|
||
raise ValueError('缺少客户 ID')
|
||
if not warehouse_id:
|
||
raise ValueError('缺少仓库 ID')
|
||
|
||
try:
|
||
customer_id_int = int(customer_id)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('customer 必须为整数') from exc
|
||
|
||
try:
|
||
warehouse_id_int = int(warehouse_id)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('warehouse 必须为整数') from exc
|
||
|
||
kind_int: int | None
|
||
if kind is None or kind == '':
|
||
kind_int = None
|
||
else:
|
||
try:
|
||
kind_int = int(kind)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('kind 必须为整数') from exc
|
||
|
||
try:
|
||
customer = basic_info_models.Customer.objects.get(id=customer_id_int, merchant=merchant)
|
||
except basic_info_models.Customer.DoesNotExist as exc:
|
||
raise ValueError(f'客户 {customer_id_int} 不存在') from exc
|
||
|
||
try:
|
||
warehouse = basic_info_models.WareHouse.objects.get(id=warehouse_id_int, merchant=merchant)
|
||
except basic_info_models.WareHouse.DoesNotExist as exc:
|
||
raise ValueError(f'仓库 {warehouse_id_int} 不存在') from exc
|
||
|
||
if operator.merchant_id != merchant.id:
|
||
raise ValueError('经办人所属商户与预销售单所属商户不一致')
|
||
|
||
normalized_items = _normalize_pre_sales_items(merchant=merchant, items=items)
|
||
|
||
with transaction.atomic():
|
||
pre_sales_order = models.PreSalesOrder.objects.create(
|
||
merchant=merchant,
|
||
customer=customer,
|
||
warehouse=warehouse,
|
||
created_by=created_by,
|
||
operator=operator,
|
||
kind=kind_int or models.SalesOrderKindEnum.WHOLESALE,
|
||
remarks=remarks,
|
||
)
|
||
|
||
# enforce cross-merchant consistency
|
||
try:
|
||
pre_sales_order.full_clean()
|
||
except ValidationError as exc:
|
||
raise ValueError(str(exc)) from exc
|
||
|
||
models.PreSalesOrderItem.objects.bulk_create(
|
||
[
|
||
models.PreSalesOrderItem(
|
||
pre_sales_order=pre_sales_order,
|
||
product_id=item['product_id'],
|
||
product_name=item.get('product_name') or '',
|
||
color=item.get('color'),
|
||
quantity=item.get('quantity'),
|
||
unit=item.get('unit') or '',
|
||
spec=item.get('spec'),
|
||
quantity_of_rolls=item.get('quantity_of_rolls'),
|
||
num_of_rolls=item.get('num_of_rolls') or 1,
|
||
order_quantity=item.get('order_quantity'),
|
||
remarks=item.get('remarks'),
|
||
)
|
||
for item in normalized_items
|
||
]
|
||
)
|
||
|
||
# domain event: pre sales order created
|
||
# must be fired only after commit to avoid triggering on rollbacks
|
||
def _send_created_signal():
|
||
try:
|
||
from .signals import pre_sales_order_created
|
||
|
||
pre_sales_order_created.send(
|
||
sender=models.PreSalesOrder,
|
||
instance=pre_sales_order,
|
||
created_by=created_by,
|
||
operator=operator,
|
||
items_count=len(normalized_items),
|
||
)
|
||
except Exception:
|
||
import logging
|
||
|
||
logging.getLogger(__name__).exception(
|
||
'[business.pre_order_services] 触发 pre_sales_order_created signal 失败(已忽略)'
|
||
)
|
||
|
||
transaction.on_commit(_send_created_signal)
|
||
|
||
pre_sales_order.refresh_from_db()
|
||
return pre_sales_order
|
||
|
||
|
||
def update_pre_sales_order(
|
||
*,
|
||
pre_sales_order: models.PreSalesOrder,
|
||
customer_id: int | str | None = None,
|
||
warehouse_id: int | str | None = None,
|
||
kind: int | str | None = None,
|
||
items: List[Dict[str, Any]] | None = None,
|
||
remarks: str | None = None,
|
||
) -> models.PreSalesOrder:
|
||
merchant = pre_sales_order.merchant
|
||
|
||
new_customer = pre_sales_order.customer
|
||
if customer_id is not None:
|
||
if customer_id == '':
|
||
raise ValueError('customer 必须为整数')
|
||
try:
|
||
customer_id_int = int(customer_id)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('customer 必须为整数') from exc
|
||
try:
|
||
new_customer = basic_info_models.Customer.objects.get(id=customer_id_int, merchant=merchant)
|
||
except basic_info_models.Customer.DoesNotExist as exc:
|
||
raise ValueError(f'客户 {customer_id_int} 不存在') from exc
|
||
|
||
new_warehouse = pre_sales_order.warehouse
|
||
if warehouse_id is not None:
|
||
if warehouse_id == '':
|
||
raise ValueError('warehouse 必须为整数')
|
||
try:
|
||
warehouse_id_int = int(warehouse_id)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('warehouse 必须为整数') from exc
|
||
try:
|
||
new_warehouse = basic_info_models.WareHouse.objects.get(id=warehouse_id_int, merchant=merchant)
|
||
except basic_info_models.WareHouse.DoesNotExist as exc:
|
||
raise ValueError(f'仓库 {warehouse_id_int} 不存在') from exc
|
||
|
||
if kind is None or kind == '':
|
||
new_kind = pre_sales_order.kind
|
||
else:
|
||
try:
|
||
new_kind = int(kind)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('kind 必须为整数') from exc
|
||
new_remarks = remarks if remarks is not None else pre_sales_order.remarks
|
||
|
||
if items is None:
|
||
raise ValueError('items 需要为非空数组')
|
||
|
||
normalized_items = _normalize_pre_sales_items(merchant=merchant, items=items)
|
||
|
||
with transaction.atomic():
|
||
pre_sales_order.customer = new_customer
|
||
pre_sales_order.warehouse = new_warehouse
|
||
pre_sales_order.kind = new_kind
|
||
pre_sales_order.remarks = new_remarks
|
||
|
||
try:
|
||
pre_sales_order.full_clean()
|
||
except ValidationError as exc:
|
||
raise ValueError(str(exc)) from exc
|
||
|
||
pre_sales_order.save(update_fields=['customer', 'warehouse', 'kind', 'remarks', 'updated_at'])
|
||
|
||
pre_sales_order.items.all().delete()
|
||
models.PreSalesOrderItem.objects.bulk_create(
|
||
[
|
||
models.PreSalesOrderItem(
|
||
pre_sales_order=pre_sales_order,
|
||
product_id=item['product_id'],
|
||
product_name=item.get('product_name') or '',
|
||
color=item.get('color'),
|
||
quantity=item.get('quantity'),
|
||
unit=item.get('unit') or '',
|
||
spec=item.get('spec'),
|
||
quantity_of_rolls=item.get('quantity_of_rolls'),
|
||
num_of_rolls=item.get('num_of_rolls') or 1,
|
||
order_quantity=item.get('order_quantity'),
|
||
remarks=item.get('remarks'),
|
||
)
|
||
for item in normalized_items
|
||
]
|
||
)
|
||
|
||
pre_sales_order.refresh_from_db()
|
||
return pre_sales_order
|
||
|
||
|
||
def delete_pre_sales_order(*, pre_sales_order: models.PreSalesOrder) -> None:
|
||
with transaction.atomic():
|
||
pre_sales_order.delete()
|
||
|
||
|
||
def convert_pre_sales_order_to_sales_order(
|
||
*,
|
||
merchant: basic_info_models.Merchant,
|
||
pre_sales_order_id: int,
|
||
operator: basic_info_models.Employee,
|
||
created_by=None,
|
||
order_date=None,
|
||
) -> models.SalesOrder:
|
||
order = models.PreSalesOrder.objects.select_related(
|
||
'customer',
|
||
'warehouse',
|
||
'operator',
|
||
'created_by',
|
||
).prefetch_related('items', 'items__allocation_records').get(
|
||
id=pre_sales_order_id,
|
||
merchant=merchant,
|
||
)
|
||
|
||
warehouse = order.warehouse
|
||
if warehouse.mode != basic_info_models.WareHouseModeEnum.RESTRICT_IN_OUT:
|
||
raise PermissionError('仅支持严进严出仓库模式')
|
||
|
||
items_payload: List[Dict[str, Any]] = []
|
||
for item in order.items.all():
|
||
if not item.product_id:
|
||
raise ValueError(f'明细 {item.id} 缺少 product_id')
|
||
if item.quantity is None or str(item.quantity) == '':
|
||
raise ValueError(f'明细 {item.id} 缺少 quantity')
|
||
|
||
consume_ids: List[int] = []
|
||
for record in item.allocation_records.all():
|
||
if record.status != models.AllocationRecordStatusEnum.ACTIVE:
|
||
continue
|
||
consume_ids.extend(record.stock_id_list)
|
||
|
||
if not consume_ids:
|
||
raise ValueError(f'明细 {item.id} 缺少配货库存明细')
|
||
|
||
quantity_from_details: Decimal | None = None
|
||
if consume_ids:
|
||
details_by_id = stock_models.StockChangeDetail.objects.filter(
|
||
id__in=consume_ids,
|
||
merchant=merchant,
|
||
).in_bulk()
|
||
if details_by_id:
|
||
total = Decimal('0')
|
||
for detail in details_by_id.values():
|
||
total += detail.quantity
|
||
quantity_from_details = total
|
||
|
||
items_payload.append(
|
||
{
|
||
'product_id': item.product_id,
|
||
'price': '0',
|
||
'quantity': str(quantity_from_details or item.quantity),
|
||
'unit': item.unit or None,
|
||
'color': item.color,
|
||
'spec': item.spec,
|
||
'remarks': item.remarks,
|
||
'order_quantity': item.order_quantity,
|
||
'consume_detail_ids': consume_ids,
|
||
}
|
||
)
|
||
|
||
resolved_order_date = order_date or (order.created_at.date() if order.created_at else timezone.localdate())
|
||
|
||
return business_services.create_sales_order(
|
||
merchant=merchant,
|
||
customer=order.customer,
|
||
order_date=resolved_order_date,
|
||
warehouse=warehouse,
|
||
operator=operator,
|
||
items=items_payload,
|
||
remarks=order.remarks or '',
|
||
created_by=created_by,
|
||
from_pre_sales_order_id=order.id,
|
||
)
|
||
|
||
|
||
def list_pre_purchase_orders(*, merchant: basic_info_models.Merchant):
|
||
return models.PrePurchaseOrder.objects.filter(merchant=merchant).select_related(
|
||
'merchant', 'supplier', 'warehouse', 'created_by'
|
||
).prefetch_related('items')
|
||
|
||
|
||
def get_pre_purchase_order(*, merchant: basic_info_models.Merchant, pre_purchase_order_id: int) -> models.PrePurchaseOrder:
|
||
try:
|
||
return models.PrePurchaseOrder.objects.select_related(
|
||
'merchant', 'supplier', 'warehouse', 'created_by'
|
||
).prefetch_related('items').get(id=pre_purchase_order_id, merchant=merchant)
|
||
except models.PrePurchaseOrder.DoesNotExist as exc:
|
||
raise ValueError('预采购单不存在') from exc
|
||
|
||
|
||
def create_pre_purchase_order(
|
||
*,
|
||
merchant: basic_info_models.Merchant,
|
||
supplier_id: int | str | None,
|
||
warehouse_id: int | str | None,
|
||
operator: basic_info_models.Employee,
|
||
created_by=None,
|
||
kind: int | str | None = None,
|
||
items: List[Dict[str, Any]],
|
||
remarks: str | None = '',
|
||
) -> models.PrePurchaseOrder:
|
||
if not supplier_id:
|
||
raise ValueError('缺少供应商 ID')
|
||
if not warehouse_id:
|
||
raise ValueError('缺少仓库 ID')
|
||
|
||
try:
|
||
supplier_id_int = int(supplier_id)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('supplier 必须为整数') from exc
|
||
|
||
try:
|
||
warehouse_id_int = int(warehouse_id)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('warehouse 必须为整数') from exc
|
||
|
||
kind_int: int | None
|
||
if kind is None or kind == '':
|
||
kind_int = None
|
||
else:
|
||
try:
|
||
kind_int = int(kind)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('kind 必须为整数') from exc
|
||
|
||
try:
|
||
supplier = basic_info_models.Supplier.objects.get(id=supplier_id_int, merchant=merchant)
|
||
except basic_info_models.Supplier.DoesNotExist as exc:
|
||
raise ValueError(f'供应商 {supplier_id_int} 不存在') from exc
|
||
|
||
try:
|
||
warehouse = basic_info_models.WareHouse.objects.get(id=warehouse_id_int, merchant=merchant)
|
||
except basic_info_models.WareHouse.DoesNotExist as exc:
|
||
raise ValueError(f'仓库 {warehouse_id_int} 不存在') from exc
|
||
|
||
if operator.merchant_id != merchant.id:
|
||
raise ValueError('经办人所属商户与预采购单所属商户不一致')
|
||
|
||
normalized_items = _normalize_pre_purchase_items(merchant=merchant, items=items)
|
||
|
||
with transaction.atomic():
|
||
pre_purchase_order = models.PrePurchaseOrder.objects.create(
|
||
merchant=merchant,
|
||
supplier=supplier,
|
||
warehouse=warehouse,
|
||
created_by=created_by,
|
||
operator=operator,
|
||
kind=kind_int or models.PurchaseOrderKindEnum.WHOLESALE,
|
||
remarks=remarks,
|
||
)
|
||
|
||
try:
|
||
pre_purchase_order.full_clean()
|
||
except ValidationError as exc:
|
||
raise ValueError(str(exc)) from exc
|
||
|
||
models.PrePurchaseOrderItem.objects.bulk_create(
|
||
[
|
||
models.PrePurchaseOrderItem(
|
||
pre_purchase_order=pre_purchase_order,
|
||
product_id=item['product_id'],
|
||
product_name=item.get('product_name') or '',
|
||
color=item.get('color'),
|
||
quantity=item.get('quantity'),
|
||
unit=item.get('unit') or '',
|
||
spec=item.get('spec'),
|
||
quantity_of_rolls=item.get('quantity_of_rolls'),
|
||
num_of_rolls=item.get('num_of_rolls') or 1,
|
||
order_quantity=item.get('order_quantity'),
|
||
remarks=item.get('remarks'),
|
||
)
|
||
for item in normalized_items
|
||
]
|
||
)
|
||
|
||
pre_purchase_order.refresh_from_db()
|
||
return pre_purchase_order
|
||
|
||
|
||
def update_pre_purchase_order(
|
||
*,
|
||
pre_purchase_order: models.PrePurchaseOrder,
|
||
supplier_id: int | str | None = None,
|
||
warehouse_id: int | str | None = None,
|
||
kind: int | str | None = None,
|
||
items: List[Dict[str, Any]] | None = None,
|
||
remarks: str | None = None,
|
||
) -> models.PrePurchaseOrder:
|
||
merchant = pre_purchase_order.merchant
|
||
|
||
new_supplier = pre_purchase_order.supplier
|
||
if supplier_id is not None:
|
||
if supplier_id == '':
|
||
raise ValueError('supplier 必须为整数')
|
||
try:
|
||
supplier_id_int = int(supplier_id)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('supplier 必须为整数') from exc
|
||
try:
|
||
new_supplier = basic_info_models.Supplier.objects.get(id=supplier_id_int, merchant=merchant)
|
||
except basic_info_models.Supplier.DoesNotExist as exc:
|
||
raise ValueError(f'供应商 {supplier_id_int} 不存在') from exc
|
||
|
||
new_warehouse = pre_purchase_order.warehouse
|
||
if warehouse_id is not None:
|
||
if warehouse_id == '':
|
||
raise ValueError('warehouse 必须为整数')
|
||
try:
|
||
warehouse_id_int = int(warehouse_id)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('warehouse 必须为整数') from exc
|
||
try:
|
||
new_warehouse = basic_info_models.WareHouse.objects.get(id=warehouse_id_int, merchant=merchant)
|
||
except basic_info_models.WareHouse.DoesNotExist as exc:
|
||
raise ValueError(f'仓库 {warehouse_id_int} 不存在') from exc
|
||
|
||
if kind is None or kind == '':
|
||
new_kind = pre_purchase_order.kind
|
||
else:
|
||
try:
|
||
new_kind = int(kind)
|
||
except (TypeError, ValueError) as exc:
|
||
raise ValueError('kind 必须为整数') from exc
|
||
|
||
new_remarks = remarks if remarks is not None else pre_purchase_order.remarks
|
||
|
||
if items is None:
|
||
raise ValueError('items 需要为非空数组')
|
||
normalized_items = _normalize_pre_purchase_items(merchant=merchant, items=items)
|
||
|
||
with transaction.atomic():
|
||
pre_purchase_order.supplier = new_supplier
|
||
pre_purchase_order.warehouse = new_warehouse
|
||
pre_purchase_order.kind = new_kind
|
||
pre_purchase_order.remarks = new_remarks
|
||
|
||
try:
|
||
pre_purchase_order.full_clean()
|
||
except ValidationError as exc:
|
||
raise ValueError(str(exc)) from exc
|
||
|
||
pre_purchase_order.save(update_fields=['supplier', 'warehouse', 'kind', 'remarks', 'updated_at'])
|
||
|
||
pre_purchase_order.items.all().delete()
|
||
models.PrePurchaseOrderItem.objects.bulk_create(
|
||
[
|
||
models.PrePurchaseOrderItem(
|
||
pre_purchase_order=pre_purchase_order,
|
||
product_id=item['product_id'],
|
||
product_name=item.get('product_name') or '',
|
||
color=item.get('color'),
|
||
quantity=item.get('quantity'),
|
||
unit=item.get('unit') or '',
|
||
spec=item.get('spec'),
|
||
quantity_of_rolls=item.get('quantity_of_rolls'),
|
||
num_of_rolls=item.get('num_of_rolls') or 1,
|
||
order_quantity=item.get('order_quantity'),
|
||
remarks=item.get('remarks'),
|
||
)
|
||
for item in normalized_items
|
||
]
|
||
)
|
||
|
||
pre_purchase_order.refresh_from_db()
|
||
return pre_purchase_order
|
||
|
||
|
||
def delete_pre_purchase_order(*, pre_purchase_order: models.PrePurchaseOrder) -> None:
|
||
with transaction.atomic():
|
||
pre_purchase_order.delete()
|