Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

RecurringInvoiceTest.php 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php
  2. use App\Enums\Accounting\EndType;
  3. use App\Enums\Accounting\Frequency;
  4. use App\Enums\Accounting\IntervalType;
  5. use App\Models\Accounting\RecurringInvoice;
  6. use Illuminate\Support\Carbon;
  7. beforeEach(function () {
  8. $this->withOfferings();
  9. });
  10. it('creates a basic recurring invoice with line items and calculates totals correctly', function () {
  11. $recurringInvoice = RecurringInvoice::factory()
  12. ->withLineItems()
  13. ->create();
  14. $recurringInvoice->refresh();
  15. expect($recurringInvoice)
  16. ->hasLineItems()->toBeTrue()
  17. ->lineItems->count()->toBe(3)
  18. ->subtotal->toBeGreaterThan(0)
  19. ->total->toBeGreaterThan(0);
  20. });
  21. test('recurring invoice properly handles months with fewer days for monthly frequency', function () {
  22. // Start from January 31st
  23. Carbon::setTestNow('2024-01-31');
  24. RecurringInvoice::unsetEventDispatcher();
  25. // Create a recurring invoice set for the 31st of each month
  26. $recurringInvoice = RecurringInvoice::factory()
  27. ->withLineItems()
  28. ->withSchedule(
  29. frequency: Frequency::Monthly,
  30. startDate: Carbon::now(),
  31. )
  32. ->approved()
  33. ->create();
  34. // First invoice should be the start date
  35. expect($recurringInvoice->calculateNextDate())
  36. ->toBeInstanceOf(Carbon::class)
  37. ->toDateString()->toBe('2024-01-31');
  38. // Now set last_date to simulate first invoice being generated
  39. $recurringInvoice->update(['last_date' => '2024-01-31']);
  40. $recurringInvoice->refresh();
  41. expect($recurringInvoice->calculateNextDate())
  42. ->toBeInstanceOf(Carbon::class)
  43. ->toDateString()->toBe('2024-02-29');
  44. // Update last_date to Feb 29 and check next date (should be March 31)
  45. $recurringInvoice->update(['last_date' => '2024-02-29']);
  46. $recurringInvoice->refresh();
  47. expect($recurringInvoice->calculateNextDate())
  48. ->toBeInstanceOf(Carbon::class)
  49. ->toDateString()->toBe('2024-03-31');
  50. // Update last_date to March 31 and check next date (should be April 30)
  51. $recurringInvoice->update(['last_date' => '2024-03-31']);
  52. $recurringInvoice->refresh();
  53. expect($recurringInvoice->calculateNextDate())
  54. ->toBeInstanceOf(Carbon::class)
  55. ->toDateString()->toBe('2024-04-30');
  56. // Update last_date to April 30 and check next date (should be May 31)
  57. $recurringInvoice->update(['last_date' => '2024-04-30']);
  58. $recurringInvoice->refresh();
  59. expect($recurringInvoice->calculateNextDate())
  60. ->toBeInstanceOf(Carbon::class)
  61. ->toDateString()->toBe('2024-05-31');
  62. });
  63. test('recurring invoice properly handles months with fewer days for yearly frequency', function () {
  64. // Start from January 31st
  65. Carbon::setTestNow('2024-02-29');
  66. RecurringInvoice::unsetEventDispatcher();
  67. // Create a recurring invoice set for the 31st of each month
  68. $recurringInvoice = RecurringInvoice::factory()
  69. ->withLineItems()
  70. ->withSchedule(
  71. frequency: Frequency::Yearly,
  72. startDate: Carbon::now(),
  73. )
  74. ->approved()
  75. ->create();
  76. // First invoice should be the start date
  77. expect($recurringInvoice->calculateNextDate())
  78. ->toBeInstanceOf(Carbon::class)
  79. ->toDateString()->toBe('2024-02-29');
  80. // Next date should be Feb 28, 2025 (non-leap year)
  81. $recurringInvoice->update(['last_date' => '2024-02-29']);
  82. $recurringInvoice->refresh();
  83. expect($recurringInvoice->calculateNextDate())
  84. ->toBeInstanceOf(Carbon::class)
  85. ->toDateString()->toBe('2025-02-28');
  86. // Next date should be Feb 29, 2026 (leap year)
  87. $recurringInvoice->update(['last_date' => '2025-02-28']);
  88. $recurringInvoice->refresh();
  89. expect($recurringInvoice->calculateNextDate())
  90. ->toBeInstanceOf(Carbon::class)
  91. ->toDateString()->toBe('2026-02-28');
  92. });
  93. test('recurring invoice properly handles weekly frequency and custom weekly intervals', function () {
  94. Carbon::setTestNow('2024-01-31'); // Wednesday
  95. RecurringInvoice::unsetEventDispatcher();
  96. // Test regular weekly frequency
  97. $recurringInvoice = RecurringInvoice::factory()
  98. ->withLineItems()
  99. ->withSchedule(
  100. frequency: Frequency::Weekly,
  101. startDate: Carbon::now(),
  102. )
  103. ->approved()
  104. ->create();
  105. // First invoice should be the start date
  106. expect($recurringInvoice->calculateNextDate())
  107. ->toBeInstanceOf(Carbon::class)
  108. ->toDateString()->toBe('2024-01-31');
  109. // Next date should be that Friday
  110. $recurringInvoice->update(['last_date' => '2024-01-31']);
  111. $recurringInvoice->refresh();
  112. expect($recurringInvoice->calculateNextDate())
  113. ->toBeInstanceOf(Carbon::class)
  114. ->toDateString()->toBe('2024-02-07');
  115. // Test custom weekly frequency (every 2 weeks)
  116. $recurringInvoice = RecurringInvoice::factory()
  117. ->withLineItems()
  118. ->withCustomSchedule(
  119. startDate: Carbon::now(), // Wednesday
  120. endType: EndType::Never,
  121. intervalType: IntervalType::Week,
  122. intervalValue: 2,
  123. )
  124. ->approved()
  125. ->create();
  126. // First invoice should be the start date
  127. expect($recurringInvoice->calculateNextDate())
  128. ->toBeInstanceOf(Carbon::class)
  129. ->toDateString()->toBe('2024-01-31');
  130. // Next date should be two weeks from start, on Friday
  131. $recurringInvoice->update(['last_date' => '2024-01-31']);
  132. $recurringInvoice->refresh();
  133. expect($recurringInvoice->calculateNextDate())
  134. ->toBeInstanceOf(Carbon::class)
  135. ->toDateString()->toBe('2024-02-14');
  136. });
  137. test('recurring invoice generates correct sequence of invoices across different month lengths', function () {
  138. Carbon::setTestNow('2024-01-31');
  139. $recurringInvoice = RecurringInvoice::factory()
  140. ->withLineItems()
  141. ->withSchedule(
  142. frequency: Frequency::Monthly,
  143. startDate: Carbon::now(),
  144. )
  145. ->approved()
  146. ->create();
  147. // Generate first invoice
  148. $recurringInvoice->generateDueInvoices();
  149. $invoices = $recurringInvoice->invoices()
  150. ->orderBy('date')
  151. ->get();
  152. expect($invoices)->toHaveCount(1)
  153. ->and($invoices->pluck('date')->map->toDateString()->toArray())->toBe([
  154. '2024-01-31',
  155. ]);
  156. // Move time forward to February (leap year)
  157. Carbon::setTestNow('2024-02-29');
  158. $recurringInvoice->generateDueInvoices();
  159. $invoices = $recurringInvoice->invoices()
  160. ->orderBy('date')
  161. ->get();
  162. expect($invoices)->toHaveCount(2)
  163. ->and($invoices->pluck('date')->map->toDateString()->toArray())->toBe([
  164. '2024-01-31',
  165. '2024-02-29',
  166. ]);
  167. // Move time forward to March
  168. Carbon::setTestNow('2024-03-31');
  169. $recurringInvoice->generateDueInvoices();
  170. $invoices = $recurringInvoice->invoices()
  171. ->orderBy('date')
  172. ->get();
  173. expect($invoices)->toHaveCount(3)
  174. ->and($invoices->pluck('date')->map->toDateString()->toArray())->toBe([
  175. '2024-01-31',
  176. '2024-02-29',
  177. '2024-03-31',
  178. ]);
  179. });