forked from erp-dev/erp
297 lines
12 KiB
Python
297 lines
12 KiB
Python
from decimal import Decimal
|
|
|
|
from django.test import TestCase
|
|
from django.utils import timezone
|
|
|
|
from basic_info import models as basic_models
|
|
from business import models as business_models, services
|
|
from api_v1.views.business.payment.views import PaymentOrderSerializer
|
|
from api_v1.views.business.receipt.views import ReceiptOrderSerializer
|
|
|
|
from .fixtures import create_basic_fixtures
|
|
|
|
|
|
class PaymentReceiptServiceTestCase(TestCase):
|
|
def setUp(self):
|
|
(
|
|
self.merchant,
|
|
self.supplier,
|
|
self.warehouse_strict,
|
|
self.warehouse_relaxed,
|
|
self.product,
|
|
self.operator,
|
|
) = create_basic_fixtures()
|
|
self.customer = basic_models.Customer.objects.create(
|
|
merchant=self.merchant,
|
|
name='客户B',
|
|
mobile='13888888888',
|
|
created_by=None,
|
|
)
|
|
self.bank_account = basic_models.BankAccount.objects.create(
|
|
merchant=self.merchant,
|
|
name='测试账户',
|
|
auto_number='BANK-SVC',
|
|
)
|
|
|
|
def test_create_payment_order_and_approve(self):
|
|
order = services.create_payment_order(
|
|
merchant=self.merchant,
|
|
supplier=self.supplier,
|
|
payment_date=timezone.now().date(),
|
|
amount='120.50',
|
|
discount_amount='20.50',
|
|
operator=self.operator,
|
|
remarks='付款测试',
|
|
bank_account=self.bank_account,
|
|
markup='服务层附言',
|
|
)
|
|
self.assertEqual(order.status, business_models.PaymentOrderStatusEnum.PENDING)
|
|
self.assertEqual(order.bank_account, self.bank_account)
|
|
self.assertEqual(order.markup, '服务层附言')
|
|
self.assertEqual(order.amount, Decimal('121'))
|
|
self.assertEqual(order.discount_amount, Decimal('21'))
|
|
self.assertEqual(order.settlement_amount, Decimal('142'))
|
|
reviewed = services.review_payment_order(
|
|
payment_order=order,
|
|
target_status=business_models.PaymentOrderStatusEnum.APPROVED,
|
|
reviewed_by=self.operator,
|
|
)
|
|
self.assertEqual(reviewed.status, business_models.PaymentOrderStatusEnum.APPROVED)
|
|
balance = business_models.SupplierBalance.objects.get(
|
|
merchant=self.merchant,
|
|
supplier=self.supplier,
|
|
)
|
|
self.assertEqual(balance.balance, Decimal('-142'))
|
|
record = business_models.BalanceChangeRecord.objects.get(
|
|
merchant=self.merchant,
|
|
source_type=business_models.BalanceChangeSourceEnum.PAYMENT_ORDER,
|
|
source_id=order.id,
|
|
)
|
|
self.assertEqual(record.direction, business_models.BalanceChangeDirectionEnum.DECREASE)
|
|
self.assertEqual(record.delta, Decimal('-142'))
|
|
self.assertEqual(record.balance_after, balance.balance)
|
|
with self.assertRaises(ValueError):
|
|
services.review_payment_order(
|
|
payment_order=order,
|
|
target_status=business_models.PaymentOrderStatusEnum.CANCELLED,
|
|
reviewed_by=self.operator,
|
|
)
|
|
|
|
def test_create_receipt_order_and_cancel(self):
|
|
order = services.create_receipt_order(
|
|
merchant=self.merchant,
|
|
customer=self.customer,
|
|
receipt_date=timezone.now().date(),
|
|
amount='80.00',
|
|
discount_amount='5',
|
|
operator=self.operator,
|
|
remarks='收款测试',
|
|
bank_account=self.bank_account,
|
|
markup='收款附言',
|
|
)
|
|
self.assertEqual(order.bank_account, self.bank_account)
|
|
self.assertEqual(order.discount_amount, Decimal('5'))
|
|
self.assertEqual(order.settlement_amount, Decimal('85'))
|
|
cancelled = services.review_receipt_order(
|
|
receipt_order=order,
|
|
target_status=business_models.ReceiptOrderStatusEnum.CANCELLED,
|
|
reviewed_by=self.operator,
|
|
)
|
|
self.assertEqual(cancelled.status, business_models.ReceiptOrderStatusEnum.CANCELLED)
|
|
self.assertFalse(
|
|
business_models.BalanceChangeRecord.objects.filter(
|
|
merchant=self.merchant,
|
|
source_type=business_models.BalanceChangeSourceEnum.RECEIPT_ORDER,
|
|
source_id=order.id,
|
|
).exists()
|
|
)
|
|
|
|
def test_payment_amount_can_be_negative(self):
|
|
order = services.create_payment_order(
|
|
merchant=self.merchant,
|
|
supplier=self.supplier,
|
|
payment_date=timezone.now().date(),
|
|
amount='-25.50',
|
|
operator=self.operator,
|
|
)
|
|
self.assertEqual(order.amount, Decimal('-26'))
|
|
|
|
def test_payment_discount_can_exceed_amount(self):
|
|
order = services.create_payment_order(
|
|
merchant=self.merchant,
|
|
supplier=self.supplier,
|
|
payment_date=timezone.now().date(),
|
|
amount='50',
|
|
discount_amount='60',
|
|
operator=self.operator,
|
|
)
|
|
self.assertEqual(order.settlement_amount, Decimal('110'))
|
|
|
|
def test_receipt_discount_can_exceed_amount(self):
|
|
order = services.create_receipt_order(
|
|
merchant=self.merchant,
|
|
customer=self.customer,
|
|
receipt_date=timezone.now().date(),
|
|
amount='50',
|
|
discount_amount='60',
|
|
operator=self.operator,
|
|
)
|
|
self.assertEqual(order.settlement_amount, Decimal('110'))
|
|
|
|
def test_receipt_amount_can_be_negative(self):
|
|
order = services.create_receipt_order(
|
|
merchant=self.merchant,
|
|
customer=self.customer,
|
|
receipt_date=timezone.now().date(),
|
|
amount='-40',
|
|
operator=self.operator,
|
|
)
|
|
self.assertEqual(order.amount, Decimal('-40'))
|
|
|
|
def test_payment_order_rejects_foreign_bank_account(self):
|
|
other_merchant = basic_models.Merchant.objects.create(
|
|
name='无关商户',
|
|
type=basic_models.MerchantTypeEnum.FACTORY,
|
|
)
|
|
foreign_bank = basic_models.BankAccount.objects.create(
|
|
merchant=other_merchant,
|
|
name='其他账户',
|
|
auto_number='BANK-FOREIGN',
|
|
)
|
|
with self.assertRaises(ValueError):
|
|
services.create_payment_order(
|
|
merchant=self.merchant,
|
|
supplier=self.supplier,
|
|
payment_date=timezone.now().date(),
|
|
amount='10',
|
|
operator=self.operator,
|
|
bank_account=foreign_bank,
|
|
)
|
|
|
|
def test_receipt_order_rejects_foreign_bank_account(self):
|
|
other_merchant = basic_models.Merchant.objects.create(
|
|
name='第二商户',
|
|
type=basic_models.MerchantTypeEnum.FACTORY,
|
|
)
|
|
foreign_bank = basic_models.BankAccount.objects.create(
|
|
merchant=other_merchant,
|
|
name='第二账户',
|
|
auto_number='BANK-SECOND',
|
|
)
|
|
with self.assertRaises(ValueError):
|
|
services.create_receipt_order(
|
|
merchant=self.merchant,
|
|
customer=self.customer,
|
|
receipt_date=timezone.now().date(),
|
|
amount='10',
|
|
operator=self.operator,
|
|
bank_account=foreign_bank,
|
|
)
|
|
|
|
def test_receipt_approval_is_idempotent(self):
|
|
order = services.create_receipt_order(
|
|
merchant=self.merchant,
|
|
customer=self.customer,
|
|
receipt_date=timezone.now().date(),
|
|
amount='10',
|
|
operator=self.operator,
|
|
)
|
|
services.review_receipt_order(
|
|
receipt_order=order,
|
|
target_status=business_models.ReceiptOrderStatusEnum.APPROVED,
|
|
reviewed_by=self.operator,
|
|
)
|
|
reviewed_again = services.review_receipt_order(
|
|
receipt_order=order,
|
|
target_status=business_models.ReceiptOrderStatusEnum.APPROVED,
|
|
reviewed_by=self.operator,
|
|
)
|
|
self.assertEqual(reviewed_again.status, business_models.ReceiptOrderStatusEnum.APPROVED)
|
|
balance = business_models.CustomerBalance.objects.get(
|
|
merchant=self.merchant,
|
|
customer=self.customer,
|
|
)
|
|
self.assertEqual(balance.balance, Decimal('-10'))
|
|
records = business_models.BalanceChangeRecord.objects.filter(
|
|
merchant=self.merchant,
|
|
source_type=business_models.BalanceChangeSourceEnum.RECEIPT_ORDER,
|
|
source_id=order.id,
|
|
)
|
|
self.assertEqual(records.count(), 1)
|
|
self.assertEqual(records.first().balance_after, balance.balance)
|
|
|
|
def test_payment_approval_is_idempotent(self):
|
|
order = services.create_payment_order(
|
|
merchant=self.merchant,
|
|
supplier=self.supplier,
|
|
payment_date=timezone.now().date(),
|
|
amount='75.00',
|
|
operator=self.operator,
|
|
)
|
|
services.review_payment_order(
|
|
payment_order=order,
|
|
target_status=business_models.PaymentOrderStatusEnum.APPROVED,
|
|
reviewed_by=self.operator,
|
|
)
|
|
services.review_payment_order(
|
|
payment_order=order,
|
|
target_status=business_models.PaymentOrderStatusEnum.APPROVED,
|
|
reviewed_by=self.operator,
|
|
)
|
|
balance = business_models.SupplierBalance.objects.get(
|
|
merchant=self.merchant,
|
|
supplier=self.supplier,
|
|
)
|
|
self.assertEqual(balance.balance, Decimal('-75.00'))
|
|
records = business_models.BalanceChangeRecord.objects.filter(
|
|
merchant=self.merchant,
|
|
source_type=business_models.BalanceChangeSourceEnum.PAYMENT_ORDER,
|
|
source_id=order.id,
|
|
)
|
|
self.assertEqual(records.count(), 1)
|
|
self.assertEqual(records.first().balance_after, balance.balance)
|
|
|
|
def test_payment_order_external_source_fields_default_and_serialized(self):
|
|
order = services.create_payment_order(
|
|
merchant=self.merchant,
|
|
supplier=self.supplier,
|
|
payment_date=timezone.now().date(),
|
|
amount='88.00',
|
|
operator=self.operator,
|
|
)
|
|
self.assertFalse(order.is_external_source)
|
|
self.assertIsNone(order.external_source_id)
|
|
self.assertEqual(order.human_id, f'FK{order.created_at:%Y%m%d}{order.id:06d}')
|
|
|
|
order.is_external_source = True
|
|
order.external_source_id = 'XT20260001'
|
|
order.save(update_fields=['is_external_source', 'external_source_id', 'updated_at'])
|
|
order.refresh_from_db()
|
|
|
|
serializer = PaymentOrderSerializer(order)
|
|
self.assertTrue(serializer.data['is_external_source'])
|
|
self.assertEqual(serializer.data['external_source_id'], 'XT20260001')
|
|
self.assertEqual(serializer.data['human_id'], order.human_id)
|
|
|
|
def test_receipt_order_external_source_fields_default_and_serialized(self):
|
|
order = services.create_receipt_order(
|
|
merchant=self.merchant,
|
|
customer=self.customer,
|
|
receipt_date=timezone.now().date(),
|
|
amount='66.00',
|
|
operator=self.operator,
|
|
)
|
|
self.assertFalse(order.is_external_source)
|
|
self.assertIsNone(order.external_source_id)
|
|
self.assertEqual(order.human_id, f'SK{order.created_at:%Y%m%d}{order.id:06d}')
|
|
|
|
order.is_external_source = True
|
|
order.external_source_id = 'SK20225323'
|
|
order.save(update_fields=['is_external_source', 'external_source_id', 'updated_at'])
|
|
order.refresh_from_db()
|
|
|
|
serializer = ReceiptOrderSerializer(order)
|
|
self.assertTrue(serializer.data['is_external_source'])
|
|
self.assertEqual(serializer.data['external_source_id'], 'SK20225323')
|
|
self.assertEqual(serializer.data['human_id'], order.human_id)
|