diff --git a/app/Services/Admin/AdminService.php b/app/Services/Admin/AdminService.php index c510739..bd1ecb4 100644 --- a/app/Services/Admin/AdminService.php +++ b/app/Services/Admin/AdminService.php @@ -244,10 +244,22 @@ class AdminService implements AdminServiceInterface $user->card += $diff; $user->save(); - $addCard->num_card = $newNumCard; - $addCard->seller = $data['seller']; - $addCard->date = $data['date']; - $addCard->save(); + $targetRecord = AddCard::where('buyer', $addCard->buyer) + ->where('seller', $data['seller']) + ->whereDate('date', $data['date']) + ->where('id', '!=', $addCard->id) + ->first(); + + if ($targetRecord) { + $targetRecord->num_card += $newNumCard; + $targetRecord->save(); + $addCard->delete(); + } else { + $addCard->num_card = $newNumCard; + $addCard->seller = $data['seller']; + $addCard->date = $data['date']; + $addCard->save(); + } }); } diff --git a/docs/crob-job.md b/docs/crob-job.md index 4770c4f..2b621cb 100644 --- a/docs/crob-job.md +++ b/docs/crob-job.md @@ -1,28 +1,81 @@ -# Hướng dẫn thiết lập Cron Job cho Laravel trên Ubuntu Server +# Hướng dẫn thiết lập Cron Job cho Laravel trên Server (Ubuntu) -Để hệ thống tự động chạy các tác vụ định kỳ (bao gồm tác vụ tự động reset thẻ vào cuối tháng qua lệnh `cards:reset`), bạn cần cấu hình Cron Job trên hệ điều hành Ubuntu Server trỏ đến bộ lập lịch (Task Scheduler) của Laravel. +Để hệ thống tự động thực hiện các tác vụ định kỳ (bao gồm việc tự động reset thẻ vào lúc 23:59 ngày cuối tháng thông qua lệnh `cards:reset`), bạn cần cấu hình Cron Job trên hệ điều hành Ubuntu Server. -## Các bước thiết lập +Dưới đây là hướng dẫn chi tiết cho cả hai trường hợp: **Chạy qua Docker Compose** (mặc định của dự án) và **Chạy trực tiếp trên Host (Không dùng Docker)**. -1. **Mở cấu hình Crontab:** - Truy cập vào terminal của máy chủ Ubuntu dưới quyền của user chạy web (thường là `www-data` hoặc user deploy của bạn) và chạy lệnh: +--- + +## TRƯỜNG HỢP 1: Chạy qua Docker Compose (Khuyên dùng cho dự án này) + +Vì dự án chạy trong container Docker, Cron Job trên server Ubuntu (máy Host) cần gọi lệnh của Laravel vào bên trong container `app`. + +### Các bước thiết lập: + +1. **Mở file cấu hình Crontab của Ubuntu:** + Mở terminal của Ubuntu Server và chạy lệnh: ```bash crontab -e ``` -2. **Thêm cấu hình chạy Scheduler:** - Thêm dòng dưới đây vào cuối file cấu hình crontab để gọi Laravel scheduler kiểm tra tác vụ mỗi phút: +2. **Thêm cấu hình chạy Laravel Scheduler:** + Thêm dòng dưới đây vào cuối file crontab: ```bash - * * * * * cd /đường-dẫn-đến-thư-mục-dự-án && php artisan schedule:run >> /dev/null 2>&1 + * * * * * cd /đường-dẫn-đến-thư-mục-dự-án && docker compose -f docker/docker-compose.yml exec -T app php artisan schedule:run >> /dev/null 2>&1 ``` - *Lưu ý:* Thay thế `/đường-dẫn-đến-thư-mục-dự-án` bằng đường dẫn tuyệt đối đến thư mục chứa dự án trên server (Ví dụ: `/var/www/thankcard-system`). + *Lưu ý quan trọng:* + - Thay `/đường-dẫn-đến-thư-mục-dự-án` thành đường dẫn tuyệt đối đến dự án trên server (Ví dụ: `/var/www/thankcard-system`). + - Tham số **`-T`** (trong `exec -T`) là bắt buộc để tắt chế độ TTY (Tương tác nhập liệu), giúp cron chạy ngầm thành công mà không bị lỗi TTY error. 3. **Lưu và Thoát:** - Nếu bạn dùng trình soạn thảo `nano`: Nhấn `Ctrl + O` -> nhấn `Enter` để lưu, sau đó bấm `Ctrl + X` để thoát. - - Hệ thống sẽ hiển thị thông báo `crontab: installing new crontab`. -4. **Kiểm tra trạng thái:** - Chạy lệnh sau để hiển thị danh sách các cron job đang hoạt động trên user đó: +--- + +## TRƯỜNG HỢP 2: Chạy trực tiếp trên Ubuntu Host (Nếu deploy dạng truyền thống) + +Nếu bạn cấu hình chạy PHP và Web Server (Nginx/Apache) trực tiếp trên Ubuntu mà không qua Docker. + +### Các bước thiết lập: + +1. **Mở cấu hình Crontab:** + Truy cập terminal của Ubuntu Server dưới quyền của user chạy Web (thường là `www-data` hoặc user deploy) và chạy lệnh: + ```bash + crontab -e + ``` + +2. **Thêm cấu hình chạy Laravel Scheduler:** + Thêm dòng dưới đây vào cuối file crontab: + ```bash + * * * * * cd /đường-dẫn-đến-thư-mục-dự-án && php artisan schedule:run >> /dev/null 2>&1 + ``` + *Lưu ý:* Thay `/đường-dẫn-đến-thư-mục-dự-án` bằng đường dẫn tuyệt đối đến thư mục chứa dự án. + +3. **Lưu và Thoát.** + +--- + +## Hướng dẫn kiểm tra trạng thái Cron Job + +1. **Xem danh sách cron job đang hoạt động:** ```bash crontab -l ``` + +2. **Kiểm tra danh sách các tác vụ đã lên lịch trong Laravel:** + ```bash + # Nếu dùng Docker: + docker compose -f docker/docker-compose.yml exec -T app php artisan schedule:list + + # Nếu không dùng Docker: + php artisan schedule:list + ``` + +3. **Chạy thử scheduler ngay lập tức bằng tay để kiểm thử:** + ```bash + # Nếu dùng Docker: + docker compose -f docker/docker-compose.yml exec -T app php artisan schedule:run + + # Nếu không dùng Docker: + php artisan schedule:run + ``` diff --git a/resources/views/components/user-avatar-section.blade.php b/resources/views/components/user-avatar-section.blade.php index 9275ee6..fc7bcd4 100644 --- a/resources/views/components/user-avatar-section.blade.php +++ b/resources/views/components/user-avatar-section.blade.php @@ -4,13 +4,16 @@
Avatar
-
- - - @error('avatar'){{ $message }}@enderror -
+ @if($mode !== 'edit') +
+ + + @error('avatar'){{ $message }}@enderror +
+ @endif +@if($mode !== 'edit') @once @push('scripts') @endpush @endonce +@endif diff --git a/resources/views/components/user-form.blade.php b/resources/views/components/user-form.blade.php index 0f5da09..d6f1469 100644 --- a/resources/views/components/user-form.blade.php +++ b/resources/views/components/user-form.blade.php @@ -69,11 +69,6 @@ Bạn có chắc chắn muốn cho nhân viên {{ $user->msnv }} nghỉ việc không? Tài khoản này sẽ không thể đăng nhập hoặc thực hiện các giao dịch thẻ sau khi xác nhận.

@else - - @if($isSelf || $isEdit) - - @endif - diff --git a/tests/Feature/AdminUserListStatsTest.php b/tests/Feature/AdminUserListStatsTest.php index 6f80c2a..da3890d 100644 --- a/tests/Feature/AdminUserListStatsTest.php +++ b/tests/Feature/AdminUserListStatsTest.php @@ -743,7 +743,7 @@ class AdminUserListStatsTest extends TestCase 'buyer' => 9002, 'num_card' => 5, 'seller' => 9001, - 'date' => Carbon::now()->format('Y-m-d'), + 'date' => Carbon::now()->startOfMonth()->toDateString(), ]); // Transaction 2 (newest) @@ -751,14 +751,14 @@ class AdminUserListStatsTest extends TestCase 'buyer' => 9002, 'num_card' => 10, 'seller' => 9001, - 'date' => Carbon::now()->format('Y-m-d'), + 'date' => Carbon::now()->startOfMonth()->addDay()->toDateString(), ]); // Try to update older Transaction 1: change num_card from 5 to 6 (increase of 1) $response = $this->actingAs($admin)->put(route('admin.add_cards.update', $oldAddCard->id), [ 'num_card' => 6, 'seller' => 9001, - 'date' => Carbon::now()->format('Y-m-d'), + 'date' => Carbon::now()->startOfMonth()->toDateString(), ]); $response->assertRedirect(); @@ -771,6 +771,72 @@ class AdminUserListStatsTest extends TestCase $this->assertEquals(16, $member->card); } + public function test_update_add_card_merges_records_on_same_date(): void + { + $admin = User::create([ + 'msnv' => 9001, + 'name' => 'Admin User', + 'mail' => 'admin@example.com', + 'pass' => md5('password'), + 'departments' => 1, + 'role' => config('constants.ROLE_ADMIN'), + 'status' => config('constants.STATUS_ACTIVE'), + 'card' => 0, + 'flag_send' => config('constants.FLAG_SEND_DISABLED'), + 'first_login' => config('constants.FIRST_LOGIN_FALSE'), + ]); + + $member = User::create([ + 'msnv' => 9002, + 'name' => 'Member User', + 'mail' => 'member@example.com', + 'pass' => md5('password'), + 'departments' => 1, + 'role' => config('constants.ROLE_MEMBER'), + 'status' => config('constants.STATUS_ACTIVE'), + 'card' => 15, + 'flag_send' => config('constants.FLAG_SEND_ENABLED'), + 'first_login' => config('constants.FIRST_LOGIN_FALSE'), + ]); + + // Record A on day 1 + $recordA = \App\Models\AddCard::create([ + 'buyer' => 9002, + 'num_card' => 5, + 'seller' => 9001, + 'date' => Carbon::now()->startOfMonth()->toDateString(), + ]); + + // Record B on day 10 + $recordB = \App\Models\AddCard::create([ + 'buyer' => 9002, + 'num_card' => 10, + 'seller' => 9001, + 'date' => Carbon::now()->startOfMonth()->addDays(9)->toDateString(), + ]); + + // Update Record B: set date to day 1, and set num_card to 10 + $response = $this->actingAs($admin)->put(route('admin.add_cards.update', $recordB->id), [ + 'num_card' => 10, + 'seller' => 9001, + 'date' => Carbon::now()->startOfMonth()->toDateString(), + ]); + + $response->assertRedirect(); + $response->assertSessionHasNoErrors(); + + // Record B should be deleted (merged) + $this->assertNull(\App\Models\AddCard::find($recordB->id)); + + // Record A should have 15 cards (5 + 10) + $recordA->refresh(); + $this->assertEquals(15, $recordA->num_card); + + // Member's card balance remains 15 + $member->refresh(); + $this->assertEquals(15, $member->card); + } + public function test_delete_add_card_successfully_updates_balance(): void { $admin = User::create([