AeliaWCEU_VAT_AssistantReportsBase_Report

A base report class with properties and methods common to all EU VAT Assistant reports.

Defined (1)

The class is defined in the following location(s).

/src/lib/classes/reporting/reports/base/base_report.php  
  1. class Base_Report extends \WC_Admin_Report { 
  2. // @var string The text domain to use for localisation. 
  3. protected $text_domain; 
  4. // @var bool Indicates if debug mode is active. 
  5. protected $debug; 
  6. // @var string A list of the tax classes that are not part of MOSS 
  7. protected $non_moss_tax_classes; 
  8. /** 
  9. * Used for caching. A list of the exchange rates used to convert from various 
  10. * currencies to the VAT currency, in several target dates. The list keys will 
  11. * be in <currency>-<YYYY-MM-DD> format, and the value will be the exchange rate. 
  12. * Example: "USD-2014-12-31" => 0.12345 
  13. * @var array 
  14. */ 
  15. protected $vat_currency_exchange_rates = array(); 
  16.  
  17.  
  18. // @var \Aelia\WC\Logger The logger used by the class. 
  19. protected $logger; 
  20.  
  21. /** 
  22. * Logs a message. 
  23. * @param string message The message to log. 
  24. * @param bool debug Indicates if the message is for debugging. Debug messages 
  25. * are not saved if the "debug mode" flag is turned off. 
  26. */ 
  27. protected function log($message, $debug = true) { 
  28. $this->logger->log($message, $debug); 
  29.  
  30. /** 
  31. * Returns the instance of the EU VAT Assistant plugin. 
  32. * @return \Aelia\WC\EU_VAT_Assistant\WC_Aelia_EU_VAT_Assistant 
  33. * @since 1.3.18.150324 
  34. */ 
  35. protected function EUVA() { 
  36. return WC_Aelia_EU_VAT_Assistant::instance(); 
  37.  
  38. /** 
  39. * Determines if a country is part of the EU. 
  40. * @param string country The country code to check. 
  41. * @return bool 
  42. * @since 1.3.18.150324 
  43. */ 
  44. protected function is_eu_country($country) { 
  45. return $this->EUVA()->is_eu_country($country); 
  46.  
  47. /** 
  48. * Indicates if a combination of country and tax class makes the tax rate fall 
  49. * under VAT MOSS. 
  50. * @param string country A country code. 
  51. * @param string tax_rate_class A tax rate class. 
  52. * @return bool 
  53. * @since 1.3.18.150324 
  54. */ 
  55. protected function is_tax_moss($country, $tax_rate_class) { 
  56. // Tag the tax record to indicate if it's part of MOSS or not 
  57. return ($country !== $this->base_country()) && 
  58. (!in_array($tax_rate_class, $this->non_moss_tax_classes)); 
  59.  
  60. /** 
  61. * Returns shop base country. 
  62. * @return string 
  63. * @since 1.3.10.150306 
  64. */ 
  65. protected function base_country() { 
  66. return wc()->countries->get_base_country(); 
  67.  
  68. /** 
  69. * Creates the temporary table that will be used to generate the report. 
  70. * @param string table_name The name of the table to create. Used mainly for 
  71. * logging purposes, as the SQL already contains all the instructions to create 
  72. * it. 
  73. * @param string $sql The SQL used to create the temporary table. 
  74. * @return bool True on success, or false on failure. 
  75. * @since 1.3.20.150330 
  76. */ 
  77. protected function create_temporary_table($table_name, $sql) { 
  78. global $wpdb; 
  79.  
  80. try { 
  81. $result = $wpdb->query($sql); 
  82.  
  83. if($result === false) { 
  84. $message = sprintf(__('Creation of temporary table "%s" failed. Please ' . 
  85. 'check PHP error log for error messages ' . 
  86. 'related to the operation.', $this->text_domain),  
  87. $table_name); 
  88. $this->log($message, false); 
  89. trigger_error(E_USER_WARNING, $message); 
  90. else { 
  91. $this->log(sprintf(__('Table "%s" created successfullly.', $this->text_domain),  
  92. $table_name)); 
  93. $result = true; 
  94. catch(Exception $e) { 
  95. $message = sprintf(__('Unexpected error occurred while creating temporary ' . 
  96. 'table "%s". Error message: "%s". Please' . 
  97. 'check PHP error log for further details ' . 
  98. 'related to the operation.', $this->text_domain),  
  99. $table_name,  
  100. $e->getMessage()); 
  101. $this->log($message, false); 
  102. trigger_error(E_USER_WARNING, $message); 
  103. $result = false; 
  104. return $result; 
  105.  
  106. /** 
  107. * Returns a list of the order statuses to use when fetching the order data 
  108. * for the report. 
  109. * @param bool apply_prefix Indicates if the "wc-" prefix should be appended 
  110. * to each of the statuses. The prefix was introduced in WooCommerce 2.2. 
  111. * @return array 
  112. * @since 1.3.19.150327 
  113. */ 
  114. protected function order_statuses_to_include($apply_prefix = false) { 
  115. $order_statuses_to_include = array('processing', 'completed'); 
  116.  
  117. // If requested, include refunded orders 
  118. if(get_arr_value(Definitions::ARG_INCLUDE_REFUNDED_ORDERS, $_REQUEST) === 'yes') { 
  119. $order_statuses_to_include[] = 'refunded'; 
  120.  
  121. if($apply_prefix) { 
  122. foreach($order_statuses_to_include as &$status) { 
  123. $status = 'wc-' . $status; 
  124. return apply_filters('wc_aelia_euva_report_order_statuses_to_include', $order_statuses_to_include, $apply_prefix, $this); 
  125.  
  126. /** 
  127. * Returns the last day of the quarter of which a date is part. 
  128. * @param string target_date The date for which the quarter, and its last day,  
  129. * have to be extracted. 
  130. * @return string A string representing the last day of a quarter, in YYYY-MM-DD 
  131. * format. 
  132. * @throws InvalidArgumentException if an invalid string is passed as a 
  133. * parameter. 
  134. */ 
  135. protected function get_last_day_of_quarter($target_date) { 
  136. $target_timestamp = strtotime($target_date); 
  137. if($target_timestamp === false) { 
  138. throw new \InvalidArgumentException(sprintf(__('Invalid target date specified: "%s". ' . 
  139. 'The argument must be in YYYY-MM-DD format.',  
  140. $this->text_domain),  
  141. $target_date)); 
  142. $target_date_parts = explode('-', date('Y-m-d', $target_timestamp)); 
  143.  
  144. $quarter_year = array_shift($target_date_parts); 
  145. $quarter_month = array_shift($target_date_parts); 
  146. $quarter_last_month = ceil($quarter_month / 3) * 3; 
  147.  
  148. $last_day_of_quarter = date('Y-m-t', strtotime("$quarter_year-{$quarter_last_month}-01")); 
  149.  
  150. // Debug 
  151. //var_dump($last_day_of_quarter);die(); 
  152. return $last_day_of_quarter; 
  153.  
  154. /** 
  155. * Constructor. 
  156. */ 
  157. public function __construct() { 
  158. $this->text_domain = WC_Aelia_EU_VAT_Assistant::$text_domain; 
  159. $this->debug = WC_Aelia_EU_VAT_Assistant::instance()->debug_mode(); 
  160. $this->logger = new Logger(Definitions::PLUGIN_SLUG); 
  161.  
  162. // Keep a list of the tax classes that are not part of MOSS 
  163. $this->non_moss_tax_classes = WC_Aelia_EU_VAT_Assistant::settings()->get(Settings::FIELD_TAX_CLASSES_EXCLUDED_FROM_MOSS, array()); 
  164.  
  165. /** 
  166. * Returns the value of a configuration parameter for the EU VAT Assistant 
  167. * plugin. 
  168. * @param string settings_key The key to retrieve the parameter. 
  169. * @param mixed default The default value to return if the parameter is not 
  170. * found. 
  171. * @return mixed 
  172. */ 
  173. protected function settings($settings_key, $default = null) { 
  174. return WC_Aelia_EU_VAT_Assistant::settings()->get($settings_key, $default); 
  175.  
  176. /** 
  177. * Returns the currency to use for the VAT returns. 
  178. * @return string 
  179. */ 
  180. protected function vat_currency() { 
  181. return $this->settings(Settings::FIELD_VAT_CURRENCY); 
  182.  
  183. /** 
  184. * Formats a price, adding the VAT currency symbol. 
  185. * @param float price The price to format. 
  186. * @return string 
  187. */ 
  188. protected function format_price($price) { 
  189. $args = array( 
  190. 'currency' => $this->vat_currency(),  
  191. ); 
  192. return wc_price($price, $args); 
  193.  
  194. /** 
  195. * Get the legend for the main chart sidebar. 
  196. * @return array 
  197. */ 
  198. public function get_chart_legend() { 
  199. // No legend is needed (this report) doesn't have a chart 
  200. return array(); 
  201.  
  202. /** 
  203. * Output an export link 
  204. */ 
  205. public function get_export_button() { 
  206. $current_range = !empty($_GET['range']) ? sanitize_text_field($_GET['range']) : 'last_month'; 
  207. ?> 
  208. <a 
  209. href="#" 
  210. download="report-<?php echo esc_attr($current_range); ?>-<?php echo date_i18n('Y-m-d', current_time('timestamp')); ?>.csv" 
  211. class="export_csv" 
  212. data-export="table" 
  213. <?php echo __('Export CSV', $this->text_domain); ?> 
  214. </a> 
  215. <?php 
  216.  
  217. /** 
  218. * Returns an array of ranges that are used to produce the reports. 
  219. * @return array 
  220. * @since 0.9.7.141221 
  221. */ 
  222. protected function get_report_ranges() { 
  223. $ranges = array(); 
  224.  
  225. $current_time = current_time('timestamp'); 
  226. $label_fmt = __('Q%d %d', $this->text_domain); 
  227.  
  228. // Current quarter 
  229. $quarter = ceil(date('m', $current_time) / 3); 
  230. $year = date('Y'); 
  231. $ranges['quarter'] = sprintf($label_fmt, $quarter, $year); 
  232.  
  233. // Quarter before this one 
  234. $month = date('m', strtotime('-3 MONTH', $current_time)); 
  235. $year = date('Y', strtotime('-3 MONTH', $current_time)); 
  236. $quarter = ceil($month / 3); 
  237. $ranges['previous_quarter'] = sprintf($label_fmt, $quarter, $year); 
  238.  
  239. // Two quarters ago 
  240. $month = date('m', strtotime('-6 MONTH', $current_time)); 
  241. $year = date('Y', strtotime('-6 MONTH', $current_time)); 
  242. $quarter = ceil($month / 3); 
  243. $ranges['quarter_before_previous'] = sprintf($label_fmt, $quarter, $year); 
  244.  
  245. return array_reverse($ranges); 
  246.  
  247. /** 
  248. * Renders a header on top of the standard reporting UI. 
  249. */ 
  250. protected function render_ui_header() { 
  251. // To be implemented by descendant classes 
  252.  
  253. /** 
  254. * Output the report 
  255. */ 
  256. public function output_report() { 
  257. $ranges = $this->get_report_ranges(); 
  258. $current_range = !empty($_GET['range']) ? sanitize_text_field($_GET['range']) : 'quarter'; 
  259.  
  260. if(!in_array($current_range, array_merge(array_keys($ranges), array('custom')))) { 
  261. $current_range = 'quarter'; 
  262. $this->calculate_current_range($current_range); 
  263.  
  264. $hide_sidebar = true; 
  265.  
  266. // Render a header on top of the standard reporting UI 
  267. $this->render_ui_header(); 
  268.  
  269. include(WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php'); 
  270.  
  271. /** 
  272. * Get the current range and calculate the start and end dates 
  273. * @param string $current_range 
  274. */ 
  275. public function calculate_current_range($current_range) { 
  276. $this->chart_groupby = 'month'; 
  277. switch ($current_range) { 
  278. case 'quarter_before_previous': 
  279. $month = date('m', strtotime('-6 MONTH', current_time('timestamp'))); 
  280. $year = date('Y', strtotime('-6 MONTH', current_time('timestamp'))); 
  281. break; 
  282. case 'previous_quarter': 
  283. $month = date('m', strtotime('-3 MONTH', current_time('timestamp'))); 
  284. $year = date('Y', strtotime('-3 MONTH', current_time('timestamp'))); 
  285. break; 
  286. case 'quarter': 
  287. $month = date('m', current_time('timestamp')); 
  288. $year = date('Y', current_time('timestamp')); 
  289. break; 
  290. default: 
  291. parent::calculate_current_range($current_range); 
  292. return; 
  293. break; 
  294.  
  295. if($month <= 3) { 
  296. $this->start_date = strtotime($year . '-01-01'); 
  297. $this->end_date = strtotime(date('Y-m-t', strtotime($year . '-03-01'))); 
  298. elseif($month > 3 && $month <= 6) { 
  299. $this->start_date = strtotime($year . '-04-01'); 
  300. $this->end_date = strtotime(date('Y-m-t', strtotime($year . '-06-01'))); 
  301. elseif($month > 6 && $month <= 9) { 
  302. $this->start_date = strtotime($year . '-07-01'); 
  303. $this->end_date = strtotime(date('Y-m-t', strtotime($year . '-09-01'))); 
  304. elseif($month > 9) { 
  305. $this->start_date = strtotime($year . '-10-01'); 
  306. $this->end_date = strtotime(date('Y-m-t', strtotime($year . '-12-01'))); 
  307.  
  308. /** 
  309. * Returns the details of the specified tax rate IDs. 
  310. * @param array tax_rate_ids An array of tax rate IDs. 
  311. * @return array An array with the details of each tax rate ID. 
  312. */ 
  313. protected function get_tax_rates_data(array $tax_rate_ids) { 
  314. global $wpdb; 
  315. if(empty($tax_rate_ids)) { 
  316. return; 
  317.  
  318. $SQL = " 
  319. SELECT 
  320. TR.tax_rate_id 
  321. , TR.tax_rate 
  322. , TR.tax_rate_class 
  323. , TR.tax_rate_country 
  324. FROM 
  325. {$wpdb->prefix}woocommerce_tax_rates TR 
  326. WHERE 
  327. (TR.tax_rate_id IN (%s)); 
  328. "; 
  329. // We cannot use $wpdb::prepare(). We need the result of the implode() 
  330. // call to be injected as is, while the prepare() method would wrap it in quotes. 
  331. $SQL = sprintf($SQL, implode(', ', $tax_rate_ids)); 
  332.  
  333. // Retrieve the details of the tax rates 
  334. return $wpdb->get_results($SQL, OBJECT_K); 
  335.  
  336. /** 
  337. * Get report totals such as order totals and discount amounts. 
  338. * Data example: 
  339. * '_order_total' => array( 
  340. * 'type' => 'meta',  
  341. * 'function' => 'SUM',  
  342. * 'name' => 'total_sales' 
  343. * ) 
  344. * @param array $args 
  345. * @return array|string depending on query_type 
  346. */ 
  347. public function get_order_report_data($args = array()) { 
  348. $args = array_merge(array( 
  349. 'filter_range' => true,  
  350. 'nocache' => true,  
  351. 'debug' => $this->debug,  
  352. ), $args); 
  353. return parent::get_order_report_data($args); 
  354.  
  355. /** 
  356. * Returns the name of the country passed as an argument. 
  357. * @param string country_code A ountry code. 
  358. * @return string The country name. 
  359. * @since 0.9.7.141221 
  360. */ 
  361. protected function get_country_name($country_code) { 
  362. if(empty($this->countries)) { 
  363. $this->countries = WC()->countries->countries; 
  364. return $this->countries[$country_code]; 
  365.  
  366. /** 
  367. * Returns the exchange for a currency in a specific date. 
  368. * @param string from_currency The currency for which the exchange rate should 
  369. * be retrieved. 
  370. * @param string target_date Indicates for which date the exchange rates should 
  371. * be retrieved. 
  372. * @return float|null An exchange rate, if any is found, or null. 
  373. */ 
  374. protected function get_vat_currency_exchange_rate($from_currency, $target_date) { 
  375. $rate = null; 
  376. // There's no point in trying to get historical rates for a date in the 
  377. // future 
  378. if($target_date > date('Y-m-d')) { 
  379. return $rate; 
  380.  
  381. $rate_key = $from_currency . '-' . $target_date; 
  382.  
  383. // If we already have the exchange rate for the date, return it 
  384. if(empty($this->vat_currency_exchange_rates[$target_date])) { 
  385. $exchange_rate_provider_class = apply_filters('wc_aelia_euva_report_fx_provider_class',  
  386. '\Aelia\WC\EU_VAT_Assistant\Exchange_Rates_ECB_Historical_Model'); 
  387. $rates = $exchange_rate_provider_class::get_rates_for_date($target_date, $this->vat_currency()); 
  388.  
  389. $this->vat_currency_exchange_rates[$target_date] = apply_filters('wc_aelia_eu_vat_assistant_eu_vat_report_exchange_rates',  
  390. $rates,  
  391. $target_date,  
  392. $this); 
  393.  
  394. /** The rates represent how many "FROM CURRENCY" correspond to one "VAT 
  395. * CURRENCY". We will need to know the opposite, i.e. how many VAT CURRENCY 
  396. * correspond to one FROM CURRENCY, therefore we have to calculate the 
  397. * quotient as "1 divided by <returned rate>". 
  398. */ 
  399. if(!empty($this->vat_currency_exchange_rates[$target_date][$from_currency])) { 
  400. $rate = 1 / $this->vat_currency_exchange_rates[$target_date][$from_currency]; 
  401.  
  402. // Debug 
  403. //var_dump("[$target_date] - $from_currency to " . $this->vat_currency() . ": $rate"); 
  404. return $rate;