Dmitriy | Инициализация.
This commit is contained in:
17
src/Admin/Menu.php
Normal file
17
src/Admin/Menu.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Admin;
|
||||
|
||||
class Menu {
|
||||
public static function init(): void {
|
||||
add_action("admin_menu", function () {
|
||||
PriceUpdatesMenu::init();
|
||||
OptionsSubMenu::init(PriceUpdatesMenu::getSlug());
|
||||
});
|
||||
|
||||
add_action("admin_enqueue_scripts", function ($hook) {
|
||||
PriceUpdatesMenu::enqueue($hook);
|
||||
OptionsSubMenu::enqueue($hook);
|
||||
});
|
||||
}
|
||||
}
|
||||
45
src/Admin/OptionsSubMenu.php
Normal file
45
src/Admin/OptionsSubMenu.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Admin;
|
||||
|
||||
use Cosmopet\PriceUpdates\RestApi;
|
||||
use Cosmopet\PriceUpdates\Controllers\OptionsController;
|
||||
|
||||
class OptionsSubMenu {
|
||||
private static string $slug = "price-updates-options";
|
||||
|
||||
public static function init(string $parentSlug): void {
|
||||
add_submenu_page(
|
||||
$parentSlug,
|
||||
"Настройки",
|
||||
"Настройки",
|
||||
"manage_options",
|
||||
self::$slug,
|
||||
fn () => require_once(PRICE_UPDATES_PLUGIN_DIR . "/src/Views/OptionsPage.php"),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
public static function enqueue($hook) {
|
||||
if (!str_contains($hook, self::$slug)) return;
|
||||
|
||||
wp_enqueue_style(
|
||||
self::$slug . "-style",
|
||||
PRICE_UPDATES_PLUGIN_URL . "assets/css/options-page.css",
|
||||
[],
|
||||
filemtime(PRICE_UPDATES_PLUGIN_DIR . '/assets/css/options-page.css')
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
self::$slug . "-script",
|
||||
PRICE_UPDATES_PLUGIN_URL . "assets/js/options-page.js",
|
||||
["jquery"],
|
||||
filemtime(PRICE_UPDATES_PLUGIN_DIR . '/assets/js/options-page.js'),
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(self::$slug . "-script", "priceUpdatesOptionsSettings", [
|
||||
"ajaxUrl" => RestApi::getAjaxUrl() . OptionsController::getNamespace(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
48
src/Admin/PriceUpdatesMenu.php
Normal file
48
src/Admin/PriceUpdatesMenu.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Admin;
|
||||
|
||||
use Cosmopet\PriceUpdates\RestApi;
|
||||
|
||||
class PriceUpdatesMenu {
|
||||
private static string $slug = "price-updates";
|
||||
|
||||
public static function init(): void {
|
||||
add_menu_page(
|
||||
"Обновление цен",
|
||||
"Обновление цен",
|
||||
"manage_options",
|
||||
self::$slug,
|
||||
fn () => require_once(PRICE_UPDATES_PLUGIN_DIR . "/src/Views/PriceUpdatesPage.php"),
|
||||
"dashicons-cloud",
|
||||
76
|
||||
);
|
||||
}
|
||||
|
||||
public static function enqueue($hook) {
|
||||
if (!str_contains($hook, self::$slug)) return;
|
||||
|
||||
wp_enqueue_style(
|
||||
self::$slug . "-style",
|
||||
PRICE_UPDATES_PLUGIN_URL . "assets/css/price-updates-page.css",
|
||||
[],
|
||||
filemtime(PRICE_UPDATES_PLUGIN_DIR . '/assets/css/price-updates-page.css')
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
self::$slug . "-script",
|
||||
PRICE_UPDATES_PLUGIN_URL . "assets/js/price-updates-page.js",
|
||||
["jquery"],
|
||||
filemtime(PRICE_UPDATES_PLUGIN_DIR . '/assets/js/price-updates-page.js'),
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(self::$slug . "-script", "priceUpdatesSettings", [
|
||||
"ajaxUrl" => RestApi::getAjaxUrl(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getSlug(): string {
|
||||
return self::$slug;
|
||||
}
|
||||
}
|
||||
61
src/Classes/ParseProduct.php
Normal file
61
src/Classes/ParseProduct.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Classes;
|
||||
|
||||
class ParseProduct {
|
||||
/**
|
||||
* @var string Артикул товара
|
||||
*/
|
||||
private string $sku;
|
||||
|
||||
/**
|
||||
* @var int Цены товара (Базовая и Аукционная)
|
||||
*/
|
||||
private int $price = 0;
|
||||
|
||||
public function __construct(string $sku, int $price) {
|
||||
$this->setSku($sku);
|
||||
$this->setPrice($price);
|
||||
}
|
||||
|
||||
public static function validateSku($sku): bool {
|
||||
if (gettype($sku) !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function validatePrice($price): bool {
|
||||
if (!is_numeric($price)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSku(): string {
|
||||
return $this->sku;
|
||||
}
|
||||
|
||||
public function setSku(string $sku): ParseProduct {
|
||||
$this->sku = $sku;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPrice(): int {
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
public function setPrice(int $price): ParseProduct {
|
||||
$this->price = $price;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toArray(): array {
|
||||
return [
|
||||
"sku" => $this->getSku(),
|
||||
"price" => $this->getPrice(),
|
||||
];
|
||||
}
|
||||
}
|
||||
45
src/Classes/ParseProductError.php
Normal file
45
src/Classes/ParseProductError.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Classes;
|
||||
|
||||
class ParseProductError {
|
||||
/**
|
||||
* @var string Артикул товара
|
||||
*/
|
||||
private string $sku;
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
private string $message;
|
||||
|
||||
public function __construct(string $sku, string $message) {
|
||||
$this->setSku($sku);
|
||||
$this->setMessage($message);
|
||||
}
|
||||
|
||||
public function getSku(): string {
|
||||
return $this->sku;
|
||||
}
|
||||
|
||||
public function setSku(string $sku): ParseProductError {
|
||||
$this->sku = $sku;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMessage(): string {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function setMesage(string $message): ParseProductError {
|
||||
$this->message = $message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toArray(): array {
|
||||
return [
|
||||
"sku" => $this->getSku(),
|
||||
"message" => $this->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
21
src/Controllers/AbstractController.php
Normal file
21
src/Controllers/AbstractController.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Controllers;
|
||||
|
||||
use Cosmopet\PriceUpdates\RestApi;
|
||||
use Cosmopet\PriceUpdates\Controllers\Interface\RegisterControllerInterface;
|
||||
|
||||
class AbstractController implements RegisterControllerInterface {
|
||||
protected static string $namespace = "";
|
||||
protected static array $methods = [];
|
||||
|
||||
public static function register(): void {
|
||||
foreach (static::$methods as $method) {
|
||||
register_rest_route(RestApi::$uri . static::$namespace, $method["uri"], array_diff_key($method, ["uri"]));
|
||||
}
|
||||
}
|
||||
|
||||
public static function getNamespace(): string {
|
||||
return static::$namespace;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Controllers\Interface;
|
||||
|
||||
interface RegisterControllerInterface {
|
||||
public static function register(): void;
|
||||
}
|
||||
55
src/Controllers/OptionsController.php
Normal file
55
src/Controllers/OptionsController.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Controllers;
|
||||
|
||||
use Cosmopet\PriceUpdates\Services\OptionsService;
|
||||
|
||||
use Cosmopet\PriceUpdates\Parser\GoogleTableParser;
|
||||
|
||||
class OptionsController extends AbstractController {
|
||||
protected static string $namespace = "/options";
|
||||
protected static array $methods = [
|
||||
[
|
||||
"uri" => "/get/",
|
||||
"methods" => "GET",
|
||||
"callback" => ["\Cosmopet\PriceUpdates\Controllers\OptionsController", "get"],
|
||||
],
|
||||
[
|
||||
"uri" => "/update/",
|
||||
"methods" => "POST",
|
||||
"callback" => ["\Cosmopet\PriceUpdates\Controllers\OptionsController", "update"],
|
||||
],
|
||||
];
|
||||
|
||||
public static function get() {
|
||||
$response = OptionsService::get();
|
||||
|
||||
if (isset($data["error"])) {
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => $data["error"],
|
||||
], 404);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
wp_send_json($response, 200, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
public static function update() {
|
||||
$result = OptionsService::update($_POST);
|
||||
|
||||
if (OptionsService::update($_POST) === false) {
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => "Ошибка сохранения настроек.",
|
||||
], 400);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
GoogleTableParser::init();
|
||||
|
||||
wp_send_json($result, 200);
|
||||
}
|
||||
}
|
||||
118
src/Controllers/PriceUpdatesController.php
Normal file
118
src/Controllers/PriceUpdatesController.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Controllers;
|
||||
|
||||
use Cosmopet\PriceUpdates\Services\WCPriceUpdate;
|
||||
|
||||
use Cosmopet\PriceUpdates\Parser\ExcelParser;
|
||||
use Cosmopet\PriceUpdates\Parser\GoogleTableParser;
|
||||
|
||||
class PriceUpdatesController extends AbstractController {
|
||||
private static array $allowedExtensions = ["xls", "xlsx"];
|
||||
|
||||
protected static array $methods = [
|
||||
[
|
||||
"uri" => "/update/",
|
||||
"methods" => "POST",
|
||||
"callback" => ["\Cosmopet\PriceUpdates\Controllers\PriceUpdatesController", "update"],
|
||||
],
|
||||
];
|
||||
|
||||
public static function update() {
|
||||
if (!isset($_POST["type"])){
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => "Тип обнвления отсутствует.",
|
||||
], 400);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
switch ($_POST["type"]) {
|
||||
case "excel":
|
||||
if (!isset($_FILES["file"])) {
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => "Файл отсутствует.",
|
||||
], 400);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$fileExtension = strtolower(pathinfo($_FILES["file"]["name"], PATHINFO_EXTENSION));
|
||||
|
||||
if (!in_array($fileExtension, self::$allowedExtensions)) {
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => "Разрешены только файлы с типом XLX или XLXS.",
|
||||
], 400);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$data = ExcelParser::parse($_FILES["file"]);
|
||||
|
||||
if (isset($data["error"])) {
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => $data["error"],
|
||||
], 400);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
wp_send_json(WCPriceUpdate::update($data), 200, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
break;
|
||||
|
||||
case "google-table":
|
||||
if (!isset($_POST["url"])){
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => "Нобходимо указать ссылку.",
|
||||
], 400);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$url = $_POST["url"];
|
||||
|
||||
if (gettype($url) !== "string"){
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => "Неверный тип ссылки.",
|
||||
], 400);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
if (!GoogleTableParser::isValidGoogleSheetsUrl($url)){
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => "Ссылка должна вести на гугл-таблицу.",
|
||||
], 400);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$data = GoogleTableParser::parse($url);
|
||||
|
||||
if (isset($data["error"])) {
|
||||
wp_send_json([
|
||||
"error" => true,
|
||||
"message" => $data["error"],
|
||||
], !empty($data["code"]) ? $data["code"] : 400);
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
wp_send_json(WCPriceUpdate::update($data), 200, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static function can() {
|
||||
return current_user_can("manage_options");
|
||||
}
|
||||
}
|
||||
16
src/Parser/AbstractParser.php
Normal file
16
src/Parser/AbstractParser.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Parser;
|
||||
|
||||
use Cosmopet\PriceUpdates\Parser\Interface\ParserInterface;
|
||||
|
||||
abstract class AbstractParser implements ParserInterface {
|
||||
/**
|
||||
* @param $data Файл
|
||||
*
|
||||
* @return \Cosmopet\PriceUpdates\Classes\ParseProduct[]
|
||||
*/
|
||||
public static function parse($data): array {
|
||||
return self::$data;
|
||||
}
|
||||
}
|
||||
55
src/Parser/ExcelParser.php
Normal file
55
src/Parser/ExcelParser.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Parser;
|
||||
|
||||
use Shuchkin\SimpleXLSX;
|
||||
|
||||
use Cosmopet\PriceUpdates\Classes\ParseProduct;
|
||||
|
||||
class ExcelParser extends AbstractParser {
|
||||
public static function parse($data): array {
|
||||
$file = wp_handle_upload($data, [ "test_form" => false ]);
|
||||
|
||||
if (!isset($file["file"])) {
|
||||
return [
|
||||
"error" => "Ошибка обработки файла.",
|
||||
];
|
||||
}
|
||||
|
||||
$parse = SimpleXLSX::parse($file["file"]);
|
||||
|
||||
unlink($file["file"]);
|
||||
|
||||
if ($parse === false) {
|
||||
return [
|
||||
"error" => SimpleXLSX::parseError(),
|
||||
];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach($parse->rows() as $index => $row) {
|
||||
if ($index === 0) continue;
|
||||
|
||||
$sku = $row[0];
|
||||
$price = intval($row[1]);
|
||||
|
||||
if (!ParseProduct::validateSku($sku)) {
|
||||
$ceilNumber = $index + 1;
|
||||
|
||||
$result[] = new ParseProductError("Номер строки: $ceilNumber", "Неверный артикул товара.");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ParseProduct::validatePrice($price)) {
|
||||
$result[] = new ParseProductError($sku, "Неверный формат цены.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[] = new ParseProduct($sku, $price);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
103
src/Parser/GoogleTableParser.php
Normal file
103
src/Parser/GoogleTableParser.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Parser;
|
||||
|
||||
use Cosmopet\PriceUpdates\Classes\ParseProduct;
|
||||
use Cosmopet\PriceUpdates\Classes\ParseProductError;
|
||||
|
||||
use Cosmopet\PriceUpdates\Services\OptionsService;
|
||||
|
||||
use Cosmopet\PriceUpdates\Utils\GoogleAuthConfigValidator;
|
||||
|
||||
class GoogleTableParser extends AbstractParser {
|
||||
private static \Google_Service_Sheets $service;
|
||||
|
||||
public static function init() {
|
||||
$client = new \Google_Client();
|
||||
|
||||
$client->setApplicationName('Google Sheets API');
|
||||
$client->setScopes([\Google_Service_Sheets::SPREADSHEETS]);
|
||||
$client->setAccessType('offline');
|
||||
|
||||
$file = file_get_contents(OptionsService::$filePath);
|
||||
$data = json_decode($file, true);
|
||||
|
||||
if (GoogleAuthConfigValidator::validate($data)) {
|
||||
$client->setAuthConfig(OptionsService::$filePath);
|
||||
}
|
||||
|
||||
self::$service = new \Google_Service_Sheets($client);
|
||||
}
|
||||
|
||||
public static function parse($data): array {
|
||||
preg_match('/\/d\/([a-zA-Z0-9-_]+)/', $data, $matches);
|
||||
|
||||
$spreadsheetId = $matches[1];
|
||||
|
||||
if (empty($spreadsheetId)) {
|
||||
return [
|
||||
"error" => "ID Гугл таблицы отсутствует.",
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$rows = self::$service->spreadsheets_values->get($spreadsheetId, "A:B")?->values;
|
||||
|
||||
if (count($rows) === 0) return [];
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach($rows as $index => $row) {
|
||||
if ($index === 0) continue;
|
||||
|
||||
$sku = $row[0];
|
||||
$price = intval($row[1]);
|
||||
|
||||
if (!ParseProduct::validateSku($sku)) {
|
||||
$ceilNumber = $index + 1;
|
||||
|
||||
$result[] = new ParseProductError("Номер строки: $ceilNumber", "Неверный артикул товара.");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ParseProduct::validatePrice($price)) {
|
||||
$result[] = new ParseProductError($sku, "Неверный формат цены.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[] = new ParseProduct($sku, $price);
|
||||
}
|
||||
|
||||
return $result;
|
||||
} catch (\Throwable $e) {
|
||||
$error = json_decode($e->getMessage());
|
||||
|
||||
return [
|
||||
"code" => !empty($error?->error?->code) ? $error?->error?->code : 400,
|
||||
"error" => !empty($error?->error?->message) ? $error?->error?->message : "Ошибка при обработке гугл-таблицы.",
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public static function isValidGoogleSheetsUrl(string $url): bool {
|
||||
$patterns = [
|
||||
// Стандартные ссылки на таблицы
|
||||
'~^https?://docs\.google\.com/spreadsheets/(?:d|u/\d+)/([a-zA-Z0-9-_]+)~i',
|
||||
|
||||
// Ссылки через Google Drive
|
||||
'~^https?://drive\.google\.com/(?:open\?.*id=|file/d/)([a-zA-Z0-9-_]+).*[?&]usp=sheets~i',
|
||||
|
||||
// Ссылки на публичные таблицы
|
||||
'~^https?://docs\.google\.com/spreadsheets/d/e/[a-zA-Z0-9-_]+/pub~i',
|
||||
];
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match($pattern, $url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
7
src/Parser/Interface/ParserInterface.php
Normal file
7
src/Parser/Interface/ParserInterface.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Parser\Interface;
|
||||
|
||||
interface ParserInterface {
|
||||
public static function parse($data): array;
|
||||
}
|
||||
26
src/RestApi.php
Normal file
26
src/RestApi.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates;
|
||||
|
||||
class RestApi {
|
||||
public static string $uri = "price-updates/api/v1";
|
||||
|
||||
private static array $controllers = [
|
||||
"\Cosmopet\PriceUpdates\Controllers\PriceUpdatesController",
|
||||
"\Cosmopet\PriceUpdates\Controllers\OptionsController",
|
||||
];
|
||||
|
||||
public static function register(): void {
|
||||
add_action("rest_api_init", function() {
|
||||
foreach(self::$controllers as $controller) {
|
||||
if (method_exists($controller, "register")) {
|
||||
$controller::register();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static function getAjaxUrl(): string {
|
||||
return get_rest_url(null, self::$uri);
|
||||
}
|
||||
}
|
||||
27
src/Services/OptionsService.php
Normal file
27
src/Services/OptionsService.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Services;
|
||||
|
||||
use Cosmopet\PriceUpdates\Utils\GoogleAuthConfigValidator;
|
||||
|
||||
class OptionsService {
|
||||
public static string $filePath = PRICE_UPDATES_PLUGIN_DIR . "/config/google/credentials.json";
|
||||
|
||||
public static function get() {
|
||||
$file = file_get_contents(self::$filePath);
|
||||
|
||||
if ($file === false) {
|
||||
return [
|
||||
"error" => "Файл не найден"
|
||||
];
|
||||
}
|
||||
|
||||
return json_decode($file, true);
|
||||
}
|
||||
|
||||
public static function update($data) {
|
||||
$data = array_map(fn ($val) => stripcslashes($val), $data);
|
||||
$data = stripcslashes(json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
return file_put_contents(self::$filePath, $data);
|
||||
}
|
||||
}
|
||||
76
src/Services/WCPriceUpdate.php
Normal file
76
src/Services/WCPriceUpdate.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Services;
|
||||
|
||||
use Cosmopet\PriceUpdates\Classes\ParseProductError;
|
||||
|
||||
class WCPriceUpdate {
|
||||
public static function update(array $data): array {
|
||||
if (count($data) === 0) return [];
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach($data as $item) {
|
||||
if ($item instanceof ParseProductError) {
|
||||
$result[] = array_merge($item->toArray(), [ "isError" => true ]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$products = wc_get_products([ "sku" => $item->getSku() ]);
|
||||
|
||||
if (count($products) === 0) {
|
||||
$result[] = array_merge($item->toArray(), [ "message" => "Товар не найден, проверьте артикул.", "isError" => true ]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$product = $products[0];
|
||||
|
||||
$price = [
|
||||
"regular" => (int) $product->get_regular_price(),
|
||||
"sale" => (int) $product->get_sale_price(),
|
||||
"new" => $item->getPrice(),
|
||||
];
|
||||
|
||||
// Если цена товара равна базовой и акционная цена отсутствует, его нет смысла обновлять, т.к. данные везде совпадают
|
||||
if ($price["regular"] === $price["new"] && $price["sale"] === 0) {
|
||||
$result[] = array_merge($item->toArray(), $price, [ "message" => "Товар не обновлён. Цены совпадают.", "isError" => true ]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Если цена товара меньше базовой, НО равна акционной, его нет смысла обновлять, ибо ничего не обновиться
|
||||
if ($price["sale"] === $price["new"]) {
|
||||
$result[] = array_merge($item->toArray(), $price, [ "message" => "Товар не обновлён. Акционные цены совпадают.", "isError" => true ]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($price["regular"] <= $price["new"]) {
|
||||
$product->set_regular_price($price["new"]);
|
||||
|
||||
if ($price["sale"] > 0) {
|
||||
$product->set_sale_price(0);
|
||||
}
|
||||
} else {
|
||||
$product->set_sale_price($price["new"]);
|
||||
}
|
||||
|
||||
|
||||
$product->save();
|
||||
|
||||
$result[] = [
|
||||
"sku" => $item->getSku(),
|
||||
"url" => $product->get_permalink(),
|
||||
"regular" => [
|
||||
"new" => (int) $product->get_regular_price(),
|
||||
"old" => $price["regular"],
|
||||
],
|
||||
"sale" => [
|
||||
"new" => (int) $product->get_sale_price(),
|
||||
"old" => $price["sale"]
|
||||
],
|
||||
"currency" => get_woocommerce_currency(),
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
31
src/Utils/GoogleAuthConfigValidator.php
Normal file
31
src/Utils/GoogleAuthConfigValidator.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Cosmopet\PriceUpdates\Utils;
|
||||
|
||||
class GoogleAuthConfigValidator {
|
||||
private static array $fields = [
|
||||
"type",
|
||||
"project_id",
|
||||
"private_key_id",
|
||||
"private_key",
|
||||
"client_email",
|
||||
"client_id",
|
||||
"auth_uri",
|
||||
"token_uri",
|
||||
"auth_provider_x509_cert_url",
|
||||
"client_x509_cert_url",
|
||||
"universe_domain"
|
||||
];
|
||||
|
||||
public static function validate($data): bool {
|
||||
if (gettype($data) !== "array" || count($data) !== count(self::$fields)) return false;
|
||||
|
||||
foreach (array_keys($data) as $key) {
|
||||
if (!in_array($key, self::$fields)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
57
src/Views/OptionsPage.php
Normal file
57
src/Views/OptionsPage.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<div class="price-updates-options">
|
||||
<h1>Настройки</h1>
|
||||
<form class="price-updates-options__form">
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="type">type</label>
|
||||
<input type="text" id="type" name="type" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="project_id">project_id</label>
|
||||
<input type="text" id="project_id" name="project_id" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="private_key_id">private_key_id</label>
|
||||
<input type="text" id="private_key_id" name="private_key_id" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="private_key">private_key</label>
|
||||
<input type="text" id="private_key" name="private_key" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="client_email">client_email</label>
|
||||
<input type="text" id="client_email" name="client_email" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="client_id">client_id</label>
|
||||
<input type="text" id="client_id" name="client_id" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="auth_uri">auth_uri</label>
|
||||
<input type="text" id="auth_uri" name="auth_uri" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="token_uri">token_uri</label>
|
||||
<input type="text" id="token_uri" name="token_uri" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="auth_provider_x509_cert_url">auth_provider_x509_cert_url</label>
|
||||
<input type="text" id="auth_provider_x509_cert_url" name="auth_provider_x509_cert_url" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="client_x509_cert_url">client_x509_cert_url</label>
|
||||
<input type="text" id="client_x509_cert_url" name="client_x509_cert_url" />
|
||||
</div>
|
||||
<div class="price-updates-options__input-wrapper">
|
||||
<label for="universe_domain">universe_domain</label>
|
||||
<input type="text" id="universe_domain" name="universe_domain" />
|
||||
</div>
|
||||
<div class="price-updates-options__submit-wrapper">
|
||||
<button type="submit" class="price-updates-options__submit">Обновить</button>
|
||||
<div class="price-updates-options__success-message hidden"></div>
|
||||
<div class="price-updates-options__error-message hidden"></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="price-updates-loader-wrapper hidden">
|
||||
<div class="price-updates-loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
55
src/Views/PriceUpdatesPage.php
Normal file
55
src/Views/PriceUpdatesPage.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<div class="price-updates-wrapper">
|
||||
<h1>Обновление цен у товаров</h1>
|
||||
|
||||
<div class="price-updates-variations">
|
||||
<div>
|
||||
<input type="radio" id="excel" name="price-updates-variant" value="excel" checked />
|
||||
<label for="excel">Обновить через файл Excel</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="google-table" name="price-updates-variant" value="google-table" />
|
||||
<label for="google-table">Обновить через google-таблицу</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="price-updates-contents">
|
||||
<form data-price-updates-variant="excel">
|
||||
<input type="file" name="file" require />
|
||||
<button type="submit">Обновить</button>
|
||||
<input type="hidden" name="type" value="excel" accept=".xlsx,.xls" />
|
||||
</form>
|
||||
<form data-price-updates-variant="google-table" class="hidden">
|
||||
<input type="url" name="url" placeholder="Ссылка на гугл таблицу" require />
|
||||
<button type="submit">Обновить</button>
|
||||
<input type="hidden" name="type" value="google-table" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<span class="price-updates-caption">
|
||||
Обновляются только те товары, у которых <b>присутствует артикул и цена не совпадает с базовой или акционной</b>.
|
||||
</span>
|
||||
|
||||
<div class="price-updates-count hidden">
|
||||
<span class="price-updates-count-success"></span>
|
||||
<span class="price-updates-count-error"></span>
|
||||
</div>
|
||||
|
||||
<div class="price-updates-response hidden">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Артикул</th>
|
||||
<th>Базовая цена</th>
|
||||
<th>Акционная цена</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="price-updates-error hidden"></div>
|
||||
|
||||
<div class="price-updates-loader-wrapper hidden">
|
||||
<div class="price-updates-loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user