diff --git a/beike/Services/TaxService.php b/beike/Services/TaxService.php index e7897bbf..f5b503a4 100644 --- a/beike/Services/TaxService.php +++ b/beike/Services/TaxService.php @@ -11,130 +11,197 @@ namespace Beike\Services; +use Beike\Models\Address; +use Beike\Models\TaxRate; + class TaxService { - private $tax_rates = array(); + private $taxRates = array(); + private static $taxRules; - public function __construct($registry) { - $this->config = $registry->get('config'); - $this->db = $registry->get('db'); + const AVAILABLE_TYPES = ['shipping', 'payment', 'store']; + + public function __construct($data = array()) + { + $countryId = system_setting('base.config_country_id'); + $zoneId = system_setting('base.config_zone_id'); + + $shippingAddress = $data['shipping_address'] ?? null; + $paymentAddress = $data['payment_address'] ?? null; + + if ($shippingAddress) { + if ($shippingAddress instanceof Address) { + $this->setShippingAddress($shippingAddress->country_id, $shippingAddress->zone_id); + } elseif ($shippingAddress instanceof \ArrayObject) { + $this->setShippingAddress($countryId, $zoneId); + } else { + $this->setShippingAddress($shippingAddress['country_id'], $shippingAddress['zone_id']); + } + } elseif (system_setting('base.config_tax_default') == 'shipping') { + $this->setShippingAddress($countryId, $zoneId); + } + + if ($paymentAddress) { + if ($paymentAddress instanceof Address) { + $this->setPaymentAddress($paymentAddress->country_id, $paymentAddress->zone_id); + } elseif ($paymentAddress instanceof \ArrayObject) { + $this->setShippingAddress($countryId, $zoneId); + } else { + $this->setPaymentAddress($paymentAddress['country_id'], $paymentAddress['zone_id']); + } + } elseif (system_setting('base.config_tax_default') == 'payment') { + $this->setPaymentAddress($countryId, $zoneId); + } + + $this->setStoreAddress($countryId, $zoneId); } - public function unsetRates() { - $this->tax_rates = array(); + public static function getInstance($data = array()) + { + return new self($data); } - public function setShippingAddress($country_id, $zone_id) { - $tax_query = $this->db->query("SELECT tr1.tax_class_id, tr2.tax_rate_id, tr2.name, tr2.rate, tr2.type, tr1.priority FROM " . DB_PREFIX . "tax_rule tr1 LEFT JOIN " . DB_PREFIX . "tax_rate tr2 ON (tr1.tax_rate_id = tr2.tax_rate_id) INNER JOIN " . DB_PREFIX . "tax_rate_to_customer_group tr2cg ON (tr2.tax_rate_id = tr2cg.tax_rate_id) LEFT JOIN " . DB_PREFIX . "zone_to_geo_zone z2gz ON (tr2.geo_zone_id = z2gz.geo_zone_id) LEFT JOIN " . DB_PREFIX . "geo_zone gz ON (tr2.geo_zone_id = gz.geo_zone_id) WHERE tr1.based = 'shipping' AND tr2cg.customer_group_id = '" . (int)$this->config->get('config_customer_group_id') . "' AND z2gz.country_id = '" . (int)$country_id . "' AND (z2gz.zone_id = '0' OR z2gz.zone_id = '" . (int)$zone_id . "') ORDER BY tr1.priority ASC"); + private function getTaxRules($type, $countryId, $zoneId) + { + if (self::$taxRules !== null && isset(self::$taxRules["$type-$countryId-$zoneId"])) { + return self::$taxRules["$type-$countryId-$zoneId"]; + } - foreach ($tax_query->rows as $result) { - $this->tax_rates[$result['tax_class_id']][$result['tax_rate_id']] = array( - 'tax_rate_id' => $result['tax_rate_id'], - 'name' => $result['name'], - 'rate' => $result['rate'], - 'type' => $result['type'], - 'priority' => $result['priority'] + $customerGroupId = (int)system_setting('base.config_customer_group_id'); + $sqlBuilder = \DB::table('tax_rule') + ->leftJoin('tax_rate', 'tax_rule.tax_rate_id', '=', 'tax_rate.tax_rate_id') + ->join('tax_rate_to_customer_group', 'tax_rate.tax_rate_id', '=', 'tax_rate_to_customer_group.tax_rate_id') + ->leftJoin('zone_to_geo_zone', 'tax_rate.geo_zone_id', '=', 'zone_to_geo_zone.geo_zone_id') + ->leftJoin('geo_zone', 'tax_rate.geo_zone_id', '=', 'geo_zone.geo_zone_id') + ->select('tax_rule.*', 'tax_rate.*') + ->where('tax_rule.based', $type) + ->where('tax_rate_to_customer_group.customer_group_id', $customerGroupId) + ->where('zone_to_geo_zone.country_id', $countryId) + ->where(function ($query) use ($zoneId) { + $query->where('zone_to_geo_zone.zone_id', '=', 0) + ->orWhere('zone_to_geo_zone.zone_id', '=', (int)$zoneId); + }) + ->orderBy('tax_rule.priority', 'asc'); + $data = $sqlBuilder->get(); + self::$taxRules["$type-$countryId-$zoneId"] = $data; + return $data; + } + + private function setAddress($type, $countryId, $zoneId) + { + if (!in_array($type, self::AVAILABLE_TYPES)) { + throw new \Exception('invalid tax types'); + } + + $data = $this->getTaxRules($type, $countryId, $zoneId); + + foreach ($data as $result) { + $this->taxRates[$result->tax_class_id][$result->tax_rate_id] = array( + 'tax_rate_id' => $result->tax_rate_id, + 'name' => $result->name, + 'rate' => $result->rate, + 'type' => $result->type, + 'priority' => $result->priority ); } } - public function setPaymentAddress($country_id, $zone_id) { - $tax_query = $this->db->query("SELECT tr1.tax_class_id, tr2.tax_rate_id, tr2.name, tr2.rate, tr2.type, tr1.priority FROM " . DB_PREFIX . "tax_rule tr1 LEFT JOIN " . DB_PREFIX . "tax_rate tr2 ON (tr1.tax_rate_id = tr2.tax_rate_id) INNER JOIN " . DB_PREFIX . "tax_rate_to_customer_group tr2cg ON (tr2.tax_rate_id = tr2cg.tax_rate_id) LEFT JOIN " . DB_PREFIX . "zone_to_geo_zone z2gz ON (tr2.geo_zone_id = z2gz.geo_zone_id) LEFT JOIN " . DB_PREFIX . "geo_zone gz ON (tr2.geo_zone_id = gz.geo_zone_id) WHERE tr1.based = 'payment' AND tr2cg.customer_group_id = '" . (int)$this->config->get('config_customer_group_id') . "' AND z2gz.country_id = '" . (int)$country_id . "' AND (z2gz.zone_id = '0' OR z2gz.zone_id = '" . (int)$zone_id . "') ORDER BY tr1.priority ASC"); - - foreach ($tax_query->rows as $result) { - $this->tax_rates[$result['tax_class_id']][$result['tax_rate_id']] = array( - 'tax_rate_id' => $result['tax_rate_id'], - 'name' => $result['name'], - 'rate' => $result['rate'], - 'type' => $result['type'], - 'priority' => $result['priority'] - ); - } + public function unsetRates() + { + $this->taxRates = array(); } - public function setStoreAddress($country_id, $zone_id) { - $tax_query = $this->db->query("SELECT tr1.tax_class_id, tr2.tax_rate_id, tr2.name, tr2.rate, tr2.type, tr1.priority FROM " . DB_PREFIX . "tax_rule tr1 LEFT JOIN " . DB_PREFIX . "tax_rate tr2 ON (tr1.tax_rate_id = tr2.tax_rate_id) INNER JOIN " . DB_PREFIX . "tax_rate_to_customer_group tr2cg ON (tr2.tax_rate_id = tr2cg.tax_rate_id) LEFT JOIN " . DB_PREFIX . "zone_to_geo_zone z2gz ON (tr2.geo_zone_id = z2gz.geo_zone_id) LEFT JOIN " . DB_PREFIX . "geo_zone gz ON (tr2.geo_zone_id = gz.geo_zone_id) WHERE tr1.based = 'store' AND tr2cg.customer_group_id = '" . (int)$this->config->get('config_customer_group_id') . "' AND z2gz.country_id = '" . (int)$country_id . "' AND (z2gz.zone_id = '0' OR z2gz.zone_id = '" . (int)$zone_id . "') ORDER BY tr1.priority ASC"); - - foreach ($tax_query->rows as $result) { - $this->tax_rates[$result['tax_class_id']][$result['tax_rate_id']] = array( - 'tax_rate_id' => $result['tax_rate_id'], - 'name' => $result['name'], - 'rate' => $result['rate'], - 'type' => $result['type'], - 'priority' => $result['priority'] - ); - } + public function setShippingAddress($countryId, $zoneId) + { + $this->setAddress('shipping', $countryId, $zoneId); } - public function calculate($value, $tax_class_id, $calculate = true) { - if ($tax_class_id && $calculate) { + public function setPaymentAddress($countryId, $zoneId) + { + $this->setAddress('payment', $countryId, $zoneId); + } + + public function setStoreAddress($countryId, $zoneId) + { + $this->setAddress('store', $countryId, $zoneId); + } + + /** + * $tax = \App\Models\Library\Tax::getInstance(); + * $tax->setShippingAddress(1, 0); + * $tax->calculate(123.45, 9, $tax->config->getValue('config_tax')) + * + * @param $value + * @param $taxClassId + * @param bool|true $calculate + * @return mixed + */ + public function calculate($value, $taxClassId, $calculate = true) + { + if ($taxClassId && $calculate) { $amount = 0; - - $tax_rates = $this->getRates($value, $tax_class_id); - - foreach ($tax_rates as $tax_rate) { + $taxRates = $this->getRates($value, $taxClassId); + foreach ($taxRates as $taxRate) { if ($calculate != 'P' && $calculate != 'F') { - $amount += $tax_rate['amount']; - } elseif ($tax_rate['type'] == $calculate) { - $amount += $tax_rate['amount']; + $amount += $taxRate['amount']; + } elseif ($taxRate['type'] == $calculate) { + $amount += $taxRate['amount']; } } - return $value + $amount; } else { return $value; } } - public function getTax($value, $tax_class_id) { + public function getTax($value, $taxClassId) + { $amount = 0; - - $tax_rates = $this->getRates($value, $tax_class_id); - - foreach ($tax_rates as $tax_rate) { - $amount += $tax_rate['amount']; + $taxRates = $this->getRates($value, $taxClassId); + foreach ($taxRates as $taxRate) { + $amount += $taxRate['amount']; } - return $amount; } - public function getRateName($tax_rate_id) { - $tax_query = $this->db->query("SELECT name FROM " . DB_PREFIX . "tax_rate WHERE tax_rate_id = '" . (int)$tax_rate_id . "'"); - - if ($tax_query->num_rows) { - return $tax_query->row['name']; + public function getRateName($taxRateId) + { + $taxRate = TaxRate::query()->find($taxRateId); + if ($taxRate) { + return $taxRate->name; } else { return false; } } - public function getRates($value, $tax_class_id) { - $tax_rate_data = array(); + public function getRates($value, $taxClassId) + { + $taxRateData = array(); - if (isset($this->tax_rates[$tax_class_id])) { - foreach ($this->tax_rates[$tax_class_id] as $tax_rate) { - if (isset($tax_rate_data[$tax_rate['tax_rate_id']])) { - $amount = $tax_rate_data[$tax_rate['tax_rate_id']]['amount']; + if (isset($this->taxRates[$taxClassId])) { + foreach ($this->taxRates[$taxClassId] as $taxRate) { + if (isset($taxRateData[$taxRate['tax_rate_id']])) { + $amount = $taxRateData[$taxRate['tax_rate_id']]['amount']; } else { $amount = 0; } - if ($tax_rate['type'] == 'F') { - $amount += $tax_rate['rate']; - } elseif ($tax_rate['type'] == 'P') { - $amount += ($value / 100 * $tax_rate['rate']); + if ($taxRate['type'] == 'F') { + $amount += $taxRate['rate']; + } elseif ($taxRate['type'] == 'P') { + $amount += ($value / 100 * $taxRate['rate']); } - $tax_rate_data[$tax_rate['tax_rate_id']] = array( - 'tax_rate_id' => $tax_rate['tax_rate_id'], - 'name' => $tax_rate['name'], - 'rate' => $tax_rate['rate'], - 'type' => $tax_rate['type'], - 'amount' => $amount + $taxRateData[$taxRate['tax_rate_id']] = array( + 'tax_rate_id' => $taxRate['tax_rate_id'], + 'name' => $taxRate['name'], + 'rate' => $taxRate['rate'], + 'type' => $taxRate['type'], + 'amount' => $amount ); } } - return $tax_rate_data; + return $taxRateData; } }