Доработка экспорта заказов
This commit is contained in:
parent
b44194d5a3
commit
a11a5da2e1
|
@ -86,11 +86,13 @@ $config = [
|
||||||
'modules' => [
|
'modules' => [
|
||||||
'exchange' => [
|
'exchange' => [
|
||||||
'class' => 'carono\exchange1c\ExchangeModule',
|
'class' => 'carono\exchange1c\ExchangeModule',
|
||||||
|
'exchangeDocuments' => true,
|
||||||
|
'validateModelOnSave' => true,
|
||||||
'groupClass' => 'app\models\SupplyGroup',
|
'groupClass' => 'app\models\SupplyGroup',
|
||||||
'productClass' => 'app\models\Supply',
|
'productClass' => 'app\models\Supply',
|
||||||
'offerClass' => 'app\models\SupplyEdgeProduct',
|
'offerClass' => 'app\models\SupplyEdgeProduct',
|
||||||
'partnerClass' => 'app\models\Account',
|
'partnerClass' => 'app\models\Account',
|
||||||
'documentClass' => 'app\models\Purchase',
|
'documentClass' => 'app\models\Order',
|
||||||
'auth' => function ($mail, $pswd) {
|
'auth' => function ($mail, $pswd) {
|
||||||
// Необходимо уничтожить AccountForm
|
// Необходимо уничтожить AccountForm
|
||||||
// return (new \app\models\AccountForm())->authentication($mail, $pswd);
|
// return (new \app\models\AccountForm())->authentication($mail, $pswd);
|
||||||
|
|
|
@ -5,33 +5,43 @@ declare(strict_types=1);
|
||||||
namespace app\models;
|
namespace app\models;
|
||||||
|
|
||||||
use yii;
|
use yii;
|
||||||
use yii\web\User as Account;
|
|
||||||
|
|
||||||
use app\models\traits\SearchByEdge;
|
use app\models\Account;
|
||||||
|
use app\models\Product;
|
||||||
|
use app\models\SupplyEdgeProduct;
|
||||||
|
use app\models\traits\Xml2Array;
|
||||||
|
|
||||||
use Exception;
|
use carono\exchange1c\interfaces\OfferInterface;
|
||||||
|
use carono\exchange1c\interfaces\ProductInterface;
|
||||||
|
use carono\exchange1c\controllers\ApiController;
|
||||||
|
|
||||||
|
use exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Заказ
|
* Поставка (выгрузка товаров от поставщиков)
|
||||||
*
|
*
|
||||||
* @see Account Заказчик
|
* Представляет собой предложения от поставщиков которые добавляются
|
||||||
* @see Supply Поставки для заказа
|
* в универсальные лоты товаров в асспортименте магазина
|
||||||
|
*
|
||||||
|
* @see Product Продукт (туда добавляются поставки)
|
||||||
*/
|
*/
|
||||||
class Order extends Document
|
class Supply extends Product implements ProductInterface, OfferInterface
|
||||||
{
|
{
|
||||||
use SearchByEdge;
|
use Xml2Array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Поставки для записи
|
* Количество
|
||||||
|
*
|
||||||
|
* Используется при выводе в корзине
|
||||||
*/
|
*/
|
||||||
public array $supplies;
|
public int $amnt = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Имя коллекции
|
* Имя коллекции
|
||||||
*/
|
*/
|
||||||
public static function collectionName(): string
|
public static function collectionName(): string
|
||||||
{
|
{
|
||||||
return 'order';
|
return 'supply';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,7 +52,10 @@ class Order extends Document
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::attributes(),
|
parent::attributes(),
|
||||||
[
|
[
|
||||||
'stts'
|
'cost',
|
||||||
|
'onec',
|
||||||
|
'oemn',
|
||||||
|
'ocid'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +68,10 @@ class Order extends Document
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::attributeLabels(),
|
parent::attributeLabels(),
|
||||||
[
|
[
|
||||||
'stts' => 'Статус'
|
'cost' => 'Стоимость (cost)',
|
||||||
|
'onec' => 'Данные 1С',
|
||||||
|
'oemn' => 'OEM-номера',
|
||||||
|
'ocid' => 'Идентификатор 1C (ocid)'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -68,302 +84,492 @@ class Order extends Document
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::rules(),
|
parent::rules(),
|
||||||
[
|
[
|
||||||
[
|
// [
|
||||||
'stts',
|
// [
|
||||||
'string',
|
// 'oemn'
|
||||||
'message' => '{attribute} должен быть строкой'
|
// ],
|
||||||
],
|
// 'arrayValidator',
|
||||||
[
|
// 'message' => '{attribute} должен быть массивом.'
|
||||||
'stts',
|
// ]
|
||||||
'default',
|
|
||||||
'value' => 'preparing'
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Подключение к аккаунту
|
* После сохранения
|
||||||
*/
|
*/
|
||||||
public function connect(Account $account): ?AccountEdgeOrder
|
public function afterSave($data, $vars): void
|
||||||
{
|
{
|
||||||
// Запись ребра: АККАУНТ -> ЗАКАЗ
|
if (AccountEdgeSupply::searchByVertex(yii::$app->user->id, $this->readId())) {
|
||||||
return AccountEdgeOrder::write($account->id, $this->readId(), 'current') ?? throw new Exception('Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
|
// Ребро: "АККАУНТ -> ПОСТАВКА" уже существует
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Ребра не существует
|
||||||
|
|
||||||
|
// Запись ребра: АККАУНТ -> ПОСТАВКА
|
||||||
|
(new AccountEdgeSupply)->write(yii::$app->user->id, $this->readId(), 'import');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Запись товара
|
* Запись реквизитов из 1С
|
||||||
*
|
|
||||||
* $supply = [ Supply $supply, int $amount = 1 ]
|
|
||||||
*
|
|
||||||
* @param Supply|array $supply Поставка
|
|
||||||
* @param Account $trgt Заказчик
|
|
||||||
*
|
|
||||||
* @return int Количество записанных поставок
|
|
||||||
*
|
|
||||||
* @todo Создать параметр разделителя для администрации
|
|
||||||
*/
|
*/
|
||||||
public function writeSupply(Supply|string|array $supply, Account $trgt = null): int
|
public function setRequisite1c($name, $value): mixed
|
||||||
{
|
{
|
||||||
// Инициализация
|
return true;
|
||||||
$trgt ?? $trgt = yii::$app->user ?? throw new Exception('Не удалось инициализировать заказчика');
|
|
||||||
|
|
||||||
if ($supply instanceof Supply) {
|
|
||||||
// Передана инстанция класса поставки или второй элемент массива не является числом
|
|
||||||
|
|
||||||
// Унификация входных данных
|
|
||||||
$supply = [$supply->catn => 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($this->_key)) {
|
|
||||||
// Корзина не инициализирована
|
|
||||||
|
|
||||||
// Инициализация
|
|
||||||
if (!$this->save()) {
|
|
||||||
// Инициализация заказа не удалась
|
|
||||||
|
|
||||||
throw new Exception('Ошибка при записи заказа в базу данных');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация ребра: АККАУНТ -> ЗАКАЗ
|
|
||||||
if (!AccountEdgeOrder::write($trgt->readId(), $this->readId(), 'create')) {
|
|
||||||
// Инициализация не удалась
|
|
||||||
|
|
||||||
throw new Exception('Ошибка при записи ребра от аккаунта до заказа в базу данных');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация
|
|
||||||
$amount = 0;
|
|
||||||
|
|
||||||
foreach (is_array($supply) ? $supply : [$supply => 1] as $supply_raw => $amount_raw) {
|
|
||||||
// Перебор поставок
|
|
||||||
|
|
||||||
for ($i = 0; $i < $amount_raw; $i++) {
|
|
||||||
// Создание рёбер соразмерно запросу (добавление нескольких продуктов в корзину)
|
|
||||||
|
|
||||||
// Запись ребра: ЗАКАЗ -> ПОСТАВКА
|
|
||||||
if (!$supply_model = Supply::searchByCatn($supply_raw) or !OrderEdgeSupply::write($this->readId(), $supply_model->readId(), 'write')) {
|
|
||||||
// Поставка не найдена или запись ребра не удалась
|
|
||||||
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
// Ребро создано (товар подключен к заказу)
|
|
||||||
|
|
||||||
// Постинкрементация счётчика добавленных товаров
|
|
||||||
$amount++;
|
|
||||||
|
|
||||||
// Запись в журнал
|
|
||||||
$this->journal('write', ['target' => $supply_model->readId()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($amount === 0) {
|
|
||||||
// Отправка уведомления
|
|
||||||
self::notification('Неудачная попытка добавить товар в корзину');
|
|
||||||
} else if ($amount === 1) {
|
|
||||||
// Отправка уведомления
|
|
||||||
self::notification('Товар ' . $supply_model->catn . ' добавлен в корзину');
|
|
||||||
} else {
|
|
||||||
// Отправка уведомления
|
|
||||||
self::notification('Добавлено ' . $amount . ' товаров в корзину');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $amount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Удаление поставки
|
* Запись группы из 1С
|
||||||
*
|
|
||||||
* @param Supply|string|array $supply Товары
|
|
||||||
*
|
|
||||||
* @return int Количество удалённых рёбер
|
|
||||||
*/
|
*/
|
||||||
public function deleteSupply(Supply|string|array $supply): int
|
public function setGroup1c($group): mixed
|
||||||
{
|
{
|
||||||
// Инициализация
|
// Чтение группы
|
||||||
$amount = 0;
|
// if ($group = SupplyGroup::readByOcid($group->id)) {
|
||||||
|
// // Запись ребра: ПОСТАВКА => ГРУППА ПОСТАВОК
|
||||||
|
// return static::writeEdgeBetweenGroup(static::collectionName() . '/' . $this->_key, $group->collectionName() . '/' . $group->_key);
|
||||||
|
// }
|
||||||
|
|
||||||
if ($supply instanceof Supply) {
|
return true;
|
||||||
// Передана инстанция класса поставки или второй элемент массива не является числом
|
|
||||||
|
|
||||||
// Унификация входных данных
|
|
||||||
$supply = [$supply->catn => 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (is_array($supply) ? $supply : [$supply => 1] as $catn => $amount_raw) {
|
|
||||||
// Перебор товаров
|
|
||||||
|
|
||||||
if ($supply = Supply::searchByCatn($catn)) {
|
|
||||||
foreach (OrderEdgeSupply::searchByVertex($this->readId(), $supply->readId(), limit: $amount_raw) as $edge) {
|
|
||||||
// Перебор рёбер до продукта (если товаров в заказе несколько)
|
|
||||||
|
|
||||||
// Удаление
|
|
||||||
$edge->delete();
|
|
||||||
|
|
||||||
// Запись в журнал
|
|
||||||
$this->journal('delete', ['target' => $supply->readId()]);
|
|
||||||
|
|
||||||
// Постинкрементация счётчика удалённых рёбер
|
|
||||||
$amount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($amount === 0) {
|
|
||||||
// Отправка уведомления
|
|
||||||
self::notification('Неудачная попытка удалить товар из корзины');
|
|
||||||
} else if ($amount === 1) {
|
|
||||||
// Отправка уведомления
|
|
||||||
self::notification('Товар ' . $supply->catn . ' удалён из корзины');
|
|
||||||
} else {
|
|
||||||
// Отправка уведомления
|
|
||||||
self::notification('Удалено ' . $amount . ' товаров из корзины');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $amount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Поиск заказа
|
* Поиск через связь с аккаунтом
|
||||||
|
*
|
||||||
|
* @param string|null $id Идентификатор пользователя
|
||||||
|
* @param string|array|null $select Запрашиваемые значения
|
||||||
*/
|
*/
|
||||||
public static function search(Account $account = null, string $type = 'current', int $limit = 1, int $page = 1, string $select = null): self|array|null
|
public static function searchByAccount(string|null $id = null, string|array|null $select = null, int|null $limit = 10): array
|
||||||
{
|
{
|
||||||
// Инициализация
|
isset($id) ?: $id = yii::$app->user->id ?? throw new Exception('Не найден идентификатор');
|
||||||
$account or $account = yii::$app->user ?? throw new Exception('Не удалось инициализировать пользователя');
|
|
||||||
|
|
||||||
// Генерация сдвига по запрашиваемым данным (пагинация)
|
return self::searchByEdge(
|
||||||
$offset = $limit * ($page - 1);
|
|
||||||
|
|
||||||
if (strcasecmp($type, 'all') !== 0) {
|
|
||||||
// Если не указан параметр поиска всех заказов
|
|
||||||
|
|
||||||
$where_type = [
|
|
||||||
'account_edge_order.type' => $type
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$where_type = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$return = self::searchByEdge(
|
|
||||||
from: 'account',
|
from: 'account',
|
||||||
to: 'order',
|
to: 'supply',
|
||||||
subquery_where: [
|
subquery_where: [
|
||||||
[
|
[
|
||||||
'account._id' => $account->id
|
'account._id' => $id
|
||||||
|
]
|
||||||
],
|
],
|
||||||
$where_type
|
where: 'supply._id == account_edge_supply[0]._to AND supply.onec["ЗначенияСвойств"] != null',
|
||||||
],
|
|
||||||
foreach: ['edge' => 'account_edge_order'],
|
|
||||||
where: 'edge._to == order._id',
|
|
||||||
limit: $limit,
|
|
||||||
offset: $offset,
|
|
||||||
sort: ['DESC'],
|
|
||||||
select: $select,
|
select: $select,
|
||||||
direction: 'INBOUND'
|
limit: $limit
|
||||||
);
|
);
|
||||||
|
|
||||||
return $limit === 1 ? $return[0] ?? null : $return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Поиск содержимого заказа
|
* Запись данных свойств по UUID 1C
|
||||||
*
|
*
|
||||||
* @todo В будущем возможно заказ не только поставок реализовать
|
* Ищет записанные свойства из 1C по их идентификатору и добавляет к ним
|
||||||
|
* недостающие данные. Это костыль оставшийся от реляционных баз данных
|
||||||
|
*
|
||||||
|
* @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
|
||||||
*/
|
*/
|
||||||
public function content(int $limit = 1, int $page = 1): Supply|array|null
|
public static function createProperties1c($properties, Account|null $account = null): void
|
||||||
{
|
{
|
||||||
// Генерация сдвига по запрашиваемым данным (пагинация)
|
// Инициализация
|
||||||
$offset = $limit * ($page - 1);
|
$account ?? $account = yii::$app->user->identity;
|
||||||
|
$models = self::searchByAccount($account->readId());
|
||||||
|
$properties = self::xml2array($properties->xml);
|
||||||
|
|
||||||
// Поиск рёбер: ЗАКАЗ -> ПОСТАВКА
|
$account->on(ApiController::EVENT_AFTER_OFFER_SYNC, self::afterImport1c());
|
||||||
$supplies = Supply::searchByEdge(
|
|
||||||
from: 'order',
|
|
||||||
to: 'supply',
|
|
||||||
edge: 'order_edge_supply',
|
|
||||||
subquery_where: [
|
|
||||||
[
|
|
||||||
'order._id' => $this->readId()
|
|
||||||
]
|
|
||||||
],
|
|
||||||
foreach: ['edge' => 'order_edge_supply'],
|
|
||||||
where: 'edge._to == supply._id',
|
|
||||||
limit: $limit,
|
|
||||||
offset: $offset,
|
|
||||||
direction: 'INBOUND'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Инициализация реестра дубликатов
|
foreach ($models as $model) {
|
||||||
$registry = [];
|
// Перебор записей
|
||||||
|
|
||||||
// Подсчёт и перестройка массива для очистки от дубликатов
|
// Инициализация
|
||||||
foreach ($supplies as $key => &$supply) {
|
$changes = false;
|
||||||
// Перебор поставок
|
$transit = $model->onec;
|
||||||
|
|
||||||
if (in_array($supply->catn, $registry)) {
|
foreach ($model->onec['ЗначенияСвойств'] as $attribute_name => $attribute_value) {
|
||||||
// Если данная поставка найдена в реестре
|
// Перебор аттрибутов
|
||||||
|
|
||||||
// Удаление
|
foreach ($properties as $property) {
|
||||||
unset($supplies[$key]);
|
// Перебор свойств
|
||||||
|
|
||||||
// Пропуск итерации
|
if (is_array($attribute_value) && is_array($property) && $attribute_value['Ид'] === $property['Ид']) {
|
||||||
continue;
|
// Совпадение идентификаторов
|
||||||
|
|
||||||
|
// Объединение данных
|
||||||
|
$transit['ЗначенияСвойств'][$attribute_name] = array_merge($attribute_value, $property, $transit['ЗначенияСвойств'][$attribute_name]);
|
||||||
|
|
||||||
|
// Запись индикатора наличия изменений
|
||||||
|
$changes = true;
|
||||||
|
} else {
|
||||||
|
// Объединение данных
|
||||||
|
$transit['ЗначенияСвойств'][$attribute_name] = $property;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($changes) {
|
||||||
|
// Если указано, что записаны изменения
|
||||||
|
|
||||||
|
// Настройка ($transit нужен из-за особенностей __set())
|
||||||
|
$model->onec = $transit;
|
||||||
|
|
||||||
|
foreach ($model->onec['ЗначенияСвойств'] as $property) {
|
||||||
|
// Перебор всех свойств
|
||||||
|
|
||||||
|
if (is_array($property)) {
|
||||||
|
// if ($property['Ид'] === $account->opts['import_sections_oem']) {
|
||||||
|
// // Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера
|
||||||
|
|
||||||
|
// Настройка
|
||||||
|
$model->catn = $property['Значение'];
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запись
|
||||||
|
$model->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запись параметров из 1С
|
||||||
|
*/
|
||||||
|
public function setProperty1c($property): mixed
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запись изображений из 1С
|
||||||
|
*
|
||||||
|
* @todo Добавить параметры в админ-панель
|
||||||
|
* Запретить доступ к изображениям
|
||||||
|
*/
|
||||||
|
public function addImage1c($path, $caption): bool
|
||||||
|
{
|
||||||
|
// Инициализация
|
||||||
|
$i = 0;
|
||||||
|
|
||||||
|
if (!file_exists(YII_PATH_PUBLIC . $catalog = '/img/supplies/' . $this->_key)) {
|
||||||
|
// Директория для изображений продукта не найдена
|
||||||
|
|
||||||
|
if (!mkdir(YII_PATH_PUBLIC . $catalog, 0775, true)) {
|
||||||
|
// не удалось записать директорию
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->imgs ?? [] as $image) {
|
||||||
|
// Перебор имеющихся изображений
|
||||||
|
|
||||||
|
if ($path === $image['sorc']) {
|
||||||
|
// Изображение уже записано на сервер
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Инициализация
|
// Инициализация
|
||||||
$amount = 0;
|
$urn = basename($path);
|
||||||
|
|
||||||
// Повторный перебор для поиска дубликатов
|
// Запись
|
||||||
foreach ($supplies as &$supply4check) {
|
copy($path, $path_local = YII_PATH_PUBLIC . $catalog . '/' . $urn);
|
||||||
if ($supply == $supply4check) {
|
|
||||||
// Найден дубликат
|
|
||||||
|
|
||||||
// Постинкрементация счётчика
|
// Запись свойства
|
||||||
$amount++;
|
$this->imgs = array_merge(
|
||||||
|
$this->imgs ?? [],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'dscr' => $caption,
|
||||||
|
'path' => $path_local,
|
||||||
|
'sorc' => $path
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
// Запись в реестр
|
// Отправка в базу данных
|
||||||
$registry[] = $supply4check->catn;
|
return $this->update();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Запись количества для заказа
|
|
||||||
$supply->amnt = $amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Поиск стоимости для каждой поставки
|
|
||||||
foreach ($supplies as $key => &$supply) {
|
|
||||||
// Перебор поставок
|
|
||||||
|
|
||||||
// Чтение стоимости
|
|
||||||
$cost = $supply->readCost();
|
|
||||||
|
|
||||||
if ($cost < 1) {
|
|
||||||
// Если стоимость равна нулю (явная ошибка)
|
|
||||||
|
|
||||||
// Удаление из базы данных
|
|
||||||
$this->deleteSupply($supply->readId());
|
|
||||||
|
|
||||||
// Удаление из списка
|
|
||||||
unset($supplies[$key]);
|
|
||||||
|
|
||||||
// Пропуск итерации
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Запись цены
|
|
||||||
$supply->cost = $cost['ЦенаЗаЕдиницу'] . ' ' . $cost['Валюта'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $supplies;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Отправка уведомления
|
* Запись ребра (предложения от поставок к продуктам) из 1С
|
||||||
|
*
|
||||||
|
* @todo Разобраться зачем нужно возвращать SupplyEdgeProduct
|
||||||
|
* Вернуть создание карточек, но только по условиям (загрузка от админа, например)
|
||||||
*/
|
*/
|
||||||
public static function notification(string $text, string|null $type = Notification::TYPE_NOTICE): Notification|array|null
|
public function getOffer1c($offer): SupplyEdgeProduct
|
||||||
{
|
{
|
||||||
// Отправка
|
if (empty($this->catn)) {
|
||||||
return Notification::_write($text, type: $type);
|
// Не передан каталожный номер
|
||||||
|
|
||||||
|
// Разработчику библеотеки надо дать по жопе
|
||||||
|
return new SupplyEdgeProduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!yii::$app->user->isGuest
|
||||||
|
&& yii::$app->user->identity->agnt
|
||||||
|
&& (yii::$app->user->identity->type === 'administrator'
|
||||||
|
|| yii::$app->user->identity->type === 'moderator')
|
||||||
|
) {
|
||||||
|
// Пользователь аутентифицирован и авторизован
|
||||||
|
|
||||||
|
// Инициализация п̸̨͇͑͋͠р̷̬̂́̀̊о̸̜̯̹̅͒͘͝д̴̨̨̨̟̈́̆у̴̨̭̮̠́͋̈́к̴̭͊̋̎т̵̛̣͈̔̐͆а̵̨͖͑
|
||||||
|
$product = self::initEmpty($this->catn);
|
||||||
|
|
||||||
|
if (!is_array($product)) {
|
||||||
|
// Создался только один товар и вернулся в виде модели
|
||||||
|
|
||||||
|
$product = [$product];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($this->oemn)) {
|
||||||
|
// Значение OEM было инициализировано
|
||||||
|
|
||||||
|
foreach ($this->oemn as $oem) {
|
||||||
|
// Перебор артикулов из массива ОЕМ-номеров
|
||||||
|
|
||||||
|
// Инициализация и запись
|
||||||
|
$product[] = self::initEmpty($oem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($product as $product) {
|
||||||
|
// Перебор всех инициализированных продуктов
|
||||||
|
|
||||||
|
if ($this->catn !== $product->catn) {
|
||||||
|
// Каталожные номера не соответствуют друг другу
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Код ниже скорее всего устарел
|
||||||
|
|
||||||
|
if (SupplyEdgeProduct::searchByVertex($this->readId(), $product->readId())) {
|
||||||
|
// Ребро уже существует
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запись ребра: ПОСТАВКА -> ПРОДУКТ
|
||||||
|
$return = (new SupplyEdgeProduct)->write(
|
||||||
|
$this->readId(),
|
||||||
|
$product->readId(),
|
||||||
|
'connect',
|
||||||
|
[
|
||||||
|
'onec' => self::xml2array($offer->xml)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает последнее сохранённое ребро
|
||||||
|
// Надо будет с этим разобраться
|
||||||
|
return $return ?? new SupplyEdgeProduct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запись продукта из 1С (поставка)
|
||||||
|
*
|
||||||
|
* @see Supply
|
||||||
|
*
|
||||||
|
* @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
|
||||||
|
* Разобраться и создать возможность загрузки от лица другого аккаунта
|
||||||
|
*/
|
||||||
|
public static function createModel1c($product): ?self
|
||||||
|
{
|
||||||
|
// Инициализация
|
||||||
|
$model = self::searchByOcid($id = (string) $product->Ид) ?? new self;
|
||||||
|
$account ?? $account = yii::$app->user->identity;
|
||||||
|
|
||||||
|
// Настройка
|
||||||
|
$model->ocid = $id ?? null;
|
||||||
|
$model->catn = (string) $product->Артикул;
|
||||||
|
$model->dscr = (string) $product->Описание;
|
||||||
|
$model->onec = self::xml2array($product->xml);
|
||||||
|
|
||||||
|
if (isset($model->onec['ЗначенияСвойств'])) {
|
||||||
|
// Свойства инициализированы
|
||||||
|
|
||||||
|
foreach ($model->onec['ЗначенияСвойств'] as $property) {
|
||||||
|
// Перебор всех свойств
|
||||||
|
|
||||||
|
if (is_array($property)) {
|
||||||
|
if (!empty($account->opts['import_sections_oem']) && $property['Ид'] === $account->opts['import_sections_oem']) {
|
||||||
|
// Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера
|
||||||
|
|
||||||
|
// Настройка
|
||||||
|
$model->oemn = array_merge(self::searchOemn($property['Значение']), self::searchOemn((string) $product->Артикул));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запись
|
||||||
|
if ($model->save()) {
|
||||||
|
// Поставка успешно сохранена
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed|null $context
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getExportFields1c($context = null) {
|
||||||
|
return $this->onec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация продукта
|
||||||
|
*
|
||||||
|
* @param string $catn Артикул, каталожный номер
|
||||||
|
*/
|
||||||
|
public static function initEmpty(string $catn): Product|array
|
||||||
|
{
|
||||||
|
$oemn = self::searchOemn($catn);
|
||||||
|
|
||||||
|
if (count($oemn) === 1) {
|
||||||
|
// Передан только один артикул
|
||||||
|
|
||||||
|
if ($model = Product::searchByCatn($catn)) {
|
||||||
|
// Продукт уже существует
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запись пустого продукта
|
||||||
|
return Product::writeEmpty($catn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация
|
||||||
|
$models = [];
|
||||||
|
|
||||||
|
foreach ($oemn as $catn) {
|
||||||
|
// Перебор всех найденных артикулов
|
||||||
|
|
||||||
|
if ($model = Product::searchByCatn($catn)) {
|
||||||
|
// Продукт уже существует
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запись
|
||||||
|
if ($model = Product::writeEmpty($catn)) {
|
||||||
|
// Записано
|
||||||
|
|
||||||
|
// Запись в массив сохранённых моделей
|
||||||
|
$models[] = $model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $models;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поиск OEM номеров
|
||||||
|
*
|
||||||
|
* @param string $oemn Необработанная строка с OEM-номерами
|
||||||
|
* @param string $delimiters Разделители
|
||||||
|
*
|
||||||
|
* @todo НЕ ЗАБЫТЬ СДЕЛАТЬ НАСТРОЙКУ РАЗДЕЛИТЕЛЕЙ
|
||||||
|
*
|
||||||
|
* @return array OEM-номера
|
||||||
|
*/
|
||||||
|
public static function searchOemn(string $oemn, string $delimiters = '\s\+\/,'): array
|
||||||
|
{
|
||||||
|
// Инициализация
|
||||||
|
$catn = [];
|
||||||
|
|
||||||
|
// Конвертация
|
||||||
|
preg_match_all("/[^$delimiters]+/", $oemn, $catn);
|
||||||
|
|
||||||
|
return $catn[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запись цены из 1С
|
||||||
|
*/
|
||||||
|
public function setPrice1c($price)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $types
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function createPriceTypes1c($types) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* offers.xml > ПакетПредложений > Предложения > Предложение > ХарактеристикиТовара > ХарактеристикаТовара
|
||||||
|
*
|
||||||
|
* Характеристики товара
|
||||||
|
* $name - Наименование
|
||||||
|
* $value - Значение
|
||||||
|
*
|
||||||
|
* @param \Zenwalker\CommerceML\Model\Simple $specification
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSpecification1c($specification) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запись данных на случай ошибки при экспорте из 1С
|
||||||
|
*/
|
||||||
|
public function setRaw1cData($cml, $object): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поиск по идентификатору из 1С
|
||||||
|
*
|
||||||
|
* @param string $ocid Идентификатор из 1С
|
||||||
|
*
|
||||||
|
* @return Supply|null
|
||||||
|
*/
|
||||||
|
public static function searchByOcid(string $ocid): ?Supply
|
||||||
|
{
|
||||||
|
return static::findOne([static::getIdFieldName1c() => $ocid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Чтение группы из 1С
|
||||||
|
*/
|
||||||
|
public function getGroup1c(): ?SupplyGroup
|
||||||
|
{
|
||||||
|
return new SupplyGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Чтение названия поля в котором хранится идентификатор из 1С
|
||||||
|
*/
|
||||||
|
public static function getIdFieldName1c(): string
|
||||||
|
{
|
||||||
|
return 'ocid';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поиск по OEM-номерам
|
||||||
|
*
|
||||||
|
* @todo Реализовать с помощью LIKE
|
||||||
|
*/
|
||||||
|
public static function searchByOemn(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Прочитать стоимость
|
||||||
|
*/
|
||||||
|
public function readCost(Product $product = null): array
|
||||||
|
{
|
||||||
|
if (isset($product)) {
|
||||||
|
return SupplyEdgeProduct::searchByVertex($this->readId(), $product->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return SupplyEdgeProduct::search($this->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,10 @@ use yii\web\UploadedFile;
|
||||||
use yii\imagine\Image;
|
use yii\imagine\Image;
|
||||||
|
|
||||||
use app\models\traits\SearchByEdge;
|
use app\models\traits\SearchByEdge;
|
||||||
use Exception;
|
|
||||||
use moonland\phpexcel\Excel;
|
use moonland\phpexcel\Excel;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Продукт (в ассортименте магазина)
|
* Продукт (в ассортименте магазина)
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace app\models;
|
|
||||||
|
|
||||||
use carono\exchange1c\interfaces\DocumentInterface;
|
|
||||||
|
|
||||||
class Purchase extends Document implements DocumentInterface
|
|
||||||
{
|
|
||||||
public static function collectionName(): string
|
|
||||||
{
|
|
||||||
return 'purchase';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DocumentInterface[]
|
|
||||||
*/
|
|
||||||
public static function findDocuments1c(): ?array
|
|
||||||
{
|
|
||||||
return self::find()->andWhere(['status_id' => 2])->all();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return OfferInterface[]
|
|
||||||
*/
|
|
||||||
public function getOffers1c(): mixed
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRequisites1c(): mixed
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Получаем контрагента у документа
|
|
||||||
*
|
|
||||||
* @return PartnerInterface
|
|
||||||
*/
|
|
||||||
public function getPartner1c(): Account
|
|
||||||
{
|
|
||||||
// !!!!!!!!!!!!!!!!!!!
|
|
||||||
return $this->user ?? new Account;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getExportFields1c($context = null)
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Возвращаем имя поля в базе данных, в котором хранится ID из 1с
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function getIdFieldName1c()
|
|
||||||
{
|
|
||||||
return 'onec["Ид"]';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setRaw1cData($cml, $object): void
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace app\models;
|
|
||||||
|
|
||||||
class PurchaseEdgeSupply extends Edge
|
|
||||||
{
|
|
||||||
public static function collectionName(): string
|
|
||||||
{
|
|
||||||
return 'purchase_edge_supply';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,13 +5,13 @@ declare(strict_types=1);
|
||||||
namespace app\models;
|
namespace app\models;
|
||||||
|
|
||||||
use yii;
|
use yii;
|
||||||
use yii\web\User;
|
|
||||||
|
|
||||||
use app\models\Account;
|
use app\models\Account;
|
||||||
use app\models\Product;
|
use app\models\Product;
|
||||||
use app\models\SupplyEdgeProduct;
|
use app\models\SupplyEdgeProduct;
|
||||||
use app\models\traits\Xml2Array;
|
use app\models\traits\Xml2Array;
|
||||||
|
|
||||||
|
use carono\exchange1c\interfaces\OfferInterface;
|
||||||
use carono\exchange1c\interfaces\ProductInterface;
|
use carono\exchange1c\interfaces\ProductInterface;
|
||||||
use carono\exchange1c\controllers\ApiController;
|
use carono\exchange1c\controllers\ApiController;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ use exception;
|
||||||
*
|
*
|
||||||
* @see Product Продукт (туда добавляются поставки)
|
* @see Product Продукт (туда добавляются поставки)
|
||||||
*/
|
*/
|
||||||
class Supply extends Product implements ProductInterface
|
class Supply extends Product implements ProductInterface, OfferInterface
|
||||||
{
|
{
|
||||||
use Xml2Array;
|
use Xml2Array;
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ class Supply extends Product implements ProductInterface
|
||||||
[
|
[
|
||||||
'cost',
|
'cost',
|
||||||
'onec',
|
'onec',
|
||||||
|
'oemn',
|
||||||
'ocid'
|
'ocid'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -69,6 +70,7 @@ class Supply extends Product implements ProductInterface
|
||||||
[
|
[
|
||||||
'cost' => 'Стоимость (cost)',
|
'cost' => 'Стоимость (cost)',
|
||||||
'onec' => 'Данные 1С',
|
'onec' => 'Данные 1С',
|
||||||
|
'oemn' => 'OEM-номера',
|
||||||
'ocid' => 'Идентификатор 1C (ocid)'
|
'ocid' => 'Идентификатор 1C (ocid)'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -410,12 +412,45 @@ class Supply extends Product implements ProductInterface
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $types
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function createPriceTypes1c($types) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* offers.xml > ПакетПредложений > Предложения > Предложение > ХарактеристикиТовара > ХарактеристикаТовара
|
||||||
|
*
|
||||||
|
* Характеристики товара
|
||||||
|
* $name - Наименование
|
||||||
|
* $value - Значение
|
||||||
|
*
|
||||||
|
* @param \Zenwalker\CommerceML\Model\Simple $specification
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSpecification1c($specification) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed|null $context
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getExportFields1c($context = null) {
|
||||||
|
return [
|
||||||
|
'Ид' => 'ocid',
|
||||||
|
'Наименование' => 'catn'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Инициализация продукта
|
* Инициализация продукта
|
||||||
*
|
*
|
||||||
* @param string $catn Артикул, каталожный номер
|
* @param string $catn Артикул, каталожный номер
|
||||||
*/
|
*/
|
||||||
public static function initEmpty(string $catn): self|array
|
public static function initEmpty(string $catn): Product|array
|
||||||
{
|
{
|
||||||
$oemn = self::searchOemn($catn);
|
$oemn = self::searchOemn($catn);
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,11 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace app\models;
|
namespace app\models;
|
||||||
|
|
||||||
|
use carono\exchange1c\interfaces\GroupInterface;
|
||||||
/**
|
/**
|
||||||
* Группировка поставок
|
* Группировка поставок
|
||||||
*/
|
*/
|
||||||
class SupplyGroup extends ProductGroup
|
class SupplyGroup extends ProductGroup implements GroupInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Имя коллекции
|
* Имя коллекции
|
||||||
|
@ -16,4 +17,9 @@ class SupplyGroup extends ProductGroup
|
||||||
{
|
{
|
||||||
return 'supply_group';
|
return 'supply_group';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function createTree1c($groups): Document|null {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,13 @@ trait SearchByEdge
|
||||||
string $from,
|
string $from,
|
||||||
string $to,
|
string $to,
|
||||||
string|null $edge = null,
|
string|null $edge = null,
|
||||||
|
string $direction = 'ANY',
|
||||||
int|null $limit = 10,
|
int|null $limit = 10,
|
||||||
int|null $offset = 0,
|
int|null $offset = 0,
|
||||||
array $sort = ['ASC'],
|
array $sort = ['ASC'],
|
||||||
string|array $subquery_where = [],
|
string|array $subquery_where = [],
|
||||||
array $foreach = [],
|
array $foreach = [],
|
||||||
string|array $where = [],
|
string|array $where = [],
|
||||||
string $direction = 'ANY',
|
|
||||||
array|null $let = [],
|
array|null $let = [],
|
||||||
string|array $select = null,
|
string|array $select = null,
|
||||||
callable|null $handle = null,
|
callable|null $handle = null,
|
||||||
|
@ -41,7 +41,8 @@ trait SearchByEdge
|
||||||
->in($edge ?? $from . '_edge_' . $to)
|
->in($edge ?? $from . '_edge_' . $to)
|
||||||
->where($subquery_where);
|
->where($subquery_where);
|
||||||
|
|
||||||
$subquery = $subquery->select($edge ?? $from . '_edge_' . $to)
|
$subquery = $subquery
|
||||||
|
->select($edge ?? $from . '_edge_' . $to)
|
||||||
->createCommand();
|
->createCommand();
|
||||||
|
|
||||||
$query = static::find()
|
$query = static::find()
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
$supply->catn
|
$supply->catn
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
$supply->desc
|
$supply->dscr
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1 ml-auto">
|
<div class="col-1 ml-auto">
|
||||||
<input id="cart_list_amnt_$supply->catn" class="form-control text-center" type="text" value="$supply->amnt" onchange="return cart_list_amount_update('$supply->catn', this)" aria-invalid="false">
|
<input id="cart_list_amnt_$supply->catn" class="form-control text-center" type="text" value="$supply->amnt" onchange="return cart_list_amount_update('$supply->catn', this)" aria-invalid="false">
|
||||||
|
|
Reference in New Issue