changes
This commit is contained in:
325
app/workflows.py
325
app/workflows.py
@@ -1,325 +0,0 @@
|
||||
# workflows.py
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
from decimal import Decimal
|
||||
from app.models import (
|
||||
Transaction,User, Transaction_Detail, Product, Product_Status, Payment_Schedule,
|
||||
Transaction_Phase, Transaction_Status, Payment_Status, Entry_Category,
|
||||
Payment_Plan,Product_Booked
|
||||
)
|
||||
from app.payment import account_entry_api
|
||||
from datetime import datetime, timedelta # Corrected: Import timedelta
|
||||
|
||||
@api_view(['POST'])
|
||||
@transaction.atomic
|
||||
def create_reservation(request):
|
||||
"""
|
||||
Tạo một giao dịch giữ chỗ (24h hoặc giữ chỗ thường).
|
||||
"""
|
||||
data = request.data
|
||||
try:
|
||||
phase_code = data.get('phase_code')
|
||||
deposit_received = data.get('deposit_received')
|
||||
customer_id = data.get('customer_id')
|
||||
product_id = data.get('product_id')
|
||||
policy_id = data.get('policy_id')
|
||||
user_id = data.get('user_id')
|
||||
origin_price = data.get('origin_price')
|
||||
discount_amount = data.get('discount_amount', 0)
|
||||
sale_price = data.get('sale_price')
|
||||
deposit_amount = data.get('deposit_amount')
|
||||
installments = data.get('installments', [])
|
||||
payment_plan = data.get('payment_plan', None)
|
||||
|
||||
reservation_phase = Transaction_Phase.objects.get(code=phase_code)
|
||||
initial_detail_status = Transaction_Status.objects.get(code='new')
|
||||
# Correction: Use 'unpaid' for unconfirmed payment status
|
||||
unconfirmed_payment_status = Payment_Status.objects.get(code='unpaid')
|
||||
|
||||
if phase_code == 'reserved24H':
|
||||
product_status_code = 'resvered24H'
|
||||
elif phase_code == 'deposit':
|
||||
product_status_code = 'deposit'
|
||||
elif phase_code == 'fulfillwish':
|
||||
product_status_code = 'deposit'
|
||||
else:
|
||||
product_status_code = 'resvered'
|
||||
product_status_reserved = Product_Status.objects.get(code=product_status_code)
|
||||
|
||||
transaction_obj = Transaction.objects.create(
|
||||
customer_id=customer_id, product_id=product_id, policy_id=policy_id,
|
||||
phase=reservation_phase, date=datetime.now().strftime('%Y-%m-%d'),
|
||||
origin_price=origin_price, discount_amount=discount_amount, sale_price=sale_price,
|
||||
deposit_amount=deposit_amount, deposit_received=0,
|
||||
deposit_remaining=deposit_amount, amount_received=0,
|
||||
payment_plan=payment_plan
|
||||
)
|
||||
|
||||
amount_recived = installments[0].get('amount') if installments else 0
|
||||
transaction_detail_obj = Transaction_Detail.objects.create(
|
||||
transaction=transaction_obj, phase=reservation_phase, status=initial_detail_status,
|
||||
date=datetime.now().strftime('%Y-%m-%d'),amount=deposit_amount,amount_recived=amount_recived,
|
||||
due_date=datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=2),
|
||||
creator_id=user_id
|
||||
)
|
||||
|
||||
product_obj = Product.objects.get(id=product_id)
|
||||
product_obj.status = product_status_reserved
|
||||
product_obj.save()
|
||||
|
||||
product_booked_obj = Product_Booked.objects.create(
|
||||
transaction=transaction_obj,
|
||||
product=product_obj
|
||||
)
|
||||
|
||||
schedules = []
|
||||
for i, installment in enumerate(installments):
|
||||
schedule = Payment_Schedule.objects.create(
|
||||
txn_detail=transaction_detail_obj,
|
||||
from_date=datetime.now().strftime('%Y-%m-%d'),
|
||||
to_date=datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=installment.get('due_days', 0)),
|
||||
amount=installment.get('amount'), cycle=i + 1,
|
||||
cycle_days=installment.get('due_days', 0), type_id=1,
|
||||
status=unconfirmed_payment_status, updater_id=user_id,
|
||||
detail={'note': f"Thanh toán cọc đợt {i+1}"}
|
||||
)
|
||||
schedules.append(schedule.id)
|
||||
|
||||
return Response({
|
||||
'message': f'Giao dịch "{reservation_phase.name}" đã được tạo thành công.',
|
||||
'transaction_id': transaction_obj.id,
|
||||
'transaction_code': transaction_obj.code,
|
||||
'transaction_detail_id': transaction_detail_obj.id,
|
||||
'product_status': product_obj.status.name,
|
||||
'payment_schedule_ids': schedules
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@api_view(['POST'])
|
||||
@transaction.atomic
|
||||
def confirm_payment_schedule(request):
|
||||
"""
|
||||
Kế toán xác nhận một công nợ (Payment_Schedule).
|
||||
Job chạy ngầm sẽ lo việc chuyển trạng thái detail.
|
||||
"""
|
||||
data = request.data
|
||||
try:
|
||||
payment_schedule_id = data.get('payment_schedule_id')
|
||||
accountant_id = data.get('user_id')
|
||||
internal_account_code = data.get('internal_account_code', 'HOAC02VND')
|
||||
|
||||
# Correction: Use 'paid' for confirmed payment status
|
||||
confirmed_payment_status = Payment_Status.objects.get(code='paid')
|
||||
|
||||
schedule_obj = Payment_Schedule.objects.select_related('txn_detail', 'txn_detail__transaction__customer', 'status').get(id=payment_schedule_id)
|
||||
|
||||
if schedule_obj.status.code == 'paid':
|
||||
return Response({'message': 'Công nợ này đã được xác nhận trước đó.'}, status=status.HTTP_200_OK)
|
||||
|
||||
schedule_obj.status = confirmed_payment_status
|
||||
schedule_obj.updater_id = accountant_id
|
||||
|
||||
entry_content = f"Xác nhận thanh toán đợt {schedule_obj.cycle} cho GD {schedule_obj.txn_detail.code} của KH {schedule_obj.txn_detail.transaction.customer.code}"
|
||||
deposit_category = Entry_Category.objects.get(code='THU_COC')
|
||||
|
||||
entry_data = account_entry_api(
|
||||
code=internal_account_code, amount=schedule_obj.amount, content=entry_content,
|
||||
type='CR', category=deposit_category.id, userid=accountant_id,
|
||||
ref=schedule_obj.txn_detail.code
|
||||
)
|
||||
|
||||
if 'error' in entry_data:
|
||||
raise Exception(entry_data['error'])
|
||||
|
||||
schedule_obj.entry_id = entry_data['id']
|
||||
schedule_obj.save()
|
||||
|
||||
transaction_obj = schedule_obj.txn_detail.transaction
|
||||
|
||||
schedule_amount = Decimal(str(schedule_obj.amount))
|
||||
|
||||
transaction_obj.amount_received = (transaction_obj.amount_received or Decimal('0')) + schedule_amount
|
||||
transaction_obj.deposit_received = (transaction_obj.deposit_received or Decimal('0')) + schedule_amount
|
||||
transaction_obj.deposit_remaining = (transaction_obj.deposit_remaining or Decimal('0')) - schedule_amount
|
||||
transaction_obj.save()
|
||||
|
||||
return Response({
|
||||
'message': 'Công nợ đã được xác nhận thành công.'
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@api_view(['POST'])
|
||||
@transaction.atomic
|
||||
def approve_transaction_detail(request):
|
||||
"""
|
||||
Duyệt một Transaction_Detail.
|
||||
Nếu phase là 'reserved24H', sẽ tự động chuyển Transaction sang phase 'reserved'.
|
||||
"""
|
||||
data = request.data
|
||||
try:
|
||||
transaction_detail_id = data.get('transaction_detail_id')
|
||||
approver_id = data.get('user_id')
|
||||
|
||||
detail_obj = Transaction_Detail.objects.select_related('status', 'phase', 'transaction').get(id=transaction_detail_id)
|
||||
|
||||
user_obj = User.objects.get(id=approver_id)
|
||||
|
||||
if detail_obj.status.code != 'pending':
|
||||
return Response(
|
||||
{'error': f'Giao dịch không ở trạng thái "Chờ duyệt". Trạng thái hiện tại: {detail_obj.status.name}'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# Correction: Use 'approved' for approved status
|
||||
approved_status = Transaction_Status.objects.get(code='approved')
|
||||
detail_obj.status = approved_status
|
||||
detail_obj.approver = user_obj
|
||||
detail_obj.approve_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
detail_obj.save()
|
||||
|
||||
if detail_obj.phase.code == 'reserved24H':
|
||||
transaction_obj = detail_obj.transaction
|
||||
|
||||
next_phase_obj = Transaction_Phase.objects.get(code='reserved')
|
||||
transaction_obj.phase = next_phase_obj
|
||||
transaction_obj.save()
|
||||
|
||||
productId = transaction_obj.product_id
|
||||
product_obj = Product.objects.get(id=productId)
|
||||
product_obj.status = Product_Status.objects.get(code='resvered')
|
||||
product_obj.save()
|
||||
|
||||
new_detail_obj = Transaction_Detail.objects.create(
|
||||
transaction=transaction_obj, phase=next_phase_obj,
|
||||
status=Transaction_Status.objects.get(code='new'),
|
||||
date=datetime.now().strftime('%Y-%m-%d'),
|
||||
amount=0, creator_id=approver_id
|
||||
)
|
||||
|
||||
return Response({
|
||||
'message': 'Giao dịch đã được duyệt và tự động chuyển sang giai đoạn "Giữ chỗ".',
|
||||
'transaction_id': transaction_obj.id,
|
||||
'new_transaction_phase': next_phase_obj.name,
|
||||
'new_transaction_detail_id': new_detail_obj.id
|
||||
}, status=status.HTTP_200_OK)
|
||||
else:
|
||||
return Response({
|
||||
'message': 'Giao dịch đã được duyệt thành công.',
|
||||
'transaction_detail_id': detail_obj.id,
|
||||
'new_status': approved_status.name
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
return Response({'error ': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@api_view(['POST'])
|
||||
@transaction.atomic
|
||||
def advance_transaction_phase(request):
|
||||
"""
|
||||
Chuyển một Transaction sang một giai đoạn mới và tự động tạo công nợ nếu cần.
|
||||
"""
|
||||
data = request.data
|
||||
try:
|
||||
transaction_id = data.get('transaction_id')
|
||||
next_phase_code = data.get('next_phase_code')
|
||||
user_id = data.get('user_id')
|
||||
product_status = data.get('product_status')
|
||||
|
||||
|
||||
if not all([transaction_id, next_phase_code, user_id]):
|
||||
return Response({'error': 'transaction_id, next_phase_code, và user_id là bắt buộc.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
transaction_obj = Transaction.objects.select_related('phase', 'product', 'policy').get(id=transaction_id)
|
||||
|
||||
next_phase_obj = Transaction_Phase.objects.get(code=next_phase_code)
|
||||
initial_detail_status = Transaction_Status.objects.get(code='new')
|
||||
|
||||
next_product_status = Product_Status.objects.get(code=product_status)
|
||||
|
||||
transaction_obj.phase = next_phase_obj
|
||||
transaction_obj.save()
|
||||
|
||||
productId = transaction_obj.product_id
|
||||
product_obj = Product.objects.get(id=productId)
|
||||
product_obj.status = next_product_status
|
||||
product_obj.save()
|
||||
|
||||
new_detail_obj = Transaction_Detail.objects.create(
|
||||
transaction=transaction_obj, phase=next_phase_obj, status=initial_detail_status,
|
||||
date=datetime.now().strftime('%Y-%m-%d'),
|
||||
amount=0, creator_id=user_id
|
||||
)
|
||||
|
||||
if next_phase_code == 'pertrade':
|
||||
unconfirmed_payment_status = Payment_Status.objects.get(code='unpaid')
|
||||
total_plan_amount = 0
|
||||
|
||||
# Ưu tiên kế hoạch thanh toán riêng trong giao dịch
|
||||
if transaction_obj.payment_plan and isinstance(transaction_obj.payment_plan, list):
|
||||
for i, plan_item in enumerate(transaction_obj.payment_plan):
|
||||
plan_amount = Decimal(str(plan_item.get('amount', 0)))
|
||||
due_days = plan_item.get('due_days', 0)
|
||||
total_plan_amount += plan_amount
|
||||
Payment_Schedule.objects.create(
|
||||
txn_detail=new_detail_obj,
|
||||
from_date=datetime.now().strftime('%Y-%m-%d'),
|
||||
to_date=datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=due_days),
|
||||
amount=plan_amount,
|
||||
cycle=i + 1,
|
||||
cycle_days=due_days,
|
||||
type_id=2, # Giả sử type_id=2 cho thanh toán mua bán
|
||||
status=unconfirmed_payment_status,
|
||||
updater_id=user_id,
|
||||
detail={'note': f"Thanh toán đợt {i + 1} theo kế hoạch riêng"}
|
||||
)
|
||||
new_detail_obj.amount = total_plan_amount
|
||||
new_detail_obj.save()
|
||||
|
||||
# Nếu không có kế hoạch riêng, dùng chính sách bán hàng
|
||||
elif transaction_obj.policy:
|
||||
payment_plans_from_policy = Payment_Plan.objects.filter(policy=transaction_obj.policy).select_related('type').order_by('cycle')
|
||||
if payment_plans_from_policy.exists():
|
||||
for plan in payment_plans_from_policy:
|
||||
plan_amount = 0
|
||||
if plan.type.code == 'percentage':
|
||||
plan_amount = (transaction_obj.sale_price * plan.value) / 100
|
||||
elif plan.type.code == 'money':
|
||||
plan_amount = plan.value
|
||||
|
||||
total_plan_amount += Decimal(str(plan_amount))
|
||||
Payment_Schedule.objects.create(
|
||||
txn_detail=new_detail_obj, from_date=datetime.now().strftime('%Y-%m-%d'),
|
||||
to_date=datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=plan.days),
|
||||
amount=plan_amount, cycle=plan.cycle, cycle_days=plan.days,
|
||||
type_id=2, status=unconfirmed_payment_status, updater_id=user_id,
|
||||
detail={'note': f"Thanh toán đợt {plan.cycle} theo chính sách {transaction_obj.policy.name}"}
|
||||
)
|
||||
new_detail_obj.amount = total_plan_amount
|
||||
new_detail_obj.save()
|
||||
else:
|
||||
raise Exception("Giao dịch không có chính sách bán hàng hoặc kế hoạch thanh toán riêng để tạo công nợ.")
|
||||
|
||||
return Response({
|
||||
'message': f'Giao dịch đã được chuyển thành công sang giai đoạn "{next_phase_obj.name}".',
|
||||
'transaction_id': transaction_obj.id,
|
||||
'new_transaction_detail_id': new_detail_obj.id,
|
||||
'new_transaction_detail_code': new_detail_obj.code,
|
||||
'product_id': productId,
|
||||
'customer':transaction_obj.customer.id,
|
||||
'new_product_status': next_product_status.name
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
except Transaction.DoesNotExist:
|
||||
return Response({'error': 'Giao dịch không tồn tại.'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Transaction_Phase.DoesNotExist:
|
||||
return Response({'error': f'Giai đoạn mới "{next_phase_code}" không hợp lệ.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
Reference in New Issue
Block a user