您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

RecurringInvoiceFactory.php 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <?php
  2. namespace Database\Factories\Accounting;
  3. use App\Enums\Accounting\DayOfMonth;
  4. use App\Enums\Accounting\DayOfWeek;
  5. use App\Enums\Accounting\EndType;
  6. use App\Enums\Accounting\Frequency;
  7. use App\Enums\Accounting\IntervalType;
  8. use App\Enums\Accounting\Month;
  9. use App\Enums\Accounting\RecurringInvoiceStatus;
  10. use App\Enums\Setting\PaymentTerms;
  11. use App\Models\Accounting\DocumentLineItem;
  12. use App\Models\Accounting\RecurringInvoice;
  13. use App\Models\Common\Client;
  14. use Illuminate\Database\Eloquent\Factories\Factory;
  15. use Illuminate\Support\Carbon;
  16. /**
  17. * @extends Factory<RecurringInvoice>
  18. */
  19. class RecurringInvoiceFactory extends Factory
  20. {
  21. /**
  22. * The name of the factory's corresponding model.
  23. */
  24. protected $model = RecurringInvoice::class;
  25. /**
  26. * Define the model's default state.
  27. *
  28. * @return array<string, mixed>
  29. */
  30. public function definition(): array
  31. {
  32. return [
  33. 'company_id' => 1,
  34. 'client_id' => Client::inRandomOrder()->value('id'),
  35. 'header' => 'Invoice',
  36. 'subheader' => 'Invoice',
  37. 'order_number' => $this->faker->unique()->numerify('ORD-####'),
  38. 'payment_terms' => PaymentTerms::Net30,
  39. 'status' => RecurringInvoiceStatus::Draft,
  40. 'currency_code' => 'USD',
  41. 'terms' => $this->faker->sentence,
  42. 'footer' => $this->faker->sentence,
  43. 'created_by' => 1,
  44. 'updated_by' => 1,
  45. ];
  46. }
  47. public function withLineItems(int $count = 3): static
  48. {
  49. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) use ($count) {
  50. DocumentLineItem::factory()
  51. ->count($count)
  52. ->forInvoice($recurringInvoice)
  53. ->create();
  54. $this->recalculateTotals($recurringInvoice);
  55. });
  56. }
  57. public function withSchedule(
  58. ?Frequency $frequency = null,
  59. ?Carbon $startDate = null,
  60. ?EndType $endType = null
  61. ): static {
  62. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) use ($frequency, $endType, $startDate) {
  63. $this->ensureLineItems($recurringInvoice);
  64. $frequency ??= $this->faker->randomElement(Frequency::class);
  65. $endType ??= EndType::Never;
  66. // Adjust the start date range based on frequency
  67. $startDate = match ($frequency) {
  68. Frequency::Daily => Carbon::parse($this->faker->dateTimeBetween('-30 days')), // At most 30 days back
  69. default => $startDate ?? Carbon::parse($this->faker->dateTimeBetween('-1 year')),
  70. };
  71. $state = match ($frequency) {
  72. Frequency::Daily => $this->withDailySchedule($startDate, $endType),
  73. Frequency::Weekly => $this->withWeeklySchedule($startDate, $endType),
  74. Frequency::Monthly => $this->withMonthlySchedule($startDate, $endType),
  75. Frequency::Yearly => $this->withYearlySchedule($startDate, $endType),
  76. Frequency::Custom => $this->withCustomSchedule($startDate, $endType),
  77. };
  78. $state->callAfterCreating(collect([$recurringInvoice]));
  79. });
  80. }
  81. public function withDailySchedule(Carbon $startDate, EndType $endType): static
  82. {
  83. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) use ($startDate, $endType) {
  84. $this->ensureLineItems($recurringInvoice);
  85. $recurringInvoice->updateQuietly([
  86. 'frequency' => Frequency::Daily,
  87. 'start_date' => $startDate,
  88. 'end_type' => $endType,
  89. ]);
  90. });
  91. }
  92. public function withWeeklySchedule(Carbon $startDate, EndType $endType): static
  93. {
  94. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) use ($startDate, $endType) {
  95. $this->ensureLineItems($recurringInvoice);
  96. $recurringInvoice->updateQuietly([
  97. 'frequency' => Frequency::Weekly,
  98. 'day_of_week' => DayOfWeek::from($startDate->dayOfWeek),
  99. 'start_date' => $startDate,
  100. 'end_type' => $endType,
  101. ]);
  102. });
  103. }
  104. public function withMonthlySchedule(Carbon $startDate, EndType $endType): static
  105. {
  106. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) use ($startDate, $endType) {
  107. $this->ensureLineItems($recurringInvoice);
  108. $recurringInvoice->updateQuietly([
  109. 'frequency' => Frequency::Monthly,
  110. 'day_of_month' => DayOfMonth::from($startDate->day),
  111. 'start_date' => $startDate,
  112. 'end_type' => $endType,
  113. ]);
  114. });
  115. }
  116. public function withYearlySchedule(Carbon $startDate, EndType $endType): static
  117. {
  118. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) use ($startDate, $endType) {
  119. $this->ensureLineItems($recurringInvoice);
  120. $recurringInvoice->updateQuietly([
  121. 'frequency' => Frequency::Yearly,
  122. 'month' => Month::from($startDate->month),
  123. 'day_of_month' => DayOfMonth::from($startDate->day),
  124. 'start_date' => $startDate,
  125. 'end_type' => $endType,
  126. ]);
  127. });
  128. }
  129. public function withCustomSchedule(
  130. Carbon $startDate,
  131. EndType $endType,
  132. ?IntervalType $intervalType = null,
  133. ?int $intervalValue = null
  134. ): static {
  135. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) use ($intervalType, $intervalValue, $startDate, $endType) {
  136. $this->ensureLineItems($recurringInvoice);
  137. $intervalType ??= $this->faker->randomElement(IntervalType::class);
  138. $intervalValue ??= match ($intervalType) {
  139. IntervalType::Day => $this->faker->numberBetween(1, 7),
  140. IntervalType::Week => $this->faker->numberBetween(1, 4),
  141. IntervalType::Month => $this->faker->numberBetween(1, 3),
  142. IntervalType::Year => 1,
  143. };
  144. $state = [
  145. 'frequency' => Frequency::Custom,
  146. 'interval_type' => $intervalType,
  147. 'interval_value' => $intervalValue,
  148. 'start_date' => $startDate,
  149. 'end_type' => $endType,
  150. ];
  151. // Add interval-specific attributes
  152. switch ($intervalType) {
  153. case IntervalType::Day:
  154. // No additional attributes needed
  155. break;
  156. case IntervalType::Week:
  157. $state['day_of_week'] = DayOfWeek::from($startDate->dayOfWeek);
  158. break;
  159. case IntervalType::Month:
  160. $state['day_of_month'] = DayOfMonth::from($startDate->day);
  161. break;
  162. case IntervalType::Year:
  163. $state['month'] = Month::from($startDate->month);
  164. $state['day_of_month'] = DayOfMonth::from($startDate->day);
  165. break;
  166. }
  167. return $recurringInvoice->updateQuietly($state);
  168. });
  169. }
  170. public function endAfter(int $occurrences = 12): static
  171. {
  172. return $this->state([
  173. 'end_type' => EndType::After,
  174. 'max_occurrences' => $occurrences,
  175. ]);
  176. }
  177. public function endOn(?Carbon $endDate = null): static
  178. {
  179. $endDate ??= now()->addMonths($this->faker->numberBetween(1, 12));
  180. return $this->state([
  181. 'end_type' => EndType::On,
  182. 'end_date' => $endDate,
  183. ]);
  184. }
  185. public function autoSend(string $sendTime = '09:00'): static
  186. {
  187. return $this->state([
  188. 'auto_send' => true,
  189. 'send_time' => $sendTime,
  190. ]);
  191. }
  192. public function approved(): static
  193. {
  194. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) {
  195. $this->ensureLineItems($recurringInvoice);
  196. if (! $recurringInvoice->hasSchedule()) {
  197. $this->withSchedule()->callAfterCreating(collect([$recurringInvoice]));
  198. $recurringInvoice->refresh();
  199. }
  200. $approvedAt = $recurringInvoice->start_date
  201. ? $recurringInvoice->start_date->copy()->subDays($this->faker->numberBetween(1, 7))
  202. : now()->subDays($this->faker->numberBetween(1, 30));
  203. $recurringInvoice->approveDraft($approvedAt);
  204. });
  205. }
  206. public function active(): static
  207. {
  208. return $this->withLineItems()
  209. ->withSchedule()
  210. ->approved();
  211. }
  212. public function ended(): static
  213. {
  214. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) {
  215. $this->ensureLineItems($recurringInvoice);
  216. if (! $recurringInvoice->canBeEnded()) {
  217. $this->active()->callAfterCreating(collect([$recurringInvoice]));
  218. }
  219. $endedAt = $recurringInvoice->last_date
  220. ? $recurringInvoice->last_date->copy()->addDays($this->faker->numberBetween(1, 7))
  221. : now()->subDays($this->faker->numberBetween(1, 30));
  222. $recurringInvoice->updateQuietly([
  223. 'ended_at' => $endedAt,
  224. 'status' => RecurringInvoiceStatus::Ended,
  225. ]);
  226. });
  227. }
  228. public function configure(): static
  229. {
  230. return $this->afterCreating(function (RecurringInvoice $recurringInvoice) {
  231. $this->ensureLineItems($recurringInvoice);
  232. });
  233. }
  234. protected function ensureLineItems(RecurringInvoice $recurringInvoice): void
  235. {
  236. if (! $recurringInvoice->hasLineItems()) {
  237. $this->withLineItems()->callAfterCreating(collect([$recurringInvoice]));
  238. }
  239. }
  240. protected function recalculateTotals(RecurringInvoice $recurringInvoice): void
  241. {
  242. $recurringInvoice->refresh();
  243. if (! $recurringInvoice->hasLineItems()) {
  244. return;
  245. }
  246. $subtotal = $recurringInvoice->lineItems()->sum('subtotal') / 100;
  247. $taxTotal = $recurringInvoice->lineItems()->sum('tax_total') / 100;
  248. $discountTotal = $recurringInvoice->lineItems()->sum('discount_total') / 100;
  249. $grandTotal = $subtotal + $taxTotal - $discountTotal;
  250. $recurringInvoice->update([
  251. 'subtotal' => $subtotal,
  252. 'tax_total' => $taxTotal,
  253. 'discount_total' => $discountTotal,
  254. 'total' => $grandTotal,
  255. ]);
  256. }
  257. }