Initial commit
This commit is contained in:
15
common/codeception.yml
Normal file
15
common/codeception.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace: common\tests
|
||||
actor_suffix: Tester
|
||||
paths:
|
||||
tests: tests
|
||||
output: tests/_output
|
||||
data: tests/_data
|
||||
support: tests/_support
|
||||
bootstrap: _bootstrap.php
|
||||
settings:
|
||||
colors: true
|
||||
memory_limit: 1024M
|
||||
modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'config/codeception-local.php'
|
||||
19
common/components/PostmarkComponent.php
Normal file
19
common/components/PostmarkComponent.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace common\components;
|
||||
|
||||
use Postmark\PostmarkClient;
|
||||
use yii\base\Component;
|
||||
|
||||
class PostmarkComponent extends Component
|
||||
{
|
||||
/** @var PostmarkClient */
|
||||
public PostmarkClient $client;
|
||||
public mixed $serverToken;
|
||||
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->client = new PostmarkClient($this->serverToken);
|
||||
}
|
||||
}
|
||||
469
common/components/SonarApiComponent.php
Normal file
469
common/components/SonarApiComponent.php
Normal file
@@ -0,0 +1,469 @@
|
||||
<?php
|
||||
|
||||
namespace common\components;
|
||||
|
||||
use common\jobs\EmailJob;
|
||||
use common\models\Account;
|
||||
use common\models\InvoiceItem;
|
||||
use common\models\SalesAgent;
|
||||
use common\models\Service;
|
||||
use Yii;
|
||||
use yii\helpers\Url;
|
||||
use yii\httpclient\Client;
|
||||
use yii\httpclient\Request;
|
||||
use yii\httpclient\RequestEvent;
|
||||
|
||||
class SonarApiComponent extends \yii\base\Component
|
||||
{
|
||||
public Client $client;
|
||||
public string $baseUrl;
|
||||
public string $bearerToken;
|
||||
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->client = new Client([
|
||||
'baseUrl' => $this->baseUrl,
|
||||
'requestConfig' => [
|
||||
'format' => Client::FORMAT_JSON
|
||||
],
|
||||
'responseConfig' => [
|
||||
'format' => Client::FORMAT_JSON
|
||||
],
|
||||
]);
|
||||
|
||||
// Setup event for auth before each send
|
||||
$this->client->on(Request::EVENT_BEFORE_SEND, function (RequestEvent $event) {
|
||||
$event->request->addHeaders(['Authorization' => 'Bearer ' . $this->bearerToken]);
|
||||
});
|
||||
}
|
||||
|
||||
public function getAccounts(int $page = 1, int $limit = 100)
|
||||
{
|
||||
$data = [
|
||||
'form_params' => [
|
||||
'query' => 'query accountsWithSalesAgents(
|
||||
$paginator: Paginator,
|
||||
$search: [Search],
|
||||
$sorter: [Sorter]
|
||||
) {
|
||||
accounts(
|
||||
paginator: $paginator
|
||||
search: $search
|
||||
sorter: $sorter
|
||||
reverse_relation_filters: {
|
||||
relation: "custom_field_data",
|
||||
search: {
|
||||
integer_fields: {
|
||||
attribute: "custom_field_id",
|
||||
search_value: 12,
|
||||
operator: EQ
|
||||
}
|
||||
}
|
||||
}
|
||||
general_search_mode: ROOT_PLUS_RELATIONS
|
||||
account_status_id: 1
|
||||
) {
|
||||
entities {
|
||||
id
|
||||
name
|
||||
account_status {
|
||||
id
|
||||
name
|
||||
}
|
||||
account_services {
|
||||
entities {
|
||||
id
|
||||
quantity
|
||||
name_override
|
||||
price_override
|
||||
price_override_reason
|
||||
service {
|
||||
id
|
||||
name
|
||||
amount
|
||||
enabled
|
||||
application
|
||||
type
|
||||
}
|
||||
}
|
||||
page_info {
|
||||
page
|
||||
records_per_page
|
||||
total_count
|
||||
total_pages
|
||||
}
|
||||
}
|
||||
custom_field_data(custom_field_id:12) {
|
||||
entities {
|
||||
id
|
||||
custom_field_id
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
page_info {
|
||||
page
|
||||
records_per_page
|
||||
total_count
|
||||
total_pages
|
||||
}
|
||||
}
|
||||
}',
|
||||
'variables' => [
|
||||
'paginator' => [
|
||||
'page' => $page,
|
||||
'records_per_page' => $limit
|
||||
],
|
||||
'search' => [],
|
||||
'sorter' => [
|
||||
[
|
||||
'attribute' => 'updated_at',
|
||||
'direction' => 'ASC',
|
||||
]
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->client->createRequest()
|
||||
->setMethod('POST')
|
||||
->setData($data)
|
||||
->send();
|
||||
|
||||
$account = json_decode($response->getContent(), true);
|
||||
|
||||
return $account['form_params']['data'];
|
||||
}
|
||||
|
||||
public function getInvoices(string $startDate, string $endDate)
|
||||
{
|
||||
$page = 1;
|
||||
$limit = 100;
|
||||
$invoices = [];
|
||||
do {
|
||||
$data = [
|
||||
'form_params' => [
|
||||
'query' => 'query accountInvoice($paginator: Paginator, $search: [Search], $sorter: [Sorter]) {
|
||||
invoices(
|
||||
paginator: $paginator
|
||||
search: $search
|
||||
sorter: $sorter
|
||||
general_search_mode: ROOT_PLUS_RELATIONS
|
||||
) {
|
||||
entities {
|
||||
id
|
||||
account_id
|
||||
total_debits
|
||||
void
|
||||
remaining_due
|
||||
date
|
||||
due_date
|
||||
end_date
|
||||
delinquent
|
||||
debits {
|
||||
entities {
|
||||
id
|
||||
quantity
|
||||
service_id
|
||||
service_name
|
||||
amount
|
||||
}
|
||||
}
|
||||
credits {
|
||||
entities {
|
||||
amount
|
||||
}
|
||||
}
|
||||
}
|
||||
page_info {
|
||||
page
|
||||
records_per_page
|
||||
total_count
|
||||
total_pages
|
||||
}
|
||||
}
|
||||
}',
|
||||
'variables' => [
|
||||
'paginator' => [
|
||||
'page' => $page,
|
||||
'records_per_page' => $limit
|
||||
],
|
||||
'search' => [
|
||||
[
|
||||
'date_fields' => [
|
||||
['attribute' => 'date', 'search_value' => $startDate, 'operator' => 'GTE'],
|
||||
['attribute' => 'date', 'search_value' => $endDate, 'operator' => 'LTE'],
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
],
|
||||
'sorter' => [
|
||||
[
|
||||
'attribute' => 'updated_at',
|
||||
'direction' => 'ASC',
|
||||
]
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->client->createRequest()
|
||||
->setMethod('POST')
|
||||
->setData($data)
|
||||
->send();
|
||||
|
||||
|
||||
$responseData = json_decode($response->getContent(), true);
|
||||
$invoices = array_merge($invoices, $responseData['form_params']['data']['invoices']['entities']);
|
||||
$page++;
|
||||
} while ($page < ($responseData['form_params']['data']['invoices']['page_info']['total_pages'] + 1));
|
||||
|
||||
return $invoices;
|
||||
}
|
||||
|
||||
public function getInvoice(int $invoiceId = 1)
|
||||
{
|
||||
$page = 1;
|
||||
$limit = 100;
|
||||
$data = [
|
||||
'form_params' => [
|
||||
'query' => 'query accountInvoice($paginator: Paginator, $search: [Search], $sorter: [Sorter]) {
|
||||
invoices(
|
||||
id: ' . $invoiceId . '
|
||||
paginator: $paginator
|
||||
search: $search
|
||||
sorter: $sorter
|
||||
general_search_mode: ROOT_PLUS_RELATIONS
|
||||
) {
|
||||
entities {
|
||||
id
|
||||
account_id
|
||||
total_debits
|
||||
void
|
||||
remaining_due
|
||||
date
|
||||
due_date
|
||||
end_date
|
||||
delinquent
|
||||
debits {
|
||||
entities {
|
||||
id
|
||||
quantity
|
||||
service_id
|
||||
service_name
|
||||
amount
|
||||
}
|
||||
}
|
||||
credits {
|
||||
entities {
|
||||
amount
|
||||
}
|
||||
}
|
||||
}
|
||||
page_info {
|
||||
page
|
||||
records_per_page
|
||||
total_count
|
||||
total_pages
|
||||
}
|
||||
}
|
||||
}',
|
||||
'variables' => [
|
||||
'paginator' => [
|
||||
'page' => $page,
|
||||
'records_per_page' => $limit
|
||||
],
|
||||
'search' => [],
|
||||
'sorter' => [
|
||||
[
|
||||
'attribute' => 'updated_at',
|
||||
'direction' => 'ASC',
|
||||
]
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->client->createRequest()
|
||||
->setMethod('POST')
|
||||
->setData($data)
|
||||
->send();
|
||||
|
||||
$invoice = json_decode($response->getContent(), true);
|
||||
|
||||
return $invoice['form_params']['data']['invoices']['entities'][0];
|
||||
}
|
||||
|
||||
public function storeInvoices($invoices)
|
||||
{
|
||||
foreach ($invoices as $invoice) {
|
||||
$this->storeInvoice($invoice);
|
||||
}
|
||||
}
|
||||
|
||||
public function storeInvoice($invoice)
|
||||
{
|
||||
\Yii::debug($invoice);
|
||||
// $remainingDue is the Entire Invoice remaining to be paid amount, 0 = everything paid
|
||||
$remainingDue = $invoice['remaining_due'];
|
||||
// debits = charges on the account
|
||||
// credits = payments on the account
|
||||
|
||||
foreach ($invoice['debits']['entities'] as $i => $rawItem) {
|
||||
$invoiceItem = InvoiceItem::find()->where(['sonar_id' => (int)$rawItem['id']])->one();
|
||||
if (null === $invoiceItem) { // create new invoice item
|
||||
|
||||
$account = Account::findOne(['sonar_id' => (int)$invoice['account_id']]);
|
||||
$service = Service::findOne(['sonar_id' => (int)$rawItem['service_id']]);
|
||||
\Yii::debug($rawItem);
|
||||
if ($service && $account) {
|
||||
\Yii::debug($invoice);
|
||||
$payment = (isset($invoice['credits']['entities'][$i]['amount'])) ? $invoice['credits']['entities'][$i]['amount'] : 0;
|
||||
// @todo check payment - i think it is wrong to assume we have the same credits and debits ^ CGS
|
||||
$invoiceItem = new InvoiceItem([
|
||||
'sonar_id' => (int)$rawItem['id'],
|
||||
'account_id' => $account->id,
|
||||
'service_id' => $service->id,
|
||||
'name' => $rawItem['service_name'],
|
||||
'status' => InvoiceItem::STATUS_OPEN,
|
||||
'charge' => $rawItem['amount'],
|
||||
'payment' => $payment,
|
||||
'is_commissionable' => $service->hasCommission(),
|
||||
]);
|
||||
$invoiceItem->save();
|
||||
}
|
||||
}
|
||||
|
||||
// is the invoice item paid?
|
||||
if ($invoiceItem && $remainingDue == 0) {
|
||||
$invoiceItem->status = InvoiceItem::STATUS_PAYMENT_RECEIVED;
|
||||
$invoiceItem->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function mapAccounts($accounts)
|
||||
{
|
||||
$mapped = [];
|
||||
$i = 0;
|
||||
$db = \Yii::$app->db;
|
||||
foreach ($accounts as $account) {
|
||||
$mapped[$i]['sonar_id'] = (int)$account['id'];
|
||||
$mapped[$i]['name'] = $account['name'];
|
||||
|
||||
/**
|
||||
* [
|
||||
* 'id' => '132'
|
||||
* 'quantity' => 1
|
||||
* 'name_override' => 'Bradley'
|
||||
* 'price_override' => 0
|
||||
* 'price_override_reason' => 'testing'
|
||||
* 'service' => [
|
||||
* 'id' => '10'
|
||||
* 'name' => 'Business Giga Speed Internet'
|
||||
* 'amount' => 18000
|
||||
* 'enabled' => true
|
||||
* 'application' => 'DEBIT'
|
||||
* 'type' => 'DATA'
|
||||
* ]
|
||||
* ]
|
||||
*/
|
||||
$mapped[$i]['services'] = []; // init empty array
|
||||
|
||||
foreach ($account['account_services']['entities'] as $key => $account_service) {
|
||||
$mapped[$i]['services'][$key]['sonar_id'] = (int)$account_service['service']['id'];
|
||||
$mapped[$i]['services'][$key]['name'] = (!empty($account_service['name_override'])) ? $account_service['name_override'] : $account_service['service']['name'];
|
||||
$mapped[$i]['services'][$key]['price'] = (!empty($account_service['price_override'])) ? $account_service['price_override'] : $account_service['service']['amount'];
|
||||
if ($account_service['service']['application'] === 'CREDIT') {
|
||||
$mapped[$i]['services'][$key]['price'] = -1 * $mapped[$i]['services'][$key]['price'];// store as a negative
|
||||
}
|
||||
|
||||
// set to 0 if null after credit
|
||||
if (null === $mapped[$i]['services'][$key]['price']) {
|
||||
$mapped[$i]['services'][$key]['price'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$name = $account['custom_field_data']['entities'][0]['value'];
|
||||
$salesAgent = SalesAgent::findOne(['name' => $name]);
|
||||
if (null === $salesAgent) {
|
||||
$salesAgent = new SalesAgent(['name' => $name]);
|
||||
$salesAgent->save();
|
||||
}
|
||||
|
||||
$mapped[$i]['sales_agent_id'] = $salesAgent->id;
|
||||
|
||||
$i++;
|
||||
}
|
||||
return $mapped;
|
||||
}
|
||||
|
||||
public function storeAccounts()
|
||||
{
|
||||
$page = 1;
|
||||
do {
|
||||
$batch = $this->getAccounts($page, 100);
|
||||
$accounts = $this->mapAccounts($batch['accounts']['entities']);
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$accountModel = Account::findOne(['sonar_id' => $account['sonar_id']]);
|
||||
if (null === $accountModel) {
|
||||
$accountModel = new Account([
|
||||
'sonar_id' => $account['sonar_id'],
|
||||
'name' => $account['name'],
|
||||
'sales_agent_id' => $account['sales_agent_id'],
|
||||
]);
|
||||
$accountModel->save();
|
||||
} else {
|
||||
//$accountModel->sonar_id = $account['sonar_id'];
|
||||
$accountModel->name = $account['name'];
|
||||
$accountModel->sales_agent_id = $account['sales_agent_id'];
|
||||
$accountModel->save();
|
||||
}
|
||||
|
||||
foreach ($account['services'] as $rawServiceData) {
|
||||
$serviceModel = Service::findOne(['sonar_id' => (int)$rawServiceData['sonar_id']]);
|
||||
if (null === $serviceModel) {
|
||||
$serviceModel = new Service([
|
||||
'sonar_id' => $rawServiceData['sonar_id'],
|
||||
'name' => $rawServiceData['name'],
|
||||
'price' => $rawServiceData['price'],
|
||||
'account_id' => $accountModel->id,
|
||||
'active' => 1, // @todo pull active state in from sonar api
|
||||
]);
|
||||
$serviceModel->save();
|
||||
} else {
|
||||
$serviceModel->commission = $serviceModel->getFormattedDollar($serviceModel->commission, false);
|
||||
$serviceModel->sonar_id = $rawServiceData['sonar_id'];
|
||||
$serviceModel->name = $rawServiceData['name'];
|
||||
$serviceModel->price = $rawServiceData['price'];
|
||||
$serviceModel->account_id = $accountModel->id;
|
||||
if (!empty($serviceModel->dirtyAttributes)) {
|
||||
if (isset($serviceModel->dirtyAttributes['price'])) {
|
||||
//Yii::$app->queue->push(new EmailJob([
|
||||
// 'templateAlias' => EmailJob::PRICE_CHANGE,
|
||||
// "email" => Yii::$app->params['adminEmail'],
|
||||
// 'templateModel' => [
|
||||
// "action_edit_url" => Yii::$app->urlManager->createAbsoluteUrl(
|
||||
// ['service/update', 'id' => $serviceModel->id]
|
||||
// ),
|
||||
// ]
|
||||
//]));
|
||||
}
|
||||
}
|
||||
$serviceModel->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$page++;
|
||||
} while ($page < ($batch['accounts']['page_info']['total_pages'] + 1));
|
||||
}
|
||||
|
||||
public function processInvoices(int $invoiceId)
|
||||
{
|
||||
dump($this->getInvoice($invoiceId));
|
||||
}
|
||||
|
||||
}
|
||||
4
common/config/.gitignore
vendored
Normal file
4
common/config/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
codeception-local.php
|
||||
main-local.php
|
||||
params-local.php
|
||||
test-local.php
|
||||
37
common/config/__autocomplete.php
Normal file
37
common/config/__autocomplete.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This class only exists here for IDE (PHPStorm/Netbeans/...) autocompletion.
|
||||
* This file is never included anywhere.
|
||||
* Adjust this file to match classes configured in your application config, to enable IDE autocompletion for custom components.
|
||||
* Example: A property phpdoc can be added in `__Application` class as `@property \vendor\package\Rollbar|__Rollbar $rollbar` and adding a class in this file
|
||||
* ```php
|
||||
* // @property of \vendor\package\Rollbar goes here
|
||||
* class __Rollbar {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Yii {
|
||||
/**
|
||||
* @var \yii\web\Application|\yii\console\Application|__Application
|
||||
*/
|
||||
public static $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* @property yii\rbac\DbManager $authManager
|
||||
* @property \Da\User\Model\User $user
|
||||
* @property \common\components\SonarApiComponent $sonar
|
||||
* @property \common\components\HubspotApiComponent $hubspot
|
||||
* @property \common\components\PostmarkComponent $postmark
|
||||
* @property \yii\queue\db\Queue $queue
|
||||
*
|
||||
*/
|
||||
class __Application {
|
||||
}
|
||||
|
||||
/**
|
||||
* @property app\models\User $identity
|
||||
*/
|
||||
class __WebUser {
|
||||
}
|
||||
5
common/config/bootstrap.php
Normal file
5
common/config/bootstrap.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
Yii::setAlias('@common', dirname(__DIR__));
|
||||
Yii::setAlias('@frontend', dirname(dirname(__DIR__)) . '/frontend');
|
||||
Yii::setAlias('@backend', dirname(dirname(__DIR__)) . '/api');
|
||||
Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');
|
||||
44
common/config/main.php
Normal file
44
common/config/main.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use common\components\PostmarkComponent;
|
||||
use common\components\SonarApiComponent;
|
||||
use common\components\HubspotApiComponent;
|
||||
use yii\caching\FileCache;
|
||||
use yii\queue\db\Queue;
|
||||
|
||||
$params = array_merge(
|
||||
require __DIR__ . '/params.php',
|
||||
require __DIR__ . '/params-local.php'
|
||||
);
|
||||
|
||||
return [
|
||||
'name' => $params['company_name'] . ' - ' . $params['product_name'],
|
||||
'aliases' => [
|
||||
'@bower' => '@vendor/bower-asset',
|
||||
'@npm' => '@vendor/npm-asset',
|
||||
],
|
||||
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
|
||||
'components' => [
|
||||
'cache' => [
|
||||
'class' => FileCache::class,
|
||||
],
|
||||
'sonar' => [
|
||||
'class' => SonarApiComponent::class,
|
||||
'baseUrl' => $params['sonar.url'] . '/api/graphql',
|
||||
'bearerToken' => $params['sonar.bearerToken'],
|
||||
],
|
||||
'postmark' => [
|
||||
'class' => PostmarkComponent::class,
|
||||
'serverToken' => $params['postmark.serverToken'],
|
||||
],
|
||||
'authManager' => [
|
||||
'class' => 'yii\rbac\DbManager',
|
||||
],
|
||||
'urlManager' => [
|
||||
'enablePrettyUrl' => true,
|
||||
'showScriptName' => false,
|
||||
'rules' => [
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
14
common/config/params.php
Normal file
14
common/config/params.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
return [
|
||||
'adminEmail' => 'admin@example.com',
|
||||
'supportEmail' => 'support@example.com',
|
||||
'senderEmail' => 'noreply@example.com',
|
||||
'senderName' => 'Example.com mailer',
|
||||
'user.passwordResetTokenExpire' => 3600,
|
||||
'user.passwordMinLength' => 8,
|
||||
'sonar.url' => 'https://yourname.sonar.software',
|
||||
'sonar.bearerToken' => '',
|
||||
'postmark.serverToken' => 'postmark-server-key',
|
||||
'postmark.messageStream' => 'outbound',
|
||||
'sentry.dsn' => 'https://asdf@o4507934844780544.ingest.us.sentry.io/4508006893158400',
|
||||
];
|
||||
11
common/config/test.php
Normal file
11
common/config/test.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
return [
|
||||
'id' => 'app-common-tests',
|
||||
'basePath' => dirname(__DIR__),
|
||||
'components' => [
|
||||
'user' => [
|
||||
'class' => \yii\web\User::class,
|
||||
'identityClass' => 'common\models\User',
|
||||
],
|
||||
],
|
||||
];
|
||||
11
common/fixtures/AuthAssignmentFixture.php
Normal file
11
common/fixtures/AuthAssignmentFixture.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace common\fixtures;
|
||||
|
||||
use yii\test\ActiveFixture;
|
||||
|
||||
class AuthAssignmentFixture extends ActiveFixture
|
||||
{
|
||||
public $tableName = '{{%auth_assignment}}';
|
||||
public $depends = [UserFixture::class];
|
||||
}
|
||||
11
common/fixtures/UserFixture.php
Normal file
11
common/fixtures/UserFixture.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace common\fixtures;
|
||||
|
||||
use yii\test\ActiveFixture;
|
||||
|
||||
class UserFixture extends ActiveFixture
|
||||
{
|
||||
public $modelClass = 'common\models\User';
|
||||
public $depends = [];
|
||||
}
|
||||
12
common/fixtures/data/auth_assignment.php
Normal file
12
common/fixtures/data/auth_assignment.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
'item_name' => 'admin',
|
||||
'user_id' => 1,
|
||||
],
|
||||
[
|
||||
'item_name' => 'user',
|
||||
'user_id' => 2,
|
||||
],
|
||||
];
|
||||
22
common/fixtures/data/user.php
Normal file
22
common/fixtures/data/user.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
//password
|
||||
'password_hash' => '$2y$13$k2hQ8aV/z6V0Y7pRbq1ufOUSaJI7EhNvvTUIoj2s/rxAmgyY95KPa',
|
||||
'email' => 'admin@example.com',
|
||||
'auth_key' => '1',
|
||||
'status' => '1',
|
||||
'created_at' => '1402312317',
|
||||
'updated_at' => '1402312317',
|
||||
],
|
||||
[
|
||||
//password
|
||||
'password_hash' => '$2y$13$k2hQ8aV/z6V0Y7pRbq1ufOUSaJI7EhNvvTUIoj2s/rxAmgyY95KPa',
|
||||
'email' => 'user@example.com',
|
||||
'auth_key' => '1',
|
||||
'status' => '1',
|
||||
'created_at' => '1402312317',
|
||||
'updated_at' => '1402312317',
|
||||
],
|
||||
];
|
||||
65
common/jobs/EmailJob.php
Normal file
65
common/jobs/EmailJob.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace common\jobs;
|
||||
|
||||
use Yii;
|
||||
use yii\base\BaseObject;
|
||||
use yii\queue\RetryableJobInterface;
|
||||
|
||||
class EmailJob extends BaseObject implements RetryableJobInterface
|
||||
{
|
||||
|
||||
const MAX_ATTEMPTS = 3;
|
||||
const TTR = 600; // in seconds
|
||||
|
||||
public const PASSWORD_RESET = 'password-reset';
|
||||
public const PASSWORD_HAS_BEEN_RESET = 'password-has-been-reset';
|
||||
public const WELCOME_EMAIL = 'welcome-email';
|
||||
public const VERIFY_EMAIL = 'verify-email';
|
||||
public const ADMIN_NOTIFY = 'admin-new-user';
|
||||
public const PRICE_CHANGE = 'price-change';
|
||||
public const PAYOUT_NOTIFY = 'payout-notify';
|
||||
public array $templateModel;
|
||||
public string $email;
|
||||
public string $templateAlias;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute($queue)
|
||||
{
|
||||
// Merge these values which are always on emails
|
||||
$this->templateModel = array_merge($this->templateModel, [
|
||||
"product" => Yii::$app->params['product_name'],
|
||||
"product_name" => Yii::$app->params['product_name'],
|
||||
"support_url" => Yii::$app->params['support_url'],
|
||||
"product_url" => Yii::$app->urlManager->createAbsoluteUrl(['site/index']),
|
||||
"company_name" => Yii::$app->params['company_name'],
|
||||
"company_address" => Yii::$app->params['company_address'],
|
||||
]);
|
||||
|
||||
Yii::$app->postmark->client->sendEmailWithTemplate(
|
||||
Yii::$app->params['supportEmail'],
|
||||
$this->email,
|
||||
$this->templateAlias,
|
||||
$this->templateModel,
|
||||
messageStream: Yii::$app->params['postmark.messageStream']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getTtr()
|
||||
{
|
||||
return self::TTR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function canRetry($attempt, $error)
|
||||
{
|
||||
return ($attempt < self::MAX_ATTEMPTS);
|
||||
}
|
||||
}
|
||||
13
common/models/AuthAssignment.php
Normal file
13
common/models/AuthAssignment.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace common\models;
|
||||
|
||||
use yii\db\ActiveRecord;
|
||||
|
||||
class AuthAssignment extends ActiveRecord
|
||||
{
|
||||
public static function tableName()
|
||||
{
|
||||
return '{{%auth_assignment}}';
|
||||
}
|
||||
}
|
||||
79
common/models/LoginForm.php
Normal file
79
common/models/LoginForm.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace common\models;
|
||||
|
||||
use Yii;
|
||||
use yii\base\Model;
|
||||
|
||||
/**
|
||||
* Login form
|
||||
*/
|
||||
class LoginForm extends Model
|
||||
{
|
||||
public $email;
|
||||
public $password;
|
||||
public $rememberMe = true;
|
||||
|
||||
private $_user;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
// username and password are both required
|
||||
[['email', 'password'], 'required'],
|
||||
// rememberMe must be a boolean value
|
||||
['rememberMe', 'boolean'],
|
||||
// password is validated by validatePassword()
|
||||
['password', 'validatePassword'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the password.
|
||||
* This method serves as the inline validation for password.
|
||||
*
|
||||
* @param string $attribute the attribute currently being validated
|
||||
* @param array $params the additional name-value pairs given in the rule
|
||||
*/
|
||||
public function validatePassword($attribute, $params)
|
||||
{
|
||||
if (!$this->hasErrors()) {
|
||||
$user = $this->getUser();
|
||||
if (!$user || !$user->validatePassword($this->password)) {
|
||||
$this->addError($attribute, 'Incorrect email or password.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in a user using the provided email and password.
|
||||
*
|
||||
* @return bool whether the user is logged in successfully
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
if ($this->validate()) {
|
||||
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds user by [[email]]
|
||||
*
|
||||
* @return User|null
|
||||
*/
|
||||
protected function getUser()
|
||||
{
|
||||
if ($this->_user === null) {
|
||||
$this->_user = User::findByEmail($this->email);
|
||||
}
|
||||
|
||||
return $this->_user;
|
||||
}
|
||||
}
|
||||
61
common/models/Meal.php
Normal file
61
common/models/Meal.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace common\models;
|
||||
|
||||
/**
|
||||
* This is the model class for table "meal".
|
||||
*
|
||||
* @property int $id
|
||||
* @property string $file_name
|
||||
* @property int $calories
|
||||
* @property int $protein
|
||||
* @property int $fat
|
||||
* @property int $carbohydrates
|
||||
* @property int $fiber
|
||||
* @property int $meal
|
||||
* @property int $created_at
|
||||
* @property int $updated_at
|
||||
*/
|
||||
class Meal extends \yii\db\ActiveRecord
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function tableName()
|
||||
{
|
||||
return 'meal';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
[['file_name', 'calories', 'protein', 'fat', 'carbohydrates', 'fiber', 'meal', 'created_at', 'updated_at'], 'required'],
|
||||
[['calories', 'protein', 'fat', 'carbohydrates', 'fiber', 'meal', 'created_at', 'updated_at'], 'integer'],
|
||||
[['file_name'], 'string', 'max' => 255],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function attributeLabels()
|
||||
{
|
||||
return [
|
||||
'id' => 'ID',
|
||||
'file_name' => 'File Name',
|
||||
'calories' => 'Calories',
|
||||
'protein' => 'Protein',
|
||||
'fat' => 'Fat',
|
||||
'carbohydrates' => 'Carbohydrates',
|
||||
'fiber' => 'Fiber',
|
||||
'meal' => 'Meal',
|
||||
'created_at' => 'Created At',
|
||||
'updated_at' => 'Updated At',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
332
common/models/User.php
Normal file
332
common/models/User.php
Normal file
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
|
||||
namespace common\models;
|
||||
|
||||
use common\jobs\EmailJob;
|
||||
use Yii;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\behaviors\TimestampBehavior;
|
||||
use yii\db\ActiveRecord;
|
||||
use yii\db\AfterSaveEvent;
|
||||
use yii\web\IdentityInterface;
|
||||
|
||||
/**
|
||||
* User model
|
||||
*
|
||||
* @property integer $id
|
||||
* @property string $password_hash
|
||||
* @property string $password_reset_token
|
||||
* @property string $verification_token
|
||||
* @property string $email
|
||||
* @property string $first_name
|
||||
* @property string $name
|
||||
* @property string $auth_key
|
||||
* @property integer $status
|
||||
* @property integer $sales_agent_id
|
||||
* @property integer $created_at
|
||||
* @property integer $updated_at
|
||||
* @property string $password write-only password
|
||||
*/
|
||||
class User extends ActiveRecord implements IdentityInterface
|
||||
{
|
||||
// User statuses
|
||||
public const STATUS_UNVERIFIED = 10;
|
||||
public const STATUS_INACTIVE = 0;
|
||||
public const STATUS_ACTIVE = 1;
|
||||
public const STATUS_VERIFIED = 2;
|
||||
|
||||
public const PAYOUT_INTERVAL_MONTHLY = 0;
|
||||
public array $userStatusArray;
|
||||
public string $role = '';
|
||||
public bool $welcomeEmailSent = false;
|
||||
public string $firstName;
|
||||
|
||||
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
|
||||
$this->userStatusArray = [
|
||||
self::STATUS_UNVERIFIED => Yii::t('app', 'Unverified'),
|
||||
self::STATUS_INACTIVE => Yii::t('app', 'Inactive'),
|
||||
self::STATUS_ACTIVE => Yii::t('app', 'Active'),
|
||||
self::STATUS_VERIFIED => Yii::t('app', 'Verified (not active)'),
|
||||
];
|
||||
|
||||
// register event
|
||||
$this->on(self::EVENT_AFTER_INSERT, [$this, 'emailTrigger']);
|
||||
$this->on(self::EVENT_AFTER_UPDATE, [$this, 'emailTrigger']);
|
||||
}
|
||||
|
||||
|
||||
public function emailTrigger(AfterSaveEvent $event)
|
||||
{
|
||||
if ($event->sender->status == self::STATUS_ACTIVE && !$event->sender->welcome_email_sent) {
|
||||
Yii::$app->queue->push(new EmailJob([
|
||||
'templateAlias' => EmailJob::WELCOME_EMAIL,
|
||||
'email' => $event->sender->email,
|
||||
'templateModel' => [
|
||||
"name" => $event->sender->first_name,
|
||||
"action_url" => Yii::$app->urlManager->createAbsoluteUrl(['site/login']),
|
||||
]
|
||||
]));
|
||||
|
||||
$event->sender->welcome_email_sent = true;
|
||||
$event->sender->save(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function tableName()
|
||||
{
|
||||
return '{{%user}}';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
['status', 'default', 'value' => self::STATUS_UNVERIFIED],
|
||||
[['email'], 'email'],
|
||||
[['email'], 'unique'],
|
||||
[['sales_agent_id', 'created_at', 'updated_at'], 'integer'],
|
||||
[
|
||||
'sales_agent_id',
|
||||
'required',
|
||||
'when' => function ($model) {
|
||||
return $model->role === 'sales-agent';
|
||||
},
|
||||
'whenClient' => "function (attribute, value) {
|
||||
return $('#role').val() == 'sales-agent';
|
||||
}"
|
||||
],
|
||||
[
|
||||
'status',
|
||||
'in',
|
||||
'range' => [
|
||||
self::STATUS_ACTIVE,
|
||||
self::STATUS_UNVERIFIED,
|
||||
self::STATUS_VERIFIED,
|
||||
self::STATUS_INACTIVE,
|
||||
]
|
||||
],
|
||||
[['role'], 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
public function afterSave($insert, $changedAttributes)
|
||||
{
|
||||
$auth = Yii::$app->authManager;
|
||||
|
||||
// delete exiting roles if set
|
||||
$auth->revokeAll($this->id);
|
||||
// assign new role
|
||||
if (!empty($this->role)) {
|
||||
$auth->assign($auth->getRole($this->role), $this->id);
|
||||
}
|
||||
|
||||
parent::afterSave($insert, $changedAttributes);
|
||||
}
|
||||
|
||||
public function afterFind()
|
||||
{
|
||||
$rolesAssignedToUser = Yii::$app->authManager->getRolesByUser($this->id);
|
||||
// we only use one role for the user
|
||||
if (!empty($rolesAssignedToUser)) {
|
||||
$this->role = array_key_first($rolesAssignedToUser);
|
||||
}
|
||||
|
||||
parent::afterFind();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function behaviors()
|
||||
{
|
||||
return [
|
||||
TimestampBehavior::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get names for dropdown lists
|
||||
*
|
||||
* @param $dropdown
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getStatusName($dropdown = false)
|
||||
{
|
||||
return $dropdown ? $this->userStatusArray : $this->userStatusArray[$this->status];
|
||||
}
|
||||
|
||||
public function getAuthAssignment()
|
||||
{
|
||||
return $this->hasOne(\common\models\AuthAssignment::class, ['user_id' => 'id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function findIdentity($id)
|
||||
{
|
||||
return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds user by email
|
||||
*
|
||||
* @param string $email
|
||||
* @return ActiveRecord|array|null
|
||||
*/
|
||||
public static function findByEmail($email)
|
||||
{
|
||||
return static::findOne(['email' => $email, 'status' => self::STATUS_ACTIVE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function findIdentityByAccessToken($token, $type = null)
|
||||
{
|
||||
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds user by password reset token
|
||||
*
|
||||
* @param string $token password reset token
|
||||
* @return ActiveRecord|array|null
|
||||
*/
|
||||
public static function findByPasswordResetToken($token)
|
||||
{
|
||||
if (!static::isPasswordResetTokenValid($token)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::findOne(['password_reset_token' => $token, 'status' => self::STATUS_ACTIVE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds user by verification email token
|
||||
*
|
||||
* @param string $token verify email token
|
||||
* @return static|null
|
||||
*/
|
||||
public static function findByVerificationToken($token)
|
||||
{
|
||||
return static::findOne([
|
||||
'verification_token' => $token,
|
||||
'status' => self::STATUS_UNVERIFIED
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds out if password reset token is valid
|
||||
*
|
||||
* @param string $token password reset token
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPasswordResetTokenValid($token)
|
||||
{
|
||||
if (empty($token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$timestamp = (int)substr($token, strrpos($token, '_') + 1);
|
||||
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
|
||||
return $timestamp + $expire >= time();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->getPrimaryKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAuthKey()
|
||||
{
|
||||
return $this->auth_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAuthKey($authKey)
|
||||
{
|
||||
return $this->getAuthKey() === $authKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates password
|
||||
*
|
||||
* @param string $password password to validate
|
||||
* @return bool if password provided is valid for current user
|
||||
*/
|
||||
public function validatePassword($password)
|
||||
{
|
||||
return Yii::$app->security->validatePassword($password, $this->password_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates password hash from password and sets it to the model
|
||||
*
|
||||
* @param string $password
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates "remember me" authentication key
|
||||
*/
|
||||
public function generateAuthKey()
|
||||
{
|
||||
$this->auth_key = Yii::$app->security->generateRandomString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates new password reset token
|
||||
*/
|
||||
public function generatePasswordResetToken()
|
||||
{
|
||||
$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates new token for email verification
|
||||
*/
|
||||
public function generateEmailVerificationToken()
|
||||
{
|
||||
$this->verification_token = Yii::$app->security->generateRandomString() . '_' . time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes password reset token
|
||||
*/
|
||||
public function removePasswordResetToken()
|
||||
{
|
||||
$this->password_reset_token = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets query for [[SalesAgent]].
|
||||
*
|
||||
* @return \yii\db\ActiveQuery|yii\db\ActiveQuery
|
||||
*/
|
||||
public function getSalesAgent()
|
||||
{
|
||||
return $this->hasOne(SalesAgent::class, ['id' => 'sales_agent_id']);
|
||||
}
|
||||
}
|
||||
75
common/models/search/User.php
Normal file
75
common/models/search/User.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace common\models\search;
|
||||
|
||||
use common\models\User as UserModel;
|
||||
use Yii;
|
||||
use yii\base\Model;
|
||||
use yii\data\ActiveDataProvider;
|
||||
use common\models\SalesAgent;
|
||||
|
||||
|
||||
class User extends UserModel
|
||||
{
|
||||
public string $role = '';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
[['id', 'status'], 'integer'],
|
||||
[['email', 'first_name', 'role'], 'safe'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates data provider instance with search query applied
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return ActiveDataProvider
|
||||
*/
|
||||
public function search($params)
|
||||
{
|
||||
$query = UserModel::find()
|
||||
->joinWith('authAssignment', 'salesAgent');
|
||||
|
||||
$dataProvider = new ActiveDataProvider([
|
||||
'query' => $query,
|
||||
]);
|
||||
|
||||
$dataProvider->sort->attributes['salesAgentName'] = [
|
||||
'asc' => ['sales_agent_id' => SORT_ASC],
|
||||
'desc' => ['sales_agent_id' => SORT_DESC],
|
||||
];
|
||||
|
||||
$dataProvider->sort->attributes['role'] = [
|
||||
'asc' => ['auth_assignment.item_name' => SORT_ASC],
|
||||
'desc' => ['auth_assignment.item_name' => SORT_DESC],
|
||||
];
|
||||
|
||||
$this->load($params);
|
||||
|
||||
if (!$this->validate()) {
|
||||
return $dataProvider;
|
||||
}
|
||||
|
||||
if (!empty($this->salesAgentName)) {
|
||||
$salesAgent = SalesAgent::find()->where(['name' => $this->salesAgentName])->one();
|
||||
$this->sales_agent_id = $salesAgent ? $salesAgent->id : null;
|
||||
}
|
||||
|
||||
$query->andFilterWhere([
|
||||
'id' => $this->id,
|
||||
'status' => $this->status,
|
||||
'sales_agent_id' => $this->sales_agent_id,
|
||||
'auth_assignment.item_name' => $this->role
|
||||
]);
|
||||
|
||||
$query->andFilterWhere(['like', 'email', $this->email]);
|
||||
|
||||
return $dataProvider;
|
||||
}
|
||||
}
|
||||
16
common/rbac/SalesAgentRule.php
Normal file
16
common/rbac/SalesAgentRule.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace common\rbac;
|
||||
|
||||
class SalesAgentRule extends \yii\rbac\Rule
|
||||
{
|
||||
public $name = 'assignedToSalesAgent';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute($user, $item, $params)
|
||||
{
|
||||
return isset($params['post']) && $params['post']->sales_agent_id == $user;
|
||||
}
|
||||
}
|
||||
9
common/tests/_bootstrap.php
Normal file
9
common/tests/_bootstrap.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true);
|
||||
defined('YII_ENV') or define('YII_ENV', 'test');
|
||||
defined('YII_APP_BASE_PATH') or define('YII_APP_BASE_PATH', __DIR__.'/../../');
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
|
||||
require __DIR__ . '/../config/bootstrap.php';
|
||||
23
common/tests/_data/user.php
Normal file
23
common/tests/_data/user.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
'auth_key' => 'HP187Mvq7Mmm3CTU80dLkGmni_FUH_lR',
|
||||
//password_0
|
||||
'password_hash' => '$2y$13$.NVUBcghbQPiEcBh4LmI7.ctT3FDofwpgrKIAfwWvsr.wBM0l6Y7u',
|
||||
'password_reset_token' => 'ExzkCOaYc1L8IOBs4wdTGGbgNiG3Wz1I_1402312317',
|
||||
'created_at' => '1402312317',
|
||||
'updated_at' => '1402312317',
|
||||
'email' => 'nicole.paucek@schultz.info',
|
||||
],
|
||||
[
|
||||
//password
|
||||
'password_hash' => '$2y$13$k2hQ8aV/z6V0Y7pRbq1ufOUSaJI7EhNvvTUIoj2s/rxAmgyY95KPa',
|
||||
'email' => 'admin@example.com',
|
||||
'auth_key' => '1',
|
||||
'status' => '1',
|
||||
'sales_agent_id' => '0',
|
||||
'created_at' => '1402312317',
|
||||
'updated_at' => '1402312317',
|
||||
],
|
||||
];
|
||||
2
common/tests/_output/.gitignore
vendored
Normal file
2
common/tests/_output/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
1
common/tests/_support/.gitignore
vendored
Normal file
1
common/tests/_support/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_generated
|
||||
26
common/tests/_support/UnitTester.php
Normal file
26
common/tests/_support/UnitTester.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace common\tests;
|
||||
|
||||
/**
|
||||
* Inherited Methods
|
||||
* @method void wantToTest($text)
|
||||
* @method void wantTo($text)
|
||||
* @method void execute($callable)
|
||||
* @method void expectTo($prediction)
|
||||
* @method void verify($prediction)
|
||||
* @method void amGoingTo($argumentation)
|
||||
* @method void am($role)
|
||||
* @method void lookForwardTo($achieveValue)
|
||||
* @method void comment($description)
|
||||
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
|
||||
*
|
||||
* @SuppressWarnings(PHPMD)
|
||||
*/
|
||||
class UnitTester extends \Codeception\Actor
|
||||
{
|
||||
use _generated\UnitTesterActions;
|
||||
/**
|
||||
* Define custom actions here
|
||||
*/
|
||||
}
|
||||
7
common/tests/unit.suite.yml
Normal file
7
common/tests/unit.suite.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
suite_namespace: common\tests\unit
|
||||
actor: UnitTester
|
||||
bootstrap: false
|
||||
modules:
|
||||
enabled:
|
||||
- Yii2:
|
||||
part: fixtures
|
||||
67
common/tests/unit/models/LoginFormTest.php
Normal file
67
common/tests/unit/models/LoginFormTest.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace unit\models;
|
||||
|
||||
use Yii;
|
||||
use common\models\LoginForm;
|
||||
use common\fixtures\UserFixture;
|
||||
|
||||
/**
|
||||
* Login form test
|
||||
*/
|
||||
class LoginFormTest extends \Codeception\Test\Unit
|
||||
{
|
||||
/**
|
||||
* @var \common\tests\UnitTester
|
||||
*/
|
||||
protected $tester;
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function _fixtures()
|
||||
{
|
||||
return [
|
||||
'user' => [
|
||||
'class' => UserFixture::class,
|
||||
'dataFile' => codecept_data_dir() . 'user.php'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function testLoginNoUser()
|
||||
{
|
||||
$model = new LoginForm([
|
||||
'email' => 'not_existing_username',
|
||||
'password' => 'not_existing_password',
|
||||
]);
|
||||
|
||||
verify($model->login())->false();
|
||||
verify(Yii::$app->user->isGuest)->true();
|
||||
}
|
||||
|
||||
public function testLoginWrongPassword()
|
||||
{
|
||||
$model = new LoginForm([
|
||||
'email' => 'nicole.paucek@schultz.info',
|
||||
'password' => 'wrong_password',
|
||||
]);
|
||||
|
||||
verify($model->login())->false();
|
||||
verify( $model->errors)->arrayHasKey('password');
|
||||
verify(Yii::$app->user->isGuest)->true();
|
||||
}
|
||||
|
||||
public function testLoginCorrect()
|
||||
{
|
||||
$model = new LoginForm([
|
||||
'email' => 'admin@example.com',
|
||||
'password' => 'password',
|
||||
]);
|
||||
|
||||
verify($model->login())->true();
|
||||
verify($model->errors)->arrayHasNotKey('password');
|
||||
verify(Yii::$app->user->isGuest)->false();
|
||||
}
|
||||
}
|
||||
45
common/tests/unit/traits/FormattedDollarTraitTest.php
Normal file
45
common/tests/unit/traits/FormattedDollarTraitTest.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace unit\traits;
|
||||
|
||||
use common\traits\FormattedDollarTrait;
|
||||
use Yii;
|
||||
|
||||
use function PHPUnit\Framework\assertEquals;
|
||||
|
||||
/**
|
||||
* Formatted dollar trait test
|
||||
*/
|
||||
class FormattedDollarTraitTest extends \Codeception\Test\Unit
|
||||
{
|
||||
/**
|
||||
* @dataProvider floatDataProvider
|
||||
* @return void
|
||||
*/
|
||||
public function testConvertToCents($test, $expected)
|
||||
{
|
||||
$mock = $this->getMockForTrait(FormattedDollarTrait::class);
|
||||
|
||||
$this->assertEquals($expected, $mock->convertToCents($test));
|
||||
}
|
||||
|
||||
public function floatDataProvider()
|
||||
{
|
||||
return [
|
||||
[12.445, 1244],
|
||||
[-13.678901234, -1367],
|
||||
["-10.4", -1040],
|
||||
["-10", -1000],
|
||||
["11.445", 1144],
|
||||
["533.3.3533.11,445", 533335331144],
|
||||
["1,40032,0030.445", 140032003044],
|
||||
[124.99, 12499],
|
||||
[-1.4, -140],
|
||||
[14, 1400],
|
||||
[.99, 99],
|
||||
[2.3, 230],
|
||||
[-30, -3000],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
76
common/widgets/Alert.php
Normal file
76
common/widgets/Alert.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace common\widgets;
|
||||
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* Alert widget renders a message from session flash. All flash messages are displayed
|
||||
* in the sequence they were assigned using setFlash. You can set message as following:
|
||||
*
|
||||
* ```php
|
||||
* Yii::$app->session->setFlash('error', 'This is the message');
|
||||
* Yii::$app->session->setFlash('success', 'This is the message');
|
||||
* Yii::$app->session->setFlash('info', 'This is the message');
|
||||
* ```
|
||||
*
|
||||
* Multiple messages could be set as follows:
|
||||
*
|
||||
* ```php
|
||||
* Yii::$app->session->setFlash('error', ['Error 1', 'Error 2']);
|
||||
* ```
|
||||
*
|
||||
* @author Kartik Visweswaran <kartikv2@gmail.com>
|
||||
* @author Alexander Makarov <sam@rmcreative.ru>
|
||||
*/
|
||||
class Alert extends \yii\bootstrap5\Widget
|
||||
{
|
||||
/**
|
||||
* @var array the alert types configuration for the flash messages.
|
||||
* This array is setup as $key => $value, where:
|
||||
* - key: the name of the session flash variable
|
||||
* - value: the bootstrap alert type (i.e. danger, success, info, warning)
|
||||
*/
|
||||
public $alertTypes = [
|
||||
'error' => 'alert-danger',
|
||||
'danger' => 'alert-danger',
|
||||
'success' => 'alert-success',
|
||||
'info' => 'alert-info',
|
||||
'warning' => 'alert-warning'
|
||||
];
|
||||
/**
|
||||
* @var array the options for rendering the close button tag.
|
||||
* Array will be passed to [[\yii\bootstrap\Alert::closeButton]].
|
||||
*/
|
||||
public $closeButton = [];
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$session = Yii::$app->session;
|
||||
$flashes = $session->getAllFlashes();
|
||||
$appendClass = isset($this->options['class']) ? ' ' . $this->options['class'] : '';
|
||||
|
||||
foreach ($flashes as $type => $flash) {
|
||||
if (!isset($this->alertTypes[$type])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ((array) $flash as $i => $message) {
|
||||
echo \yii\bootstrap5\Alert::widget([
|
||||
'body' => $message,
|
||||
'closeButton' => $this->closeButton,
|
||||
'options' => array_merge($this->options, [
|
||||
'id' => $this->getId() . '-' . $type . '-' . $i,
|
||||
'class' => $this->alertTypes[$type] . $appendClass,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
$session->removeFlash($type);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user