diff --git a/api/__pycache__/settings.cpython-313.pyc b/api/__pycache__/settings.cpython-313.pyc index 3057f227..cfea867b 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 3be27995..050da398 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 = True +DEBUG = False 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 = 'dev' +MODE = 'prod' 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 2c6da666..1a591314 100644 Binary files a/app/__pycache__/payment.cpython-313.pyc and b/app/__pycache__/payment.cpython-313.pyc differ diff --git a/app/payment.py b/app/payment.py index c8e05e59..9b48188a 100644 --- a/app/payment.py +++ b/app/payment.py @@ -12,16 +12,6 @@ import json # ========================================================================================== # HELPER FUNCTIONS # ========================================================================================== -def safe_json_serialize(obj): - """Serialize an toàn cho JSONField""" - if isinstance(obj, (datetime, date)): - return obj.isoformat() - if isinstance(obj, dict): - return {k: safe_json_serialize(v) for k, v in obj.items()} - if isinstance(obj, (list, tuple)): - return [safe_json_serialize(item) for item in obj] - return obj - def get_latest_payment_date(schedule): """Lấy ngày nộp gần nhất từ entry (type='PAYMENT')""" @@ -130,162 +120,42 @@ def get_allocation_rule(): return 'principal-fee' -# ========================================================================================== -# LOGIC TÍNH LÃI ĐÚNG - CÁCH ĐƠN GIẢN NHẤT # ========================================================================================== -def recalculate_penalty_amount(schedule, up_to_date=None): - """ - TÍNH LẠI TOÀN BỘ LÃI PHẠT từ đầu đến ngày chỉ định - - Logic: - 1. Quét tất cả các PAYMENT entry để xây dựng timeline - 2. Tính lãi cho từng khoảng thời gian với gốc tương ứng - 3. Trả về tổng lãi (bao gồm cả lãi đã trả + lãi còn lại) - - Đây là cách DUY NHẤT để tránh cộng dồn sai! - """ - if up_to_date is None: - up_to_date = datetime.now().date() - elif isinstance(up_to_date, str): - up_to_date = datetime.strptime(up_to_date, "%Y-%m-%d").date() - - to_date = schedule.to_date - if isinstance(to_date, datetime): - to_date = to_date.date() - - # Nếu chưa quá hạn - if up_to_date <= to_date: - return Decimal('0') - - # Xây dựng timeline: [(date, gốc còn lại sau ngày đó)] - timeline = [] - - # Lấy tất cả PAYMENT entries, sắp xếp theo thời gian - entry_data = schedule.entry or [] - if isinstance(entry_data, str): - try: - entry_data = json.loads(entry_data) - except json.JSONDecodeError: - entry_data = [] - - payments = [e for e in entry_data if e.get('type') == 'PAYMENT'] - payments.sort(key=lambda x: x.get('date', '')) - - # Điểm bắt đầu: to_date với gốc ban đầu - original_amount = Decimal(str(schedule.amount or 0)) - current_principal = original_amount - - timeline.append((to_date, current_principal)) - - # Thêm các điểm thanh toán - for payment in payments: - payment_date = datetime.strptime(payment['date'], "%Y-%m-%d").date() - principal_paid = Decimal(str(payment.get('principal', 0))) - current_principal -= principal_paid - if current_principal < 0: - current_principal = Decimal('0') - timeline.append((payment_date, current_principal)) - - # Tính lãi cho từng khoảng - total_penalty = Decimal('0') - - for i in range(len(timeline)): - start_date, principal = timeline[i] - - # Xác định ngày kết thúc của khoảng này - if i < len(timeline) - 1: - end_date = timeline[i + 1][0] - else: - end_date = up_to_date - - # Tính số ngày và lãi - days = (end_date - start_date).days - if days > 0 and principal > 0: - penalty = principal * Decimal('0.0005') * Decimal(days) - penalty = penalty.quantize(Decimal('0.01')) - total_penalty += penalty - - return total_penalty +DAILY_PENALTY_RATE = Decimal('0.0005') # 0.05% mỗi ngày +def safe_json_serialize(obj): + """Serialize an toàn cho JSONField""" + if isinstance(obj, (datetime, date)): + return obj.isoformat() + if isinstance(obj, dict): + return {k: safe_json_serialize(v) for k, v in obj.items()} + if isinstance(obj, (list, tuple)): + return [safe_json_serialize(item) for item in obj] + return obj -def update_penalty_after_allocation(schedule): - """ - Cập nhật lãi phạt SAU KHI đã phân bổ xong - Gọi hàm này SAU KHI đã cập nhật paid_amount, amount_remain - """ - today = datetime.now().date() - - # Tính lại TOÀN BỘ lãi từ đầu đến giờ - total_penalty = recalculate_penalty_amount(schedule, up_to_date=today) - - # Cập nhật - schedule.penalty_amount = total_penalty - penalty_paid = Decimal(str(schedule.penalty_paid or 0)) - schedule.penalty_remain = total_penalty - penalty_paid - schedule.remain_amount = Decimal(str(schedule.amount_remain or 0)) + schedule.penalty_remain - schedule.batch_date = today - - # Lưu trace - entry_list = schedule.entry or [] - if isinstance(entry_list, str): - try: - entry_list = json.loads(entry_list) - except json.JSONDecodeError: - entry_list = [] - - # Xóa trace ongoing cũ - entry_list = [e for e in entry_list if e.get('type') != 'PENALTY_RECALC'] - - entry_list.append({ - "type": "PENALTY_RECALC", - "date": today.isoformat(), - "penalty_total": float(total_penalty), - "penalty_paid": float(penalty_paid), - "penalty_remain": float(schedule.penalty_remain), - "note": f"Tính lại tổng lãi đến {today}: {total_penalty:,.0f}" - }) - schedule.entry = safe_json_serialize(entry_list) - - schedule.save(update_fields=[ - 'penalty_amount', 'penalty_remain', 'remain_amount', - 'batch_date', 'entry' - ]) - - -# ========================================================================================== def allocate_payment_to_schedules(product_id): if not product_id: return {"status": "no_product", "message": "Không có product_id"} updated_schedules = [] updated_entries = [] - errors = [] - paid_payment_status = Payment_Status.objects.filter(id=2).first() paid_txn_status = Transaction_Status.objects.filter(id=2).first() + today = datetime.now().date() + DAILY_PENALTY_RATE = Decimal('0.0005') # Giả định 0.05% with transaction.atomic(): try: product = Product.objects.get(id=product_id) 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} + return {"status": "error", "errors": ["Không tìm thấy Transaction"]} txn = booked.transaction + txn_detail = Transaction_Detail.objects.filter(transaction=txn).order_by('-create_time').first() - 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"Product {product_id}: Không tìm thấy Transaction_Detail") - return {"status": "error", "errors": errors} - + # Lấy các bút toán CR còn dư tiền (Sắp xếp chính xác để trừ theo thứ tự thời gian) entries_with_remain = Internal_Entry.objects.select_for_update().filter( product=product, type__code='CR', @@ -293,177 +163,169 @@ def allocate_payment_to_schedules(product_id): ).exclude(account__id=5).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": [] - } + return {"status": "success", "message": "Không có tiền để phân bổ"} + # Lấy lịch nợ (chỉ lấy 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": [] - } - total_principal_allocated = Decimal('0') total_penalty_allocated = Decimal('0') for entry in entries_with_remain: entry_date = entry.date - entry_date_str = entry_date if isinstance(entry_date, str) else entry_date.strftime("%Y-%m-%d") + if isinstance(entry_date, str): + entry_date = datetime.strptime(entry_date, "%Y-%m-%d").date() remaining = Decimal(str(entry.allocation_remain)) - if remaining <= 0: - continue + if remaining <= 0: continue entry_allocation_detail = entry.allocation_detail or [] entry_principal_allocated = Decimal('0') entry_penalty_allocated = Decimal('0') for sch in schedules: - if remaining <= 0: - break + if remaining <= 0: break - # RULE: principal-fee (gốc trước, lãi sau) - penalty_remain = Decimal(str(sch.penalty_remain or 0)) - amount_remain = Decimal(str(sch.amount_remain or 0)) - paid_amount = Decimal(str(sch.paid_amount or 0)) - penalty_paid = Decimal(str(sch.penalty_paid or 0)) + current_amount_remain = Decimal(str(sch.amount_remain or 0)) + + # --- BƯỚC 1: LẤY LÃI TÍCH LŨY TỪ TRACE --- + last_entry_date = None + accumulated_penalty_to_last = Decimal('0') + + if sch.entry: + for e in sch.entry: + if e.get('type') == 'PAYMENT': + e_date = datetime.strptime(e['date'], "%Y-%m-%d").date() + # Dùng <= để lấy được cả bút toán cùng ngày nộp trước đó + if e_date <= entry_date: + if e.get('penalty_to_this_entry') is not None: + # Luôn cập nhật để lấy con số lũy kế MỚI NHẤT + accumulated_penalty_to_last = Decimal(str(e['penalty_to_this_entry'])) + + if not last_entry_date or e_date > last_entry_date: + last_entry_date = e_date - # Trả gốc trước - to_principal = min(remaining, amount_remain) + # --- BƯỚC 2: TÍNH LÃI PHÁT SINH MỚI --- + penalty_added_to_entry = Decimal('0') + days_for_trace = 0 + if current_amount_remain > 0: + if last_entry_date: + days_between = max(0, (entry_date - last_entry_date).days) + penalty_added_to_entry = current_amount_remain * Decimal(days_between) * DAILY_PENALTY_RATE + days_for_trace = days_between + else: + days_overdue_to_entry = max(0, (entry_date - sch.to_date).days) + penalty_added_to_entry = current_amount_remain * Decimal(days_overdue_to_entry) * DAILY_PENALTY_RATE + days_for_trace = days_overdue_to_entry + + # Tổng nợ lãi tại thời điểm này + penalty_to_this_entry = accumulated_penalty_to_last + penalty_added_to_entry + + # QUAN TRỌNG: Nợ lãi thực tế cần trả ngay bây giờ + penalty_to_pay_now = max(Decimal('0'), penalty_to_this_entry - Decimal(str(sch.penalty_paid or 0))) + + # --- BƯỚC 3: PHÂN BỔ TIỀN --- + to_principal = min(remaining, current_amount_remain) remaining -= to_principal - paid_amount += to_principal - amount_remain -= to_principal + amount_remain_after = current_amount_remain - to_principal - # Trả lãi sau (nếu còn tiền) - to_penalty = min(remaining, penalty_remain) + to_penalty = min(remaining, penalty_to_pay_now) remaining -= to_penalty - penalty_paid += to_penalty - penalty_remain -= to_penalty - + allocated_here = to_principal + to_penalty + + # Nếu vẫn không có gì để phân bổ cho kỳ này, bỏ qua sang kỳ sau if allocated_here <= 0: continue 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 = amount_remain + penalty_remain + # --- BƯỚC 4: LÃI DỰ PHÒNG ĐẾN NAY --- + days_from_entry_to_today = max(0, (today - entry_date).days) + 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 - if amount_remain <= 0: - sch.batch_date = datetime.strptime(entry_date_str, "%Y-%m-%d").date() + # --- CẬP NHẬT DỮ LIỆU --- + sch.paid_amount = Decimal(str(sch.paid_amount or 0)) + to_principal + sch.penalty_paid = Decimal(str(sch.penalty_paid or 0)) + to_penalty + sch.amount_remain = amount_remain_after + 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 - # Lưu trace PAYMENT + # Ghi Trace sch_entry_list = sch.entry or [] sch_entry_list.append({ "type": "PAYMENT", "code": entry.code, - "date": entry_date_str, + "date": entry_date.strftime("%Y-%m-%d"), "amount": float(allocated_here), "principal": float(to_principal), "penalty": float(to_penalty), - "rule": "principal-fee" + "penalty_added_to_entry": float(penalty_added_to_entry), + "penalty_to_this_entry": float(penalty_to_this_entry), + "amount_remain_after_allocation": float(amount_remain_after), }) - sch.entry = safe_json_serialize(sch_entry_list) + sch.entry = sch_entry_list # Lưu lại list - # Lưu schedule (chưa tính lại lãi) - sch.save(update_fields=[ - 'paid_amount', 'penalty_paid', 'amount_remain', - 'penalty_remain', 'remain_amount', 'entry', 'batch_date' - ]) - - # ===== KEY: TÍNH LẠI LÃI SAU KHI PHÂN BỔ ===== - update_penalty_after_allocation(sch) - - # Cập nhật lại status nếu đã trả hết - if sch.amount_remain <= 0 and sch.penalty_remain <= 0 and paid_payment_status: + if sch.amount_remain <= 0 and sch.penalty_remain <= 0: sch.status = paid_payment_status - sch.save(update_fields=['status']) - - if sch.id not in updated_schedules: - updated_schedules.append(sch.id) + + sch.save() + if sch.id not in updated_schedules: updated_schedules.append(sch.id) 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") + "schedule_id": sch.id, "amount": float(allocated_here), + "principal": float(to_principal), "penalty": float(to_penalty) }) - total_allocated = entry_principal_allocated + entry_penalty_allocated - entry.allocation_amount = (entry.allocation_amount or Decimal('0')) + total_allocated + # Cập nhật Entry nguồn + entry.allocation_amount = (entry.allocation_amount or Decimal('0')) + (entry_principal_allocated + entry_penalty_allocated) entry.allocation_remain = remaining 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) - + entry.save() + total_principal_allocated += entry_principal_allocated total_penalty_allocated += entry_penalty_allocated - # Cập nhật Transaction & Transaction_Detail + ## Cập nhật Transaction (Giữ nguyên gốc) if total_principal_allocated > 0 or total_penalty_allocated > 0: + # Cập nhật tiền gốc đã nhận txn_detail.amount_received = F('amount_received') + total_principal_allocated txn_detail.amount_remaining = F('amount_remaining') - total_principal_allocated - 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']) - txn_detail.refresh_from_db() - - if txn_detail.amount_remaining <= 0 and paid_txn_status: - txn_detail.status = paid_txn_status - txn_detail.save(update_fields=['status']) - + txn_detail.save() + txn.amount_received = F('amount_received') + total_principal_allocated txn.amount_remain = F('amount_remain') - total_principal_allocated - 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.save() + + # QUAN TRỌNG: Kiểm tra để đóng Status + txn_detail.refresh_from_db() txn.refresh_from_db() - if txn.amount_remain <= 0 and paid_txn_status: - txn.status = paid_txn_status - txn.save(update_fields=['status']) + # Nếu gốc đã hết (amount_remaining <= 0) + # VÀ không còn kỳ lịch thanh toán nào chưa hoàn thành (đã sạch nợ lãi) + has_pending_sch = Payment_Schedule.objects.filter(txn_detail=txn_detail, status__id=1).exists() + + if txn_detail.amount_remaining <= 0 and not has_pending_sch: + if paid_txn_status: + txn_detail.status = paid_txn_status + txn_detail.save() + txn.status = paid_txn_status + txn.save() + + return {"status": "success", "updated_schedules": updated_schedules} except Exception as exc: - errors.append(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": "principal-fee", - "total_principal_allocated": float(total_principal_allocated), - "total_penalty_allocated": float(total_penalty_allocated) - } - - + return {"status": "error", "errors": [str(exc)]} # ========================================================================================== def allocate_penalty_reduction(product_id): """ @@ -800,9 +662,6 @@ def delete_entry(request): schedule.entry = safe_json_serialize(schedule_entries) schedule.save(update_fields=['entry']) - # Tính lại lãi sau khi hoàn tác - update_penalty_after_allocation(schedule) - schedules_reversed.append({ 'schedule_id': schedule.id, 'schedule_code': schedule.code, diff --git a/static/contract/PXLTTDSH_703_1770264134.docx b/static/contract/PXLTTDSH_703_1770264134.docx new file mode 100644 index 00000000..816ef8d6 Binary files /dev/null and b/static/contract/PXLTTDSH_703_1770264134.docx differ diff --git a/static/contract/PXLTTDSH_703_1770264134.pdf b/static/contract/PXLTTDSH_703_1770264134.pdf new file mode 100644 index 00000000..597f7d95 Binary files /dev/null and b/static/contract/PXLTTDSH_703_1770264134.pdf differ diff --git a/static/contract/PXLTTDSH_703_1770264219.docx b/static/contract/PXLTTDSH_703_1770264219.docx new file mode 100644 index 00000000..dd54d9c1 Binary files /dev/null and b/static/contract/PXLTTDSH_703_1770264219.docx differ diff --git a/static/contract/PXLTTDSH_703_1770264219.pdf b/static/contract/PXLTTDSH_703_1770264219.pdf new file mode 100644 index 00000000..19b2cda2 Binary files /dev/null and b/static/contract/PXLTTDSH_703_1770264219.pdf differ diff --git a/static/contract/PXLTTDSH_704_1770264500.docx b/static/contract/PXLTTDSH_704_1770264500.docx new file mode 100644 index 00000000..74aad75b Binary files /dev/null and b/static/contract/PXLTTDSH_704_1770264500.docx differ diff --git a/static/contract/PXLTTDSH_704_1770264500.pdf b/static/contract/PXLTTDSH_704_1770264500.pdf new file mode 100644 index 00000000..1cce46d1 Binary files /dev/null and b/static/contract/PXLTTDSH_704_1770264500.pdf differ diff --git a/static/contract/PXLTTDSH_704_1770264627.docx b/static/contract/PXLTTDSH_704_1770264627.docx new file mode 100644 index 00000000..7eb92610 Binary files /dev/null and b/static/contract/PXLTTDSH_704_1770264627.docx differ diff --git a/static/contract/PXLTTDSH_704_1770264627.pdf b/static/contract/PXLTTDSH_704_1770264627.pdf new file mode 100644 index 00000000..ef76a73a Binary files /dev/null and b/static/contract/PXLTTDSH_704_1770264627.pdf differ diff --git a/static/contract/PXLTTDSH_708_1770272884.docx b/static/contract/PXLTTDSH_708_1770272884.docx new file mode 100644 index 00000000..b2f6971e Binary files /dev/null and b/static/contract/PXLTTDSH_708_1770272884.docx differ diff --git a/static/contract/PXLTTDSH_708_1770272884.pdf b/static/contract/PXLTTDSH_708_1770272884.pdf new file mode 100644 index 00000000..56355ea0 Binary files /dev/null and b/static/contract/PXLTTDSH_708_1770272884.pdf differ diff --git a/static/contract/TTTHNVDSH_703_1770264222.docx b/static/contract/TTTHNVDSH_703_1770264222.docx new file mode 100644 index 00000000..96846ae7 Binary files /dev/null and b/static/contract/TTTHNVDSH_703_1770264222.docx differ diff --git a/static/contract/TTTHNVDSH_703_1770264222.pdf b/static/contract/TTTHNVDSH_703_1770264222.pdf new file mode 100644 index 00000000..262b4703 Binary files /dev/null and b/static/contract/TTTHNVDSH_703_1770264222.pdf differ diff --git a/static/contract/TTTHNVDSH_704_1770264631.docx b/static/contract/TTTHNVDSH_704_1770264631.docx new file mode 100644 index 00000000..6ce5a64f Binary files /dev/null and b/static/contract/TTTHNVDSH_704_1770264631.docx differ diff --git a/static/contract/TTTHNVDSH_704_1770264631.pdf b/static/contract/TTTHNVDSH_704_1770264631.pdf new file mode 100644 index 00000000..ba2d1d65 Binary files /dev/null and b/static/contract/TTTHNVDSH_704_1770264631.pdf differ diff --git a/static/files/20260205025933-entry.xlsx b/static/files/20260205025933-entry.xlsx new file mode 100644 index 00000000..ec239e50 Binary files /dev/null and b/static/files/20260205025933-entry.xlsx differ diff --git a/static/files/20260205030543-entry.xlsx b/static/files/20260205030543-entry.xlsx new file mode 100644 index 00000000..ec239e50 Binary files /dev/null and b/static/files/20260205030543-entry.xlsx differ diff --git a/static/files/20260205031633-entry.xlsx b/static/files/20260205031633-entry.xlsx new file mode 100644 index 00000000..ec239e50 Binary files /dev/null and b/static/files/20260205031633-entry.xlsx differ diff --git a/static/files/20260205032018-entry.xlsx b/static/files/20260205032018-entry.xlsx new file mode 100644 index 00000000..ec239e50 Binary files /dev/null and b/static/files/20260205032018-entry.xlsx differ diff --git a/static/files/20260205032528-entry.xlsx b/static/files/20260205032528-entry.xlsx new file mode 100644 index 00000000..c7554696 Binary files /dev/null and b/static/files/20260205032528-entry.xlsx differ diff --git a/static/files/20260205034954-entry.xlsx b/static/files/20260205034954-entry.xlsx new file mode 100644 index 00000000..4795dc5c Binary files /dev/null and b/static/files/20260205034954-entry.xlsx differ diff --git a/static/files/20260205044325-entry.xlsx b/static/files/20260205044325-entry.xlsx new file mode 100644 index 00000000..eafd77a4 Binary files /dev/null and b/static/files/20260205044325-entry.xlsx differ diff --git a/static/files/20260205045500-entry.xlsx b/static/files/20260205045500-entry.xlsx new file mode 100644 index 00000000..354fbfbb Binary files /dev/null and b/static/files/20260205045500-entry.xlsx differ diff --git a/static/files/20260205053000-entry.xlsx b/static/files/20260205053000-entry.xlsx new file mode 100644 index 00000000..acd0da3a Binary files /dev/null and b/static/files/20260205053000-entry.xlsx differ diff --git a/static/files/20260205053214-entry.xlsx b/static/files/20260205053214-entry.xlsx new file mode 100644 index 00000000..eda7be31 Binary files /dev/null and b/static/files/20260205053214-entry.xlsx differ diff --git a/static/files/20260205053411-entry.xlsx b/static/files/20260205053411-entry.xlsx new file mode 100644 index 00000000..af2d78d7 Binary files /dev/null and b/static/files/20260205053411-entry.xlsx differ diff --git a/static/files/20260205054105-entry.xlsx b/static/files/20260205054105-entry.xlsx new file mode 100644 index 00000000..af2d78d7 Binary files /dev/null and b/static/files/20260205054105-entry.xlsx differ diff --git a/static/files/20260205054615-entry.xlsx b/static/files/20260205054615-entry.xlsx new file mode 100644 index 00000000..92ac6c79 Binary files /dev/null and b/static/files/20260205054615-entry.xlsx differ diff --git a/static/files/20260205055513-entry.xlsx b/static/files/20260205055513-entry.xlsx new file mode 100644 index 00000000..92ac6c79 Binary files /dev/null and b/static/files/20260205055513-entry.xlsx differ