diff --git a/database/factories/OrderFactory.php b/database/factories/OrderFactory.php new file mode 100644 index 0000000..18ade40 --- /dev/null +++ b/database/factories/OrderFactory.php @@ -0,0 +1,315 @@ +faker->numberBetween(50000, 1000000); + $discount = $this->faker->numberBetween(0, (int) ($subtotal * 0.3)); // Max 30% discount + $total = $subtotal - $discount; + $cogs = (int) ($subtotal * 0.4); // COGS is typically 40% of subtotal + + // Generate invoice number: INV + YYYYMMDD + 4 digit sequence + // For testing, use random sequence to ensure uniqueness + $sequence = $this->faker->numberBetween(1, 9999); + $invoiceNumber = 'INV'.now()->format('Ymd').str_pad($sequence, 4, '0', STR_PAD_LEFT); + + return [ + 'outlet_id' => Outlet::factory(), + 'user_id' => User::factory(), + 'customer_id' => null, + 'voucher_id' => null, + 'invoice_number' => $invoiceNumber, + 'cogs' => $cogs, + 'subtotal' => $subtotal, + 'discount' => $discount, + 'total' => $total, + 'channel' => fake()->randomElement(OrderChannel::cases()), + 'status' => fake()->randomElement(OrderStatus::cases()), + 'ordered_at' => $this->faker->dateTimeBetween('-1 year', 'now'), + ]; + } + + /* + |-------------------------------------------------------------------------- + | Status States + |-------------------------------------------------------------------------- + */ + + public function draft(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'status' => OrderStatus::DRAFT, + ]; + }); + } + + public function pending(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'status' => OrderStatus::PENDING, + ]; + }); + } + + public function partial(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'status' => OrderStatus::PARTIAL, + ]; + }); + } + + public function paid(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'status' => OrderStatus::PAID, + ]; + }); + } + + public function overdue(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'status' => OrderStatus::OVERDUE, + ]; + }); + } + + public function canceled(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'status' => OrderStatus::CANCELED, + ]; + }); + } + + /* + |-------------------------------------------------------------------------- + | Channel States + |-------------------------------------------------------------------------- + */ + + public function outlet(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'channel' => OrderChannel::OUTLET, + ]; + }); + } + + public function shopee(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'channel' => OrderChannel::SHOPEE, + ]; + }); + } + + public function tokopedia(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'channel' => OrderChannel::TOKOPEDIA, + ]; + }); + } + + public function website(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'channel' => OrderChannel::WEBSITE, + ]; + }); + } + + public function instagram(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'channel' => OrderChannel::INSTAGRAM, + ]; + }); + } + + public function whatsapp(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'channel' => OrderChannel::WHATSAPP, + ]; + }); + } + + public function other(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'channel' => OrderChannel::OTHER, + ]; + }); + } + + /* + |-------------------------------------------------------------------------- + | Relationship States + |-------------------------------------------------------------------------- + */ + + public function withCustomer(?Customer $customer = null): Factory + { + return $this->state(function (array $attributes) use ($customer) { + return [ + 'customer_id' => $customer?->id ?? Customer::factory(), + ]; + }); + } + + public function withVoucher(?Voucher $voucher = null): Factory + { + return $this->state(function (array $attributes) use ($voucher) { + return [ + 'voucher_id' => $voucher?->id ?? Voucher::factory(), + ]; + }); + } + + public function withOutlet(?Outlet $outlet = null): Factory + { + return $this->state(function (array $attributes) use ($outlet) { + return [ + 'outlet_id' => $outlet?->id ?? Outlet::factory(), + ]; + }); + } + + public function withUser(?User $user = null): Factory + { + return $this->state(function (array $attributes) use ($user) { + return [ + 'user_id' => $user?->id ?? User::factory(), + ]; + }); + } + + /* + |-------------------------------------------------------------------------- + | Financial States + |-------------------------------------------------------------------------- + */ + + public function withAmounts(int $subtotal, ?int $discount = null, ?int $cogs = null): Factory + { + return $this->state(function (array $attributes) use ($subtotal, $discount, $cogs) { + $discount = $discount ?? $this->faker->numberBetween(0, (int) ($subtotal * 0.3)); + $total = $subtotal - $discount; + $cogs = $cogs ?? (int) ($subtotal * 0.4); + + return [ + 'subtotal' => $subtotal, + 'discount' => $discount, + 'total' => $total, + 'cogs' => $cogs, + ]; + }); + } + + public function withDiscount(int $discount): Factory + { + return $this->state(function (array $attributes) use ($discount) { + $subtotal = $attributes['subtotal'] ?? $this->faker->numberBetween(50000, 1000000); + $total = $subtotal - $discount; + + return [ + 'discount' => $discount, + 'total' => max(0, $total), // Ensure total is not negative + ]; + }); + } + + public function withoutDiscount(): Factory + { + return $this->state(function (array $attributes) { + $subtotal = $attributes['subtotal'] ?? $this->faker->numberBetween(50000, 1000000); + + return [ + 'discount' => 0, + 'total' => $subtotal, + ]; + }); + } + + /* + |-------------------------------------------------------------------------- + | Date States + |-------------------------------------------------------------------------- + */ + + public function today(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'ordered_at' => now(), + 'created_at' => now(), + ]; + }); + } + + public function yesterday(): Factory + { + return $this->state(function (array $attributes) { + $yesterday = now()->subDay(); + + return [ + 'ordered_at' => $yesterday, + 'created_at' => $yesterday, + ]; + }); + } + + public function thisWeek(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'ordered_at' => $this->faker->dateTimeBetween('-7 days', 'now'), + ]; + }); + } + + public function thisMonth(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'ordered_at' => $this->faker->dateTimeBetween('-30 days', 'now'), + ]; + }); + } + + public function lastMonth(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'ordered_at' => $this->faker->dateTimeBetween('-60 days', '-30 days'), + ]; + }); + } +}