diff --git a/api/__pycache__/settings.cpython-313.pyc b/api/__pycache__/settings.cpython-313.pyc index cfea867b..a3bf485e 100644 Binary files a/api/__pycache__/settings.cpython-313.pyc and b/api/__pycache__/settings.cpython-313.pyc differ diff --git a/api/settings.py b/api/settings.py index 050da398..3be27995 100644 --- a/api/settings.py +++ b/api/settings.py @@ -21,7 +21,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent SECRET_KEY = 'django-insecure-_u202k$8qq2p*cr_eo(7k!0ngr5^n)27@85+5oy8&41(u6&j54' # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = False +DEBUG = True ALLOWED_HOSTS = ['*'] @@ -81,7 +81,7 @@ ASGI_APPLICATION = 'api.asgi.application' # https://docs.djangoproject.com/en/4.1/ref/settings/#databases #prod:5.223.52.193 dev:5.223.42.146 -MODE = 'prod' +MODE = 'dev' DBHOST = '172.17.0.1' if MODE == 'prod' else '5.223.42.146' DATABASES = { diff --git a/app/__pycache__/payment.cpython-313.pyc b/app/__pycache__/payment.cpython-313.pyc index 1a591314..242703f7 100644 Binary files a/app/__pycache__/payment.cpython-313.pyc and b/app/__pycache__/payment.cpython-313.pyc differ diff --git a/app/__pycache__/signals.cpython-313.pyc b/app/__pycache__/signals.cpython-313.pyc index 1bd5e096..883cc715 100644 Binary files a/app/__pycache__/signals.cpython-313.pyc and b/app/__pycache__/signals.cpython-313.pyc differ diff --git a/app/__pycache__/workflow_engine.cpython-313.pyc b/app/__pycache__/workflow_engine.cpython-313.pyc index 2e93c8e5..9e91cb75 100644 Binary files a/app/__pycache__/workflow_engine.cpython-313.pyc and b/app/__pycache__/workflow_engine.cpython-313.pyc differ diff --git a/app/payment.py b/app/payment.py index 9b48188a..f2865cd3 100644 --- a/app/payment.py +++ b/app/payment.py @@ -246,9 +246,11 @@ def allocate_payment_to_schedules(product_id): # --- BƯỚC 4: LÃI DỰ PHÒNG ĐẾN NAY --- days_from_entry_to_today = max(0, (today - entry_date).days) + print(f" - Lai du phong: {days_from_entry_to_today} , ngay nhap: {entry_date}, ngay hien tai: {today}") additional_penalty_to_today = Decimal('0') if amount_remain_after > 0: additional_penalty_to_today = amount_remain_after * Decimal(days_from_entry_to_today) * DAILY_PENALTY_RATE + print(f"lai du phong la : {additional_penalty_to_today}") # --- CẬP NHẬT DỮ LIỆU --- sch.paid_amount = Decimal(str(sch.paid_amount or 0)) + to_principal @@ -257,8 +259,11 @@ def allocate_payment_to_schedules(product_id): sch.penalty_amount = penalty_to_this_entry + additional_penalty_to_today sch.penalty_remain = max(Decimal('0'), sch.penalty_amount - sch.penalty_paid) sch.remain_amount = sch.amount_remain + sch.penalty_remain - sch.ovd_days = days_for_trace + days_from_entry_to_today - + if amount_remain_after > 0: + sch.ovd_days = max(0, (today - sch.to_date).days) + else : + sch.ovd_days = days_for_trace + days_from_entry_to_today + print(f"Lai la : {penalty_to_this_entry + additional_penalty_to_today} = {sch.penalty_amount}") # Ghi Trace sch_entry_list = sch.entry or [] sch_entry_list.append({ @@ -586,8 +591,11 @@ def account_multi_entry(request): @api_view(['POST']) def delete_entry(request): - """View function để xóa bút toán (tương thích với urls.py)""" + """Xóa bút toán - reset sạch entry = [], lưu hết trước, xóa entry sau, đặt lại txn/txndetail, rồi phân bổ lại""" entry_id = request.data.get('id') + + if not entry_id: + return Response({'error': 'Thiếu id bút toán'}, status=400) try: with transaction.atomic(): @@ -597,7 +605,7 @@ def delete_entry(request): return Response({ 'error': f'Bút toán với ID {entry_id} không tồn tại' }, status=404) - + entry_info = { 'id': entry.id, 'code': entry.code, @@ -609,59 +617,70 @@ def delete_entry(request): 'allocation_amount': float(entry.allocation_amount or 0), 'allocation_remain': float(entry.allocation_remain or 0) } - + + if entry.type.code != 'CR': + return Response({'error': 'Hiện chỉ hỗ trợ xóa bút toán thu tiền (CR)'}, status=400) + + product_id = entry.product_id + if not product_id: + return Response({'error': 'Bút toán không gắn với product nào'}, status=400) + allocation_detail = entry.allocation_detail or [] schedules_reversed = [] - total_principal_reversed = Decimal('0') - total_penalty_reversed = Decimal('0') - total_reduction_reversed = Decimal('0') - + + # ================================================================= + # Bước 1: Reset các lịch bị ảnh hưởng theo công thức & lưu + # ================================================================= for allocation in allocation_detail: schedule_id = allocation.get('schedule_id') allocated_amount = Decimal(str(allocation.get('amount', 0))) principal = Decimal(str(allocation.get('principal', 0))) penalty = Decimal(str(allocation.get('penalty', 0))) allocation_type = allocation.get('type', 'PAYMENT') - + if not schedule_id or allocated_amount <= 0: continue - + try: schedule = Payment_Schedule.objects.select_for_update().get(id=schedule_id) - + + current_amount_remain = Decimal(str(schedule.amount_remain or 0)) + + # Tính tổng principal đã phân bổ vào đúng lịch này từ tất cả entry CR + principal_allocated_to_schedule = Decimal('0') + for e in Internal_Entry.objects.filter(product_id=product_id, type__code='CR'): + for alloc in (e.allocation_detail or []): + if alloc.get('schedule_id') == schedule.id: + principal_allocated_to_schedule += Decimal(str(alloc.get('principal', 0))) + if allocation_type == 'REDUCTION': schedule.penalty_reduce = (schedule.penalty_reduce or Decimal('0')) - allocated_amount schedule.penalty_remain = (schedule.penalty_remain or Decimal('0')) + allocated_amount schedule.remain_amount = (schedule.remain_amount or Decimal('0')) + allocated_amount - total_reduction_reversed += allocated_amount - schedule.save(update_fields=['penalty_reduce', 'penalty_remain', 'remain_amount']) else: schedule.paid_amount = (schedule.paid_amount or Decimal('0')) - principal schedule.penalty_paid = (schedule.penalty_paid or Decimal('0')) - penalty schedule.amount_remain = (schedule.amount_remain or Decimal('0')) + principal schedule.penalty_remain = (schedule.penalty_remain or Decimal('0')) + penalty schedule.remain_amount = (schedule.remain_amount or Decimal('0')) + allocated_amount - total_principal_reversed += principal - total_penalty_reversed += penalty - - if schedule.amount_remain > 0 or schedule.penalty_remain > 0: - try: - unpaid_status = Payment_Status.objects.get(id=1) - schedule.status = unpaid_status - except Payment_Status.DoesNotExist: - pass - - schedule.save(update_fields=[ - 'paid_amount', 'penalty_paid', 'amount_remain', - 'penalty_remain', 'remain_amount', 'status' - ]) - - # Xóa entry trace của bút toán này - schedule_entries = schedule.entry or [] - schedule_entries = [e for e in schedule_entries if e.get('code') != entry.code] - schedule.entry = safe_json_serialize(schedule_entries) - schedule.save(update_fields=['entry']) - + + # Reset theo công thức + schedule.entry = [] + schedule.amount_remain = current_amount_remain + principal_allocated_to_schedule + schedule.remain_amount = schedule.amount_remain + schedule.paid_amount = schedule.amount - schedule.amount_remain + schedule.penalty_paid = Decimal('0') + schedule.penalty_reduce = Decimal('0') + schedule.penalty_amount = Decimal('0') + schedule.penalty_remain = Decimal('0') + schedule.ovd_days = 0 + schedule.status = Payment_Status.objects.get(id=1) + + schedule.save(update_fields=[ + 'entry', 'paid_amount', 'amount_remain', 'remain_amount', + 'penalty_paid', 'penalty_reduce', 'penalty_remain', 'ovd_days', 'status','penalty_amount' + ]) + schedules_reversed.append({ 'schedule_id': schedule.id, 'schedule_code': schedule.code, @@ -670,101 +689,146 @@ def delete_entry(request): 'penalty_reversed': float(penalty), 'type': allocation_type }) - + except Payment_Schedule.DoesNotExist: continue - + + # ================================================================= + # Bước 2: Reset allocation của tất cả entry CR của sản phẩm & lưu + # ================================================================= + all_cr_entries = Internal_Entry.objects.filter( + product_id=product_id, + type__code='CR' + ) + for e in all_cr_entries: + e.allocation_detail = [] + e.allocation_amount = Decimal('0') + e.allocation_remain = Decimal(str(e.amount)) + e.save(update_fields=[ + 'allocation_detail', 'allocation_amount', 'allocation_remain' + ]) + + # ================================================================= + # Bước 3: Hoàn tác số dư tài khoản (lưu trước khi xóa entry) + # ================================================================= + account = Internal_Account.objects.select_for_update().get(id=entry.account_id) + entry_amount = float(entry.amount) + if entry.type.code == 'CR': + account.balance = (account.balance or 0) - entry_amount + else: + account.balance = (account.balance or 0) + entry_amount + account.save(update_fields=['balance']) + + # ================================================================= + # Bước 4: XÓA ENTRY (sau khi reset và lưu hết) + # ================================================================= + entry.delete() + + # ================================================================= + # Bước 5: ĐẶT LẠI Transaction & Transaction_Detail TRƯỚC KHI PHÂN BỔ LẠI + # ================================================================= txn_detail_updated = False txn_updated = False - + if entry.product: try: booked = Product_Booked.objects.filter(product=entry.product).first() if booked and booked.transaction: txn = booked.transaction - + + # Lấy tất cả lịch liên quan + all_schedules = Payment_Schedule.objects.filter( + txn_detail__transaction=txn + ) + + # Tính tổng paid_amount từ các lịch (trước khi phân bổ lại) + total_paid_all = Decimal('0') + total_remain_all = Decimal('0') + total_deposit_paid = Decimal('0') + + for sch in all_schedules: + paid = Decimal(str(sch.paid_amount or 0)) + remain = Decimal(str(sch.amount_remain or 0)) + + total_paid_all += paid + total_remain_all += remain + + if sch.type_id == 1: # type=1 là lịch đặt cọc + total_deposit_paid += paid + + # Tính lại Transaction_Detail try: current = Transaction_Current.objects.get(transaction=txn) txn_detail = current.detail except Transaction_Current.DoesNotExist: txn_detail = Transaction_Detail.objects.filter(transaction=txn).order_by('-create_time').first() - - if txn_detail: - if entry.account_id != 5: - fields_to_update = [] - if total_principal_reversed > 0: - txn_detail.amount_received = F('amount_received') - total_principal_reversed - txn_detail.amount_remaining = F('amount_remaining') + total_principal_reversed - fields_to_update.extend(['amount_received', 'amount_remaining']) - if total_penalty_reversed > 0 and hasattr(txn_detail, 'penalty_amount'): - txn_detail.penalty_amount = F('penalty_amount') - total_penalty_reversed - fields_to_update.append('penalty_amount') - if fields_to_update: - txn_detail.save(update_fields=fields_to_update) - txn_detail.refresh_from_db() - txn_detail_updated = True - - if txn_detail.amount_remaining > 0: - try: - unpaid_status = Transaction_Status.objects.get(id=1) - txn_detail.status = unpaid_status - txn_detail.save(update_fields=['status']) - except: - pass - if entry.account_id != 5: - fields_to_update = [] - if total_principal_reversed > 0: - txn.amount_received = F('amount_received') - total_principal_reversed - txn.amount_remain = F('amount_remain') + total_principal_reversed - fields_to_update.extend(['amount_received', 'amount_remain']) - if total_penalty_reversed > 0 and hasattr(txn, 'penalty_amount'): - txn.penalty_amount = F('penalty_amount') - total_penalty_reversed - fields_to_update.append('penalty_amount') - if fields_to_update: - txn.save(update_fields=fields_to_update) - txn.refresh_from_db() - txn_updated = True - - if txn.amount_remain > 0: + if txn_detail: + # amount_received = amount_remaining hiện tại + tổng paid_amount từ lịch + txn_detail.amount_received = Decimal(str(txn_detail.amount_remaining or 0)) + total_paid_all + # amount_remaining = amount - amount_received vừa tính + txn_detail.amount_remaining = Decimal(str(txn_detail.amount or 0)) - txn_detail.amount_received + txn_detail.save(update_fields=['amount_received', 'amount_remaining']) + txn_detail.refresh_from_db() + txn_detail_updated = True + + if txn_detail.amount_remaining > 0: try: unpaid_status = Transaction_Status.objects.get(id=1) - txn.status = unpaid_status - txn.save(update_fields=['status']) + txn_detail.status = unpaid_status + txn_detail.save(update_fields=['status']) except: pass - + + # Tính lại Transaction - đặt lại các trường về trạng thái ban đầu + txn.amount_received = Decimal('0') + txn.amount_remain = Decimal(str(txn.amount or 0)) # amount gốc hợp đồng + if hasattr(txn, 'deposit_received'): + txn.deposit_received = Decimal('0') + txn.deposit_remaining = txn.deposit_amount if hasattr(txn, 'deposit_amount') else Decimal('0') + + txn.save(update_fields=['amount_received', 'amount_remain', 'deposit_received', 'deposit_remaining']) + txn.refresh_from_db() + txn_updated = True + + if txn.amount_remain > 0: + try: + unpaid_status = Transaction_Status.objects.get(id=1) + txn.status = unpaid_status + txn.save(update_fields=['status']) + except: + pass + except Exception as e: - print(f"Lỗi khi hoàn tác Transaction: {str(e)}") - - account = Internal_Account.objects.select_for_update().get(id=entry.account_id) - entry_amount = float(entry.amount) - - if entry.type.code == 'CR': - account.balance = (account.balance or 0) - entry_amount - else: - account.balance = (account.balance or 0) + entry_amount - - account.save(update_fields=['balance']) - - entry.delete() - - return Response({ - 'success': True, - 'message': 'Đã xóa bút toán và hoàn tác tất cả phân bổ thành công', - 'entry': entry_info, - 'reversed': { - 'schedules_count': len(schedules_reversed), - 'schedules': schedules_reversed, - 'total_principal_reversed': float(total_principal_reversed), - 'total_penalty_reversed': float(total_penalty_reversed), - 'total_reduction_reversed': float(total_reduction_reversed), - 'transaction_detail_updated': txn_detail_updated, - 'transaction_updated': txn_updated - }, - 'account_balance_restored': True - }) - + print(f"Lỗi khi đặt lại Transaction trước phân bổ: {str(e)}") + + # ================================================================= + # Bước 6: Phân bổ lại toàn bộ sản phẩm (sẽ tự tính lại txn/txndetail đúng) + # ================================================================= + def trigger_reallocate(): + if product_id: + try: + allocate_payment_to_schedules(product_id) + allocate_penalty_reduction(product_id) + except Exception as exc: + print(f"Lỗi khi re-allocate sau xóa: {exc}") + traceback.print_exc() + + transaction.on_commit(trigger_reallocate) + + return Response({ + 'success': True, + 'message': 'Đã xóa bút toán, reset sạch entry = [], lưu hết trước, xóa entry sau, đặt lại txn/txndetail trước phân bổ, đang phân bổ lại toàn bộ...', + 'entry': entry_info, + 'reversed': { + 'schedules_count': len(schedules_reversed), + 'schedules': schedules_reversed, + 'transaction_detail_updated': txn_detail_updated, + 'transaction_updated': txn_updated + }, + 'account_balance_restored': True + }) + except Exception as e: import traceback print(traceback.format_exc()) diff --git a/app/scheduler.py b/app/scheduler.py index a81142c6..982332f6 100644 --- a/app/scheduler.py +++ b/app/scheduler.py @@ -35,7 +35,6 @@ def scan_and_run_due_jobs(): if uninitialized_jobs.exists(): #logger.info(f"Found {uninitialized_jobs.count()} uninitialized jobs. Calculating next run time...") - # Lấy timezone của dự án tz = pytz.timezone(settings.TIME_ZONE) for job in uninitialized_jobs: @@ -69,12 +68,19 @@ def scan_and_run_due_jobs(): # BƯỚC 2: Quét và chạy các job đến hạn như bình thường #logger.info("Scanning for due batch jobs...") - - # Lấy các job cần chạy (có next_run_at không null và đã đến hạn) - due_jobs = Batch_Job.objects.filter(is_active=True, next_run_at__lte=now) + active_jobs = Batch_Job.objects.filter(is_active=True) + + # Tách riêng job chạy mỗi phút (* * * * *) + every_minute_jobs = active_jobs.filter(cron_schedule="* * * * *") + + # Các job bình thường (không phải * * * * *), kiểm tra next_run_at + normal_due_jobs = active_jobs.filter(next_run_at__lte=now).exclude(cron_schedule="* * * * *") + + # Gộp hai QuerySet bằng union (hoặc |) + due_jobs = every_minute_jobs | normal_due_jobs if not due_jobs.exists(): - #logger.info("-> No due jobs found at this time.") + logger.info("-> No due jobs found at this time.") return #logger.info(f"-> Found {due_jobs.count()} due jobs to run.") @@ -159,4 +165,3 @@ def start(): # Chạy tác vụ quét job mỗi 5 giây scheduler.add_job(scan_and_run_due_jobs, 'interval', seconds=5, id='scan_due_jobs_job', replace_existing=True) scheduler.start() - #logger.info("APScheduler started... Jobs will be scanned every 5 seconds.") diff --git a/app/signals.py b/app/signals.py index 3e4bb6f6..c9576130 100644 --- a/app/signals.py +++ b/app/signals.py @@ -58,9 +58,18 @@ def generic_post_save_handler(sender, instance, created, **kwargs): """ def send_update_after_commit(): change_type = "created" if created else "updated" - # Re-fetch the instance to ensure we have the committed data - refreshed_instance = sender.objects.get(pk=instance.pk) - send_model_update(refreshed_instance, change_type) + try: + # Re-fetch the instance to ensure we have the committed data + refreshed_instance = sender.objects.get(pk=instance.pk) + send_model_update(refreshed_instance, change_type) + except sender.DoesNotExist: + # Object đã bị xóa (ví dụ: delete_entry vừa xóa Internal_Entry) + # Bỏ qua việc gửi update, hoặc gửi thông báo "deleted" nếu cần + print(f"Object {sender.__name__} {instance.pk} đã bị xóa, bỏ qua gửi update.") + # Optional: vẫn gửi "deleted" để frontend biết object không còn + send_model_update(instance, "deleted") + except Exception as exc: + print(f"Lỗi trong send_update_after_commit: {exc}") transaction.on_commit(send_update_after_commit) diff --git a/static/contract/PXLTTDSH_703_1770264134.docx b/static/contract/PXLTTDSH_703_1770264134.docx deleted file mode 100644 index 816ef8d6..00000000 Binary files a/static/contract/PXLTTDSH_703_1770264134.docx and /dev/null differ diff --git a/static/contract/PXLTTDSH_703_1770264134.pdf b/static/contract/PXLTTDSH_703_1770264134.pdf deleted file mode 100644 index 597f7d95..00000000 Binary files a/static/contract/PXLTTDSH_703_1770264134.pdf and /dev/null differ diff --git a/static/contract/PXLTTDSH_703_1770264219.pdf b/static/contract/PXLTTDSH_703_1770264219.pdf deleted file mode 100644 index 19b2cda2..00000000 Binary files a/static/contract/PXLTTDSH_703_1770264219.pdf and /dev/null differ diff --git a/static/contract/PXLTTDSH_704_1770264500.docx b/static/contract/PXLTTDSH_704_1770264500.docx deleted file mode 100644 index 74aad75b..00000000 Binary files a/static/contract/PXLTTDSH_704_1770264500.docx and /dev/null differ diff --git a/static/contract/PXLTTDSH_704_1770264500.pdf b/static/contract/PXLTTDSH_704_1770264500.pdf deleted file mode 100644 index 1cce46d1..00000000 Binary files a/static/contract/PXLTTDSH_704_1770264500.pdf and /dev/null differ diff --git a/static/contract/PXLTTDSH_704_1770264627.docx b/static/contract/PXLTTDSH_704_1770264627.docx deleted file mode 100644 index 7eb92610..00000000 Binary files a/static/contract/PXLTTDSH_704_1770264627.docx and /dev/null differ diff --git a/static/contract/PXLTTDSH_704_1770264627.pdf b/static/contract/PXLTTDSH_704_1770264627.pdf deleted file mode 100644 index ef76a73a..00000000 Binary files a/static/contract/PXLTTDSH_704_1770264627.pdf and /dev/null differ diff --git a/static/contract/PXLTTDSH_708_1770272884.docx b/static/contract/PXLTTDSH_708_1770272884.docx deleted file mode 100644 index b2f6971e..00000000 Binary files a/static/contract/PXLTTDSH_708_1770272884.docx and /dev/null differ diff --git a/static/contract/PXLTTDSH_703_1770264219.docx b/static/contract/PXLTTD_746_1770629679.docx similarity index 53% rename from static/contract/PXLTTDSH_703_1770264219.docx rename to static/contract/PXLTTD_746_1770629679.docx index dd54d9c1..b0a54333 100644 Binary files a/static/contract/PXLTTDSH_703_1770264219.docx and b/static/contract/PXLTTD_746_1770629679.docx differ diff --git a/static/contract/PXLTTDSH_708_1770272884.pdf b/static/contract/PXLTTD_746_1770629679.pdf similarity index 60% rename from static/contract/PXLTTDSH_708_1770272884.pdf rename to static/contract/PXLTTD_746_1770629679.pdf index 56355ea0..ed1243db 100644 Binary files a/static/contract/PXLTTDSH_708_1770272884.pdf and b/static/contract/PXLTTD_746_1770629679.pdf differ diff --git a/static/contract/TTTHNVDSH_703_1770264222.docx b/static/contract/TTTHNVDSH_703_1770264222.docx deleted file mode 100644 index 96846ae7..00000000 Binary files a/static/contract/TTTHNVDSH_703_1770264222.docx and /dev/null differ diff --git a/static/contract/TTTHNVDSH_703_1770264222.pdf b/static/contract/TTTHNVDSH_703_1770264222.pdf deleted file mode 100644 index 262b4703..00000000 Binary files a/static/contract/TTTHNVDSH_703_1770264222.pdf and /dev/null differ diff --git a/static/contract/TTTHNVDSH_704_1770264631.docx b/static/contract/TTTHNVDSH_704_1770264631.docx deleted file mode 100644 index 6ce5a64f..00000000 Binary files a/static/contract/TTTHNVDSH_704_1770264631.docx and /dev/null differ diff --git a/static/contract/TTTHNVDSH_704_1770264631.pdf b/static/contract/TTTHNVDSH_704_1770264631.pdf deleted file mode 100644 index ba2d1d65..00000000 Binary files a/static/contract/TTTHNVDSH_704_1770264631.pdf and /dev/null differ diff --git a/static/files/20260209023953-entry.xlsx b/static/files/20260209023953-entry.xlsx new file mode 100644 index 00000000..caced804 Binary files /dev/null and b/static/files/20260209023953-entry.xlsx differ diff --git a/static/files/20260209024800-entry.xlsx b/static/files/20260209024800-entry.xlsx new file mode 100644 index 00000000..caced804 Binary files /dev/null and b/static/files/20260209024800-entry.xlsx differ diff --git a/static/files/20260209030231-entry.xlsx b/static/files/20260209030231-entry.xlsx new file mode 100644 index 00000000..caced804 Binary files /dev/null and b/static/files/20260209030231-entry.xlsx differ diff --git a/static/files/20260209031004-entry.xlsx b/static/files/20260209031004-entry.xlsx new file mode 100644 index 00000000..caced804 Binary files /dev/null and b/static/files/20260209031004-entry.xlsx differ diff --git a/static/files/20260209032303-entry.xlsx b/static/files/20260209032303-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209032303-entry.xlsx differ diff --git a/static/files/20260209033719-entry.xlsx b/static/files/20260209033719-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209033719-entry.xlsx differ diff --git a/static/files/20260209033909-entry.xlsx b/static/files/20260209033909-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209033909-entry.xlsx differ diff --git a/static/files/20260209034316-entry.xlsx b/static/files/20260209034316-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209034316-entry.xlsx differ diff --git a/static/files/20260209034507-entry.xlsx b/static/files/20260209034507-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209034507-entry.xlsx differ diff --git a/static/files/20260209034655-entry.xlsx b/static/files/20260209034655-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209034655-entry.xlsx differ diff --git a/static/files/20260209034929-entry.xlsx b/static/files/20260209034929-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209034929-entry.xlsx differ diff --git a/static/files/20260209035605-entry.xlsx b/static/files/20260209035605-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209035605-entry.xlsx differ diff --git a/static/files/20260209043501-entry.xlsx b/static/files/20260209043501-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209043501-entry.xlsx differ diff --git a/static/files/20260209043712-entry.xlsx b/static/files/20260209043712-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209043712-entry.xlsx differ diff --git a/static/files/20260209044229-entry.xlsx b/static/files/20260209044229-entry.xlsx new file mode 100644 index 00000000..4184b15d Binary files /dev/null and b/static/files/20260209044229-entry.xlsx differ