use yii_app\records\Products1cNomenclature;
use yii_app\records\Products1cPropType;
use yii_app\records\Products1c;
+use yii_app\records\Products1cClassDynamic;
use yii_app\records\Products1cAdditionalCharacteristics;
use yii_app\records\ReplacementInvoice;
use yii_app\records\ReplacementInvoiceProducts;
foreach ($marketplaceOrders as $marketplaceOrder) {
/* @var MarketplaceOrders $marketplaceOrder */
$items = Json::decode(Json::encode($marketplaceOrder->items));
- $itemsToComment = [];
$itemsFiltered = [];
$summ = 0;
- foreach ($items as $item) {
- $itemsToComment[] = $item['offer_id'] . ' - ' . $item['offer_name'];
- $product1c = Products1c::find()->where(['articule' => $item['offer_id']])->one();
- if (!empty($product1c->id)) {
- $itemsFiltered []= [
- 'product_id' => $product1c->id ?? '',
- 'color' => '',
- 'price' => $item['price'],
- 'quantity' => $item['count'],
- 'seller_id' => '',
- ];
- }
- $summ += $item['price'] * $item['count'];
+
+ $productsItemsArticule = $this->getProductsItemsArticule($items);
+ $itemsToComment = $productsItemsArticule['itemsToComment'];
+
+ foreach ($productsItemsArticule['isProductWithGuid'] as $item) {
+ $itemsFiltered []= [
+ 'product_id' => $item['product_id'] ?? '',
+ 'color' => '',
+ 'price' => $item['price'],
+ 'quantity' => $item['quantity'],
+ 'seller_id' => '',
+ ];
+
+ $summ += $item['price'] * $item['quantity'];
}
if (!empty($itemsFiltered)) {
$eit = ExportImportTable::find()->where(['entity' => 'city_store', 'export_id' => 1, 'entity_id' => $marketplaceOrder->store_id])->one();
$itemsWithoutSku = array_map(
fn(array $p) => "{$p['product_id']} x{$p['quantity']}",
- $marketplaceOrder->getProductsWithoutArticule()
+ $productsItemsArticule['isProductWithoutGuid']
);
if (!empty($itemsWithoutSku)) {
$itemsWithoutSkuString = ', Товары без артикула: ' . implode(', ',$itemsWithoutSku);
return $result;
}
+ /**
+ * Получение товаров с проверкой наличия артикулов в базе
+ * @param array $items Массив товаров из заказа маркетплейса
+ * @return array
+ */
+ private function getProductsItemsArticule($items): array
+ {
+ $products = $items;
+ $isProductWithGuid = [];
+ $isProductWithoutGuid = [];
+ $itemsToComment = [];
+
+ foreach ($products as $product) {
+ $itemsToComment[] = $product['offer_id'] . ' - ' . $product['offer_name'];
+
+ /** @var Products1c $productRow */
+ $productRow = Products1c::find()
+ ->where(['articule' => $product['offer_id']])
+ ->one();
+
+ if (!empty($productRow->id)) {
+ $isProductWithGuid[$product['offer_id']] = [
+ 'product_id' => $productRow->id ?? '',
+ 'color' => '',
+ 'price' => $product['price'],
+ 'quantity' => $product['count'],
+ 'seller_id' => '',
+ ];
+ } else {
+ $product1cClassDynamic = Products1cClassDynamic::find()
+ ->where(['articule' => $product['offer_id']])
+ ->andWhere(['active' => Products1cClassDynamic::ACTIVE])
+ ->one();
+
+ if (!empty($product1cClassDynamic->id)) {
+ $isProductWithGuid[$product['offer_id']] = [
+ 'product_id' => $product1cClassDynamic->id ?? '',
+ 'color' => '',
+ 'price' => $product['price'],
+ 'quantity' => $product['count'],
+ 'seller_id' => '',
+ ];
+ } else {
+ $isProductWithoutGuid[] = [
+ 'product_id' => $product['offer_name'] ?? null,
+ 'quantity' => $product['count'] ?? null,
+ ];
+ }
+ }
+ }
+
+ return [
+ 'isProductWithoutGuid' => $isProductWithoutGuid,
+ 'isProductWithGuid' => $isProductWithGuid,
+ 'itemsToComment' => $itemsToComment,
+ ];
+ }
+
private function getCancelledMarketplaceOrders(): array {
$canceledStatusId = MarketplaceOrderStatusTypes::find()
--- /dev/null
+<?php
+
+use yii\db\Migration;
+
+/**
+ * Добавление полей marketplace_order_id и marketplace_name в таблицу sales
+ *
+ * Эти поля необходимы для связи чеков продаж с заказами маркетплейсов (ФлауВау, ЯндексМаркет)
+ * и решения проблемы с orphaned записями в sales_products
+ */
+class m260212_120000_add_marketplace_fields_to_sales extends Migration
+{
+ const TABLE_NAME = 'erp24.sales';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function safeUp()
+ {
+ $table = $this->db->schema->getTableSchema(self::TABLE_NAME);
+ if ($table === null) {
+ return;
+ }
+
+ // Добавляем поле marketplace_order_id (GUID заказа маркетплейса)
+ if (!$this->db->schema->getTableSchema(self::TABLE_NAME, true)->getColumn('marketplace_order_id')) {
+ $this->addColumn(
+ self::TABLE_NAME,
+ 'marketplace_order_id',
+ $this->string(64)->null()->comment('GUID заказа маркетплейса из таблицы marketplace_orders')
+ );
+ }
+
+ // Добавляем поле marketplace_name (название маркетплейса)
+ if (!$this->db->schema->getTableSchema(self::TABLE_NAME, true)->getColumn('marketplace_name')) {
+ $this->addColumn(
+ self::TABLE_NAME,
+ 'marketplace_name',
+ $this->string(50)->null()->comment('Название маркетплейса (ФлауВау, ЯндексМаркет)')
+ );
+ }
+
+ // Добавляем индекс для быстрого поиска по marketplace_order_id
+ $this->createIndex(
+ 'idx-sales-marketplace_order_id',
+ self::TABLE_NAME,
+ 'marketplace_order_id'
+ );
+
+ // Добавляем составной индекс для аналитики по маркетплейсам
+ $this->createIndex(
+ 'idx-sales-marketplace_name_date',
+ self::TABLE_NAME,
+ ['marketplace_name', 'date']
+ );
+
+ echo " > Поля marketplace_order_id и marketplace_name успешно добавлены в таблицу erp24.sales\n";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function safeDown()
+ {
+ // Удаляем индексы
+ $this->dropIndex('idx-sales-marketplace_name_date', self::TABLE_NAME);
+ $this->dropIndex('idx-sales-marketplace_order_id', self::TABLE_NAME);
+
+ // Удаляем поля
+ if ($this->db->schema->getTableSchema(self::TABLE_NAME, true)->getColumn('marketplace_name')) {
+ $this->dropColumn(self::TABLE_NAME, 'marketplace_name');
+ }
+
+ if ($this->db->schema->getTableSchema(self::TABLE_NAME, true)->getColumn('marketplace_order_id')) {
+ $this->dropColumn(self::TABLE_NAME, 'marketplace_order_id');
+ }
+
+ echo " > Поля marketplace_order_id и marketplace_name удалены из таблицы erp24.sales\n";
+ }
+}
--- /dev/null
+<?php
+
+use yii\db\Migration;
+
+/**
+ * Изменение полей payments и pay_arr в таблице sales на nullable
+ *
+ * Проблема: MarketplaceService создает чеки с пустыми payments и pay_arr,
+ * что приводит к ошибкам валидации при сохранении в Sales.
+ * Решение: разрешить NULL значения для этих полей.
+ */
+class m260212_130000_alter_payments_pay_arr_nullable_in_sales extends Migration
+{
+ const TABLE_NAME = 'erp24.sales';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function safeUp()
+ {
+ $table = $this->db->schema->getTableSchema(self::TABLE_NAME);
+ if ($table === null) {
+ return;
+ }
+
+ // Изменяем payments на nullable
+ $this->alterColumn(
+ self::TABLE_NAME,
+ 'payments',
+ $this->text()->null()->comment('JSON массив с информацией о платежах')
+ );
+
+ // Изменяем pay_arr на nullable
+ $this->alterColumn(
+ self::TABLE_NAME,
+ 'pay_arr',
+ $this->string(15)->null()->comment('ID типов платежей, разделенные запятыми')
+ );
+
+ echo " > Поля payments и pay_arr успешно изменены на nullable в таблице erp24.sales\n";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function safeDown()
+ {
+ // Перед откатом нужно заполнить NULL значения пустыми строками
+ $this->update(self::TABLE_NAME, ['payments' => ''], ['payments' => null]);
+ $this->update(self::TABLE_NAME, ['pay_arr' => ''], ['pay_arr' => null]);
+
+ // Возвращаем NOT NULL ограничения
+ $this->alterColumn(
+ self::TABLE_NAME,
+ 'payments',
+ $this->text()->notNull()->comment('JSON массив с информацией о платежах')
+ );
+
+ $this->alterColumn(
+ self::TABLE_NAME,
+ 'pay_arr',
+ $this->string(15)->notNull()->comment('ID типов платежей, разделенные запятыми')
+ );
+
+ echo " > Поля payments и pay_arr возвращены к NOT NULL в таблице erp24.sales\n";
+ }
+}
return 'marketplace_orders';
}
+ /**
+ * {@inheritdoc}
+ */
+ public static function primaryKey()
+ {
+ return ['id'];
+ }
+
/**
* {@inheritdoc}
*/
return $result;
}
+ public function getProductsItemsArticule($items):array
+ {
+ $products = $items;
+ $isProductWithGuid = [];
+ $isProductWithoutGuid = [];
+ $itemsToComment = [];
+
+ foreach ($products as $product) {
+ $itemsToComment[] = $product['offer_id'] . ' - ' . $product['offer_name'];
+
+ /** @var Products1c $productRow */
+ $productRow = Products1c::find()
+ ->where(['articule' => $product['offer_id']])
+ ->one();
+ if (!empty($productRow->id)) {
+ $isProductWithGuid[$product['offer_id']] = [
+ 'product_id' => $productRow->id ?? '',
+ 'color' => '',
+ 'price' => $product['price'],
+ 'quantity' => $product['count'],
+ 'seller_id' => '',
+ ];
+
+ } else {
+ $product1cClassDynamic = Products1cClassDynamic::find()
+ ->where(['articule' => $product['offer_id']])
+ ->andWhere(['active' => Products1cClassDynamic::ACTIVE])
+ ->one();
+ if (!empty($product1cClassDynamic->id)) {
+ $isProductWithGuid[$product['offer_id']]= [
+ 'product_id' => $product1cClassDynamic->id ?? '',
+ 'color' => '',
+ 'price' => $product['price'],
+ 'quantity' => $product['count'],
+ 'seller_id' => '',
+ ];
+ } else {
+ $isProductWithoutGuid[] = [
+ 'product_id' => $product['offer_name'] ?? null,
+ 'quantity' => $product['count'] ?? null,
+ ];
+ }
+ }
+ }
+
+ return [
+ 'isProductWithoutGuid' => $isProductWithoutGuid,
+ 'isProductWithGuid' => $isProductWithGuid,
+ 'itemsToComment' => $itemsToComment,
+ ];
+ }
+
public static function buildStatusesToCode(): array
{
$statuses = MarketplaceOrder1cStatuses::find()
* @property string|null $delivery_date
* @property int|null $pickup
* @property int $update_source
+ * @property string|null $marketplace_order_id GUID заказа маркетплейса из таблицы marketplace_orders
+ * @property string|null $marketplace_name Название маркетплейса (ФлауВау, ЯндексМаркет)
*/
class Sales extends \yii\db\ActiveRecord
{
public function rules()
{
return [
- [['id', 'date', 'operation', 'status', 'summ', 'skidka', 'number', 'admin_id', 'seller_id', 'store_id_1c', 'payments', 'pay_arr' /*, 'terminal_id', 'kkm_id', 'held' */], 'required'],
- [['date', 'date_up', 'order_id', 'pickup', 'terminal_id'], 'safe'],
+ [['id', 'date', 'operation', 'status', 'summ', 'skidka', 'number', 'admin_id', 'seller_id', 'store_id_1c' /*, 'terminal_id', 'kkm_id', 'held' */], 'required'],
+ [['date', 'date_up', 'order_id', 'pickup', 'terminal_id', 'payments', 'marketplace_order_id', 'marketplace_name'], 'safe'],
[['summ', 'purchase_sum', 'skidka'], 'number'],
[['update_source'], 'integer'],
[['admin_id', 'store_id', 'phone', 'status_check', 'held', 'matrix'], 'integer'],
[['number'], 'string', 'max' => 225],
[['pay_arr'], 'string', 'max' => 15],
[['terminal'], 'string', 'max' => 255],
+ [['marketplace_order_id'], 'string', 'max' => 64],
+ [['marketplace_name'], 'string', 'max' => 50],
[['date', 'operation', 'store_id_1c', 'id'], 'unique', 'targetAttribute' => ['date', 'operation', 'store_id_1c', 'id']],
[['id'], 'unique'],
];
'held' => 'Held',
'matrix' => 'Matrix',
'date_up' => 'Date Up',
+ 'marketplace_order_id' => 'Marketplace Order ID',
+ 'marketplace_name' => 'Marketplace Name',
];
}
}
$createChecks->items = Json::encode($items);
- $createChecks->payments = '';
+ $createChecks->payments = '[]';
$createChecks->held = 1;
$createChecks->kkm_id = $kkm_id;
$createChecks->comments = '';
$sales2->skidka = $arr["discount"] ?? '0';
$sales2->delivery_date = date('Y-m-d', strtotime($arr['delivery_date'] ?? ''));
$sales2->pickup = $arr['pickup'] ?? false;
+ $sales2->marketplace_order_id = $arr["marketplace_order_id"] ?? null;
+ $sales2->marketplace_name = $arr["marketplace_name"] ?? null;
$sales2->save();
+ // ✅ Проверка результата
if ($sales2->getErrors()) {
- LogService::apiErrorLog(json_encode(["error_id" => 21, "error" => $sales2->getErrors()], JSON_UNESCAPED_UNICODE));
+ LogService::apiErrorLog([
+ "error_id" => 21,
+ "error" => $sales2->getErrors(),
+ "check_id" => $arr["id"]
+ ]);
+ continue; // ← ПРОПУСТИТЬ создание товаров!
}
// Обновляем данные в очереди на создание чеков
$pay_arr[] = 2;
}
}
+ } else {
+ //Маркет плейс
+ $pay_arr[] = 4;
}
return $pay_arr;