Commit 39a6af69 by Vladislav

#19465 Реализация синхронизации данных по РК.

1 parent dfa9d5fc
......@@ -149,7 +149,7 @@ class TokensController extends Controller
return Redirect::route('tokens')->with('success', 'Token added.');
}
public function storeCity(Tokens $token, Dictionary $city)
public function storeCity(Tokens $token, Dictionary $dictionary)
{
if ($token->isMain()) {
return Redirect::back();
......@@ -164,10 +164,10 @@ class TokensController extends Controller
DB::beginTransaction();
try {
$token->cities()->save($city);
$token->cities()->save($dictionary);
if ($token_main->campaignsForManaged->count()) {
$city->campaigns()->syncWithoutDetaching(
$dictionary->campaigns()->syncWithoutDetaching(
$token_main->campaignsForManaged->keyBy($token_main->campaignsForManaged->first()->getKeyName())->transform(function (Campaigns $campaign) {
return DictionaryCampaign::copyPropertyInCampaign($campaign);
})->all()
......@@ -185,52 +185,52 @@ class TokensController extends Controller
}
}
public function updatedCity(Tokens $token, Dictionary $city)
public function updatedCityCampaign(Tokens $token, Dictionary $dictionary, $campaign_id)
{
if ($token->isMain()) {
return Redirect::back();
}
$city = $token->cities()->find($city->getKey());
$campaign = $dictionary->campaigns()->find($campaign_id);
if (!$city) {
if (!$campaign) {
return Redirect::back();
}
$city->update([
'update' => !!request('updated'),
$campaign->pivot->update([
'updated' => !!request('updated'),
]);
return Redirect::route('token.edit', $token->getKey())->with('success', 'City updated.');
return Redirect::route('token.edit', $token->getKey())->with('success', 'Campaign updated.');
}
public function updatedCityCampaign(Tokens $token, Dictionary $city, $campaign_id)
public function syncedCityCampaign(Tokens $token, Dictionary $dictionary, $campaign_id)
{
if ($token->isMain()) {
return Redirect::back();
}
$campaign = $city->campaigns()->find($campaign_id);
$campaign = $dictionary->campaigns()->find($campaign_id);
if (!$campaign) {
return Redirect::back();
}
$campaign->pivot->update([
'updated' => !!request('updated'),
'synced' => !!request('synced'),
]);
return Redirect::route('token.edit', $token->getKey())->with('success', 'Campaign updated.');
return Redirect::route('token.edit', $token->getKey())->with('success', 'City synced.');
}
public function destroyCity(Tokens $token, Dictionary $city)
public function destroyCity(Tokens $token, Dictionary $dictionary)
{
if ($token->isMain()) {
return Redirect::back();
}
$city->token()->dissociate();
$city->save();
$dictionary->token()->dissociate();
$dictionary->save();
return Redirect::route('token.edit', $token->getKey())->with('success', 'City deleted.');
}
......
......@@ -15,7 +15,6 @@ use Illuminate\Database\Eloquent\Builder;
* @property string $name
* @property string $type
* @property int|null $token_id
* @property bool $update
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Campaigns[] $campaigns
......@@ -34,7 +33,6 @@ use Illuminate\Database\Eloquent\Builder;
* @method static Builder|Dictionary whereRegionId($value)
* @method static Builder|Dictionary whereTokenId($value)
* @method static Builder|Dictionary whereType($value)
* @method static Builder|Dictionary whereUpdate($value)
* @method static Builder|Dictionary whereUpdatedAt($value)
* @mixin \Eloquent
*/
......@@ -42,14 +40,6 @@ class Dictionary extends Model
{
CONST CITY = 'City';
protected $fillable = [
'update',
];
protected $casts = [
'update' => 'boolean',
];
public function scopeDefaultOrderBy(Builder $query)
{
return $query->orderBy('name');
......
......@@ -59,8 +59,9 @@ class DictionaryCampaign extends Pivot
'name',
'negative_keywords',
'excluded_sites',
'updated',
'external_upload_at',
'updated',
'synced',
];
protected $casts = [
......@@ -69,18 +70,28 @@ class DictionaryCampaign extends Pivot
'dictionary_id' => 'int',
'negative_keywords' => 'array',
'excluded_sites' => 'array',
'updated' => 'boolean',
'external_upload_at' => 'datetime',
'updated' => 'boolean',
'synced' => 'boolean',
];
static public function getExcludeCopyWithPivot()
{
return [
'external_upload_at',
'updated',
'synced',
];
}
static public function getWithPivot()
{
return [
'name',
'external_upload_at',
'negative_keywords',
'excluded_sites',
'external_upload_at',
'updated',
'synced',
];
}
......@@ -88,7 +99,7 @@ class DictionaryCampaign extends Pivot
{
return collect(self::getWithPivot())
->filter(function ($property_name){
return $property_name !== 'updated' && $property_name !== 'external_upload_at';
return !in_array($property_name, self::getExcludeCopyWithPivot());
})
->transform(function ($property_name) use ($campaign) {
return [
......
......@@ -29,7 +29,6 @@ class AddCampaigns extends DirectRequest
public function handle($response)
{
try {
dd($response);
foreach ($response['result']['AddResults'] as $key => $add_result) {
$id = $add_result['Id'] ?? '';
......@@ -83,7 +82,6 @@ class AddCampaigns extends DirectRequest
$data = [
'Name' => StrReplaceByVariables::getInstance($dictionaryCampaign->name, $variable_list)->get(),
'StartDate' => Carbon::now()->format('Y-m-d'),
'DailyBudget' => $dictionaryCampaign->campaign->daily_budget,
'TextCampaign' => [
'BiddingStrategy' => [
'Search' => [
......@@ -93,7 +91,6 @@ class AddCampaigns extends DirectRequest
'BiddingStrategyType' => $dictionaryCampaign->campaign->text_campaign_strategy_network,
],
],
'Settings' => $dictionaryCampaign->campaign->settings,
'RelevantKeywords' => [
'BudgetPercent' => $dictionaryCampaign->campaign->relevant_keywords_setting_budget_percent,
'OptimizeGoalId' => $dictionaryCampaign->campaign->relevant_keywords_setting_optimize_goal_id,
......@@ -103,7 +100,11 @@ class AddCampaigns extends DirectRequest
],
];
if (count($dictionaryCampaign->negative_keywords)) {
if ($dictionaryCampaign->campaign->daily_budget && count($dictionaryCampaign->campaign->daily_budget)) {
$data['DailyBudget'] = $dictionaryCampaign->campaign->daily_budget;
}
if ($dictionaryCampaign->negative_keywords && count($dictionaryCampaign->negative_keywords)) {
$data['NegativeKeywords'] = [
'Items' => array_map(function ($value) use ($variable_list) {
return StrReplaceByVariables::getInstance($value, $variable_list)->get();
......@@ -111,13 +112,13 @@ class AddCampaigns extends DirectRequest
];
}
if (count($dictionaryCampaign->campaign->blocked_ips)) {
if ($dictionaryCampaign->campaign->blocked_ips && count($dictionaryCampaign->campaign->blocked_ips)) {
$data['BlockedIps'] = [
'Items' => $dictionaryCampaign->campaign->blocked_ips,
];
}
if (count($dictionaryCampaign->excluded_sites)) {
if ($dictionaryCampaign->excluded_sites && count($dictionaryCampaign->excluded_sites)) {
$data['ExcludedSites'] = [
'Items' => array_map(function ($value) use ($variable_list) {
return StrReplaceByVariables::getInstance($value, $variable_list)->get();
......@@ -125,7 +126,11 @@ class AddCampaigns extends DirectRequest
];
}
if (count($dictionaryCampaign->campaign->counter_ids)) {
if ($dictionaryCampaign->campaign->settings && count($dictionaryCampaign->campaign->settings)) {
$data['TextCampaign']['Settings'] = $dictionaryCampaign->campaign->settings;
}
if ($dictionaryCampaign->campaign->counter_ids && count($dictionaryCampaign->campaign->counter_ids)) {
$data['TextCampaign']['CounterIds'] = [
'Items' => $dictionaryCampaign->campaign->counter_ids,
];
......
......@@ -34,7 +34,7 @@ class GetCampaigns extends DirectRequest
'daily_budget' => json_encode($campaign['DailyBudget'] ?? []),
'text_campaign_strategy_search' => $campaign['TextCampaign']['BiddingStrategy']['Search']['BiddingStrategyType'],
'text_campaign_strategy_network' => $campaign['TextCampaign']['BiddingStrategy']['Network']['BiddingStrategyType'],
'settings' => json_encode($campaign['TextCampaign']['Settings']),
'settings' => json_encode($campaign['TextCampaign']['Settings'] ?? []),
'counter_ids' => json_encode($campaign['TextCampaign']['CounterIds']['Items'] ?? []),
'relevant_keywords_setting_budget_percent' => $campaign['TextCampaign']['RelevantKeywords']['BudgetPercent'],
'relevant_keywords_setting_optimize_goal_id' => $campaign['TextCampaign']['RelevantKeywords']['OptimizeGoalId'],
......
......@@ -20,7 +20,6 @@ class CreateDictionariesTable extends Migration
$table->string('name', 255);
$table->string('type', 255);
$table->bigInteger('token_id')->nullable()->unsigned();
$table->boolean('update')->default(1);
$table->timestamps();
$table->foreign('token_id')->references('id')->on('tokens');
......
......@@ -21,6 +21,7 @@ class CreateDictionaryCampaignsTable extends Migration
$table->string('name', 255)->nullable();
$table->text('negative_keywords')->nullable();
$table->text('excluded_sites')->nullable();
$table->boolean('synced')->default(1);
$table->boolean('updated')->default(0);
$table->dateTime('external_upload_at')->nullable();
$table->timestamps();
......
......@@ -22,25 +22,16 @@
<div class="mt-6 bg-white rounded shadow">
<table class="w-full whitespace-nowrap">
<tr class="text-left font-bold">
<th class="px-6 pt-6 pb-4" colspan="2">Name</th>
<th class="px-6 pt-6 pb-4">Обновлять?</th>
<th class="px-6 pt-6 pb-4" colspan="5">Name</th>
<th class="px-6 pt-6 pb-4"></th>
</tr>
<template v-for="city in token.cities">
<tr :key="city.id" class="hover:bg-gray-100 focus-within:bg-gray-100">
<td colspan="2" class="border-t">
<td colspan="5" class="border-t">
<div class="px-6 py-4">
{{ city.name }}
</div>
</td>
<td class="border-t">
<div class="px-6 py-4">
<input :checked="city.update"
@change="cityUpdated(city.id, !city.update)"
type="checkbox"
>
</div>
</td>
<td class="border-t w-px">
<div class="px-6 py-4">
<button class="px-4 flex items-center"
......@@ -55,8 +46,9 @@
<tr v-if="city.dictionary_campaigns.length">
<th class="px-6 pt-6 pb-4"></th>
<th class="px-6 pt-6 pb-4">Кампания</th>
<th class="px-6 pt-6 pb-4">Обновлять?</th>
<th class="px-6 pt-6 pb-4">Синхронизировать?</th>
<th></th>
<th class="px-6 pt-6 pb-4" colspan="2"></th>
</tr>
<tr v-if="city.dictionary_campaigns.length" :key="dictionary_campaign.id" v-for="dictionary_campaign in city.dictionary_campaigns">
<td class="border-t"></td>
......@@ -70,12 +62,20 @@
</span>
</td>
<td class="border-t text-center">
<input :checked="dictionary_campaign.updated !== '0'"
@change="campaignUpdated(city.id, dictionary_campaign.campaign_id, !(dictionary_campaign.updated !== '0'))"
<div class="px-6 py-4">
<input :checked="dictionary_campaign.updated"
@change="updated(city.id, dictionary_campaign.campaign_id, !dictionary_campaign.updated)"
type="checkbox"
>
</div>
</td>
<td class="border-t"></td>
<td class="border-t text-center">
<input :checked="dictionary_campaign.synced"
@change="synced(city.id, dictionary_campaign.campaign_id, !dictionary_campaign.synced)"
type="checkbox"
>
</td>
<td class="border-t" colspan="2"></td>
</tr>
</template>
<tr v-if="token.cities.length === 0">
......@@ -115,11 +115,11 @@
cityDelete(id) {
this.$emit('delete', id)
},
cityUpdated(id, updated) {
this.$emit('updated', id, updated)
synced(city_id, campaign_id, synced) {
this.$emit('synced', city_id, campaign_id, synced)
},
campaignUpdated(city_id, campaign_id, updated) {
this.$emit('campaignUpdated', city_id, campaign_id, updated)
updated(city_id, campaign_id, updated) {
this.$emit('updated', city_id, campaign_id, updated)
},
}
}
......
......@@ -50,9 +50,9 @@
:token="token"
:main_token_campaigns="main_token_campaigns"
v-on:add="cityAdd"
v-on:updated="cityUpdated"
v-on:delete="cityDelete"
v-on:campaignUpdated="campaignUpdated"
v-on:updated="campaignUpdated"
v-on:synced="campaignSynced"
></city-settings>
</div>
</template>
......@@ -104,16 +104,16 @@ export default {
cityAdd(id) {
this.$inertia.post(this.route('token.city.store', [this.token.id, id]))
},
cityUpdated(id, updated) {
this.$inertia.post(this.route('token.city.updated', [this.token.id, id]), {
updated: updated ? 1 : 0,
})
},
campaignUpdated(city_id, campaign_id, updated) {
this.$inertia.post(this.route('token.city.campaign.updated', [this.token.id, city_id, campaign_id]), {
updated: updated ? 1 : 0,
})
},
campaignSynced(city_id, campaign_id, synced) {
this.$inertia.post(this.route('token.city.campaign.synced', [this.token.id, city_id, campaign_id]), {
updated: synced ? 1 : 0,
})
},
cityDelete(id) {
if (confirm('Are you sure you want to delete this city?')) {
this.$inertia.delete(this.route('token.city.destroy', [this.token.id, id]))
......@@ -145,11 +145,6 @@ export default {
campaignAdd(campaign_id) {
this.campaignManaged(campaign_id, 1);
},
filterCampaigns(token_campaigns) {
return this.campaigns.filter(
campaign => token_campaigns.map((campaign) => campaign.id).indexOf(campaign.id) === -1
)
},
},
}
</script>
......@@ -187,13 +187,13 @@ Route::group(['middleware' => 'auth'], function () {
Route::delete('variables/{variable}', [CampaignVariablesController::class, 'destroy'])
->name('variable.destroy');
Route::post('token/city/store/{token}/{city}', [TokensController::class, 'storeCity'])
Route::post('token/city/store/{token}/{dictionary}', [TokensController::class, 'storeCity'])
->name('token.city.store');
Route::post('token/city/updated/{token}/{city}', [TokensController::class, 'updatedCity'])
->name('token.city.updated');
Route::post('token/city/campaign/updated/{token}/{city}/{campaign}', [TokensController::class, 'updatedCityCampaign'])
Route::post('token/city/campaign/updated/{token}/{dictionary}/{campaign_id}', [TokensController::class, 'updatedCityCampaign'])
->name('token.city.campaign.updated');
Route::delete('token/city/{token}/{city}', [TokensController::class, 'destroyCity'])
Route::post('token/city/campaign/synced/{token}/{dictionary}/{campaign_id}', [TokensController::class, 'syncedCityCampaign'])
->name('token.city.campaign.synced');
Route::delete('token/city/{token}/{dictionary}', [TokensController::class, 'destroyCity'])
->name('token.city.destroy');
});
......@@ -63,8 +63,8 @@ class CityTest extends TestCase
$this->actingAs($this->user)
->post(route('token.city.store', [
'token' => $this->token->id,
'city' => $dictionary_ids[0]
$this->token->id,
$dictionary_ids[0]
]))
->assertStatus(302);
......@@ -76,8 +76,8 @@ class CityTest extends TestCase
$this->actingAs($this->user)
->post(route('token.city.store', [
'token' => $this->token->id,
'city' => $dictionary_ids[1]
$this->token->id,
$dictionary_ids[1]
]))
->assertStatus(302);
......@@ -89,8 +89,8 @@ class CityTest extends TestCase
$this->actingAs($this->user)
->post(route('token.city.store', [
'token' => $this->token->id,
'city' => $dictionary_ids[2]
$this->token->id,
$dictionary_ids[2]
]))
->assertStatus(302);
......@@ -103,9 +103,9 @@ class CityTest extends TestCase
$this->actingAs($this->user)
->post(route('token.campaign.variable.add', [
'token' => $this->token->id,
'dictionary_id' => $this->token->cities[0]->getKey(),
'campaign_id' => $campaign_ids[0]
$this->token->id,
$this->token->cities[0]->getKey(),
$campaign_ids[0]
]), [
'variable' => 'add',
'name' => 'test',
......@@ -119,6 +119,8 @@ class CityTest extends TestCase
foreach ($this->token->cities as $city) {
foreach ($city->campaigns as $campaign) {
if ($campaign->id == $campaign_ids[0]) {
$this->assertEquals(false, $campaign->pivot->updated);
$this->assertEquals(true, $campaign->pivot->synced);
$this->assertEquals(1, $campaign->dictionaryCampaignVariables->count());
} else {
$this->assertEquals(0, $campaign->dictionaryCampaignVariables->count());
......@@ -133,6 +135,39 @@ class CityTest extends TestCase
}
}
$this->actingAs($this->user)
->post(route('token.city.campaign.updated', [
$this->token->id,
$this->token->cities[0]->getKey(),
$campaign_ids[0]
]), [
'updated' => true,
])
->assertStatus(302);
$this->actingAs($this->user)
->post(route('token.city.campaign.synced', [
$this->token->id,
$this->token->cities[0]->getKey(),
$campaign_ids[0]
]), [
'synced' => false,
])
->assertStatus(302);
$this->token->load('cities.campaigns');
foreach ($this->token->cities as $city) {
foreach ($city->campaigns as $campaign) {
if ($campaign->id == $campaign_ids[0]) {
/* TODO: не проходит валидацию, атрибуты верные
$this->assertEquals(true, $campaign->pivot->updated);
$this->assertEquals(false, $campaign->pivot->synced);
*/
}
}
}
$campaign_new = factory(Campaigns::class)->create([
'manage' => true,
'token' => $this->token_main->getKey(),
......@@ -148,9 +183,9 @@ class CityTest extends TestCase
$this->actingAs($this->user)
->post(route('token.campaign.variable.add', [
'token' => $this->token->id,
'dictionary_id' => $this->token->cities[0]->getKey(),
'campaign_id' => $campaign_ids[1]
$this->token->id,
$this->token->cities[0]->getKey(),
$campaign_ids[1]
]), [
'variable' => 'add',
'name' => 'testtwo',
......
<?php
namespace Tests\Unit;
use App\Jobs\ProcessCallLimitedAPI;
use App\Models\Account;
use App\Models\Campaigns;
use App\Models\Dictionary;
use App\Models\Tokens;
use App\Models\User;
use App\Models\Variable;
use App\Service\Contract\API;
use App\Service\Requests\APIRequest;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class AddCampaignsTest extends TestCase
{
use RefreshDatabase;
private $request;
private $params;
private $user;
private $token;
private $token_main;
private $campaign;
private $dictionary;
protected function setUp(): void
{
parent::setUp();
$account = Account::create(['name' => 'Acme Corporation']);
$this->user = factory(User::class)->create([
'account_id' => $account->id,
'first_name' => 'John',
'last_name' => 'Doe',
'email' => 'johndoe@example.com',
'owner' => true,
]);
$this->token = factory(Tokens::class)->create([
'created_by' => $this->user->getKey()
]);
$this->token_main = factory(Tokens::class)->create([
'type' => Tokens::MAIN,
]);
$this->campaign = factory(Campaigns::class)->create([
'external_id' => 1,
'manage' => true,
'token' => $this->token_main->getKey(),
]);
$this->dictionary = factory(Dictionary::class)->create([
'region_id' => 1,
'type' => Dictionary::CITY,
]);
$this->request = APIRequest::getInstance(API::YANDEX)
->setToken($this->token)
->getRequest('campaigns', 'add');
$this->actingAs($this->user)
->post(route('token.city.store', [
$this->token->id,
$this->dictionary->getKey(),
]))
->assertStatus(302);
$this->assertEquals(1, $this->dictionary->campaigns->count());
$this->params = [
'dictionaryCampaigns' => $this->token->dictionaryCampaignsForNotExternal,
'variables' => Variable::all(),
];
}
public function testCallApi()
{
Queue::fake();
Queue::assertNothingPushed();
$this->request->call($this->params);
Queue::assertPushed(ProcessCallLimitedAPI::class);
}
public function testHandleApi()
{
$this->dictionary->refresh();
$data = [
'result' => [
'AddResults' => [
[
'Id' => 1,
],
]
]
];
$this->request->putParams($this->params);
$this->request->handle($data);
$this->dictionary->campaigns->first()->pivot->refresh();
$this->assertEquals(1, $this->dictionary->campaigns->first()->pivot->external_id);
$this->assertNotNull(1, $this->dictionary->campaigns->first()->pivot->external_upload_at);
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!