This commit is contained in:
anhduy-tech
2026-01-04 22:49:23 +07:00
parent 476ecf5f9f
commit 15bb6720b6
31 changed files with 1 additions and 327 deletions

Binary file not shown.

View File

@@ -14,10 +14,9 @@ Including another URLconf
2. Add a URL to urlpatterns: re_path('blog/', include('blog.urls'))
"""
from django.urls import re_path
from app import views, cob, payment, contract, cleardata, email, backup, server, workflows,api_workflow, importdata
from app import views, cob, payment, contract, cleardata, email, backup, server,api_workflow, importdata
urlpatterns = [
# New Workflow Endpoints
re_path("workflow/execute/$", api_workflow.execute_workflow),
# Existing Endpoints

View File

@@ -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)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.