Commit 76e0f902 by Vladislav

Merge branch 'master' into ticket_19473

2 parents 7c551ac6 422f5e7d
<?php
namespace App\Console\Commands;
use App\Jobs\ProcessCallLimitedAPI;
use App\Models\AdGroup;
use App\Models\Campaigns;
use App\Models\Pivots\DictionaryCampaign;
use App\Models\Tokens;
use App\Service\API\API;
use App\Service\Direct\CheckDictionaries;
use App\Service\Direct\GetCampaigns;
use App\Service\Requests\APIRequest;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Support\Facades\Bus;
class AdGroupLoadUpdated extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'adgroups:loadUpdated';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Загрузка измененные группы';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$campaigns = AdGroup::forUpdatedSelf()->get();
if (!$campaigns->count()) {
return;
}
$token = Tokens::where('type', Tokens::MAIN)->first();
if (!$token) {
throw new \Exception('Не найден токен блин');
}
$factory = APIRequest::getInstance(API::YANDEX);
$factory->setToken($token);
$factory->getRequest('Campaigns', 'get')->call([
'ids' => $campaigns->pluck('external_id')->all(),
]);
$tokens = Tokens::has('dictionaryCampaignsEnabledForExternalSynchronized.groupsForExternalForUpdatedSelf')
->with('dictionaryCampaignsEnabledForExternalSynchronized.groupsForExternalForUpdatedSelf')
->where('type', '!=', Tokens::MAIN)
->get();
foreach ($tokens as $token) {
$factory = APIRequest::getInstance(API::YANDEX);
$factory->setToken($token);
$factory->getRequest('AdGroups', 'get')->call([
'Ids' => $token->dictionaryCampaignsEnabledForExternalSynchronized->pluck('groupsForExternalForUpdatedSelf')
->collapse()->all(),
]);
}
return 0;
}
}
......@@ -42,10 +42,10 @@ class AdGroupsAdd extends Command
*/
public function handle()
{
$tokens = Tokens::whereHas('dictionaryCampaignsEnabledForExternal.groupsForNotExternal')
$tokens = Tokens::whereHas('dictionaryCampaignsEnabledForExternalSynchronized.groupsForNotExternal')
->with([
'dictionaryCampaignsEnabledForExternal.groupsForNotExternal.dictionaryCampaign',
'dictionaryCampaignsEnabledForExternal.groupsForNotExternal.group',
'dictionaryCampaignsEnabledForExternalSynchronized.groupsForNotExternal.dictionaryCampaign',
'dictionaryCampaignsEnabledForExternalSynchronized.groupsForNotExternal.group',
])
->where('type', '!=', Tokens::MAIN)
->get();
......@@ -54,10 +54,9 @@ class AdGroupsAdd extends Command
$factory = APIRequest::getInstance(API::YANDEX);
$factory->setToken($token);
$factory->getRequest('AdGroups', 'add')
->call([
'goalAdGroups' => $token->dictionaryCampaignsEnabledForExternal->pluck('groupsForNotExternal')
'goalAdGroups' => $token->dictionaryCampaignsEnabledForExternalSynchronized->pluck('groupsForNotExternal')
->collapse(),
]);
}
......
......@@ -2,11 +2,18 @@
namespace App\Console\Commands;
use App\Jobs\ProcessCallLimitedAPI;
use App\Models\AdGroup;
use App\Models\Campaigns;
use App\Models\Pivots\DictionaryCampaign;
use App\Models\Tokens;
use App\Service\API\API;
use App\Service\Direct\CheckDictionaries;
use App\Service\Direct\GetCampaigns;
use App\Service\Requests\APIRequest;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Support\Facades\Bus;
class AdGroupsLoadUpdated extends Command
{
......@@ -22,7 +29,7 @@ class AdGroupsLoadUpdated extends Command
*
* @var string
*/
protected $description = 'Загрузка измененных групп';
protected $description = 'Загрузка измененные группы';
/**
* Create a new command instance.
......@@ -38,26 +45,42 @@ class AdGroupsLoadUpdated extends Command
* Execute the console command.
*
* @return int
* @throws \Exception
*/
public function handle()
{
$token = Tokens::has('campaignsAdGroupsForUpdatedSelf')
->with('campaignsAdGroupsForUpdatedSelf')
->where('type', '!=', Tokens::MAIN)
->first();
$adGroups = AdGroup::forUpdatedSelf()->get();
if (!$adGroups->count()) {
return;
}
$token = Tokens::where('type', Tokens::MAIN)->first();
if (!$token) {
return 0;
throw new \Exception('Не найден токен блин');
}
$factory = APIRequest::getInstance(API::YANDEX);
$factory->setToken($token);
$factory->getRequest('AdGroups', 'get')
->call([
'CampaignIds' => $token->campaignsAdGroupsForUpdatedSelf->pluck('campaign_external_id')->all(),
$factory->getRequest('AdGroups', 'get')->call([
'Ids' => $adGroups->pluck('external_id')->all(),
]);
$tokens = Tokens::has('dictionaryCampaignsEnabledForExternalSynchronized.groupsForExternalForUpdatedSelf')
->with('dictionaryCampaignsEnabledForExternalSynchronized.groupsForExternalForUpdatedSelf')
->where('type', '!=', Tokens::MAIN)
->get();
foreach ($tokens as $token) {
$factory = APIRequest::getInstance(API::YANDEX);
$factory->setToken($token);
$factory->getRequest('AdGroups', 'get')->call([
'Ids' => $token->dictionaryCampaignsEnabledForExternalSynchronized->pluck('groupsForExternalForUpdatedSelf')
->collapse()
->pluck('external_id')
->all(),
]);
}
return 0;
}
......
......@@ -42,10 +42,10 @@ class AdGroupsUpdate extends Command
*/
public function handle()
{
$tokens = Tokens::whereHas('dictionaryCampaignsEnabledForExternal.groupsForExternalForNeedUpdated')
$tokens = Tokens::whereHas('dictionaryCampaignsEnabledForExternalUpdated.groupsForExternalForNeedUpdated')
->with([
'dictionaryCampaignsEnabledForExternal.groupsForExternalForNeedUpdated.dictionaryCampaign',
'dictionaryCampaignsEnabledForExternal.groupsForExternalForNeedUpdated.group',
'dictionaryCampaignsEnabledForExternalUpdated.groupsForExternalForNeedUpdated.dictionaryCampaign',
'dictionaryCampaignsEnabledForExternalUpdated.groupsForExternalForNeedUpdated.group',
])
->where('type', '!=', Tokens::MAIN)
->get();
......@@ -57,7 +57,7 @@ class AdGroupsUpdate extends Command
$factory->getRequest('AdGroup', 'update')
->call([
'goalAdGroups' => $token->dictionaryCampaignsEnabledForExternal->pluck('groupsForExternalForNeedUpdated')
'goalAdGroups' => $token->dictionaryCampaignsEnabledForExternalUpdated->pluck('groupsForExternalForNeedUpdated')
->collapse(),
]);
}
......
......@@ -50,7 +50,8 @@ class CampaignsCheckChange extends Command
->call();
}
$tokens = Tokens::has('dictionaryCampaignsEnabledForExternal')
$tokens = Tokens::has('dictionaryCampaignsEnabledForExternalUpdated')
->with('dictionaryCampaignsEnabledForExternalUpdated')
->where('type', '!=', Tokens::MAIN)->get();
foreach ($tokens as $token) {
......
......@@ -14,21 +14,21 @@ use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Support\Facades\Bus;
class CampaignsLoadUpdatedChildrenAdGroups extends Command
class CampaignsCheckUpdatedChildrenAdGroups extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'campaigns:loadUpdatedChildrenAdGroups';
protected $signature = 'campaigns:checkUpdatedChildrenAdGroups';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Загрузка измененных РК';
protected $description = 'Проверка наличия изменения в дочерних элементах';
/**
* Create a new command instance.
......
......@@ -47,7 +47,7 @@ class CampaignsLoadUpdated extends Command
*/
public function handle()
{
$campaigns = Campaigns::forUpdatedSelf()->get();
$campaigns = Campaigns::forEnabled()->forUpdatedSelf()->get();
if (!$campaigns->count()) {
return;
}
......
......@@ -42,8 +42,8 @@ class CampaignsUpdate extends Command
*/
public function handle()
{
$tokens = Tokens::whereHas('dictionaryCampaignsEnabledForExternalNeedUpdated')
->with('dictionaryCampaignsEnabledForExternalNeedUpdated.campaign')
$tokens = Tokens::whereHas('dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated')
->with('dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated.campaign')
->where('type', '!=', Tokens::MAIN)
->get();
......@@ -53,7 +53,7 @@ class CampaignsUpdate extends Command
$factory->getRequest('Campaigns', 'update')
->call([
'dictionaryCampaigns' => $token->dictionaryCampaignsEnabledForExternalNeedUpdated,
'dictionaryCampaigns' => $token->dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated,
]);
}
......
......@@ -7,8 +7,8 @@ use App\Console\Commands\AdGroupsLoadUpdated;
use App\Console\Commands\AdGroupsUpdate;
use App\Console\Commands\CampaignsAdd;
use App\Console\Commands\CampaignsCheckChange;
use App\Console\Commands\CampaignsCheckUpdatedChildrenAdGroups;
use App\Console\Commands\CampaignsLoadGroups;
use App\Console\Commands\CampaignsLoadUpdatedChildrenAdGroups;
use App\Console\Commands\CampaignsResume;
use App\Console\Commands\CampaignsSuspend;
use App\Console\Commands\CampaignsUpdate;
......@@ -40,12 +40,14 @@ class Kernel extends ConsoleKernel
$schedule->command(RefreshLimits::class)->hourly();
$schedule->command(DictionariesLoad::class)->saturdays()->at('05:00');
$schedule->command(CampaignsCheckChange::class)->hourlyAt(5);
$schedule->command(CampaignsLoadUpdated::class)->hourlyAt(15);
$schedule->command(CampaignsCheckUpdatedChildrenAdGroups::class)->hourlyAt(10);
$schedule->command(CampaignsLoadUpdated::class)->hourlyAt(10);
$schedule->command(CampaignsAdd::class)->hourlyAt(20);
$schedule->command(CampaignsUpdate::class)->hourlyAt(20);
$schedule->command(CampaignsResume::class)->hourlyAt(20);
$schedule->command(CampaignsSuspend::class)->hourlyAt(20);
$schedule->command(CampaignsLoadUpdatedChildrenAdGroups::class)->hourlyAt(35);
$schedule->command(CampaignsLoadGroups::class)->hourlyAt(40);
$schedule->command(AdGroupsLoadUpdated::class)->hourlyAt(45);
$schedule->command(AdGroupsAdd::class)->hourlyAt(50);
......
......@@ -193,13 +193,15 @@ class CampaignVariablesController extends Controller
}
$dictionary_campaign->update([
'updated_need' => Carbon::now(),
]);
if ($dictionary_campaign->external_id) {
$dictionary_campaign->update([
'updated_need' => Carbon::now(),
]);
$dictionary_campaign->groups()->update([
'updated_need' => Carbon::now(),
]);
$dictionary_campaign->groups()->update([
'updated_need' => Carbon::now(),
]);
}
}
......
......@@ -116,8 +116,14 @@ class TokensController extends Controller
return Redirect::back();
}
$enabled = !!request('enabled');
$campaign->update([
'enabled' => !!request('enabled'),
'enabled' => $enabled,
]);
$campaign->dictionaryCampaigns()->update([
'enabled' => $enabled,
]);
return Redirect::back()->with('success', 'Campaign ' . ($campaign->enabled ? 'enabled' : 'disabled') . '.');
......
......@@ -106,8 +106,6 @@ class AdGroup extends Model
protected $casts = [
'campaign_id' => 'int',
'external_id' => 'int',
'campaign_external_id' => 'int',
'region_ids' => 'array',
'negative_keywords' => 'json',
'negative_keyword_shared_set_ids' => 'json',
......@@ -128,7 +126,6 @@ class AdGroup extends Model
static public function getPropertiesWatch()
{
return collect([
'campaign_external_id',
'name',
'region_ids',
'negative_keywords',
......@@ -158,15 +155,19 @@ class AdGroup extends Model
if (GoalAdGroup::getPropertiesCopyWithPivot()->first(function ($property_name) use ($ad_group) {
return $ad_group->{$property_name} !== $ad_group->getOriginal($property_name);
})) {
$ad_group->goalGroups()->update(
GoalAdGroup::copyPropertyFromMain($ad_group)
);
if (!is_null($ad_group->campaign_id) && is_null($ad_group->getOriginal('campaign_id'))) {
$ad_group->campaign->copyGroupInGoalGroup();
} else {
$ad_group->goalGroups()->update(
GoalAdGroup::copyPropertyFromMain($ad_group)
);
}
}
if (self::getPropertiesWatch()->first(function ($property_name) use ($ad_group) {
return $ad_group->{$property_name} !== $ad_group->getOriginal($property_name);
})) {
$ad_group->goalGroups()->update([
$ad_group->goalGroups()->forExternal()->update([
'updated_need' => Carbon::now(),
]);
}
......@@ -174,9 +175,13 @@ class AdGroup extends Model
});
}
public function scopeForUpdatedSelf(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForUpdatedSelf($query)
{
$query->whereNotNull("{$query->getModel()->getTable()}.updated_self");
return $query->whereNotNull("{$query->getModel()->getTable()}.updated_self");
}
public function goalGroups()
......
......@@ -111,7 +111,6 @@ class Campaigns extends Model
];
protected $casts = [
'external_id' => 'int',
'time_targeting' => 'array',
'negative_keywords' => 'array',
'blocked_ips' => 'array',
......@@ -194,7 +193,7 @@ class Campaigns extends Model
if (DictionaryCampaign::getPropertiesCopyWithPivot()->first(function ($property_name) use ($campaign) {
return $campaign->{$property_name} !== $campaign->getOriginal($property_name);
})) {
$campaign->dictionaryCampaigns()->enabled()->synchronized()->update(
$campaign->dictionaryCampaigns()->enabled()->forUpdated()->update(
DictionaryCampaign::copyPropertyFromMain($campaign)
);
}
......@@ -202,7 +201,7 @@ class Campaigns extends Model
if (self::getPropertiesWatch()->first(function ($property_name) use ($campaign) {
return $campaign->{$property_name} !== $campaign->getOriginal($property_name);
})) {
$campaign->dictionaryCampaigns()->enabled()->synchronized()->forExternal()->update([
$campaign->dictionaryCampaigns()->forExternal()->update([
'updated_need' => Carbon::now(),
]);
}
......@@ -232,7 +231,8 @@ class Campaigns extends Model
$campaign->groups()->get()->each(function (AdGroup $adGroup) use ($dictionaryCampaign) {
$dictionaryCampaign->groups()->updateOrCreate([
'campaign_id' => $dictionaryCampaign->external_id,
'campaign_external_id' => $dictionaryCampaign->external_id,
'ad_group_id' => $adGroup->getKey(),
], GoalAdGroup::copyPropertyFromMain($adGroup));
});
......@@ -339,37 +339,67 @@ class Campaigns extends Model
->withTimestamps();
}
public function scopeForUpdatedSelf(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForUpdatedSelf($query)
{
$query->whereNotNull('updated_self');
return $query->whereNotNull('updated_self');
}
public function scopeForUpdatedChildren(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForUpdatedChildren($query)
{
$query->whereNotNull('updated_children');
return $query->whereNotNull('updated_children');
}
public function scopeForGroupsLoadable(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForGroupsLoadable($query)
{
$query->whereNull('groups_loaded_at');
return $query->whereNull('groups_loaded_at');
}
public function scopeForManaged(Builder $query, $manage = true)
/**
* @param Builder $query
* @param bool $manage
* @return Builder
*/
public function scopeForManaged($query, $manage = true)
{
$query->where('manage', $manage);
return $query->where('manage', $manage);
}
public function scopeForEnabled(Builder $query, $enabled = true)
/**
* @param Builder $query
* @param bool $enabled
* @return Builder
*/
public function scopeForEnabled($query, $enabled = true)
{
$query->where('enabled', $enabled);
return $query->where('enabled', $enabled);
}
public function scopeDisabled(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeDisabled($query)
{
return $query->whereNotNull('disabled_at');
}
public function scopeNotDisabled(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeNotDisabled($query)
{
return $query->whereNull('disabled_at');
}
......
......@@ -2,6 +2,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
......@@ -53,24 +54,23 @@ class Contact extends Model
{
use SoftDeletes;
public function organization()
{
return $this->belongsTo(Organization::class);
}
public function getNameAttribute()
{
return $this->first_name.' '.$this->last_name;
}
/**
* @param Builder $query
* @return Builder
*/
public function scopeOrderByName($query)
{
$query->orderBy('last_name')->orderBy('first_name');
return $query->orderBy('last_name')->orderBy('first_name');
}
/**
* @param Builder $query
* @param array $filters
* @return Builder
*/
public function scopeFilter($query, array $filters)
{
$query->when($filters['search'] ?? null, function ($query, $search) {
return $query->when($filters['search'] ?? null, function ($query, $search) {
$query->where(function ($query) use ($search) {
$query->where('first_name', 'like', '%'.$search.'%')
->orWhere('last_name', 'like', '%'.$search.'%')
......@@ -87,4 +87,14 @@ class Contact extends Model
}
});
}
public function organization()
{
return $this->belongsTo(Organization::class);
}
public function getNameAttribute()
{
return $this->first_name.' '.$this->last_name;
}
}
......@@ -40,7 +40,11 @@ class Dictionary extends Model
{
CONST CITY = 'City';
public function scopeDefaultOrderBy(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeDefaultOrderBy($query)
{
return $query->orderBy('name');
}
......
......@@ -2,6 +2,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
......@@ -38,8 +39,12 @@ class Limits extends Model
{
use HasFactory;
/**
* @param Builder $query
* @return Builder
*/
public function scopeComplited($query)
{
$query->where('reserved', 0);
return $query->where('reserved', 0);
}
}
......@@ -2,6 +2,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
......@@ -53,9 +54,14 @@ class Organization extends Model
return $this->hasMany(Contact::class);
}
/**
* @param Builder $query
* @param array $filters
* @return Builder
*/
public function scopeFilter($query, array $filters)
{
$query->when($filters['search'] ?? null, function ($query, $search) {
return $query->when($filters['search'] ?? null, function ($query, $search) {
$query->where('name', 'like', '%'.$search.'%');
})->when($filters['trashed'] ?? null, function ($query, $trashed) {
if ($trashed === 'with') {
......
......@@ -44,7 +44,7 @@ use Illuminate\Support\Collection;
* @method static \Illuminate\Database\Eloquent\Builder|DictionaryCampaign forNotExternal()
* @method static \Illuminate\Database\Eloquent\Builder|DictionaryCampaign needUpdated()
* @method static \Illuminate\Database\Eloquent\Builder|DictionaryCampaign enabled($value = true)
* @method static \Illuminate\Database\Eloquent\Builder|DictionaryCampaign updated($value = true)
* @method static \Illuminate\Database\Eloquent\Builder|DictionaryCampaign forUpdated($value = true)
* @method static \Illuminate\Database\Eloquent\Builder|DictionaryCampaign synced($value = true)
* @method static \Illuminate\Database\Eloquent\Builder|DictionaryCampaign synchronized()
* @method static \Illuminate\Database\Eloquent\Builder|DictionaryCampaign forUpdatedSelf()
......@@ -102,7 +102,6 @@ class DictionaryCampaign extends Pivot
];
protected $casts = [
'external_id' => 'int',
'campaign_id' => 'int',
'dictionary_id' => 'int',
'negative_keywords' => 'array',
......@@ -175,67 +174,113 @@ class DictionaryCampaign extends Pivot
})->all();
}
public function scopeDisabled(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeDisabled($query)
{
return $query->whereNotNull('disabled_at');
}
public function scopeNotDisabled(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeNotDisabled($query)
{
return $query->whereNull('disabled_at');
}
public function scopeForExternal(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForExternal($query)
{
return $query->whereNotNull('external_id');
}
public function scopeForNotExternal(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForNotExternal($query)
{
return $query->whereNull('external_id');
}
public function scopeEnabled(Builder $query, $enabled = true)
/**
* @param Builder $query
* @param bool $enabled
* @return Builder
*/
public function scopeEnabled($query, $enabled = true)
{
return $query->where('enabled', $enabled);
}
public function scopeUpdated(Builder $query, $updated = true)
/**
* @param Builder $query
* @param bool $updated
* @return Builder
*/
public function scopeForUpdated($query, $updated = true)
{
return $query->where('updated', $updated);
}
public function scopeNeedUpdated(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeNeedUpdated($query)
{
return $query->whereNotNull('updated_need');
}
public function scopeSynced(Builder $query, $synced = true)
/**
* @param Builder $query
* @param bool $synced
* @return Builder
*/
public function scopeSynced($query, $synced = true)
{
return $query->where('synced', $synced);
}
public function scopeSynchronized(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeSynchronized($query)
{
return $query->synced()
->where(function (Builder $query) {
$query->updated()->orWhere(function (Builder $query) {
$query->updated(false)->forNotExternal();
});
});
return $query->synced()->forUpdated();
}
public function scopeForUpdatedSelf(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForUpdatedSelf($query)
{
$query->whereNotNull('updated_self');
return $query->whereNotNull('updated_self');
}
public function scopeForUpdatedChildren(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForUpdatedChildren($query)
{
$query->whereNotNull('updated_children');
return $query->whereNotNull('updated_children');
}
public function scopeJoinDictionaries(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeJoinDictionaries($query)
{
$dictionary_model = Dictionary::getModel();
......
......@@ -72,8 +72,6 @@ class GoalAdGroup extends Pivot
];
protected $casts = [
'external_id' => 'int',
'campaign_external_id' => 'int',
'ad_group_id' => 'int',
'dictionary_campaign_id' => 'int',
'negative_keywords' => 'array',
......@@ -139,24 +137,40 @@ class GoalAdGroup extends Pivot
->all();
}
public function scopeForExternal(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForExternal($query)
{
return $query->whereNotNull('external_id');
}
public function scopeForNotExternal(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForNotExternal($query)
{
return $query->whereNull('external_id');
}
public function scopeNeedUpdated(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeNeedUpdated($query)
{
return $query->whereNotNull('updated_need');
}
public function scopeForUpdatedSelf(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeForUpdatedSelf($query)
{
$query->whereNotNull('updated_self');
return $query->whereNotNull('updated_self');
}
public function group()
......
......@@ -4,6 +4,7 @@ namespace App\Models;
use App\Models\Pivots\DictionaryCampaign;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
/**
......@@ -27,8 +28,8 @@ use Illuminate\Database\Eloquent\Model;
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Campaigns[] $campaigns
* @property-read int|null $campaigns_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\AdGroup[] $campaignsAdGroups
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\AdGroup[] $campaignsAdGroupsForUpdatedSelf
* @property-read int|null $campaigns_ad_groups_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\AdGroup[] $campaignsForEnabledAdGroupsForUpdatedSelf
* @property-read int|null $campaigns_for_enabled_ad_groups_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Campaigns[] $campaignsNotEnabledNotDisabled
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Campaigns[] $campaignsEnabledDisabled
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Campaigns[] $campaignsEnabledNotDisabledUpdatedChildren
......@@ -39,9 +40,11 @@ use Illuminate\Database\Eloquent\Model;
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsForExternal
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsEnabledForNotExternal
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsNotEnabledForExternalNotDisabled
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsEnabledForExternal
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsEnabledForExternalUpdated
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsEnabledForExternalDisabled
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsEnabledForExternalNeedUpdated
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsEnabledForExternalSynchronized
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsEnabledForExternalSynchronizedUpdatedSelf
* @property-read \Illuminate\Database\Eloquent\Collection|DictionaryCampaign[] $dictionaryCampaignsEnabledForExternalSynchronizedUpdatedChildren
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Dictionary[] $campaignsForManaged
......@@ -112,9 +115,14 @@ class Tokens extends Model
: Carbon::now()->addDays(-1);
}
/**
* @param Builder $query
* @param array $filters
* @return Builder
*/
public function scopeFilter($query, array $filters)
{
$query->when($filters['login'] ?? null, function ($query, $search) {
return $query->when($filters['login'] ?? null, function ($query, $search) {
$query->where('login', 'like', '%'.$search.'%');
})->when($filters['type'] ?? null, function ($query, $type) {
$query->where('type', $type);
......@@ -145,9 +153,9 @@ class Tokens extends Model
return $this->hasManyThrough(AdGroup::class, Campaigns::class, 'token', 'campaign_id');
}
public function campaignsAdGroupsForUpdatedSelf()
public function campaignsForEnabledAdGroupsForUpdatedSelf()
{
return $this->campaignsAdGroups()->forUpdatedSelf();
return $this->campaignsAdGroups()->forUpdatedSelf()->where(Campaigns::getModel()->getTable() . '.enabled', true);
}
public function campaignsNotEnabledNotDisabled()
......@@ -185,7 +193,17 @@ class Tokens extends Model
public function dictionaryCampaignsEnabledForExternal()
{
return $this->dictionaryCampaignsForExternal()->enabled();
return $this->dictionaryCampaigns()->enabled()->forExternal();
}
public function dictionaryCampaignsEnabledForExternalUpdated()
{
return $this->dictionaryCampaigns()->enabled()->forExternal()->forUpdated();
}
public function dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated()
{
return $this->dictionaryCampaigns()->enabled()->forExternal()->forUpdated()->needUpdated();
}
public function dictionaryCampaignsEnabledForNotExternal()
......@@ -195,32 +213,27 @@ class Tokens extends Model
public function dictionaryCampaignsNotEnabledForExternalNotDisabled()
{
return $this->dictionaryCampaignsForExternal()->enabled(false)->notDisabled();
return $this->dictionaryCampaigns()->enabled(false)->forExternal()->notDisabled();
}
public function dictionaryCampaignsEnabledForExternalDisabled()
{
return $this->dictionaryCampaignsForExternal()->enabled()->disabled();
}
public function dictionaryCampaignsEnabledForExternalNeedUpdated()
{
return $this->dictionaryCampaignsForExternal()->enabled()->needUpdated();
return $this->dictionaryCampaigns()->enabled()->forExternal()->disabled();
}
public function dictionaryCampaignsEnabledForExternalSynchronized()
{
return $this->dictionaryCampaignsForExternal()->enabled()->synchronized();
return $this->dictionaryCampaigns()->enabled()->forExternal()->synchronized();
}
public function dictionaryCampaignsEnabledForExternalSynchronizedUpdatedSelf()
{
return $this->dictionaryCampaignsEnabledForExternalSynchronized()->forUpdatedSelf();
return $this->dictionaryCampaigns()->enabled()->forExternal()->synchronized()->forUpdatedSelf();
}
public function dictionaryCampaignsEnabledForExternalSynchronizedUpdatedChildren()
{
return $this->dictionaryCampaignsEnabledForExternalSynchronized()->forUpdatedChildren();
return $this->dictionaryCampaigns()->enabled()->forExternal()->synchronized()->forUpdatedChildren();
}
public function campaignsForManaged()
......
......@@ -5,6 +5,7 @@ namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Support\Facades\App;
......@@ -87,22 +88,37 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
return $this->email === 'johndoe@example.com';
}
/**
* @param Builder $query
* @return Builder
*/
public function scopeOrderByName($query)
{
$query->orderBy('last_name')->orderBy('first_name');
return $query->orderBy('last_name')->orderBy('first_name');
}
/**
* @param Builder $query
* @param string $role
* @return Builder
*/
public function scopeWhereRole($query, $role)
{
switch ($role) {
case 'user': return $query->where('owner', false);
case 'owner': return $query->where('owner', true);
}
return $query;
}
/**
* @param Builder $query
* @param array $filters
* @return Builder
*/
public function scopeFilter($query, array $filters)
{
$query->when($filters['search'] ?? null, function ($query, $search) {
return $query->when($filters['search'] ?? null, function ($query, $search) {
$query->where(function ($query) use ($search) {
$query->where('first_name', 'like', '%'.$search.'%')
->orWhere('last_name', 'like', '%'.$search.'%')
......
......@@ -59,7 +59,11 @@ class Variable extends Model
return $variable_list;
}
public function scopeDefaultOrderBy(Builder $query)
/**
* @param Builder $query
* @return Builder
*/
public function scopeDefaultOrderBy($query)
{
return $query->orderBy('name');
}
......
......@@ -66,17 +66,24 @@ class Limits implements \App\Service\Contract\Limits {
$cost = $this->limitCosts->getCostObject($request);
$maxCount = $request->getMaxCount();
if ($this->limitCosts->getCostCall($request) > $this->current()){
return 0;
}
if ($cost == 0 || $maxCount === self::NAN){
return self::NAN;
}
$allowCount = floor(($this->current() - $this->limitCosts->getCostCall($request)) / $cost);
$objectsCount = $request->getObjectsCount();
if ($this->token->limits->count() > 0){
if ($this->limitCosts->getCostCall($request) > $this->current()){
return 0;
}
$allowCount = floor(($this->current() - $this->limitCosts->getCostCall($request)) / $cost);
} else {
$allowCount = $objectsCount; //не было еще запросов, считаем что баллов хватает
}
if ($objectsCount > $maxCount) {
$objectsCount = $maxCount;
}
......@@ -99,7 +106,8 @@ class Limits implements \App\Service\Contract\Limits {
function doRezerv(\App\Service\Contract\APIRequest $request, int $objects): int
{
$limit = $this->getSpent($objects, $request);
if ($this->token->limit<$limit){
if ($this->token->limits->count() > 0 && $this->token->limit < $limit) {
throw new \Exception('Недостаточно баллов');
}
......@@ -189,7 +197,7 @@ class Limits implements \App\Service\Contract\Limits {
}
}
private function getSpent($objects, \App\Service\Contract\APIRequest $request): int
function getSpent($objects, \App\Service\Contract\APIRequest $request): int
{
$cost = $this->limitCosts->getCostObject($request);
......
......@@ -31,7 +31,7 @@ class CheckCampaignsChanges extends DirectRequest
if (in_array(self::SELF, $campaign_data['ChangesIn']) || in_array(self::CHILDREN, $campaign_data['ChangesIn'])) {
$data = [];
if (in_array(self::SELF, $campaign_data['ChangesIn'])) {
if ($this->getToken()->isMain() && in_array(self::SELF, $campaign_data['ChangesIn'])) {
$data['updated_self'] = Carbon::now();
}
......@@ -52,10 +52,16 @@ class CheckCampaignsChanges extends DirectRequest
} else {
$dictionaryCampaign = DictionaryCampaign::synchronized()->find($external_id);
$dictionaryCampaign = DictionaryCampaign::firstWhere('external_id', $external_id);
if ($dictionaryCampaign) {
$data['external_updated_at'] = Carbon::now();
if ($dictionaryCampaign->updated) {
$data['updated_need'] = Carbon::now();
}
$dictionaryCampaign->update($data);
}
......
......@@ -72,15 +72,22 @@ class CheckChanges extends DirectRequest
$adGroup->update([
'updated_self' => Carbon::now(),
]);
} else {
$adGroup = AdGroup::create([
'token_id' => $this->getToken()->getKey(),
'external_id' => $ad_group_id,
'updated_self' => Carbon::now(),
]);
}
} else {
$goalAdGroup = GoalAdGroup::where('external_id', $ad_group_id)->first();
$goalAdGroup = GoalAdGroup::with('dictionaryCampaign')->firstWhere('external_id', $ad_group_id)->first();
if ($goalAdGroup) {
if ($goalAdGroup && $goalAdGroup->dictionaryCampaign && $goalAdGroup->dictionaryCampaign->updated) {
$goalAdGroup->update([
'updated_self' => Carbon::now(),
'updated_need' => Carbon::now(),
]);
}
}
}
......
......@@ -11,6 +11,7 @@ namespace App\Service\Requests;
class DirectRequest extends APIRequest
{
CONST MAX_COUNT = 10000;
protected $max_count = -1;
private $url;
......
......@@ -78,6 +78,7 @@
</td>
<td class="border-t text-center">
<input :checked="dictionary_campaign.enabled"
disabled="disabled"
@change="enabled(city.id, dictionary_campaign.campaign_id, !dictionary_campaign.enabled)"
type="checkbox"
>
......
......@@ -112,13 +112,13 @@ export default {
},
citySynced(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,
synced: synced ? 1 : 0,
})
},
cityEnabled(city_id, campaign_id, enabled) {
this.$inertia.post(this.route('token.city.campaign.enabled', [this.token.id, city_id, campaign_id]), {
enabled: enabled ? 1 : 0,
})
// this.$inertia.post(this.route('token.city.campaign.enabled', [this.token.id, city_id, campaign_id]), {
// enabled: enabled ? 1 : 0,
// })
},
cityDelete(id) {
if (confirm('Are you sure you want to delete this city?')) {
......
......@@ -191,8 +191,8 @@ Route::group(['middleware' => 'auth'], function () {
->name('token.city.campaign.updated');
Route::post('token/city/campaign/synced/{token}/{dictionary}/{campaign_id}', [TokensController::class, 'syncedCityCampaign'])
->name('token.city.campaign.synced');
Route::post('token/city/campaign/enabled/{token}/{dictionary}/{campaign_id}', [TokensController::class, 'enabledCityCampaign'])
->name('token.city.campaign.enabled');
// Route::post('token/city/campaign/enabled/{token}/{dictionary}/{campaign_id}', [TokensController::class, 'enabledCityCampaign'])
// ->name('token.city.campaign.enabled');
Route::delete('token/city/{token}/{dictionary}', [TokensController::class, 'destroyCity'])
->name('token.city.destroy');
......
......@@ -56,12 +56,6 @@ class CallLimitedApiTest extends TestCase
dispatch($process)->onQueue('limits');
Queue::assertPushed(ProcessCallLimitedAPI::class, 2);
$this->token->limit=14;
$this->request->setService('AdGroups');
$this->request->setMethod('get');
$process->handle();
Queue::assertPushed(ProcessCallLimitedAPI::class, 3);
$this->token->limit=15;
$this->request->setService('AdGroups');
$this->request->setMethod('get');
......
......@@ -131,6 +131,38 @@ class CheckCampaignsTest extends TestCase
$this->assertEquals(1, $this->dictionary->campaigns->count());
$campaign = $this->dictionary->campaigns()->first();
$campaign->pivot->update([
'external_id' => 1,
]);
$this->request->handle([
'result' => [
'Timestamp' => 1622459200,
'Campaigns' => [
[
'ChangesIn' => ['SELF', 'CHILDREN'],
'CampaignId' => 1
]
]
]
]);
$this->assertEquals($this->request->getToken()->check_changes_campaign_at->timestamp, 1622459200);
$campaign = $this->dictionary->campaigns()->first();
$this->assertEquals(true, $campaign->pivot->updated);
$this->assertNotNull($campaign->pivot->external_updated_at);
$this->assertNotNull($campaign->pivot->updated_need);
$campaign->pivot->update([
'external_updated_at' => null,
'updated_need' => null,
'updated' => false,
]);
$this->request->handle([
'result' => [
'Timestamp' => 1622459200,
......@@ -147,9 +179,16 @@ class CheckCampaignsTest extends TestCase
$campaign = $this->dictionary->campaigns()->first();
$this->assertEquals(false, $campaign->pivot->updated);
$this->assertNotNull($campaign->pivot->external_updated_at);
$this->assertNotNull($campaign->pivot->updated_self);
$this->assertNotNull($campaign->pivot->updated_children);
$this->assertNull($campaign->pivot->updated_need);
$campaign->pivot->update([
'updated' => true,
]);
$campaign = $this->dictionary->campaigns()->first();
$campaign->pivot->update([
'external_id' => 1,
......@@ -169,7 +208,7 @@ class CheckCampaignsTest extends TestCase
$campaign->pivot->update([
'synced' => false,
'updated' => false,
]);
$campaign->update([
......
......@@ -139,7 +139,15 @@ class CheckChangesAdGroupsTest extends TestCase
$this->assertEquals(0, AdGroup::forUpdatedSelf()->count());
$this->assertEquals(1, Campaigns::forUpdatedChildren()->count());
$this->assertEquals($this->ad_group->name, GoalAdGroup::first()->name);
$goalAdGroup = GoalAdGroup::first();
$goalAdGroup->update([
'external_id' => 1,
]);
$goalAdGroup->refresh();
$this->assertEquals($this->ad_group->name, $goalAdGroup->name);
$this->request_main->setParams($this->request_main_params);
......@@ -168,13 +176,10 @@ class CheckChangesAdGroupsTest extends TestCase
$this->assertEquals(0, AdGroup::forUpdatedSelf()->count());
$this->assertEquals($this->ad_group_data['result']['AdGroups'][0]['Name'], $this->ad_group->name);
$this->assertEquals($this->ad_group_data['result']['AdGroups'][0]['Name'], GoalAdGroup::first()->name);
$this->assertEquals(1, GoalAdGroup::needUpdated()->count());
$goalAdGroup = GoalAdGroup::needUpdated()->first();
$this->assertEquals(1, GoalAdGroup::needUpdated()->count());
$goalAdGroup->update([
'external_id' => 1,
]);
$goalAdGroup->refresh();
$this->request_main = APIRequest::getInstance(API::YANDEX)
->setToken($this->token_main)
......
......@@ -159,14 +159,22 @@ class LimitsTest extends TestCase
Queue::fake();
$requestCmpgn = $request->getRequest('Campaigns', 'get');
$this->token->limit = 191;
$this->token->save();
$requestCmpgn->call();
$objects = $this->limitService->countObjectsLimit($requestCmpgn);
$this->assertEquals($objects, -1);
$this->limitService->doRezerv($requestCmpgn, $objects);
$this->token->refresh();
$this->assertEquals($this->token->limit, 181);
$requestCmpgn = $request->getRequest('Campaigns', 'update');
$this->token->limit = 191;
$requestCmpgn->call([
'dictionaryCampaigns' => $this->token->dictionaryCampaignsEnabledForExternalNeedUpdated
'dictionaryCampaigns' => $this->token->dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated
]);
$objects = $this->limitService->countObjectsLimit($requestCmpgn);
$this->assertEquals($objects, 0);
......@@ -184,6 +192,18 @@ class LimitsTest extends TestCase
$request->setService('AdExtensions');
$request->setMethod('add');
$rezerv = new \App\Models\Limits();
$rezerv->token = $this->token->id;
$rezerv->service = 'dsd';
$rezerv->method = 'asdas';
$rezerv->spent = 1;
$rezerv->day = 0;
$rezerv->current = $this->token->limit;
$rezerv->reserved = 1;
$rezerv->save();
$this->token->refresh();
//т.к. для первого вызова не будет происходить проверки баллов
$this->expectException(\Exception::class);
$this->limitService->doRezerv($request, $this->token->limit);
}
......
......@@ -98,10 +98,9 @@ class ProcessCallSliceTest extends TestCase
$this->request->call($this->params);
$this->request_resume->call([
'ids' => range(0, $this->dictionaries_count - 1)
'ids' => range(1, 2005)
]);
Queue::assertPushed(ProcessCallLimitedAPI::class);
$limits = Limits::getInstance($this->request->getToken());
......@@ -113,8 +112,8 @@ class ProcessCallSliceTest extends TestCase
$requestR = $this->request->slice($maxObjects);
$this->assertEquals(true, !!$requestR);
$this->assertEquals(2, $this->request->getObjectsCount());
$this->assertEquals($this->dictionaries_count - 2, $requestR->getObjectsCount());
$this->assertEquals($this->request->getMaxCount(), $this->request->getObjectsCount());
$this->assertEquals($this->dictionaries_count - $this->request->getMaxCount(), $requestR->getObjectsCount());
$limits = Limits::getInstance($this->request_resume->getToken());
......@@ -126,8 +125,8 @@ class ProcessCallSliceTest extends TestCase
$requestR = $this->request_resume->slice($maxObjects);
$this->assertEquals(true, !!$requestR);
$this->assertEquals(2, $this->request_resume->getObjectsCount());
$this->assertEquals($this->dictionaries_count - 2, $requestR->getObjectsCount());
$this->assertEquals($this->request_resume->getMaxCount(), $this->request_resume->getObjectsCount());
$this->assertEquals(2005 - $this->request_resume->getMaxCount(), $requestR->getObjectsCount());
}
......
......@@ -67,6 +67,12 @@ class ReplaceByVariablesTest extends TestCase
$this->dictionaryCampaign = DictionaryCampaign::first();
$this->dictionaryCampaign->update([
'external_id' => 1,
]);
$this->dictionaryCampaign->refresh();
$this->assertNull($this->dictionaryCampaign->updated_need);
$this->actingAs($this->user)
......
<?php
namespace Tests\Unit;
use App\Jobs\ProcessCallLimitedAPI;
use App\Models\Account;
use App\Models\Campaigns;
use App\Models\Dictionary;
use App\Models\Pivots\DictionaryCampaign;
use App\Models\Tokens;
use App\Models\User;
use App\Service\Contract\API;
use App\Service\Requests\APIRequest;
use Carbon\Carbon;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UpdateCampaignsTest extends TestCase
{
use RefreshDatabase;
private $request;
private $user;
private $token_main;
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_main = factory(Tokens::class)->create([
'type' => Tokens::MAIN,
'created_by' => $this->user->id,
]);
$this->token = factory(Tokens::class)->create([
'limit' => 20,
'created_by' => $this->user->getKey()
]);
$this->request = APIRequest::getInstance(API::YANDEX)
->setToken($this->token_main)
->getRequest('Campaigns', 'update');
$this->campaign = factory(Campaigns::class)->create([
'manage' => true,
'token' => $this->token_main->getKey(),
]);
$this->dictionaries = factory(Dictionary::class, 12)->create([
'token_id' => $this->token->getKey(),
'type' => Dictionary::CITY,
]);
$this->campaign->dictionaries()->syncWithoutDetaching(
$this->dictionaries->keyBy(Campaigns::getModel()->getKeyName())->transform(function (Dictionary $dictionary) {
return DictionaryCampaign::copyPropertyFromMain($this->campaign);
})->all()
);
}
function test_get_campaigns_for_update(){
$dictionaryCampaign = DictionaryCampaign::first();
$dictionaryCampaign['external_id'] = 1;
$data['external_updated_at'] = Carbon::now();
if ($dictionaryCampaign->updated) {
$data['updated_need'] = Carbon::now();
}
$dictionaryCampaign->update($data);
$tokens = Tokens::whereHas('dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated')
->with('dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated.campaign')
->where('type', '!=', Tokens::MAIN)
->get();
Queue::fake();
foreach ($tokens as $token) {
$this->assertEquals($dictionaryCampaign->toArray()['external_id'], $token->dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated->toArray()[0]['external_id']);
$this->request->call([
'dictionaryCampaigns' => $token->dictionaryCampaignsEnabledForExternalUpdatedNeedUpdated,
]);
Queue::assertPushed(ProcessCallLimitedAPI::class);
}
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!