| 1 |
<?php |
| 2 |
|
| 3 |
namespace FluentCart\Database\Seeder; |
| 4 |
|
| 5 |
use FluentCart\App\Models\Order; |
| 6 |
use FluentCart\Faker\Factory; |
| 7 |
use FluentCart\Framework\Support\Arr; |
| 8 |
|
| 9 |
class TaxSeeder |
| 10 |
{ |
| 11 |
public static function seed($count = null, $assoc_args = []) |
| 12 |
{ |
| 13 |
$db = \FluentCart\App\App::getInstance('db'); |
| 14 |
$faker = Factory::create(); |
| 15 |
|
| 16 |
if (defined('WP_CLI') && WP_CLI) { |
| 17 |
$progress = \WP_CLI\Utils\make_progress_bar('%CSeeding Tax Data', 3); |
| 18 |
} |
| 19 |
|
| 20 |
// Step 1: Seed Tax Classes |
| 21 |
static::seedTaxClasses($db, $faker); |
| 22 |
if (defined('WP_CLI') && WP_CLI) { |
| 23 |
$progress->tick(); |
| 24 |
} |
| 25 |
|
| 26 |
// Step 2: Seed Tax Rates |
| 27 |
$taxClassIds = static::seedTaxRates($db, $faker); |
| 28 |
if (defined('WP_CLI') && WP_CLI) { |
| 29 |
$progress->tick(); |
| 30 |
} |
| 31 |
|
| 32 |
// Step 3: Seed Order Tax Rates for specific orders |
| 33 |
static::seedOrderTaxRates($db, $faker, $taxClassIds); |
| 34 |
if (defined('WP_CLI') && WP_CLI) { |
| 35 |
$progress->tick(); |
| 36 |
$progress->finish(); |
| 37 |
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 38 |
echo \WP_CLI::colorize('%n'); |
| 39 |
} |
| 40 |
|
| 41 |
if (!defined('WP_CLI') || !WP_CLI) { |
| 42 |
echo "Tax seeding completed successfully!<br>"; |
| 43 |
} |
| 44 |
} |
| 45 |
|
| 46 |
private static function seedTaxClasses($db, $faker) |
| 47 |
{ |
| 48 |
$taxClasses = [ |
| 49 |
[ |
| 50 |
'title' => 'Standard Rate', |
| 51 |
'slug' => 'standard', |
| 52 |
|
| 53 |
'meta' => json_encode(['is_default' => true]), |
| 54 |
'created_at' => gmdate('Y-m-d H:i:s'), |
| 55 |
'updated_at' => gmdate('Y-m-d H:i:s'), |
| 56 |
], |
| 57 |
[ |
| 58 |
'title' => 'Reduced', |
| 59 |
'slug' => 'reduced', |
| 60 |
|
| 61 |
'meta' => json_encode(['is_default' => false]), |
| 62 |
'created_at' => gmdate('Y-m-d H:i:s'), |
| 63 |
'updated_at' => gmdate('Y-m-d H:i:s'), |
| 64 |
], |
| 65 |
[ |
| 66 |
'title' => 'Zero', |
| 67 |
'slug' => 'zero', |
| 68 |
|
| 69 |
'meta' => json_encode(['is_default' => false]), |
| 70 |
'created_at' => gmdate('Y-m-d H:i:s'), |
| 71 |
'updated_at' => gmdate('Y-m-d H:i:s'), |
| 72 |
], |
| 73 |
[ |
| 74 |
'title' => 'Digital Goods', |
| 75 |
'slug' => 'digital', |
| 76 |
|
| 77 |
'meta' => json_encode(['is_default' => false]), |
| 78 |
'created_at' => gmdate('Y-m-d H:i:s'), |
| 79 |
'updated_at' => gmdate('Y-m-d H:i:s'), |
| 80 |
], |
| 81 |
]; |
| 82 |
|
| 83 |
$db->table('fct_tax_classes')->insert($taxClasses); |
| 84 |
|
| 85 |
if (!defined('WP_CLI') || !WP_CLI) { |
| 86 |
echo "Inserted " . count($taxClasses) . " tax classes<br>"; |
| 87 |
} |
| 88 |
} |
| 89 |
|
| 90 |
private static function seedTaxRates($db, $faker) |
| 91 |
{ |
| 92 |
// Get inserted tax class IDs |
| 93 |
$taxClassIds = $db->table('fct_tax_classes')->pluck('id'); |
| 94 |
|
| 95 |
$taxRates = []; |
| 96 |
$countries = ['US', 'CA', 'GB', 'DE', 'FR', 'AU', 'IN', 'JP']; |
| 97 |
$usTaxRates = [ |
| 98 |
['country' => 'US', 'state' => 'CA', 'rate' => '8.75', 'name' => 'California Sales Tax'], |
| 99 |
['country' => 'US', 'state' => 'NY', 'rate' => '8.00', 'name' => 'New York Sales Tax'], |
| 100 |
['country' => 'US', 'state' => 'TX', 'rate' => '6.25', 'name' => 'Texas Sales Tax'], |
| 101 |
['country' => 'US', 'state' => 'FL', 'rate' => '6.00', 'name' => 'Florida Sales Tax'], |
| 102 |
['country' => 'US', 'state' => 'WA', 'rate' => '6.50', 'name' => 'Washington Sales Tax'], |
| 103 |
]; |
| 104 |
|
| 105 |
// US State Tax Rates |
| 106 |
foreach ($usTaxRates as $usRate) { |
| 107 |
foreach ($taxClassIds as $classId) { |
| 108 |
$rate = $usRate['rate']; |
| 109 |
if ($classId == 2) { // Reduced rate |
| 110 |
$rate = number_format(floatval($rate) * 0.5, 2); |
| 111 |
} elseif ($classId == 3) { // Zero rate |
| 112 |
$rate = '0.00'; |
| 113 |
} elseif ($classId == 4) { // Digital goods - slightly higher |
| 114 |
$rate = number_format(floatval($rate) * 1.1, 2); |
| 115 |
} |
| 116 |
|
| 117 |
$taxRates[] = [ |
| 118 |
'class_id' => $classId, |
| 119 |
'country' => $usRate['country'], |
| 120 |
'state' => $usRate['state'], |
| 121 |
'postcode' => null, |
| 122 |
'city' => null, |
| 123 |
'rate' => $rate, |
| 124 |
'name' => $usRate['name'] . ' - Class ' . $classId, |
| 125 |
'group' => 'state', |
| 126 |
'priority' => 1, |
| 127 |
'is_compound' => 0, |
| 128 |
'for_shipping' => $faker->boolean(30) ? 1 : 0, |
| 129 |
'for_order' => 1, |
| 130 |
// 'tax_rate_class' => $classId, |
| 131 |
]; |
| 132 |
} |
| 133 |
} |
| 134 |
|
| 135 |
// International VAT Rates |
| 136 |
$vatRates = [ |
| 137 |
['country' => 'GB', 'rate' => '20.00', 'name' => 'UK VAT'], |
| 138 |
['country' => 'DE', 'rate' => '19.00', 'name' => 'German VAT'], |
| 139 |
['country' => 'FR', 'rate' => '20.00', 'name' => 'French VAT'], |
| 140 |
['country' => 'CA', 'rate' => '13.00', 'name' => 'Canadian HST'], |
| 141 |
['country' => 'AU', 'rate' => '10.00', 'name' => 'Australian GST'], |
| 142 |
]; |
| 143 |
|
| 144 |
foreach ($vatRates as $vatRate) { |
| 145 |
foreach ($taxClassIds as $classId) { |
| 146 |
$rate = $vatRate['rate']; |
| 147 |
if ($classId == 2) { // Reduced rate |
| 148 |
$rate = number_format(floatval($rate) * 0.5, 2); |
| 149 |
} elseif ($classId == 3) { // Zero rate |
| 150 |
$rate = '0.00'; |
| 151 |
} |
| 152 |
|
| 153 |
$taxRates[] = [ |
| 154 |
'class_id' => $classId, |
| 155 |
'country' => $vatRate['country'], |
| 156 |
'state' => null, |
| 157 |
'postcode' => null, |
| 158 |
'city' => null, |
| 159 |
'rate' => $rate, |
| 160 |
'name' => $vatRate['name'] . ' - Class ' . $classId, |
| 161 |
'group' => 'country', |
| 162 |
'priority' => 1, |
| 163 |
'is_compound' => 0, |
| 164 |
'for_shipping' => $faker->randomElement([null, 0, 5, 10, 20, 30]), |
| 165 |
'for_order' => 1, |
| 166 |
// 'tax_rate_class' => $classId, |
| 167 |
]; |
| 168 |
} |
| 169 |
} |
| 170 |
|
| 171 |
// City-specific rates (examples) |
| 172 |
$cityRates = [ |
| 173 |
['country' => 'US', 'state' => 'CA', 'city' => 'Los Angeles', 'rate' => '10.25'], |
| 174 |
['country' => 'US', 'state' => 'NY', 'city' => 'New York City', 'rate' => '8.875'], |
| 175 |
['country' => 'US', 'state' => 'IL', 'city' => 'Chicago', 'rate' => '10.75'], |
| 176 |
]; |
| 177 |
|
| 178 |
foreach ($cityRates as $cityRate) { |
| 179 |
$taxRates[] = [ |
| 180 |
'class_id' => $taxClassIds[0], // Standard rate only for cities |
| 181 |
'country' => $cityRate['country'], |
| 182 |
'state' => $cityRate['state'], |
| 183 |
'postcode' => null, |
| 184 |
'city' => $cityRate['city'], |
| 185 |
'rate' => $cityRate['rate'], |
| 186 |
'name' => $cityRate['city'] . ' Local Tax', |
| 187 |
'group' => 'city', |
| 188 |
'priority' => 2, |
| 189 |
'is_compound' => 1, |
| 190 |
'for_shipping' => null, |
| 191 |
'for_order' => 1, |
| 192 |
// 'tax_rate_class' => $taxClassIds[0], |
| 193 |
]; |
| 194 |
} |
| 195 |
|
| 196 |
$db->table('fct_tax_rates')->insert($taxRates); |
| 197 |
|
| 198 |
if (!defined('WP_CLI') || !WP_CLI) { |
| 199 |
echo "Inserted " . count($taxRates) . " tax rates<br>"; |
| 200 |
} |
| 201 |
|
| 202 |
return $taxClassIds; |
| 203 |
} |
| 204 |
|
| 205 |
private static function seedOrderTaxRates($db, $faker, $taxClassIds) |
| 206 |
{ |
| 207 |
// Get orders in the specified range |
| 208 |
$orderIds = range(7536149, 7536237); |
| 209 |
$existingOrders = $db->table('fct_orders') |
| 210 |
->whereIn('id', $orderIds) |
| 211 |
->pluck('id'); |
| 212 |
|
| 213 |
if (empty($existingOrders)) { |
| 214 |
if (!defined('WP_CLI') || !WP_CLI) { |
| 215 |
echo "No orders found in the specified range (7536149-7536237)<br>"; |
| 216 |
} |
| 217 |
return; |
| 218 |
} |
| 219 |
|
| 220 |
// Get available tax rates |
| 221 |
$taxRates = $db->table('fct_tax_rates')->get(); |
| 222 |
$orderTaxRates = []; |
| 223 |
|
| 224 |
foreach ($existingOrders as $orderId) { |
| 225 |
// Get order details to calculate realistic tax amounts |
| 226 |
$order = $db->table('fct_orders')->where('id', $orderId)->first(); |
| 227 |
if (!$order) { |
| 228 |
continue; |
| 229 |
} |
| 230 |
|
| 231 |
$subtotal = $order->subtotal ?? 10000; // Default to $100 in cents if null |
| 232 |
|
| 233 |
// Randomly assign 1-3 tax rates per order |
| 234 |
$numTaxRates = $faker->numberBetween(1, 3); |
| 235 |
$selectedTaxRates = $faker->randomElements($taxRates, $numTaxRates); |
| 236 |
|
| 237 |
foreach ($selectedTaxRates as $taxRate) { |
| 238 |
$taxRatePercentage = floatval($taxRate->rate) / 100; |
| 239 |
|
| 240 |
// Calculate tax amounts (in cents, following the pattern) |
| 241 |
$orderTax = intval($subtotal * $taxRatePercentage); |
| 242 |
$shippingTax = $taxRate->for_shipping ? intval($faker->numberBetween(50, 500)) : 0; |
| 243 |
$totalTax = $orderTax + $shippingTax; |
| 244 |
|
| 245 |
$orderTaxRates[] = [ |
| 246 |
'order_id' => $orderId, |
| 247 |
'tax_rate_id' => $taxRate->id, |
| 248 |
'shipping_tax' => $shippingTax, |
| 249 |
'order_tax' => $orderTax, |
| 250 |
'total_tax' => $totalTax, |
| 251 |
'meta' => json_encode([ |
| 252 |
'rate_percentage' => $taxRate->rate, |
| 253 |
'tax_name' => $taxRate->name, |
| 254 |
'calculated_on' => gmdate('Y-m-d H:i:s'), |
| 255 |
]), |
| 256 |
'filed_at' => $faker->boolean(70) ? $faker->dateTimeThisYear()->format('Y-m-d H:i:s') : null, |
| 257 |
'created_at' => gmdate('Y-m-d H:i:s'), |
| 258 |
'updated_at' => gmdate('Y-m-d H:i:s'), |
| 259 |
]; |
| 260 |
} |
| 261 |
} |
| 262 |
|
| 263 |
if (!empty($orderTaxRates)) { |
| 264 |
$db->table('fct_order_tax_rate')->insert($orderTaxRates); |
| 265 |
|
| 266 |
// Update order tax_total field to reflect the calculated taxes |
| 267 |
foreach ($existingOrders as $orderId) { |
| 268 |
$totalOrderTax = array_sum(array_column( |
| 269 |
array_filter($orderTaxRates, function ($otr) use ($orderId) { |
| 270 |
return $otr['order_id'] == $orderId; |
| 271 |
}), |
| 272 |
'total_tax' |
| 273 |
)); |
| 274 |
|
| 275 |
if ($totalOrderTax > 0) { |
| 276 |
$db->table('fct_orders') |
| 277 |
->where('id', $orderId) |
| 278 |
->update(['tax_total' => $totalOrderTax]); |
| 279 |
} |
| 280 |
} |
| 281 |
} |
| 282 |
|
| 283 |
if (!defined('WP_CLI') || !WP_CLI) { |
| 284 |
echo "Inserted " . count($orderTaxRates) . " order tax rate records for " . count($existingOrders) . " orders<br>"; |
| 285 |
} |
| 286 |
} |
| 287 |
} |
| 288 |
|