This commit is contained in:
anhduy-tech
2026-01-30 16:37:18 +07:00
parent 69937da0b6
commit f297a5e1b4
29 changed files with 374 additions and 142 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,22 @@
# Generated by Django 5.1.7 on 2026-01-30 08:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0372_payment_schedule_amount_remain_and_more'),
]
operations = [
migrations.RemoveField(
model_name='transaction',
name='ovd_days',
),
migrations.AddField(
model_name='internal_entry',
name='allocation_remain',
field=models.DecimalField(decimal_places=2, max_digits=35, null=True),
),
]
+1 -1
View File
@@ -1408,7 +1408,6 @@ class Transaction(AutoCodeModel):
deposit_remaining = models.DecimalField(max_digits=35, decimal_places=2, null=True)
amount_received = models.DecimalField(max_digits=35, decimal_places=2, null=True)
amount_remain = models.DecimalField(max_digits=35, decimal_places=2, null=True)
ovd_days = models.IntegerField(null=True)
penalty_amount = models.DecimalField(null=True, max_digits=35, decimal_places=2)
early_discount_amount = models.DecimalField(max_digits=35, decimal_places=2, null=True)
payment_plan = models.JSONField(null=True)
@@ -1672,6 +1671,7 @@ class Internal_Entry(AutoCodeModel):
customer = models.ForeignKey(Customer, null=True, related_name='entrycus', on_delete=models.PROTECT)
product = models.ForeignKey(Product, null=True, related_name='+', on_delete=models.PROTECT)
allocation_amount = models.DecimalField(null=True, max_digits=35, decimal_places=2)
allocation_remain = models.DecimalField(null=True, max_digits=35, decimal_places=2)
allocation_detail = models.JSONField(null=True)
create_time = models.DateTimeField(null=True, auto_now_add=True)
update_time = models.DateTimeField(null=True, auto_now=True)
+351 -141
View File
@@ -43,6 +43,7 @@ def account_entry_api(code, amount, content, type, category, userid, ref=None, p
account.refresh_from_db()
new_balance = account.balance
# Tất cả entry CR đều có allocation_remain ban đầu = amount
entry = Internal_Entry.objects.create(
category=entry_category,
content=content,
@@ -56,10 +57,13 @@ def account_entry_api(code, amount, content, type, category, userid, ref=None, p
date=system_date,
ref=ref,
product=None if product is None else Product.objects.get(id=product),
customer=None if customer is None else Customer.objects.get(id=customer)
customer=None if customer is None else Customer.objects.get(id=customer),
allocation_amount=Decimal('0'),
allocation_remain=Decimal(str(amount)) if entry_type.code == 'CR' else Decimal('0'),
allocation_detail=[]
)
text = 'id,account__currency__code,ref,balance_before,balance_after,code,account,account__code,account__branch__name,account__type__name,date,amount,content,inputer,inputer__fullname,approver,approver__fullname,create_time,update_time,type,type__code,type__name'
text = 'id,account__currency__code,ref,balance_before,balance_after,code,account,account__code,account__branch__name,account__type__name,date,amount,content,inputer,inputer__fullname,approver,approver__fullname,create_time,update_time,type,type__code,type__name,allocation_amount,allocation_remain'
fields = text.split(',')
response_data = Internal_Entry.objects.filter(id=entry.id).values(*fields).first()
return response_data, entry
@@ -92,14 +96,20 @@ def get_allocation_rule():
return 'principal-fee'
# ==========================================================================================
# HÀM PHÂN BỔ THÔNG THƯỜNG
# HÀM PHÂN BỔ THEO PRODUCT_ID - QUÉT LẠI TOÀN BỘ ENTRY CŨ CÓ TIỀN THỪA
# ==========================================================================================
def allocate_payment_to_schedules(entries):
if not entries:
return {"status": "no_entries", "message": "Không có bút toán"}
def allocate_payment_to_schedules(product_id):
"""
Phân bổ thanh toán cho một sản phẩm cụ thể.
Quét tất cả entry CR có allocation_remain > 0 của product này,
phân bổ tiếp vào các lịch chưa thanh toán (status=1).
"""
if not product_id:
return {"status": "no_product", "message": "Không có product_id"}
allocation_rule = get_allocation_rule()
updated_schedules = []
updated_entries = []
errors = []
# Lấy status "đã thanh toán" một lần
@@ -115,47 +125,88 @@ def allocate_payment_to_schedules(entries):
except Transaction_Status.DoesNotExist:
errors.append("Không tìm thấy Transaction_Status id=2 (đã thanh toán)")
for entry in entries:
if entry.type.code != 'CR' or entry.account.id == 5:
continue
with transaction.atomic():
try:
# Lấy product
product = Product.objects.get(id=product_id)
amount = Decimal(str(entry.amount))
if amount <= 0:
continue
# Lấy transaction của product
booked = Product_Booked.objects.filter(product=product).first()
if not booked or not booked.transaction:
errors.append(f"Product {product_id}: Không tìm thấy Transaction")
return {
"status": "error",
"errors": errors
}
with transaction.atomic():
txn = booked.transaction
# Lấy transaction detail
txn_detail = None
try:
booked = Product_Booked.objects.filter(product=entry.product).first()
if not booked or not booked.transaction:
errors.append(f"Entry {entry.code}: Không tìm thấy Transaction")
current = Transaction_Current.objects.get(transaction=txn)
txn_detail = current.detail
except (Transaction_Current.DoesNotExist, AttributeError):
txn_detail = Transaction_Detail.objects.filter(
transaction=txn
).order_by('-create_time').first()
if not txn_detail:
errors.append(f"Product {product_id}: Không tìm thấy Transaction_Detail")
return {
"status": "error",
"errors": errors
}
# QUÉT TẤT CẢ ENTRY CR CÓ TIỀN THỪA (allocation_remain > 0) - KHÔNG PHẢI TÀI KHOẢN MIỄN LÃI
entries_with_remain = Internal_Entry.objects.select_for_update().filter(
product=product,
type__code='CR',
allocation_remain__gt=0
).exclude(
account__id=5 # Loại trừ tài khoản miễn lãi
).order_by('date', 'create_time')
if not entries_with_remain.exists():
return {
"status": "success",
"message": "Không có entry nào cần phân bổ",
"updated_schedules": [],
"updated_entries": [],
"errors": []
}
# Lấy các lịch chưa thanh toán (status=1)
schedules = Payment_Schedule.objects.select_for_update().filter(
txn_detail=txn_detail,
status__id=1
).order_by('cycle', 'from_date')
if not schedules.exists():
return {
"status": "success",
"message": "Không có lịch thanh toán cần phân bổ",
"updated_schedules": [],
"updated_entries": [],
"errors": []
}
# TỔNG TIỀN PHÂN BỔ THÀNH CÔNG (PRINCIPAL + PENALTY)
total_principal_allocated = Decimal('0')
total_penalty_allocated = Decimal('0')
# PHÂN BỔ TỪNG ENTRY
for entry in entries_with_remain:
remaining = Decimal(str(entry.allocation_remain))
if remaining <= 0:
continue
txn = booked.transaction
txn_detail = None
try:
current = Transaction_Current.objects.get(transaction=txn)
txn_detail = current.detail
except (Transaction_Current.DoesNotExist, AttributeError):
txn_detail = Transaction_Detail.objects.filter(
transaction=txn
).order_by('-create_time').first()
if not txn_detail:
errors.append(f"Entry {entry.code}: Không tìm thấy Transaction_Detail")
continue
schedules = Payment_Schedule.objects.filter(
txn_detail=txn_detail,
status__id=1 # giả sử status=1 là chưa thanh toán
).order_by('cycle', 'from_date')
if not schedules.exists():
continue
remaining = amount
total_allocated = Decimal('0')
entry_allocation_detail = entry.allocation_detail or []
entry_principal_allocated = Decimal('0')
entry_penalty_allocated = Decimal('0')
# Phân bổ vào các lịch
for sch in schedules:
if remaining <= 0:
break
@@ -169,35 +220,48 @@ def allocate_payment_to_schedules(entries):
to_penalty = Decimal('0')
to_principal = Decimal('0')
# Áp dụng quy tắc phân bổ
if allocation_rule == 'fee-principal':
# Phạt trước
to_penalty = min(remaining, penalty_remain)
remaining -= to_penalty
penalty_paid += to_penalty
penalty_remain -= to_penalty
to_principal = min(remaining, amount_remain)
remaining -= to_principal
paid_amount += to_principal
amount_remain -= to_principal
else:
# Gốc trước
to_principal = min(remaining, amount_remain)
remaining -= to_principal
paid_amount += to_principal
amount_remain -= to_principal
to_penalty = min(remaining, penalty_remain)
remaining -= to_penalty
penalty_paid += to_penalty
penalty_remain -= to_penalty
allocated_here = to_penalty + to_principal
total_allocated += allocated_here
if allocated_here <= 0:
continue
# Cập nhật entry tracking
entry_principal_allocated += to_principal
entry_penalty_allocated += to_penalty
# Cập nhật schedule
sch.paid_amount = paid_amount
sch.penalty_paid = penalty_paid
sch.amount_remain = amount_remain
sch.penalty_remain = penalty_remain
sch.remain_amount = max(Decimal('0'), remain_amount - allocated_here)
entry_list = sch.entry or []
# Lưu trace vào schedule
schedule_entry_list = sch.entry or []
date_value = entry.date
if hasattr(date_value, 'isoformat'):
@@ -205,7 +269,7 @@ def allocate_payment_to_schedules(entries):
else:
date_value = str(date_value)
entry_list.append({
schedule_entry_list.append({
"code": entry.code,
"amount": float(allocated_here),
"date": date_value,
@@ -214,8 +278,9 @@ def allocate_payment_to_schedules(entries):
"penalty": float(to_penalty),
"rule": allocation_rule
})
sch.entry = entry_list
sch.entry = schedule_entry_list
# Kiểm tra xem lịch đã thanh toán đủ chưa
if sch.amount_remain <= 0 and sch.penalty_remain <= 0 and paid_payment_status:
sch.status = paid_payment_status
@@ -224,88 +289,178 @@ def allocate_payment_to_schedules(entries):
'penalty_remain', 'remain_amount', 'entry', 'status'
])
updated_schedules.append(sch.id)
if sch.id not in updated_schedules:
updated_schedules.append(sch.id)
# Lưu chi tiết phân bổ vào entry
entry_allocation_detail.append({
"schedule_id": sch.id,
"schedule_code": sch.code,
"amount": float(allocated_here),
"principal": float(to_principal),
"penalty": float(to_penalty),
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
})
# Cập nhật entry allocation info
total_allocated_for_entry = entry_principal_allocated + entry_penalty_allocated
entry.allocation_amount = (entry.allocation_amount or Decimal('0')) + total_allocated_for_entry
entry.allocation_remain = remaining # Số tiền còn thừa
entry.allocation_detail = entry_allocation_detail
entry.save(update_fields=['allocation_amount', 'allocation_remain', 'allocation_detail'])
if entry.id not in updated_entries:
updated_entries.append(entry.id)
# Cộng vào tổng
total_principal_allocated += entry_principal_allocated
total_penalty_allocated += entry_penalty_allocated
# Cập nhật Transaction_Detail - TÁCH RIÊNG PRINCIPAL VÀ PENALTY
if total_principal_allocated > 0 or total_penalty_allocated > 0:
# Cập nhật amount_received (chỉ cộng principal)
txn_detail.amount_received = F('amount_received') + total_principal_allocated
txn_detail.amount_remaining = F('amount_remaining') - total_principal_allocated
# Cập nhật penalty_amount
if hasattr(txn_detail, 'penalty_amount'):
txn_detail.penalty_amount = F('penalty_amount') + total_penalty_allocated
txn_detail.save(update_fields=['amount_received', 'amount_remaining', 'penalty_amount'])
else:
txn_detail.save(update_fields=['amount_received', 'amount_remaining'])
# Cập nhật Transaction_Detail
txn_detail.amount_received = F('amount_received') + total_allocated
txn_detail.amount_remaining = F('amount_remaining') - total_allocated
txn_detail.save()
txn_detail.refresh_from_db()
# Kiểm tra và cập nhật status nếu đã thanh toán đủ
if txn_detail.amount_remaining <= 0 and paid_txn_status:
txn_detail.status = paid_txn_status
txn_detail.save(update_fields=['status'])
# Cập nhật Transaction
txn.amount_received = F('amount_received') + total_allocated
txn.amount_remain = F('amount_remain') - total_allocated
txn.save()
# Cập nhật Transaction - TÁCH RIÊNG PRINCIPAL VÀ PENALTY
if total_principal_allocated > 0 or total_penalty_allocated > 0:
# Cập nhật amount_received (chỉ cộng principal)
txn.amount_received = F('amount_received') + total_principal_allocated
txn.amount_remain = F('amount_remain') - total_principal_allocated
# Cập nhật penalty_amount
if hasattr(txn, 'penalty_amount'):
txn.penalty_amount = F('penalty_amount') + total_penalty_allocated
txn.save(update_fields=['amount_received', 'amount_remain', 'penalty_amount'])
else:
txn.save(update_fields=['amount_received', 'amount_remain'])
txn.refresh_from_db()
# Kiểm tra và cập nhật status nếu đã thanh toán đủ
if txn.amount_remain <= 0 and paid_txn_status:
txn.status = paid_txn_status
txn.save(update_fields=['status'])
except Exception as exc:
errors.append(f"Entry {entry.code}: Lỗi phân bổ - {str(exc)}")
except Product.DoesNotExist:
errors.append(f"Product {product_id}: Không tồn tại")
except Exception as exc:
errors.append(f"Product {product_id}: Lỗi phân bổ - {str(exc)}")
import traceback
print(traceback.format_exc())
return {
"status": "success" if not errors else "partial_failure",
"updated_schedules": updated_schedules,
"updated_entries": updated_entries,
"errors": errors,
"rule_used": allocation_rule
"rule_used": allocation_rule,
"total_principal_allocated": float(total_principal_allocated),
"total_penalty_allocated": float(total_penalty_allocated)
}
# ==========================================================================================
# HÀM MIỄN LÃI
# HÀM MIỄN LÃI - XỬ LÝ ENTRY TỪ TÀI KHOẢN MIỄN LÃI (ID=5)
# ==========================================================================================
def allocate_penalty_reduction(entries):
if not entries:
return {"status": "no_entries", "message": "Không có bút toán"}
def allocate_penalty_reduction(product_id):
"""
Xử lý miễn giảm tiền phạt cho một sản phẩm cụ thể.
Quét các entry CR từ tài khoản miễn lãi (id=5) có allocation_remain > 0.
"""
if not product_id:
return {"status": "no_product", "message": "Không có product_id"}
updated_schedules = []
updated_entries = []
errors = []
for entry in entries:
if entry.type.code != 'CR' or entry.account.id != 5:
continue # Chỉ xử lý bút toán từ tài khoản miễn lãi
with transaction.atomic():
try:
# Lấy product
product = Product.objects.get(id=product_id)
amount = Decimal(str(entry.amount))
if amount <= 0:
continue
booked = Product_Booked.objects.filter(product=product).first()
if not booked or not booked.transaction:
errors.append(f"Product {product_id}: Không tìm thấy Transaction")
return {
"status": "error",
"errors": errors
}
with transaction.atomic():
txn = booked.transaction
txn_detail = None
try:
booked = Product_Booked.objects.filter(product=entry.product).first()
if not booked or not booked.transaction:
errors.append(f"Entry {entry.code}: Không tìm thấy Transaction")
current = Transaction_Current.objects.get(transaction=txn)
txn_detail = current.detail
except (Transaction_Current.DoesNotExist, AttributeError):
txn_detail = Transaction_Detail.objects.filter(
transaction=txn
).order_by('-create_time').first()
if not txn_detail:
errors.append(f"Product {product_id}: Không tìm thấy Transaction_Detail")
return {
"status": "error",
"errors": errors
}
# Lấy các entry CR từ tài khoản miễn lãi (id=5) có tiền thừa
reduction_entries = Internal_Entry.objects.select_for_update().filter(
product=product,
type__code='CR',
account__id=5,
allocation_remain__gt=0
).order_by('date', 'create_time')
if not reduction_entries.exists():
return {
"status": "success",
"message": "Không có entry miễn lãi cần xử lý",
"updated_schedules": [],
"updated_entries": [],
"errors": []
}
# Lấy các lịch chưa thanh toán (status=1)
schedules = Payment_Schedule.objects.select_for_update().filter(
txn_detail=txn_detail,
status=1
).order_by('cycle', 'from_date')
if not schedules.exists():
return {
"status": "success",
"message": "Không có lịch thanh toán cần miễn lãi",
"updated_schedules": [],
"updated_entries": [],
"errors": []
}
# Xử lý từng entry miễn lãi
for entry in reduction_entries:
remaining_reduce = Decimal(str(entry.allocation_remain))
if remaining_reduce <= 0:
continue
txn = booked.transaction
txn_detail = None
try:
current = Transaction_Current.objects.get(transaction=txn)
txn_detail = current.detail
except (Transaction_Current.DoesNotExist, AttributeError):
txn_detail = Transaction_Detail.objects.filter(
transaction=txn
).order_by('-create_time').first()
if not txn_detail:
errors.append(f"Entry {entry.code}: Không tìm thấy Transaction_Detail")
continue
# Lấy các lịch chưa thanh toán (status=1)
schedules = Payment_Schedule.objects.filter(
txn_detail=txn_detail,
status=1
).order_by('cycle', 'from_date')
if not schedules.exists():
continue
remaining_reduce = amount # số tiền miễn lãi còn lại để phân bổ
entry_allocation_detail = entry.allocation_detail or []
entry_reduction_allocated = Decimal('0')
for schedule in schedules:
if remaining_reduce <= 0:
@@ -317,7 +472,12 @@ def allocate_penalty_reduction(entries):
# Chỉ miễn tối đa bằng số phạt còn lại
to_reduce = min(remaining_reduce, current_penalty_remain)
if to_reduce <= 0:
continue
remaining_reduce -= to_reduce
entry_reduction_allocated += to_reduce
# Cập nhật các trường
schedule.penalty_reduce = current_penalty_reduce + to_reduce
@@ -328,8 +488,8 @@ def allocate_penalty_reduction(entries):
# KHÔNG ĐỘNG ĐẾN amount_remain (nợ gốc còn lại)
# Ghi trace bút toán miễn lãi
entry_list = schedule.entry or []
# Ghi trace bút toán miễn lãi vào schedule
schedule_entry_list = schedule.entry or []
date_value = entry.date
if hasattr(date_value, 'isoformat'):
@@ -337,50 +497,82 @@ def allocate_penalty_reduction(entries):
else:
date_value = str(date_value)
entry_list.append({
schedule_entry_list.append({
"code": entry.code,
"amount": float(to_reduce),
"date": date_value,
"type": "REDUCTION",
"note": "Miễn lãi phạt quá hạn"
})
schedule.entry = entry_list
schedule.entry = schedule_entry_list
# Lưu lại
# Lưu lại schedule
schedule.save(update_fields=[
'penalty_reduce', 'penalty_remain', 'remain_amount', 'entry'
])
updated_schedules.append(schedule.id)
if schedule.id not in updated_schedules:
updated_schedules.append(schedule.id)
except Exception as exc:
errors.append(f"Entry {entry.code}: Lỗi miễn lãi - {str(exc)}")
# Lưu chi tiết vào entry
entry_allocation_detail.append({
"schedule_id": schedule.id,
"schedule_code": schedule.code,
"amount": float(to_reduce),
"type": "REDUCTION",
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
})
# Cập nhật entry allocation info
entry.allocation_amount = (entry.allocation_amount or Decimal('0')) + entry_reduction_allocated
entry.allocation_remain = remaining_reduce
entry.allocation_detail = entry_allocation_detail
entry.save(update_fields=['allocation_amount', 'allocation_remain', 'allocation_detail'])
if entry.id not in updated_entries:
updated_entries.append(entry.id)
except Product.DoesNotExist:
errors.append(f"Product {product_id}: Không tồn tại")
except Exception as exc:
errors.append(f"Product {product_id}: Lỗi miễn lãi - {str(exc)}")
import traceback
print(traceback.format_exc())
return {
"status": "success" if not errors else "partial_failure",
"updated_schedules": updated_schedules,
"updated_entries": updated_entries,
"errors": errors,
"message": f"Đã miễn lãi cho {len(updated_schedules)} lịch thanh toán (chỉ giảm phạt + tổng còn lại)"
"message": f"Đã miễn lãi cho {len(updated_schedules)} lịch thanh toán"
}
# ==========================================================================================
# BACKGROUND FUNCTION
# BACKGROUND FUNCTION - NHẬN PRODUCT_ID
# ==========================================================================================
def background_allocate(entries_created):
def background_allocate(product_id):
"""
Chạy phân bổ ngầm cho một product_id cụ thể.
Quét tất cả entry cũ + mới có tiền thừa để phân bổ.
"""
try:
# Debug type date
for e in entries_created:
print(f"Debug entry {e.code}: date type = {type(e.date)}, value = {e.date}")
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Background allocation started for product_id={product_id}")
normal_result = allocate_payment_to_schedules(entries_created)
reduction_result = allocate_penalty_reduction(entries_created)
# Phân bổ thanh toán thông thường
normal_result = allocate_payment_to_schedules(product_id)
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Background allocation completed:")
# Phân bổ miễn lãi
reduction_result = allocate_penalty_reduction(product_id)
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Background allocation completed for product_id={product_id}:")
print("Normal:", normal_result)
print("Reduction:", reduction_result)
except Exception as e:
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Background allocation error: {str(e)}")
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Background allocation error for product_id={product_id}: {str(e)}")
import traceback
print(traceback.format_exc())
# ==========================================================================================
# API TẠO MỘT BÚT TOÁN
@@ -398,27 +590,37 @@ def account_entry(request):
category=request.data['category'],
userid=request.data['user'],
ref=ref,
product=request.data['product'],
customer=request.data['customer'],
product=request.data.get('product'),
customer=request.data.get('customer'),
date=request.data.get('date')
)
if 'error' in response_data:
return Response(response_data, status=400)
if created_entry:
thread = threading.Thread(
target=background_allocate,
args=([created_entry],),
daemon=True
)
thread.start()
# Lưu product_id để chạy sau khi response
product_id_to_allocate = created_entry.product_id if created_entry else None
return Response({
# Tạo response trước
response = Response({
**response_data,
"message": "Bút toán đã tạo thành công. Phân bổ thanh toán đang chạy ngầm..."
})
# Chạy background allocation SAU KHI transaction đã commit
if product_id_to_allocate:
def run_allocation():
thread = threading.Thread(
target=background_allocate,
args=(product_id_to_allocate,),
daemon=True
)
thread.start()
transaction.on_commit(run_allocation)
return response
# ==========================================================================================
# API TẠO NHIỀU BÚT TOÁN
# ==========================================================================================
@@ -426,7 +628,7 @@ def account_entry(request):
def account_multi_entry(request):
try:
result = []
entries_created = []
product_ids = set() # Thu thập các product_id cần phân bổ
data_list = request.data.get('data', [])
with transaction.atomic():
@@ -438,30 +640,38 @@ def account_multi_entry(request):
type='CR',
category=obj['category'],
userid=request.data.get('user'),
ref=obj['ref'],
product=obj['product'],
customer=obj['customer'],
date=obj['date']
ref=obj.get('ref'),
product=obj.get('product'),
customer=obj.get('customer'),
date=obj.get('date')
)
result.append(response_data)
if created_entry:
entries_created.append(created_entry)
if created_entry and created_entry.product_id:
product_ids.add(created_entry.product_id)
if entries_created:
thread = threading.Thread(
target=background_allocate,
args=(entries_created,),
daemon=True
)
thread.start()
return Response({
# Tạo response
response = Response({
"entries": result,
"message": "Bút toán đã tạo thành công. Phân bổ thanh toán đang chạy ngầm..."
"message": f"Bút toán đã tạo thành công. Phân bổ thanh toán đang chạy ngầm cho {len(product_ids)} sản phẩm..."
})
# Chạy background allocation SAU KHI transaction đã commit
if product_ids:
def run_allocations():
for product_id in product_ids:
thread = threading.Thread(
target=background_allocate,
args=(product_id,),
daemon=True
)
thread.start()
transaction.on_commit(run_allocations)
return response
except Exception as e:
print({'error': f"Đã xảy ra lỗi không mong muốn: {str(e)}"})
return Response({'error': str(e)}, status=400)
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.