3 | # Read more: https://github.com/blog/1509-personal-api-tokens
4 | # You can generate it here: https://github.com/settings/tokens
5 |
6 | # Guest OS timezone
7 | timezone: Europe/London
8 |
9 | # Are we need check box updates for every 'vagrant up'?
10 | box_check_update: false
11 |
12 | # Virtual machine name
13 | machine_name: yii2basic
14 |
15 | # Virtual machine IP
16 | ip: 192.168.83.137
17 |
18 | # Virtual machine CPU cores number
19 | cpus: 1
20 |
21 | # Virtual machine RAM
22 | memory: 1024
23 |
--------------------------------------------------------------------------------
/migrations/m200905_232204_drop_updated_at_operation.php:
--------------------------------------------------------------------------------
1 | dropColumn('operation', 'updated_at');
16 | }
17 |
18 | /**
19 | * {@inheritdoc}
20 | */
21 | public function safeDown()
22 | {
23 | $this->addColumn('operations', 'updated_at', $this->dateTime()->after('created_at'));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/migrations/m200913_205947_add_is_paid_expense.php:
--------------------------------------------------------------------------------
1 | addColumn('expense', 'is_paid', $this->tinyInteger(1)->defaultValue(0)->after('payday'));
16 | }
17 |
18 | /**
19 | * {@inheritdoc}
20 | */
21 | public function safeDown()
22 | {
23 | $this->dropColumn('expense', 'is_paid');
24 | return false;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/yii:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | run();
21 | exit($exitCode);
22 |
--------------------------------------------------------------------------------
/rbac/UserGroupRule.php:
--------------------------------------------------------------------------------
1 | user->isGuest) {
15 | $is_manager = Yii::$app->user->identity->is_manager;
16 | if ($item->name === 'admin')
17 | return $is_manager == 1;
18 | else if ($item->name === 'cashier')
19 | return $is_manager == 1 || $is_manager == 0;
20 | }
21 |
22 | return false;
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/acceptance/LoginCest.php:
--------------------------------------------------------------------------------
1 | amOnPage(Url::toRoute('/site/login'));
10 | $I->see('Login', 'h1');
11 |
12 | $I->amGoingTo('try to login with correct credentials');
13 | $I->fillField('input[name="LoginForm[username]"]', 'admin');
14 | $I->fillField('input[name="LoginForm[password]"]', 'admin');
15 | $I->click('login-button');
16 | $I->wait(2); // wait for button to be clicked
17 |
18 | $I->expectTo('see user info');
19 | $I->see('Logout');
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/components/validators/ProductOutputValidator.php:
--------------------------------------------------------------------------------
1 | product_id)->amount;
14 |
15 | if ($model->$attribute > $amount) {
16 | $this->addError($model, $attribute, Yii::t('app', 'The quantity cannot be greater than the total quantity of the product in stock.', [
17 | 'attribute' => $attribute
18 | ]));
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/_support/FunctionalTester.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update Category: {name}', [
9 | 'name' => $model->name,
10 | ]);
11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Categories'), 'url' => ['index']];
12 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]];
13 | $this->params['breadcrumbs'][] = Yii::t('app', 'Update');
14 | ?>
15 |
16 |
17 |
= Html::encode($this->title) ?>
18 |
19 | = $this->render('_form', [
20 | 'model' => $model,
21 | ]) ?>
22 |
23 |
24 |
--------------------------------------------------------------------------------
/views/variation/update.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update Variation: {name}', [
9 | 'name' => $model->name,
10 | ]);
11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Variation'), 'url' => ['index']];
12 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]];
13 | $this->params['breadcrumbs'][] = Yii::t('app', 'Update');
14 | ?>
15 |
16 |
17 |
= Html::encode($this->title) ?>
18 |
19 | = $this->render('_form', [
20 | 'model' => $model,
21 | ]) ?>
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/_support/UnitTester.php:
--------------------------------------------------------------------------------
1 | andWhere(['is_deleted' => 0])
13 | ->all();
14 | $terms = explode(' ', $term);
15 | return array_filter($query, function($product) use ($terms) {
16 | foreach ($terms as $term) {
17 | $name = (string)$product;
18 | if (!preg_match("/{$term}/i", $name))
19 | return false;
20 | }
21 | return true;
22 | });
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/migrations/m201102_181500_drop_note_and_is_quotation_order.php:
--------------------------------------------------------------------------------
1 | dropColumn('order', 'note');
16 | $this->dropColumn('order', 'is_quotation');
17 | }
18 |
19 | /**
20 | * {@inheritdoc}
21 | */
22 | public function safeDown()
23 | {
24 | $this->addColumn('order', 'note', $this->text());
25 | $this->addColumn('order', 'is_quotation', $this->tinyInteger(1)->defaultValue(0));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/_support/AcceptanceTester.php:
--------------------------------------------------------------------------------
1 |
10 |
11 |
--------------------------------------------------------------------------------
/views/expense/create.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Create Expense');
9 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Expenses'), 'url' => ['index']];
10 | $this->params['breadcrumbs'][] = $this->title;
11 | ?>
12 |
13 |
14 |
15 |
18 | = $this->render('_form', [
19 | 'model' => $model,
20 | ]) ?>
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/views/product/create.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Create Product');
9 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Products'), 'url' => ['index']];
10 | $this->params['breadcrumbs'][] = $this->title;
11 | ?>
12 |
13 |
14 |
15 |
18 | = $this->render('_form', [
19 | 'model' => $model,
20 | ]) ?>
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/views/company/update.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update Company');
9 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['index']];
10 | $this->params['breadcrumbs'][] = Yii::t('app', 'Update');
11 | ?>
12 |
13 |
14 |
15 |
16 |
19 | = $this->render('_form', [
20 | 'model' => $model,
21 | ]) ?>
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/views/employee/create.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Create Employee');
9 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Employees'), 'url' => ['index']];
10 | $this->params['breadcrumbs'][] = $this->title;
11 | ?>
12 |
13 |
14 |
15 |
18 | = $this->render('_form', [
19 | 'model' => $model,
20 | ]) ?>
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/migrations/m201016_130100_add_employee_id_sale.php:
--------------------------------------------------------------------------------
1 | addColumn('sale', 'employee_id', $this->integer()->unsigned()->notNull()->after('updated_at'));
16 | $this->addForeignKey('fk-sale-employee_id', 'sale', 'employee_id', 'employee', 'id', 'CASCADE');
17 |
18 | }
19 |
20 | /**
21 | * {@inheritdoc}
22 | */
23 | public function safeDown()
24 | {
25 | $this->dropForeignKey('fk-sale-employee_id', 'sale');
26 | $this->dropColumn('sale', 'employee_id');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/views/payment-method/create.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Create Payment Method');
9 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Payment Methods'), 'url' => ['index']];
10 | $this->params['breadcrumbs'][] = $this->title;
11 | ?>
12 |
13 |
14 |
15 |
18 | = $this->render('_form', [
19 | 'model' => $model,
20 | ]) ?>
21 |
22 |
23 |
--------------------------------------------------------------------------------
/messages/pt-BR/kvgrid.php:
--------------------------------------------------------------------------------
1 | '',
21 | ];
22 |
--------------------------------------------------------------------------------
/views/company/address.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update Company Address');
9 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['index']];
10 | $this->params['breadcrumbs'][] = Yii::t('app', 'Update Address');
11 |
12 | ?>
13 |
14 |
15 |
16 |
17 |
20 | = $this->render('/address/_form', [
21 | 'model' => $address,
22 | ]) ?>
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/mail/layouts/html.php:
--------------------------------------------------------------------------------
1 |
8 | beginPage() ?>
9 |
10 |
11 |
12 |
13 | = Html::encode($this->title) ?>
14 | head() ?>
15 |
16 |
17 | beginBody() ?>
18 | = $content ?>
19 | endBody() ?>
20 |
21 |
22 | endPage() ?>
23 |
--------------------------------------------------------------------------------
/vagrant/provision/once-as-vagrant.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | #== Import script args ==
4 |
5 | github_token=$(echo "$1")
6 |
7 | #== Bash helpers ==
8 |
9 | function info {
10 | echo " "
11 | echo "--> $1"
12 | echo " "
13 | }
14 |
15 | #== Provision script ==
16 |
17 | info "Provision-script user: `whoami`"
18 |
19 | info "Configure composer"
20 | composer config --global github-oauth.github.com ${github_token}
21 | echo "Done!"
22 |
23 | info "Install project dependencies"
24 | cd /app
25 | composer --no-progress --prefer-dist install
26 |
27 | info "Create bash-alias 'app' for vagrant user"
28 | echo 'alias app="cd /app"' | tee /home/vagrant/.bash_aliases
29 |
30 | info "Enabling colorized prompt for guest console"
31 | sed -i "s/#force_color_prompt=yes/force_color_prompt=yes/" /home/vagrant/.bashrc
32 |
--------------------------------------------------------------------------------
/migrations/m200906_192639_add_soft_delete_operation.php:
--------------------------------------------------------------------------------
1 | addColumn('operation', 'deleted_at', $this->dateTime()->after('created_at'));
16 | $this->addColumn('operation', 'is_deleted', $this->tinyInteger(1)->defaultValue(0)->after('deleted_at'));
17 | }
18 |
19 | /**
20 | * {@inheritdoc}
21 | */
22 | public function safeDown()
23 | {
24 | $this->dropColumn('operation', 'deleted_at');
25 | $this->dropColumn('operation', 'is_deleted');
26 | return false;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/views/category/_form.php:
--------------------------------------------------------------------------------
1 |
11 |
12 |
30 |
--------------------------------------------------------------------------------
/components/traits/FilterTrait.php:
--------------------------------------------------------------------------------
1 | user->getIsGuest()) {
14 | if (self::isJoinsSet())
15 | self::makeInnerJoin($find);
16 |
17 | $find->andWhere(['company.id' => Yii::$app->user->identity->company_id]);
18 | }
19 |
20 | return $find;
21 | }
22 |
23 | protected function isJoinsSet()
24 | {
25 | return !empty(self::JOINS) && is_array(self::JOINS);
26 | }
27 |
28 | protected function makeInnerJoin(&$find)
29 | {
30 | foreach(self::JOINS as $join)
31 | $find->innerJoin($join['table'], $join['on']);
32 | }
33 | }
--------------------------------------------------------------------------------
/views/operation/create.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Create Operation');
9 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Operations'), 'url' => ['index']];
10 | $this->params['breadcrumbs'][] = $this->title;
11 | ?>
12 |
13 |
14 |
15 |
16 |
19 | = $this->render('_form', [
20 | 'model' => $model,
21 | 'products' => $products
22 | ]) ?>
23 |
24 |
25 |
--------------------------------------------------------------------------------
/tests/bin/yii:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | [
21 | 'db' => require __DIR__ . '/../../config/test_db.php'
22 | ]
23 | ]
24 | );
25 |
26 |
27 | $application = new yii\console\Application($config);
28 | $exitCode = $application->run();
29 | exit($exitCode);
30 |
--------------------------------------------------------------------------------
/views/variation/_form.php:
--------------------------------------------------------------------------------
1 |
11 |
12 |
33 |
--------------------------------------------------------------------------------
/messages/pt-BR/yii.php:
--------------------------------------------------------------------------------
1 | '',
21 | 'Delete' => '',
22 | 'Update' => '',
23 | 'View' => '',
24 | ];
25 |
--------------------------------------------------------------------------------
/views/site/login.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Login');
11 |
12 | ?>
13 |
14 |
15 | = Yii::t('app','Sign in to start your session') ?>
16 |
17 |
18 |
19 |
20 | = $form->field($model, 'email')->textInput() ?>
21 | = $form->field($model, 'password')->passwordInput() ?>
22 |
23 |
24 |
25 | = $form->field($model, 'rememberMe')->checkbox() ?>
26 |
27 |
28 | = Html::submitButton(Yii::t('app', 'Login'), ['class' => 'btn btn-primary btn-block', 'name' => 'login-button']) ?>
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/assets/AppAsset.php:
--------------------------------------------------------------------------------
1 |
19 | * @since 2.0
20 | */
21 | class AppAsset extends AssetBundle
22 | {
23 | public $basePath = '@webroot';
24 | public $baseUrl = '@web';
25 |
26 | public $css = [
27 | 'css/site.css',
28 | 'css/fix-toast.css'
29 | ];
30 |
31 | public $js = [
32 | ];
33 |
34 | public $depends = [
35 | YiiAsset::class,
36 | BootstrapAsset::class,
37 | BootstrapPluginAsset::class,
38 | ];
39 | }
40 |
--------------------------------------------------------------------------------
/codeception.yml:
--------------------------------------------------------------------------------
1 | actor: Tester
2 | bootstrap: _bootstrap.php
3 | paths:
4 | tests: tests
5 | log: tests/_output
6 | data: tests/_data
7 | helpers: tests/_support
8 | settings:
9 | memory_limit: 1024M
10 | colors: true
11 | modules:
12 | config:
13 | Yii2:
14 | configFile: 'config/test.php'
15 |
16 | # To enable code coverage:
17 | #coverage:
18 | # #c3_url: http://localhost:8080/index-test.php/
19 | # enabled: true
20 | # #remote: true
21 | # #remote_config: '../codeception.yml'
22 | # whitelist:
23 | # include:
24 | # - models/*
25 | # - controllers/*
26 | # - commands/*
27 | # - mail/*
28 | # blacklist:
29 | # include:
30 | # - assets/*
31 | # - config/*
32 | # - runtime/*
33 | # - vendor/*
34 | # - views/*
35 | # - web/*
36 | # - tests/*
37 |
--------------------------------------------------------------------------------
/views/expense/update.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update Expense: {name}', [
9 | 'name' => $model->name,
10 | ]);
11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Expenses'), 'url' => ['index']];
12 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]];
13 | $this->params['breadcrumbs'][] = Yii::t('app', 'Update');
14 | ?>
15 |
16 |
17 |
18 |
21 | = $this->render('_form', [
22 | 'model' => $model,
23 | ]) ?>
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/views/product/update.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update Product: {name}', [
9 | 'name' => $model->name,
10 | ]);
11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Products'), 'url' => ['index']];
12 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]];
13 | $this->params['breadcrumbs'][] = Yii::t('app', 'Update');
14 | ?>
15 |
16 |
17 |
18 |
21 | = $this->render('_form', [
22 | 'model' => $model,
23 | ]) ?>
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/views/employee/update.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update Employee: {name}', [
9 | 'name' => $model->full_name,
10 | ]);
11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Employees'), 'url' => ['index']];
12 | $this->params['breadcrumbs'][] = ['label' => $model->usual_name, 'url' => ['view', 'id' => $model->id]];
13 | $this->params['breadcrumbs'][] = Yii::t('app', 'Update');
14 | ?>
15 |
16 |
17 |
18 |
19 |
22 | = $this->render('_form', [
23 | 'model' => $model,
24 | ]) ?>
25 |
26 |
27 |
--------------------------------------------------------------------------------
/views/payment-method/update.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update Payment Method: {name}', [
9 | 'name' => $model->name,
10 | ]);
11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Payment Methods'), 'url' => ['index']];
12 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]];
13 | $this->params['breadcrumbs'][] = Yii::t('app', 'Update');
14 | ?>
15 |
16 |
17 |
18 |
21 | = $this->render('_form', [
22 | 'model' => $model,
23 | ]) ?>
24 |
25 |
26 |
--------------------------------------------------------------------------------
/views/expense/pay.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Expense Pay');
11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Expenses'), 'url' => ['index']];
12 | $this->params['breadcrumbs'][] = $this->title;
13 |
14 | ?>
15 | = Html::encode($this->title) ?>
16 |
17 |
--------------------------------------------------------------------------------
/views/variation/_search.php:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 | ['index'],
15 | 'method' => 'get',
16 | ]); ?>
17 |
18 | = $form->field($model, 'id') ?>
19 |
20 | = $form->field($model, 'name') ?>
21 |
22 | = $form->field($model, 'created_at') ?>
23 |
24 | = $form->field($model, 'updated_at') ?>
25 |
26 | = $form->field($model, 'category_id') ?>
27 |
28 |
29 | = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
30 | = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-outline-secondary']) ?>
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/views/product/category.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Select Category');
11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Products'), 'url' => ['index']];
12 | $this->params['breadcrumbs'][] = $this->title;
13 |
14 | ?>
15 |
16 |
17 |
= Html::encode($this->title) ?>
18 |
19 |
34 |
35 |
--------------------------------------------------------------------------------
/components/factories/PaymentMethodsDefaultFactory.php:
--------------------------------------------------------------------------------
1 | Yii::t('app', 'Money'),
15 | 'installment_limit' => '01',
16 | ],
17 | [
18 | 'name' => Yii::t('app','Credit Card'),
19 | 'installment_limit' => '12',
20 | ],
21 | [
22 | 'name' => Yii::t('app','Debit Card'),
23 | 'installment_limit' => '01',
24 | ],
25 | ];
26 |
27 | foreach ($payments_methods as $method) {
28 | $model = new PaymentMethod();
29 | $model->setAttributes(array_merge($method, ['company_id' => $company_id]));
30 | $model->save();
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/views/company/_form.php:
--------------------------------------------------------------------------------
1 |
11 |
12 |
35 |
--------------------------------------------------------------------------------
/migrations/m200721_145607_create_address_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%address}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'zip_code' => $this->string(9)->notNull(),
18 | 'number' => $this->string(8)->notNull(),
19 | 'street' => $this->string(64)->notNull(),
20 | 'neighborhood' => $this->string(64)->notNull(),
21 | 'city' => $this->string(64)->notNull(),
22 | 'federated_unit' => $this->string(2)->notNull(),
23 | 'complement' => $this->string(128),
24 | ]);
25 | }
26 |
27 | /**
28 | * {@inheritdoc}
29 | */
30 | public function safeDown()
31 | {
32 | $this->dropTable('{{%address}}');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/views/order/_form.php:
--------------------------------------------------------------------------------
1 |
10 |
11 |
36 |
--------------------------------------------------------------------------------
/models/Migration.php:
--------------------------------------------------------------------------------
1 | 180],
32 | [['version'], 'unique'],
33 | ];
34 | }
35 |
36 | /**
37 | * {@inheritdoc}
38 | */
39 | public function attributeLabels()
40 | {
41 | return [
42 | 'version' => Yii::t('app', 'Version'),
43 | 'apply_time' => Yii::t('app', 'Apply Time'),
44 | ];
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/migrations/m200721_145614_create_category_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%category}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'name' => $this->string(64)->notNull(),
18 | 'created_at' => $this->dateTime()->notNull(),
19 | 'updated_at' => $this->dateTime(),
20 | 'company_id' => $this->integer()->unsigned()->notNull(),
21 | ]);
22 |
23 | $this->addForeignKey('fk-category-company_id', 'category', 'company_id', 'company', 'id', 'CASCADE');
24 | }
25 |
26 | /**
27 | * {@inheritdoc}
28 | */
29 | public function safeDown()
30 | {
31 | $this->dropForeignKey('fk-category-company_id', 'category');
32 | $this->dropTable('{{%category}}');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/migrations/m200721_145616_create_variation_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%variation}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'name' => $this->string(64)->notNull(),
18 | 'created_at' => $this->dateTime()->notNull(),
19 | 'updated_at' => $this->dateTime(),
20 | 'category_id' => $this->integer()->unsigned()->notNull()
21 | ]);
22 |
23 | $this->addForeignKey('fk-variation-category_id', 'variation', 'category_id', 'category', 'id', 'CASCADE');
24 | }
25 |
26 | /**
27 | * {@inheritdoc}
28 | */
29 | public function safeDown()
30 | {
31 | $this->dropForeignKey('fk-variation-category_id', 'variation');
32 | $this->dropTable('{{%variation}}');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/components/behaviors/FormatterDateBehavior.php:
--------------------------------------------------------------------------------
1 | 'formatter',
20 | ActiveRecord::EVENT_BEFORE_UPDATE => 'formatter'
21 | ];
22 | }
23 |
24 | public function formatter()
25 | {
26 | $formatter = Yii::$app->formatter;
27 |
28 | foreach ($this->attributes as $attr => $format) {
29 | $date = $this->owner->$attr;
30 | if ($format == self::FORMAT_DATETIME)
31 | $date = $formatter->asDateTimeDefault($date);
32 | else if ($format == self::FORMAT_DATE)
33 | $date = $formatter->asDateDefault($date);
34 | $this->owner->$attr = $date;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/views/layouts/login.php:
--------------------------------------------------------------------------------
1 |
9 | beginPage() ?>
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | registerCsrfMetaTags() ?>
18 | = Html::encode($this->title) ?>
19 | head() ?>
20 |
21 |
22 |
23 | beginBody() ?>
24 |
25 |
28 |
29 |
30 | = $content ?>
31 |
32 | = Html::a(Yii::t('app', 'Create an Account'), ['signup/index']); ?>
33 |
34 |
35 |
36 |
37 | endBody() ?>
38 |
39 |
40 | endPage() ?>
--------------------------------------------------------------------------------
/migrations/m200721_145621_create_sale_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%sale}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'amount_paid' => $this->decimal(10,2),
18 | 'discount' => $this->decimal(10,2),
19 | 'is_canceled' => $this->tinyInteger(1),
20 | 'sale_at' => $this->dateTime(),
21 | 'canceled_at' => $this->dateTime(),
22 | 'updated_at' => $this->dateTime(),
23 | 'order_id' => $this->integer()->unsigned()->notNull()
24 | ]);
25 |
26 | $this->addForeignKey('fk-sale-order_id', 'sale', 'order_id', 'order', 'id', 'CASCADE');
27 | }
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function safeDown()
33 | {
34 | $this->dropForeignKey('fk-sale-order_id', 'sale');
35 | $this->dropTable('{{%sale}}');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/views/layouts/center.php:
--------------------------------------------------------------------------------
1 | registerCss(
9 | <<< CSS
10 | .lockscreen-wrapper {
11 | max-width: 600px;
12 | }
13 | CSS
14 | )
15 | ?>
16 | beginPage() ?>
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | registerCsrfMetaTags() ?>
25 | = Html::encode($this->title) ?>
26 | head() ?>
27 |
28 |
29 | beginBody() ?>
30 |
31 |
32 |
33 |
36 | = $content ?>
37 |
38 |
39 |
40 | endBody() ?>
41 |
42 |
43 |
44 | endPage() ?>
--------------------------------------------------------------------------------
/tests/acceptance/ContactCest.php:
--------------------------------------------------------------------------------
1 | amOnPage(Url::toRoute('/site/contact'));
10 | }
11 |
12 | public function contactPageWorks(AcceptanceTester $I)
13 | {
14 | $I->wantTo('ensure that contact page works');
15 | $I->see('Contact', 'h1');
16 | }
17 |
18 | public function contactFormCanBeSubmitted(AcceptanceTester $I)
19 | {
20 | $I->amGoingTo('submit contact form with correct data');
21 | $I->fillField('#contactform-name', 'tester');
22 | $I->fillField('#contactform-email', 'tester@example.com');
23 | $I->fillField('#contactform-subject', 'test subject');
24 | $I->fillField('#contactform-body', 'test content');
25 | $I->fillField('#contactform-verifycode', 'testme');
26 |
27 | $I->click('contact-button');
28 |
29 | $I->wait(2); // wait for button to be clicked
30 |
31 | $I->dontSeeElement('#contact-form');
32 | $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/views/layouts/register.php:
--------------------------------------------------------------------------------
1 |
9 | beginPage() ?>
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | registerCsrfMetaTags() ?>
18 | = Html::encode($this->title) ?>
19 | head() ?>
20 |
21 |
22 |
23 | beginBody() ?>
24 |
25 |
28 |
29 |
30 |
31 |
32 | = $content ?>
33 |
34 |
35 | = Html::a(Yii::t('app', 'I already have a membership'), ['site/login']); ?>
36 |
37 |
38 |
39 |
40 | endBody() ?>
41 |
42 |
43 | endPage() ?>
--------------------------------------------------------------------------------
/migrations/m200721_145620_create_order_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%order}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'code' => $this->string(20)->notNull(),
18 | 'total_value' => $this->decimal(10,2),
19 | 'note' => $this->text(),
20 | 'is_quotation' => $this->tinyInteger(1)->defaultValue(0),
21 | 'created_at' => $this->dateTime()->notNull(),
22 | 'updated_at' => $this->dateTime(),
23 | 'company_id' => $this->integer()->unsigned()->notNull()
24 | ]);
25 |
26 | $this->addForeignKey('fk-order-company_id', 'order', 'company_id', 'company', 'id', 'CASCADE');
27 | }
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function safeDown()
33 | {
34 | $this->dropForeignKey('fk-order-company_id', 'order');
35 | $this->dropTable('{{%order}}');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/views/employee/address.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update Company Address');
9 | // $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['index']];
10 | // $this->params['breadcrumbs'][] = Yii::t('app', 'Update Address');
11 |
12 | $this->title = Yii::t('app', 'Update Address Employee: {name}', [
13 | 'name' => $model->full_name,
14 | ]);
15 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Employees'), 'url' => ['index']];
16 | $this->params['breadcrumbs'][] = ['label' => $model->usual_name, 'url' => ['view', 'id' => $model->id]];
17 | $this->params['breadcrumbs'][] = Yii::t('app', 'Update Address');
18 | ?>
19 |
20 |
21 |
22 |
23 |
26 | = $this->render('/address/_form', [
27 | 'model' => $address,
28 | ]) ?>
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/vagrant/nginx/app.conf:
--------------------------------------------------------------------------------
1 | server {
2 | charset utf-8;
3 | client_max_body_size 128M;
4 | sendfile off;
5 |
6 | listen 80; ## listen for ipv4
7 | #listen [::]:80 default_server ipv6only=on; ## listen for ipv6
8 |
9 | server_name yii2basic.test;
10 | root /app/web/;
11 | index index.php;
12 |
13 | access_log /app/vagrant/nginx/log/yii2basic.access.log;
14 | error_log /app/vagrant/nginx/log/yii2basic.error.log;
15 |
16 | location / {
17 | # Redirect everything that isn't a real file to index.php
18 | try_files $uri $uri/ /index.php$is_args$args;
19 | }
20 |
21 | # uncomment to avoid processing of calls to non-existing static files by Yii
22 | #location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {
23 | # try_files $uri =404;
24 | #}
25 | #error_page 404 /404.html;
26 |
27 | location ~ \.php$ {
28 | include fastcgi_params;
29 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
30 | #fastcgi_pass 127.0.0.1:9000;
31 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
32 | try_files $uri =404;
33 | }
34 |
35 | location ~ /\.(ht|svn|git) {
36 | deny all;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/migrations/m200721_145622_create_pay_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%pay}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'value' => $this->decimal(10,2)->notNull(),
18 | 'installments' => $this->smallInteger()->unsigned(),
19 | 'payment_method_id' => $this->integer()->unsigned()->notNull(),
20 | 'sale_id' => $this->integer()->unsigned()->notNull(),
21 | ]);
22 |
23 | $this->addForeignKey('fk-pay-payment_method_id', 'pay', 'payment_method_id', 'payment_method', 'id', 'CASCADE');
24 | $this->addForeignKey('fk-pay-sale_id', 'pay', 'sale_id', 'sale', 'id', 'CASCADE');
25 | }
26 |
27 | /**
28 | * {@inheritdoc}
29 | */
30 | public function safeDown()
31 | {
32 | $this->dropForeignKey('fk-pay-payment_method_id', 'pay');
33 | $this->dropForeignKey('fk-pay-sale_id', 'pay');
34 | $this->dropTable('{{%pay}}');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/migrations/m200721_145612_create_expense_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%expense}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'name' => $this->string(64)->notNull(),
18 | 'description' => $this->text(),
19 | 'value' => $this->decimal(10,2)->notNull(),
20 | 'payday' => $this->date()->notNull(),
21 | 'paid_at' => $this->date(),
22 | 'created_at' => $this->dateTime()->notNull(),
23 | 'updated_at' => $this->dateTime(),
24 | 'company_id' => $this->integer()->unsigned()->notNull(),
25 | ]);
26 |
27 | $this->addForeignKey('fk-expense-company_id', 'expense', 'company_id', 'company', 'id', 'CASCADE');
28 | }
29 |
30 | /**
31 | * {@inheritdoc}
32 | */
33 | public function safeDown()
34 | {
35 | $this->dropForeignKey('fk-expense-company_id', 'expense');
36 | $this->dropTable('{{%expense}}');
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/config/test.php:
--------------------------------------------------------------------------------
1 | 'basic-tests',
10 | 'basePath' => dirname(__DIR__),
11 | 'aliases' => [
12 | '@bower' => '@vendor/bower-asset',
13 | '@npm' => '@vendor/npm-asset',
14 | ],
15 | 'language' => 'en-US',
16 | 'components' => [
17 | 'db' => $db,
18 | 'mailer' => [
19 | 'useFileTransport' => true,
20 | ],
21 | 'assetManager' => [
22 | 'basePath' => __DIR__ . '/../web/assets',
23 | ],
24 | 'urlManager' => [
25 | 'showScriptName' => true,
26 | ],
27 | 'user' => [
28 | 'identityClass' => 'app\models\User',
29 | ],
30 | 'request' => [
31 | 'cookieValidationKey' => 'test',
32 | 'enableCsrfValidation' => false,
33 | // but if you absolutely need it set cookie domain to localhost
34 | /*
35 | 'csrfCookie' => [
36 | 'domain' => 'localhost',
37 | ],
38 | */
39 | ],
40 | ],
41 | 'params' => $params,
42 | ];
43 |
--------------------------------------------------------------------------------
/migrations/m201005_185935_create_order_item_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%order_item}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'amount' => $this->decimal(10,2)->notNull(),
18 | 'unit_price' => $this->decimal(10,2)->notNull(),
19 | 'order_id' => $this->integer()->unsigned()->notNull(),
20 | 'product_id' => $this->integer()->unsigned()->notNull(),
21 | ]);
22 |
23 | $this->addForeignKey('fk-pay-order_id', 'order_item', 'order_id', 'order', 'id', 'CASCADE');
24 | $this->addForeignKey('fk-pay-product_id', 'order_item', 'product_id', 'product', 'id', 'CASCADE');
25 | }
26 |
27 | /**
28 | * {@inheritdoc}
29 | */
30 | public function safeDown()
31 | {
32 | $this->dropForeignKey('fk-pay-order_id', 'order_item');
33 | $this->dropForeignKey('fk-pay-product_id', 'order_item');
34 | $this->dropTable('{{%order_item}}');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/migrations/m200721_145609_create_company_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%company}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'name' => $this->string(64)->notNull(),
18 | 'trade_name' => $this->string(64),
19 | 'ein' => $this->string(18)->notNull(),
20 | 'phone_number' => $this->string(16)->notNull(),
21 | 'email' => $this->string(64)->notNull(),
22 | 'created_at' => $this->dateTime()->notNull(),
23 | 'updated_at' => $this->dateTime(),
24 | 'address_id' => $this->integer()->unsigned(),
25 | ]);
26 |
27 | $this->addForeignKey('fk-company-address_id', 'company', 'address_id', 'address', 'id', 'CASCADE');
28 | }
29 |
30 | /**
31 | * {@inheritdoc}
32 | */
33 | public function safeDown()
34 | {
35 | $this->dropForeignKey('fk-company-address_id', 'company');
36 | $this->dropTable('{{%company}}');
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/views/operation/_form.php:
--------------------------------------------------------------------------------
1 |
10 |
11 |
45 |
--------------------------------------------------------------------------------
/migrations/m200721_145613_create_payment_method_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%payment_method}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'name' => $this->string(64)->notNull(),
18 | 'installment_limit' => $this->tinyInteger()->unsigned()->notNull(),
19 | 'is_deleted' => $this->tinyInteger(1)->defaultValue(0),
20 | 'created_at' => $this->dateTime()->notNull(),
21 | 'updated_at' => $this->dateTime(),
22 | 'deleted_at' => $this->dateTime(),
23 | 'company_id' => $this->integer()->unsigned()->notNull(),
24 | ]);
25 |
26 | $this->addForeignKey('fk-payment_method-company_id', 'payment_method', 'company_id', 'company', 'id', 'CASCADE');
27 | }
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function safeDown()
33 | {
34 | $this->dropForeignKey('fk-payment_method-company_id', 'payment_method');
35 | $this->dropTable('{{%payment_method}}');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/unit/models/UserTest.php:
--------------------------------------------------------------------------------
1 | username)->equals('admin');
13 |
14 | expect_not(User::findIdentity(999));
15 | }
16 |
17 | public function testFindUserByAccessToken()
18 | {
19 | expect_that($user = User::findIdentityByAccessToken('100-token'));
20 | expect($user->username)->equals('admin');
21 |
22 | expect_not(User::findIdentityByAccessToken('non-existing'));
23 | }
24 |
25 | public function testFindUserByUsername()
26 | {
27 | expect_that($user = User::findByUsername('admin'));
28 | expect_not(User::findByUsername('not-admin'));
29 | }
30 |
31 | /**
32 | * @depends testFindUserByUsername
33 | */
34 | public function testValidateUser($user)
35 | {
36 | $user = User::findByUsername('admin');
37 | expect_that($user->validateAuthKey('test100key'));
38 | expect_not($user->validateAuthKey('test102key'));
39 |
40 | expect_that($user->validatePassword('admin'));
41 | expect_not($user->validatePassword('123456'));
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/tests/unit/models/LoginFormTest.php:
--------------------------------------------------------------------------------
1 | user->logout();
14 | }
15 |
16 | public function testLoginNoUser()
17 | {
18 | $this->model = new LoginForm([
19 | 'username' => 'not_existing_username',
20 | 'password' => 'not_existing_password',
21 | ]);
22 |
23 | expect_not($this->model->login());
24 | expect_that(\Yii::$app->user->isGuest);
25 | }
26 |
27 | public function testLoginWrongPassword()
28 | {
29 | $this->model = new LoginForm([
30 | 'username' => 'demo',
31 | 'password' => 'wrong_password',
32 | ]);
33 |
34 | expect_not($this->model->login());
35 | expect_that(\Yii::$app->user->isGuest);
36 | expect($this->model->errors)->hasKey('password');
37 | }
38 |
39 | public function testLoginCorrect()
40 | {
41 | $this->model = new LoginForm([
42 | 'username' => 'demo',
43 | 'password' => 'demo',
44 | ]);
45 |
46 | expect_that($this->model->login());
47 | expect_not(\Yii::$app->user->isGuest);
48 | expect($this->model->errors)->hasntKey('password');
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/config/console.php:
--------------------------------------------------------------------------------
1 | 'basic-console',
8 | 'basePath' => dirname(__DIR__),
9 | 'bootstrap' => ['log'],
10 | 'controllerNamespace' => 'app\commands',
11 | 'aliases' => [
12 | '@bower' => '@vendor/bower-asset',
13 | '@npm' => '@vendor/npm-asset',
14 | '@tests' => '@app/tests',
15 | ],
16 | 'components' => [
17 | 'cache' => [
18 | 'class' => 'yii\caching\FileCache',
19 | ],
20 | 'log' => [
21 | 'targets' => [
22 | [
23 | 'class' => 'yii\log\FileTarget',
24 | 'levels' => ['error', 'warning'],
25 | ],
26 | ],
27 | ],
28 | 'authManager' => [
29 | 'class' => 'yii\rbac\PhpManager',
30 | ],
31 | 'db' => $db,
32 | ],
33 | 'params' => $params,
34 | /*
35 | 'controllerMap' => [
36 | 'fixture' => [ // Fixture generation command line.
37 | 'class' => 'yii\faker\FixtureController',
38 | ],
39 | ],
40 | */
41 | ];
42 |
43 | if (YII_ENV_DEV) {
44 | // configuration adjustments for 'dev' environment
45 | $config['bootstrap'][] = 'gii';
46 | $config['modules']['gii'] = [
47 | 'class' => 'yii\gii\Module',
48 | ];
49 | }
50 |
51 | return $config;
52 |
--------------------------------------------------------------------------------
/migrations/m200721_145615_create_product_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%product}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'code' => $this->string(32),
18 | 'name' => $this->string(64)->notNull(),
19 | 'unit_price' => $this->decimal(10,2)->notNull(),
20 | 'amount' => $this->decimal(10,2)->notNull(),
21 | 'max_amount' => $this->decimal(10,2),
22 | 'min_amount' => $this->decimal(10,2),
23 | 'is_deleted' => $this->tinyInteger(1)->defaultValue(0),
24 | 'created_at' => $this->dateTime()->notNull(),
25 | 'updated_at' => $this->dateTime(),
26 | 'deleted_at' => $this->dateTime(),
27 | 'category_id' => $this->integer()->unsigned()->notNull()
28 | ]);
29 |
30 | $this->addForeignKey('fk-product-category_id', 'product', 'category_id', 'category', 'id', 'CASCADE');
31 | }
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function safeDown()
37 | {
38 | $this->dropForeignKey('fk-product-category_id', 'product');
39 | $this->dropTable('{{%product}}');
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/migrations/m200721_145618_create_operation_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%operation}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'in_out' => $this->tinyInteger(1)->notNull(),
18 | 'amount' => $this->decimal(10,2)->notNull(),
19 | 'reason' => $this->string(64)->notNull(),
20 | 'created_at' => $this->dateTime()->notNull(),
21 | 'updated_at' => $this->dateTime(),
22 | 'product_id' => $this->integer()->unsigned()->notNull(),
23 | 'employee_id' => $this->integer()->unsigned()->notNull(),
24 | ]);
25 |
26 | $this->addForeignKey('fk-operation-product_id', 'operation', 'product_id', 'product', 'id', 'CASCADE');
27 | $this->addForeignKey('fk-operation-employee_id', 'operation', 'employee_id', 'employee', 'id', 'CASCADE');
28 | }
29 |
30 | /**
31 | * {@inheritdoc}
32 | */
33 | public function safeDown()
34 | {
35 | $this->dropForeignKey('fk-operation-product_id', 'operation');
36 | $this->dropForeignKey('fk-operation-employee_id', 'operation');
37 | $this->dropTable('{{%operation}}');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/modules/api/controllers/ProductController.php:
--------------------------------------------------------------------------------
1 | ContentNegotiator::class,
18 | 'formats' => [
19 | 'application/json' => Response::FORMAT_JSON,
20 | ],
21 | ],
22 | 'access' => [
23 | 'class' => AccessControl::class,
24 | 'rules' => [
25 | [
26 | 'actions' => ['query'],
27 | 'allow' => true,
28 | 'roles' => ['cashier']
29 | ],
30 | ],
31 | ],
32 | ];
33 | }
34 |
35 | public function actionQuery($q = null, $id = null)
36 | {
37 | $out = ['results' => ['id' => '', 'name' => '', 'price' => '']];
38 |
39 | if (!is_null($q)) {
40 | $data = ProductSearch::findInNameWithVariation(trim($q));
41 | $out['results'] = array_map(function($product) {
42 | return $product->toArray();
43 | }, array_values($data));
44 | }
45 | return $out;
46 | }
47 | }
--------------------------------------------------------------------------------
/views/site/error.php:
--------------------------------------------------------------------------------
1 | title = $name;
12 |
13 | $this->registerCss(
14 | <<< CSS
15 | .error-page > .error-content {
16 | margin: 0;
17 | }
18 |
19 | .error-messages {
20 | margin-left: 20px;
21 | }
22 |
23 | .headline {
24 | font-size: 100px;
25 | float: left;
26 | }
27 | CSS
28 | );
29 | ?>
30 |
31 |
32 |
33 |
34 | = nl2br(Html::encode($message)) ?>
35 |
36 |
37 |
#= nl2br(Html::encode($exception->statusCode)) ?>
38 |
39 | = Yii::t('app', 'The above error occurred while the web server was processing your request.') ?>
40 |
41 | = Yii::t('app', 'Return to the main page here.', [
42 | 'url' => Url::to(['index'])
43 | ]); ?>
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/models/CategorySearch.php:
--------------------------------------------------------------------------------
1 | $query,
48 | ]);
49 |
50 | $this->load($params);
51 |
52 | if (!$this->validate()) {
53 | // uncomment the following line if you do not want to return any records when validation fails
54 | // $query->where('0=1');
55 | return $dataProvider;
56 | }
57 |
58 | $query->andFilterWhere(['like', 'category.name', $this->name]);
59 |
60 | return $dataProvider;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/components/formatters/AppFormatter.php:
--------------------------------------------------------------------------------
1 | nullDisplay;
15 |
16 | $value = explode(" ", $value);
17 | $value[0] = $this->asDateDefault($value[0]);
18 | return implode(" ", $value);
19 | }
20 |
21 | public function asDateDefault($value)
22 | {
23 | if ($value === null)
24 | return $this->nullDisplay;
25 |
26 | return implode("-",array_reverse(explode("/", $value)));
27 | }
28 |
29 | public function asInputOrOutput($value)
30 | {
31 | return $value == 0
32 | ? Yii::t('app', 'Output')
33 | : Yii::t('app', 'Input');
34 | }
35 |
36 | public function asAmount($value)
37 | {
38 | if ($value === null)
39 | return $this->nullDisplay;
40 | else if (empty($value))
41 | $value = 0;
42 |
43 | return number_format($value, 2, ',', '.');
44 | }
45 |
46 | public function asActive($value)
47 | {
48 | return $value
49 | ? Yii::t('app', 'No')
50 | : Yii::t('app', 'Yes');
51 | }
52 |
53 | public function getCurrencySymbol()
54 | {
55 | $formatter = new NumberFormatter($this->locale, NumberFormatter::CURRENCY);
56 | return $formatter->getSymbol(NumberFormatter::CURRENCY_SYMBOL);
57 | }
58 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in
12 | the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Yii Software LLC nor the names of its
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/components/Seller.php:
--------------------------------------------------------------------------------
1 | sender;
19 |
20 | Sale::getDb()->transaction(function ($db) use ($sale) {
21 | $sale->sale_at = new Expression('NOW()');
22 | $sale->is_sold = 1;
23 | if (!$sale->update()) {
24 | foreach($sale->firstErrors as $erro) break;
25 | throw new BadRequestHttpException($erro);
26 | }
27 |
28 | $items = $sale->order->orderItems;
29 | self::descraseItemsStock($items);
30 | });
31 | }
32 |
33 | private static function descraseItemsStock($items)
34 | {
35 | foreach ($items as $item) {
36 | $operation = new Operation();
37 | $operation->attributes = [
38 | 'in_out' => 0,
39 | 'amount' => $item->amount,
40 | 'reason' => Yii::t('app', 'Product sale.'),
41 | 'product_id' => $item->product_id,
42 | 'employee_id' => Yii::$app->user->id,
43 | ];
44 |
45 | if (!$operation->save()) {
46 | foreach($operation->firstErrors as $erro) break;
47 | throw new BadRequestHttpException($erro);
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/components/factories/AccountFactory.php:
--------------------------------------------------------------------------------
1 | getDb()->beginTransaction();
15 |
16 | try {
17 | $company = self::createCompany($company);
18 | self::createEmployee($employee, $company->id);
19 | PaymentMethodsDefaultFactory::create($company->id);
20 |
21 | $trasaction->commit();
22 | } catch (Exception $e) {
23 | $trasaction->rollBack();
24 | throw $e;
25 | }
26 | }
27 |
28 | private static function createCompany($attributes)
29 | {
30 | $company = new Company();
31 | $company->setAttributes($attributes);
32 |
33 | if (!$company->save()) {
34 | foreach ($company->firstErrors as $erro) break;
35 | throw new Exception(Yii::t('app', 'Failed to register company.') . ' ' . $erro);
36 | }
37 |
38 | return $company;
39 | }
40 |
41 | private static function createEmployee($attributes, $company_id)
42 | {
43 | $employee = new Employee();
44 | $employee->setAttributes(array_merge($attributes, ['company_id' => $company_id]));
45 | $employee->password_repeat = $employee->password;
46 |
47 | if (!$employee->save()) {
48 | foreach ($employee->firstErrors as $erro) break;
49 | throw new Exception(Yii::t('app', 'Failed to register your account.') . ' ' . $erro);
50 | }
51 |
52 | return $employee;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/modules/api/controllers/OrderController.php:
--------------------------------------------------------------------------------
1 | ContentNegotiator::class,
21 | 'formats' => [
22 | 'application/json' => Response::FORMAT_JSON,
23 | ],
24 | ],
25 | 'access' => [
26 | 'class' => AccessControl::class,
27 | 'rules' => [
28 | [
29 | 'actions' => ['index'],
30 | 'allow' => true,
31 | 'roles' => ['cashier']
32 | ],
33 | ],
34 | ],
35 | 'verbs' => [
36 | 'class' => VerbFilter::class,
37 | 'actions' => [
38 | 'index' => ['GET'],
39 | ],
40 | ],
41 | ];
42 | }
43 |
44 | public function actionIndex($id)
45 | {
46 | $order = $this->findModel($id);
47 | return $order;
48 | }
49 |
50 | protected function findModel($id)
51 | {
52 | $model = Order::find()
53 | ->andWhere(['order.id' => $id])
54 | ->one();
55 |
56 | if ($model !== null)
57 | return $model;
58 |
59 | throw new NotFoundHttpException(Yii::t('app', 'Order not exist.'));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/unit/models/ContactFormTest.php:
--------------------------------------------------------------------------------
1 | model = $this->getMockBuilder('app\models\ContactForm')
20 | ->setMethods(['validate'])
21 | ->getMock();
22 |
23 | $this->model->expects($this->once())
24 | ->method('validate')
25 | ->willReturn(true);
26 |
27 | $this->model->attributes = [
28 | 'name' => 'Tester',
29 | 'email' => 'tester@example.com',
30 | 'subject' => 'very important letter subject',
31 | 'body' => 'body of current message',
32 | ];
33 |
34 | expect_that($this->model->contact('admin@example.com'));
35 |
36 | // using Yii2 module actions to check email was sent
37 | $this->tester->seeEmailIsSent();
38 |
39 | /** @var MessageInterface $emailMessage */
40 | $emailMessage = $this->tester->grabLastSentEmail();
41 | expect('valid email is sent', $emailMessage)->isInstanceOf('yii\mail\MessageInterface');
42 | expect($emailMessage->getTo())->hasKey('admin@example.com');
43 | expect($emailMessage->getFrom())->hasKey('noreply@example.com');
44 | expect($emailMessage->getReplyTo())->hasKey('tester@example.com');
45 | expect($emailMessage->getSubject())->equals('very important letter subject');
46 | expect($emailMessage->toString())->stringContainsString('body of current message');
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/migrations/m200721_145611_create_employee_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%employee}}', [
16 | 'id' => $this->primaryKey()->unsigned(),
17 | 'full_name' => $this->string(128)->notNull(),
18 | 'usual_name' => $this->string(32)->notNull(),
19 | 'ssn' => $this->string(14)->notNull(),
20 | 'birthday' => $this->date()->notNull(),
21 | 'phone_number' => $this->string(16)->notNull(),
22 | 'email' => $this->string(64)->notNull(),
23 | 'password' => $this->string(255)->notNull(),
24 | 'is_manager' => $this->tinyInteger(1)->defaultValue(0),
25 | 'is_deleted' => $this->tinyInteger(1)->defaultValue(0),
26 | 'created_at' => $this->dateTime()->notNull(),
27 | 'updated_at' => $this->dateTime(),
28 | 'deleted_at' => $this->dateTime(),
29 | 'address_id' => $this->integer()->unsigned(),
30 | 'company_id' => $this->integer()->unsigned()->notNull(),
31 | ]);
32 |
33 | $this->addForeignKey('fk-employee-address_id', 'employee', 'address_id', 'address', 'id', 'CASCADE');
34 | $this->addForeignKey('fk-employee-company_id', 'employee', 'company_id', 'company', 'id', 'CASCADE');
35 | }
36 |
37 | /**
38 | * {@inheritdoc}
39 | */
40 | public function safeDown()
41 | {
42 | $this->dropForeignKey('fk-employee-address_id', 'employee');
43 | $this->dropForeignKey('fk-employee-company_id', 'employee');
44 | $this->dropTable('{{%employee}}');
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/functional/LoginFormCest.php:
--------------------------------------------------------------------------------
1 | amOnRoute('site/login');
8 | }
9 |
10 | public function openLoginPage(\FunctionalTester $I)
11 | {
12 | $I->see('Login', 'h1');
13 |
14 | }
15 |
16 | // demonstrates `amLoggedInAs` method
17 | public function internalLoginById(\FunctionalTester $I)
18 | {
19 | $I->amLoggedInAs(100);
20 | $I->amOnPage('/');
21 | $I->see('Logout (admin)');
22 | }
23 |
24 | // demonstrates `amLoggedInAs` method
25 | public function internalLoginByInstance(\FunctionalTester $I)
26 | {
27 | $I->amLoggedInAs(\app\models\User::findByUsername('admin'));
28 | $I->amOnPage('/');
29 | $I->see('Logout (admin)');
30 | }
31 |
32 | public function loginWithEmptyCredentials(\FunctionalTester $I)
33 | {
34 | $I->submitForm('#login-form', []);
35 | $I->expectTo('see validations errors');
36 | $I->see('Username cannot be blank.');
37 | $I->see('Password cannot be blank.');
38 | }
39 |
40 | public function loginWithWrongCredentials(\FunctionalTester $I)
41 | {
42 | $I->submitForm('#login-form', [
43 | 'LoginForm[username]' => 'admin',
44 | 'LoginForm[password]' => 'wrong',
45 | ]);
46 | $I->expectTo('see validations errors');
47 | $I->see('Incorrect username or password.');
48 | }
49 |
50 | public function loginSuccessfully(\FunctionalTester $I)
51 | {
52 | $I->submitForm('#login-form', [
53 | 'LoginForm[username]' => 'admin',
54 | 'LoginForm[password]' => 'admin',
55 | ]);
56 | $I->see('Logout (admin)');
57 | $I->dontSeeElement('form#login-form');
58 | }
59 | }
--------------------------------------------------------------------------------
/modules/api/controllers/AddressController.php:
--------------------------------------------------------------------------------
1 | ContentNegotiator::class,
19 | 'formats' => [
20 | 'application/json' => Response::FORMAT_JSON,
21 | ],
22 | ],
23 | 'access' => [
24 | 'class' => AccessControl::class,
25 | 'rules' => [
26 | [
27 | 'actions' => ['index'],
28 | 'allow' => true,
29 | 'roles' => ['cashier']
30 | ],
31 | ],
32 | ],
33 | ];
34 | }
35 |
36 | public function actionIndex($code)
37 | {
38 | if (preg_match('/\d{8}/', $code)) {
39 | $url = 'http://viacep.com.br/ws/' . $code . '/json';
40 | $response = json_decode(file_get_contents($url));
41 |
42 | if (!isset($response->erro)) {
43 | return [
44 | 'federated_unit' => $response->uf,
45 | 'city' => $response->localidade,
46 | 'neighborhood' => $response->bairro,
47 | 'street' => $response->logradouro,
48 | 'complement' => $response->complemento,
49 | 'zip_code' => $code
50 | ];
51 | }
52 | }
53 |
54 | throw new BadRequestHttpException(Yii::t('app', 'Zip code does not exist, try again with another zip code.'));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/models/PaySearch.php:
--------------------------------------------------------------------------------
1 | $query,
49 | ]);
50 |
51 | $this->load($params);
52 |
53 | if (!$this->validate()) {
54 | // uncomment the following line if you do not want to return any records when validation fails
55 | // $query->where('0=1');
56 | return $dataProvider;
57 | }
58 |
59 | // grid filtering conditions
60 | $query->andFilterWhere([
61 | 'id' => $this->id,
62 | 'value' => $this->value,
63 | 'installments' => $this->installments,
64 | 'payment_method_id' => $this->payment_method_id,
65 | 'sale_id' => $this->sale_id,
66 | ]);
67 |
68 | return $dataProvider;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/views/employee/_form.php:
--------------------------------------------------------------------------------
1 |
12 |
13 |
55 |
--------------------------------------------------------------------------------
/commands/RbacController.php:
--------------------------------------------------------------------------------
1 | authManager;
15 |
16 | // Create Rule
17 | $user_group = new UserGroupRule;
18 | $auth->add($user_group);
19 | $update_owner = new UpdateOnwer;
20 | $auth->add($update_owner);
21 |
22 | // Create Permissions
23 | $update_employee = $auth->createPermission('update_employee');
24 | $update_employee->description = 'Update Employee';
25 | $update_employee->ruleName = $update_owner->name;
26 | $auth->add($update_employee);
27 |
28 | // Create Roles
29 | $admin = $auth->createRole('admin');
30 | $admin->ruleName = $user_group->name;
31 | $cashier = $auth->createRole('cashier');
32 | $cashier->ruleName = $user_group->name;
33 | $auth->add($admin);
34 | $auth->add($cashier);
35 |
36 | // Chinding
37 | $auth->addChild($cashier, $update_employee);
38 | $auth->addChild($admin, $cashier);
39 | }
40 |
41 | private function addPermissions($permissions)
42 | {
43 | $auth = Yii::$app->authManager;
44 |
45 | foreach ($permissions as $key => $value) {
46 | $permission = $auth->createPermission($key);
47 | $permission->description = $value;
48 | $auth->add($permission);
49 | $permissions[$key] = $permission;
50 | }
51 |
52 | return $permissions;
53 | }
54 |
55 | private function childingPermissions($role, $permissions)
56 | {
57 | $auth = Yii::$app->authManager;
58 |
59 | foreach ($permissions as $key => $permission) {
60 | $auth->addChild($role, $permission);
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/models/ProductVariation.php:
--------------------------------------------------------------------------------
1 | 64],
37 | [['product_id', 'variation_id'], 'unique', 'targetAttribute' => ['product_id', 'variation_id']],
38 | [['product_id'], 'exist', 'skipOnError' => true, 'targetClass' => Product::class, 'targetAttribute' => ['product_id' => 'id']],
39 | [['variation_id'], 'exist', 'skipOnError' => true, 'targetClass' => Variation::class, 'targetAttribute' => ['variation_id' => 'id']],
40 | ];
41 | }
42 |
43 | /**
44 | * {@inheritdoc}
45 | */
46 | public function attributeLabels()
47 | {
48 | return [
49 | 'name' => Yii::t('app', 'Name'),
50 | ];
51 | }
52 |
53 | /**
54 | * Gets query for [[Product]].
55 | *
56 | * @return \yii\db\ActiveQuery
57 | */
58 | public function getProduct()
59 | {
60 | return $this->hasOne(Product::class, ['id' => 'product_id']);
61 | }
62 |
63 | /**
64 | * Gets query for [[Variation]].
65 | *
66 | * @return \yii\db\ActiveQuery
67 | */
68 | public function getVariation()
69 | {
70 | return $this->hasOne(Variation::class, ['id' => 'variation_id']);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/views/expense/_form.php:
--------------------------------------------------------------------------------
1 |
12 |
13 |
--------------------------------------------------------------------------------
/models/VariationSearch.php:
--------------------------------------------------------------------------------
1 | $query,
49 | 'sort'=> ['defaultOrder' => ['category_id' => SORT_ASC]],
50 | ]);
51 |
52 | $dataProvider->sort->attributes = array_merge($dataProvider->sort->attributes, [
53 | 'category_id' => [
54 | 'asc' => ['category.name' => SORT_ASC],
55 | 'desc' => ['category.name' => SORT_DESC],
56 | ],
57 | ]);
58 |
59 | $this->load($params);
60 |
61 | if (!$this->validate()) {
62 | // uncomment the following line if you do not want to return any records when validation fails
63 | // $query->where('0=1');
64 | return $dataProvider;
65 | }
66 |
67 | // grid filtering conditions
68 | $query->andFilterWhere([
69 | 'category_id' => $this->category_id,
70 | ]);
71 |
72 | $query->andFilterWhere(['like', 'name', $this->name]);
73 |
74 | return $dataProvider;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/models/PaymentMethodSearch.php:
--------------------------------------------------------------------------------
1 | $query,
50 | ]);
51 |
52 | $this->load($params);
53 |
54 | if (!$this->validate()) {
55 | // uncomment the following line if you do not want to return any records when validation fails
56 | // $query->where('0=1');
57 | return $dataProvider;
58 | }
59 |
60 | // grid filtering conditions
61 | $query->andFilterWhere([
62 | 'id' => $this->id,
63 | 'installment_limit' => $this->installment_limit,
64 | 'is_deleted' => $this->is_deleted,
65 | 'created_at' => $this->created_at,
66 | 'updated_at' => $this->updated_at,
67 | 'deleted_at' => $this->deleted_at,
68 | 'company_id' => $this->company_id,
69 | ]);
70 |
71 | $query->andFilterWhere(['like', 'name', $this->name]);
72 |
73 | return $dataProvider;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tests/functional/ContactFormCest.php:
--------------------------------------------------------------------------------
1 | amOnPage(['site/contact']);
8 | }
9 |
10 | public function openContactPage(\FunctionalTester $I)
11 | {
12 | $I->see('Contact', 'h1');
13 | }
14 |
15 | public function submitEmptyForm(\FunctionalTester $I)
16 | {
17 | $I->submitForm('#contact-form', []);
18 | $I->expectTo('see validations errors');
19 | $I->see('Contact', 'h1');
20 | $I->see('Name cannot be blank');
21 | $I->see('Email cannot be blank');
22 | $I->see('Subject cannot be blank');
23 | $I->see('Body cannot be blank');
24 | $I->see('The verification code is incorrect');
25 | }
26 |
27 | public function submitFormWithIncorrectEmail(\FunctionalTester $I)
28 | {
29 | $I->submitForm('#contact-form', [
30 | 'ContactForm[name]' => 'tester',
31 | 'ContactForm[email]' => 'tester.email',
32 | 'ContactForm[subject]' => 'test subject',
33 | 'ContactForm[body]' => 'test content',
34 | 'ContactForm[verifyCode]' => 'testme',
35 | ]);
36 | $I->expectTo('see that email address is wrong');
37 | $I->dontSee('Name cannot be blank', '.help-inline');
38 | $I->see('Email is not a valid email address.');
39 | $I->dontSee('Subject cannot be blank', '.help-inline');
40 | $I->dontSee('Body cannot be blank', '.help-inline');
41 | $I->dontSee('The verification code is incorrect', '.help-inline');
42 | }
43 |
44 | public function submitFormSuccessfully(\FunctionalTester $I)
45 | {
46 | $I->submitForm('#contact-form', [
47 | 'ContactForm[name]' => 'tester',
48 | 'ContactForm[email]' => 'tester@example.com',
49 | 'ContactForm[subject]' => 'test subject',
50 | 'ContactForm[body]' => 'test content',
51 | 'ContactForm[verifyCode]' => 'testme',
52 | ]);
53 | $I->seeEmailIsSent();
54 | $I->dontSeeElement('#contact-form');
55 | $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/views/payment-method/index.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Payment Methods');
12 | $this->params['breadcrumbs'][] = $this->title;
13 |
14 | $gridColumns = [
15 | ['class' => 'kartik\grid\SerialColumn'],
16 | 'name',
17 | 'installment_limit',
18 | [
19 | 'attribute' => 'is_deleted',
20 | 'label' => Yii::t('app', 'Active'),
21 | 'value' => function ($model) {
22 | return !$model->is_deleted;
23 | },
24 | 'class' => '\kartik\grid\BooleanColumn',
25 | 'falseLabel' => Yii::t('app', 'Yes'),
26 | 'trueLabel' => Yii::t('app', 'No'),
27 | 'width' => '100px',
28 | ],
29 | [
30 | 'class' => 'kartik\grid\ActionColumn',
31 | 'width' => '100px',
32 | ],
33 | ];
34 | ?>
35 |
36 |
37 |
38 | = GridView::widget([
39 | 'dataProvider' => $dataProvider,
40 | 'filterModel' => $searchModel,
41 | 'columns' => $gridColumns,
42 | 'responsive' => true,
43 | 'responsiveWrap' => false,
44 | 'hover' => true,
45 | 'toolbar' => [
46 | [
47 | 'content' =>
48 | Html::a('', Url::to(['create']), [
49 | 'class' => 'btn btn-success',
50 | 'title' => Yii::t('app', 'Add Payment Method'),
51 | ]),
52 | 'options' => ['class' => 'btn-group mr-2']
53 | ],
54 | '{toggleData}',
55 | ],
56 | 'panel' => [
57 | 'type' => GridView::TYPE_DEFAULT,
58 | 'heading' => Html::encode($this->title),
59 | // 'headingOptions' => ['class' => ''],
60 | // 'footer' => false,
61 | 'afterOptions' => ['class' => ''],
62 | ],
63 | ]); ?>
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/components/validators/ProductExists.php:
--------------------------------------------------------------------------------
1 | variations_form);
16 |
17 | if ($model->$attribute && $this->modelExists($model, $variations)) {
18 | $this->addError($model, $attribute, Yii::t('app', 'Could not save because the product already exists.'));
19 | }
20 | }
21 |
22 | protected function modelExists($model, $variations)
23 | {
24 | if (!$variations) {
25 | $product = Product::find()
26 | ->leftJoin('product_variation', 'product_variation.product_id = product.id')
27 | ->andWhere(['product.name' => $model->name])
28 | ->andWhere(['is', 'product_variation.product_id', new Expression('NULL')]);
29 |
30 | if (!$model->isNewRecord)
31 | $product->andWhere(['not', ['product.id' => $model->id]]);
32 |
33 | return $product->exists();
34 | }
35 |
36 | $query = Product::find()
37 | ->andWhere(['product.name' => $model->name])
38 | ->andWhere(['product.category_id' => $model->category_id])
39 | ->andWhere(['in', 'product_variation.name', array_values($variations)])
40 | ->andWhere(['in', 'product_variation.variation_id', array_keys($variations)])
41 | ->innerJoinWith('productVariations');
42 |
43 | if (!$model->isNewRecord)
44 | $query->andWhere(['not', ['product.id' => $model->id]]);
45 |
46 | $query = $query->asArray()->all();
47 |
48 | $product_variations = array_map(function ($product) {
49 | $array = ArrayHelper::map($product['productVariations'], 'variation_id', 'name');
50 | ksort($array);
51 | return $array;
52 | }, $query);
53 |
54 | ksort($variations);
55 | foreach ($product_variations as $vars) {
56 | $array = array_merge(array_diff_assoc($variations, $vars), array_diff_assoc($vars, $variations));
57 | if (!$array) return true;
58 | }
59 |
60 | return false;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/web/css/site.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | height: 100%;
4 | }
5 |
6 | .wrap {
7 | min-height: 100%;
8 | height: auto;
9 | margin: 0 auto -60px;
10 | padding: 0 0 60px;
11 | }
12 |
13 | .wrap > .container {
14 | padding: 70px 15px 20px;
15 | }
16 |
17 | .footer {
18 | height: 60px;
19 | background-color: #f5f5f5;
20 | border-top: 1px solid #ddd;
21 | padding-top: 20px;
22 | }
23 |
24 | .jumbotron {
25 | text-align: center;
26 | background-color: transparent;
27 | }
28 |
29 | .jumbotron .btn {
30 | font-size: 21px;
31 | padding: 14px 24px;
32 | }
33 |
34 | .not-set {
35 | color: #c55;
36 | font-style: italic;
37 | }
38 |
39 | /* add sorting icons to gridview sort links */
40 | a.asc:after, a.desc:after {
41 | position: relative;
42 | top: 1px;
43 | display: inline-block;
44 | font-family: 'Glyphicons Halflings';
45 | font-style: normal;
46 | font-weight: normal;
47 | line-height: 1;
48 | padding-left: 5px;
49 | }
50 |
51 | a.asc:after {
52 | content: /*"\e113"*/ "\e151";
53 | }
54 |
55 | a.desc:after {
56 | content: /*"\e114"*/ "\e152";
57 | }
58 |
59 | .sort-numerical a.asc:after {
60 | content: "\e153";
61 | }
62 |
63 | .sort-numerical a.desc:after {
64 | content: "\e154";
65 | }
66 |
67 | .sort-ordinal a.asc:after {
68 | content: "\e155";
69 | }
70 |
71 | .sort-ordinal a.desc:after {
72 | content: "\e156";
73 | }
74 |
75 | .grid-view th {
76 | white-space: nowrap;
77 | }
78 |
79 | .hint-block {
80 | display: block;
81 | margin-top: 5px;
82 | color: #999;
83 | }
84 |
85 | .error-summary {
86 | color: #a94442;
87 | background: #fdf7f7;
88 | border-left: 3px solid #eed3d7;
89 | padding: 10px 20px;
90 | margin: 0 0 15px 0;
91 | }
92 |
93 | /* align the logout "link" (button in form) of the navbar */
94 | .nav li > form > button.logout {
95 | padding: 15px;
96 | border: none;
97 | }
98 |
99 | @media(max-width:767px) {
100 | .nav li > form > button.logout {
101 | display:block;
102 | text-align: left;
103 | width: 100%;
104 | padding: 10px 15px;
105 | }
106 | }
107 |
108 | .nav > li > form > button.logout:focus,
109 | .nav > li > form > button.logout:hover {
110 | text-decoration: none;
111 | }
112 |
113 | .nav > li > form > button.logout:focus {
114 | outline: none;
115 | }
116 |
--------------------------------------------------------------------------------
/models/SignupForm.php:
--------------------------------------------------------------------------------
1 | 255],
32 | [['full_name'], 'string', 'max' => 128],
33 | [['name', 'trade_name', 'email'], 'string', 'max' => 64],
34 | [['usual_name'], 'string', 'max' => 32],
35 | [['ein'], 'string', 'max' => 18],
36 | [['ssn'], 'string', 'max' => 12],
37 | [['email'], 'email'],
38 | [['birthday'], 'safe'],
39 | [['password_repeat'], 'compare', 'compareAttribute' => 'password'],
40 | ];
41 | }
42 |
43 | public function scenarios()
44 | {
45 | $scenarios = parent::scenarios();
46 | $scenarios[self::SCENARIO_COMPANY] = ['name', 'ein', 'email', 'trade_name'];
47 | $scenarios[self::SCENARIO_EMPLOYEE] = ['full_name', 'usual_name', 'ssn', 'birthday', 'email', 'password', 'password_repeat'];
48 | return $scenarios;
49 | }
50 |
51 | /**
52 | * {@inheritdoc}
53 | */
54 | public function attributeLabels()
55 | {
56 | return [
57 | // Company
58 | 'id' => Yii::t('app', 'ID'),
59 | 'name' => Yii::t('app', 'Name'),
60 | 'trade_name' => Yii::t('app', 'Trade Name'),
61 | 'ein' => Yii::t('app', 'Ein'),
62 |
63 | // Employee
64 | 'full_name' => Yii::t('app', 'Full Name'),
65 | 'usual_name' => Yii::t('app', 'Usual Name'),
66 | 'ssn' => Yii::t('app', 'Ssn'),
67 | 'birthday' => Yii::t('app', 'Birthday'),
68 | 'password' => Yii::t('app', 'Password'),
69 | 'password_repeat' => Yii::t('app', 'Password Repeat'),
70 |
71 | 'email' => Yii::t('app', 'Email'),
72 | ];
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/vagrant/provision/once-as-root.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | #== Import script args ==
4 |
5 | timezone=$(echo "$1")
6 |
7 | #== Bash helpers ==
8 |
9 | function info {
10 | echo " "
11 | echo "--> $1"
12 | echo " "
13 | }
14 |
15 | #== Provision script ==
16 |
17 | info "Provision-script user: `whoami`"
18 |
19 | export DEBIAN_FRONTEND=noninteractive
20 |
21 | info "Configure timezone"
22 | timedatectl set-timezone ${timezone} --no-ask-password
23 |
24 | info "Prepare root password for MySQL"
25 | debconf-set-selections <<< "mariadb-server-10.0 mysql-server/root_password password \"''\""
26 | debconf-set-selections <<< "mariadb-server-10.0 mysql-server/root_password_again password \"''\""
27 | echo "Done!"
28 |
29 | info "Update OS software"
30 | apt-get update
31 | apt-get upgrade -y
32 |
33 | info "Install additional software"
34 | apt-get install -y php7.0-curl php7.0-cli php7.0-intl php7.0-mysqlnd php7.0-gd php7.0-fpm php7.0-mbstring php7.0-xml unzip nginx mariadb-server-10.0 php.xdebug
35 |
36 | info "Configure MySQL"
37 | sed -i "s/.*bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/mariadb.conf.d/50-server.cnf
38 | mysql -uroot <<< "CREATE USER 'root'@'%' IDENTIFIED BY ''"
39 | mysql -uroot <<< "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'"
40 | mysql -uroot <<< "DROP USER 'root'@'localhost'"
41 | mysql -uroot <<< "FLUSH PRIVILEGES"
42 | echo "Done!"
43 |
44 | info "Configure PHP-FPM"
45 | sed -i 's/user = www-data/user = vagrant/g' /etc/php/7.0/fpm/pool.d/www.conf
46 | sed -i 's/group = www-data/group = vagrant/g' /etc/php/7.0/fpm/pool.d/www.conf
47 | sed -i 's/owner = www-data/owner = vagrant/g' /etc/php/7.0/fpm/pool.d/www.conf
48 | cat << EOF > /etc/php/7.0/mods-available/xdebug.ini
49 | zend_extension=xdebug.so
50 | xdebug.remote_enable=1
51 | xdebug.remote_connect_back=1
52 | xdebug.remote_port=9000
53 | xdebug.remote_autostart=1
54 | EOF
55 | echo "Done!"
56 |
57 | info "Configure NGINX"
58 | sed -i 's/user www-data/user vagrant/g' /etc/nginx/nginx.conf
59 | echo "Done!"
60 |
61 | info "Enabling site configuration"
62 | ln -s /app/vagrant/nginx/app.conf /etc/nginx/sites-enabled/app.conf
63 | echo "Done!"
64 |
65 | info "Removing default site configuration"
66 | rm /etc/nginx/sites-enabled/default
67 | echo "Done!"
68 |
69 | info "Initailize databases for MySQL"
70 | mysql -uroot <<< "CREATE DATABASE yii2basic"
71 | mysql -uroot <<< "CREATE DATABASE yii2basic_test"
72 | echo "Done!"
73 |
74 | info "Install composer"
75 | curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
--------------------------------------------------------------------------------
/models/LoginForm.php:
--------------------------------------------------------------------------------
1 | Yii::t('app', 'Email'),
43 | 'password' => Yii::t('app', 'Password'),
44 | 'rememberMe' => Yii::t('app', 'Remember Me'),
45 | ];
46 | }
47 |
48 | /**
49 | * Validates the password.
50 | * This method serves as the inline validation for password.
51 | *
52 | * @param string $attribute the attribute currently being validated
53 | * @param array $params the additional name-value pairs given in the rule
54 | */
55 | public function validatePassword($attribute, $params)
56 | {
57 | if (!$this->hasErrors()) {
58 | $user = $this->getUser();
59 |
60 | if (!$user || !$user->validatePassword($this->password)) {
61 | $this->addError($attribute, Yii::t('app', 'Incorrect email or password.'));
62 | }
63 | }
64 | }
65 |
66 | /**
67 | * Logs in a user using the provided email and password.
68 | * @return bool whether the user is logged in successfully
69 | */
70 | public function login()
71 | {
72 | if ($this->validate()) {
73 | return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
74 | }
75 | return false;
76 | }
77 |
78 | /**
79 | * Finds user by [[email]]
80 | *
81 | * @return Employee|null
82 | */
83 | public function getUser()
84 | {
85 | if ($this->_user === false && ($user = Employee::findByEmail($this->email))) {
86 | $this->_user = $user->is_deleted ? null : $user;
87 | }
88 |
89 | return $this->_user;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/web/js/pos/main.js:
--------------------------------------------------------------------------------
1 | const app = new Vue({
2 | el: '.pos-index',
3 | data: {
4 | items: [],
5 | total: 0,
6 | orderId: '',
7 | },
8 | mounted() {
9 | this.orderId = document.querySelector('#orderitem-order_id').value;
10 | this.load();
11 | },
12 | methods: {
13 | pushItem() {
14 | $('form').yiiActiveForm('validate', true);
15 |
16 | const form = document.querySelector('form');
17 | const formData = new FormData(form);
18 |
19 | axios.post('/api/order-item/create', formData)
20 | .then(({ data }) => {
21 | this.items.push(data.orderItem);
22 | this.total = data.total;
23 | const $form = $('form');
24 | $form.get(0).reset();
25 | })
26 | .catch(({ response }) => {
27 | const { name, message } = response.data;
28 | showToast(name, message);
29 | });
30 | },
31 | popItem(index) {
32 | const id = this.items[index].id;
33 | axios.delete('/api/order-item/delete', { params: { id } })
34 | .then(({ data }) => {
35 | this.total = data.total;
36 | this.items.splice(index, 1);
37 | })
38 | .catch(({ response }) => {
39 | const { name, message } = response.data;
40 | showToast(name, message);
41 | });
42 | },
43 | load() {
44 | axios.get('/api/order/index', { params: { id: this.orderId } })
45 | .then(({ data }) => {
46 | this.items = data.orderItems;
47 | this.total = data.total_value;
48 | })
49 | .catch(({ response }) => {
50 | const { name, message } = response.data;
51 | showToast(name, message);
52 | });
53 | },
54 | }
55 | });
56 |
57 | function showToast(title, message = '') {
58 | $(document).Toasts('create', {
59 | title: title,
60 | body: message,
61 | autohide: true,
62 | delay: 5000,
63 | class: ['bg-warning', 'fix-toast']
64 | });
65 | }
66 |
67 | function setPrice(value) {
68 | $('#orderitem-default_price-disp').inputmask("setvalue", value);
69 | $('#orderitem-unit_price-disp').inputmask("setvalue", value);
70 | $('#orderitem-unit_price-disp').focus();
71 | }
72 |
73 | $('form').on('submit', e => false);
--------------------------------------------------------------------------------
/models/Address.php:
--------------------------------------------------------------------------------
1 | 8],
40 | [['number'], 'string', 'max' => 8],
41 | [['street', 'neighborhood', 'city'], 'string', 'max' => 64],
42 | [['federated_unit'], 'string', 'max' => 2],
43 | [['zip_code'], 'match', 'pattern' => '/\d{8}/'],
44 | [['complement'], 'string', 'max' => 128],
45 | ];
46 | }
47 |
48 | /**
49 | * {@inheritdoc}
50 | */
51 | public function attributeLabels()
52 | {
53 | return [
54 | 'id' => Yii::t('app', 'ID'),
55 | 'zip_code' => Yii::t('app', 'Zip Code'),
56 | 'number' => Yii::t('app', 'Number'),
57 | 'street' => Yii::t('app', 'Street'),
58 | 'neighborhood' => Yii::t('app', 'Neighborhood'),
59 | 'city' => Yii::t('app', 'City'),
60 | 'federated_unit' => Yii::t('app', 'Federated Unit'),
61 | 'complement' => Yii::t('app', 'Complement'),
62 | ];
63 | }
64 |
65 | /**
66 | * Gets query for [[Companies]].
67 | *
68 | * @return \yii\db\ActiveQuery
69 | */
70 | public function getCompany()
71 | {
72 | return $this->hasOne(Company::class, ['address_id' => 'id']);
73 | }
74 |
75 | /**
76 | * Gets query for [[Employees]].
77 | *
78 | * @return \yii\db\ActiveQuery
79 | */
80 | public function getEmployee()
81 | {
82 | return $this->hasOne(Employee::class, ['address_id' => 'id']);
83 | }
84 |
85 | public function __toString()
86 | {
87 | return "$this->street, $this->number, $this->neighborhood, $this->city - $this->federated_unit, $this->zip_code ($this->complement)";
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/widgets/Alert.php:
--------------------------------------------------------------------------------
1 | session->setFlash('error', 'This is the message');
14 | * Yii::$app->session->setFlash('success', 'This is the message');
15 | * Yii::$app->session->setFlash('info', 'This is the message');
16 | * ```
17 | *
18 | * Multiple messages could be set as follows:
19 | *
20 | * ```php
21 | * Yii::$app->session->setFlash('error', ['Error 1', 'Error 2']);
22 | * ```
23 | *
24 | * @author Kartik Visweswaran
25 | * @author Alexander Makarov
26 | */
27 | class Alert extends Widget
28 | {
29 | /**
30 | * @var array the alert types configuration for the flash messages.
31 | * This array is setup as $key => $value, where:
32 | * - key: the name of the session flash variable
33 | * - value: the bootstrap alert type (i.e. danger, success, info, warning)
34 | */
35 | public $alertTypes = [
36 | 'error' => 'alert-danger',
37 | 'danger' => 'alert-danger',
38 | 'success' => 'alert-success',
39 | 'info' => 'alert-info',
40 | 'warning' => 'alert-warning'
41 | ];
42 | /**
43 | * @var array the options for rendering the close button tag.
44 | * Array will be passed to [[\yii\bootstrap\Alert::closeButton]].
45 | */
46 | public $closeButton = [];
47 |
48 |
49 | /**
50 | * {@inheritdoc}
51 | */
52 | public function run()
53 | {
54 | $session = Yii::$app->session;
55 | $flashes = $session->getAllFlashes();
56 | $appendClass = isset($this->options['class']) ? ' ' . $this->options['class'] : '';
57 |
58 | foreach ($flashes as $type => $flash) {
59 | if (!isset($this->alertTypes[$type])) {
60 | continue;
61 | }
62 |
63 | foreach ((array) $flash as $i => $message) {
64 | echo Bootstrap4Alert::widget([
65 | 'body' => $message,
66 | 'closeButton' => $this->closeButton,
67 | 'options' => array_merge($this->options, [
68 | 'id' => $this->getId() . '-' . $type . '-' . $i,
69 | 'class' => $this->alertTypes[$type] . $appendClass,
70 | ]),
71 | ]);
72 | }
73 |
74 | $session->removeFlash($type);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/views/employee/change_password.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Change Password: {name}', [
11 | 'name' => $model->full_name,
12 | ]);
13 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Employees'), 'url' => ['index']];
14 | $this->params['breadcrumbs'][] = ['label' => $model->usual_name, 'url' => ['view', 'id' => $model->id]];
15 | $this->params['breadcrumbs'][] = Yii::t('app', 'Change Password');
16 |
17 | $this->registerJs(
18 | <<< 'JS'
19 | function viewPassword() {
20 | const inputs = $('#employee-password, #employee-password_repeat, #employee-password_new');
21 | for (var input of inputs) {
22 | const type = $(input).prop('type');
23 | if (type === "password")
24 | $(input).prop('type', 'text')
25 | else
26 | $(input).prop('type', 'password')
27 | };
28 | }
29 | JS,
30 | $this::POS_END
31 | )
32 | ?>
33 |
34 |
--------------------------------------------------------------------------------
/views/category/index.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Categories');
12 | $this->params['breadcrumbs'][] = $this->title;
13 |
14 | $this->registerJsFile('@web/js/modal.js', ['depends' => [yii\web\JqueryAsset::class]]);
15 |
16 | $gridColumns = [
17 | ['class' => 'kartik\grid\SerialColumn'],
18 | 'name',
19 | [
20 | 'class' => 'kartik\grid\ActionColumn',
21 | 'deleteOptions' => [
22 | 'data-confirm' => Yii::t('app', 'Are you sure you want to delete this category?'),
23 | ],
24 | 'buttons' => [
25 | 'update' => function($url, $model) {
26 | return Html::a(
27 | '',
28 | $url,
29 | ['value' => $url, 'title' => Yii::t('kvgrid', 'Update'), 'class' => 'btn-modal', 'data-toggle' => 'modal']
30 | );
31 | }
32 | ],
33 | 'width' => '100px',
34 | ],
35 | ];
36 | ?>
37 |
38 |
39 |
40 | = $this->render('@app/views/layouts/modal.php', ['options' => ['title' => Yii::t('app', 'Category')]]) ?>
41 |
42 | = GridView::widget([
43 | 'id' => 'grid_categories',
44 | 'dataProvider' => $dataProvider,
45 | 'filterModel' => $searchModel,
46 | 'columns' => $gridColumns,
47 | 'responsive' => true,
48 | 'responsiveWrap' => false,
49 | 'hover' => true,
50 | 'toolbar' => [
51 | [
52 | 'content' =>
53 | Html::button('', [
54 | 'value' => Url::to(['create']),
55 | 'class' => 'btn btn-success btn-modal',
56 | 'title' => Yii::t('app', 'Add Category'),
57 | ]),
58 | 'options' => ['class' => 'btn-group mr-2']
59 | ],
60 | '{toggleData}',
61 | ],
62 | 'panel' => [
63 | 'type' => GridView::TYPE_DEFAULT,
64 | 'heading' => Html::encode($this->title),
65 | 'afterOptions' => ['class' => ''],
66 | ],
67 | ]); ?>
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/models/EmployeeSearch.php:
--------------------------------------------------------------------------------
1 | $query,
50 | ]);
51 |
52 | $this->load($params);
53 |
54 | if (!$this->validate()) {
55 | // uncomment the following line if you do not want to return any records when validation fails
56 | // $query->where('0=1');
57 | return $dataProvider;
58 | }
59 |
60 | // grid filtering conditions
61 | $query->andFilterWhere([
62 | 'employee.birthday' => $this->getBirthDay(),
63 | 'employee.is_deleted' => $this->is_deleted,
64 | ]);
65 |
66 | $query->andFilterWhere(['like', 'employee.full_name', $this->full_name])
67 | ->andFilterWhere(['like', 'employee.ssn', $this->ssn])
68 | ->andFilterWhere(['like', 'employee.email', $this->email])
69 | ->andFilterWhere(['like', 'employee.phone_number', $this->getPhoneNumber()]);
70 |
71 | return $dataProvider;
72 | }
73 |
74 | public function getBirthDay()
75 | {
76 | return $this->birthday && preg_match("/\d{2}\/\d{2}\/\d{4}/", $this->birthday)
77 | ? Yii::$app->formatter->asDateDefault($this->birthday)
78 | : $this->birthday = null;
79 | }
80 |
81 | public function getPhoneNumber()
82 | {
83 | return $this->phone_number && (preg_match("/\(\d{2}\)\ \d{4}\-\d{4}/", $this->phone_number)
84 | || preg_match("/\(\d{2}\)\ \d{5}\-\d{4}/", $this->phone_number)) ? $this->phone_number
85 | : $this->phone_number = null;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/models/ExpenseSearch.php:
--------------------------------------------------------------------------------
1 | $query,
52 | 'sort'=> ['defaultOrder' => ['payday' => SORT_DESC]],
53 | ]);
54 |
55 | $this->load($params);
56 |
57 | if (!$this->validate()) {
58 | // uncomment the following line if you do not want to return any records when validation fails
59 | // $query->where('0=1');
60 | return $dataProvider;
61 | }
62 |
63 | // grid filtering conditions
64 | $query->andFilterWhere([
65 | 'id' => $this->id,
66 | 'value' => $this->value,
67 | 'payday' => $this->getPayday(),
68 | 'is_paid' => $this->is_paid,
69 | 'paid_at' => $this->getPaidAt(),
70 | 'created_at' => $this->created_at,
71 | 'updated_at' => $this->updated_at,
72 | 'company_id' => $this->company_id,
73 | ]);
74 |
75 | $query->andFilterWhere(['like', 'expense.name', $this->name])
76 | ->andFilterWhere(['like', 'description', $this->description]);
77 |
78 | return $dataProvider;
79 | }
80 |
81 | public function getPaidAt()
82 | {
83 | return $this->paid_at && preg_match("/\d{2}\/\d{2}\/\d{4}/", $this->paid_at)
84 | ? Yii::$app->formatter->asDateDefault($this->paid_at)
85 | : $this->paid_at = null;
86 | }
87 |
88 | public function getPayday()
89 | {
90 | return $this->payday && preg_match("/\d{2}\/\d{2}\/\d{4}/", $this->payday)
91 | ? Yii::$app->formatter->asDateDefault($this->payday)
92 | : $this->payday = null;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/migrations/m200721_145625_create_junction_table_for_product_and_variation_tables.php:
--------------------------------------------------------------------------------
1 | createTable('{{%product_variation}}', [
20 | 'product_id' => $this->integer()->unsigned(),
21 | 'name' => $this->string(64)->notNull(),
22 | 'variation_id' => $this->integer()->unsigned(),
23 | 'PRIMARY KEY(product_id, variation_id)',
24 | ]);
25 |
26 | // creates index for column `product_id`
27 | $this->createIndex(
28 | '{{%idx-product_variation-product_id}}',
29 | '{{%product_variation}}',
30 | 'product_id'
31 | );
32 |
33 | // add foreign key for table `{{%product}}`
34 | $this->addForeignKey(
35 | '{{%fk-product_variation-product_id}}',
36 | '{{%product_variation}}',
37 | 'product_id',
38 | '{{%product}}',
39 | 'id',
40 | 'CASCADE'
41 | );
42 |
43 | // creates index for column `variation_id`
44 | $this->createIndex(
45 | '{{%idx-product_variation-variation_id}}',
46 | '{{%product_variation}}',
47 | 'variation_id'
48 | );
49 |
50 | // add foreign key for table `{{%variation}}`
51 | $this->addForeignKey(
52 | '{{%fk-product_variation-variation_id}}',
53 | '{{%product_variation}}',
54 | 'variation_id',
55 | '{{%variation}}',
56 | 'id',
57 | 'CASCADE'
58 | );
59 | }
60 |
61 | /**
62 | * {@inheritdoc}
63 | */
64 | public function safeDown()
65 | {
66 | // drops foreign key for table `{{%product}}`
67 | $this->dropForeignKey(
68 | '{{%fk-product_variation-product_id}}',
69 | '{{%product_variation}}'
70 | );
71 |
72 | // drops index for column `product_id`
73 | $this->dropIndex(
74 | '{{%idx-product_variation-product_id}}',
75 | '{{%product_variation}}'
76 | );
77 |
78 | // drops foreign key for table `{{%variation}}`
79 | $this->dropForeignKey(
80 | '{{%fk-product_variation-variation_id}}',
81 | '{{%product_variation}}'
82 | );
83 |
84 | // drops index for column `variation_id`
85 | $this->dropIndex(
86 | '{{%idx-product_variation-variation_id}}',
87 | '{{%product_variation}}'
88 | );
89 |
90 | $this->dropTable('{{%product_variation}}');
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/views/employee/index.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Employees');
13 | $this->params['breadcrumbs'][] = $this->title;
14 |
15 | $gridColumns = [
16 | ['class' => 'kartik\grid\SerialColumn'],
17 | 'full_name',
18 | [
19 | 'attribute' => 'ssn',
20 | 'filterType' => MaskedInput::class,
21 | 'filterWidgetOptions' => [
22 | 'mask' => ['999.999.999-99'],
23 | ],
24 | ],
25 | [
26 | 'attribute' => 'birthday',
27 | 'filterType' => MaskedInput::class,
28 | 'filterWidgetOptions' => [
29 | 'clientOptions' => ['alias' => 'dd/mm/yyyy']
30 | ],
31 | 'format' => 'date'
32 | ],
33 | 'email',
34 | [
35 | 'attribute' => 'phone_number',
36 | 'filterType' => MaskedInput::class,
37 | 'filterWidgetOptions' => [
38 | 'mask' => ['(99) 9999-9999', '(99) 99999-9999'],
39 | ]
40 | ],
41 | [
42 | 'attribute' => 'is_deleted',
43 | 'label' => Yii::t('app', 'Active'),
44 | 'class' => '\kartik\grid\BooleanColumn',
45 | 'trueLabel' => Yii::t('app', 'No'),
46 | 'falseLabel' => Yii::t('app', 'Yes'),
47 | 'value' => function ($model) {
48 | return !$model->is_deleted;
49 | }
50 | ],
51 | [
52 | 'class' => 'kartik\grid\ActionColumn',
53 | 'template' => '{view} {update}',
54 | 'visibleButtons' => [
55 | 'delete' => function ($model) {
56 | return $model->id != Yii::$app->user->id;
57 | },
58 | ],
59 | 'width' => '100px',
60 | ],
61 | ];
62 |
63 | ?>
64 |
65 |
66 | = GridView::widget([
67 | 'dataProvider' => $dataProvider,
68 | 'filterModel' => $searchModel,
69 | 'columns' => $gridColumns,
70 | 'responsive' => true,
71 | 'responsiveWrap' => false,
72 | 'hover' => true,
73 | 'toolbar' => [
74 | [
75 | 'content' =>
76 | Html::a('', Url::to(['create']), [
77 | 'class' => 'btn btn-success',
78 | 'title' => Yii::t('app', 'Add Employee'),
79 | ]),
80 | 'options' => ['class' => 'btn-group mr-2']
81 | ],
82 | '{toggleData}',
83 | ],
84 | 'panel' => [
85 | 'type' => GridView::TYPE_DEFAULT,
86 | 'heading' => Html::encode($this->title),
87 | 'afterOptions' => ['class' => ''],
88 | ],
89 | ]); ?>
90 |
91 |
92 |
--------------------------------------------------------------------------------
/views/variation/index.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Variation');
14 | $this->params['breadcrumbs'][] = $this->title;
15 |
16 | $this->registerJsFile('@web/js/modal.js', ['depends' => [yii\web\JqueryAsset::class]]);
17 |
18 | $gridColumns = [
19 | ['class' => 'kartik\grid\SerialColumn'],
20 | 'name',
21 | [
22 | 'attribute' => 'category_id',
23 | 'value' => function ($model) {
24 | return $model->category->name;
25 | },
26 | 'filterType' => GridView::FILTER_SELECT2,
27 | 'filter' => ArrayHelper::map(Category::find()->orderBy('name')->asArray()->all(), 'id', 'name'),
28 | 'filterWidgetOptions' => [
29 | 'pluginOptions' => ['allowClear' => true],
30 | ],
31 | 'filterInputOptions' => ['placeholder' => Yii::t('app', 'Select')],
32 | 'group' => true, // enable grouping
33 | ],
34 | [
35 | 'class' => 'kartik\grid\ActionColumn',
36 | 'width' => '100px',
37 | 'buttons' => [
38 | 'update' => function ($url, $model) {
39 | return Html::a(
40 | '',
41 | $url,
42 | ['value' => $url, 'title' => Yii::t('kvgrid', 'Update'), 'class' => 'btn-modal', 'data-toggle' => 'modal']
43 | );
44 | }
45 | ]
46 | ],
47 | ];
48 | ?>
49 |
50 |
51 | = $this->render('@app/views/layouts/modal.php', ['options' => ['title' => Yii::t('app', 'Variation')]]) ?>
52 |
53 | = GridView::widget([
54 | 'id' => 'grid_categories',
55 | 'dataProvider' => $dataProvider,
56 | 'filterModel' => $searchModel,
57 | 'columns' => $gridColumns,
58 | 'responsive' => true,
59 | 'responsiveWrap' => false,
60 | 'hover' => true,
61 | 'toolbar' => [
62 | [
63 | 'content' =>
64 | Html::button('', [
65 | 'value' => Url::to(['create']),
66 | 'class' => 'btn btn-success btn-modal',
67 | 'title' => Yii::t('app', 'Add Variation'),
68 | ]),
69 | 'options' => ['class' => 'btn-group mr-2']
70 | ],
71 | '{toggleData}',
72 | ],
73 | 'panel' => [
74 | 'type' => GridView::TYPE_DEFAULT,
75 | 'heading' => Html::encode($this->title),
76 | 'afterOptions' => ['class' => ''],
77 | ],
78 | ]); ?>
79 |
80 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | require 'yaml'
2 | require 'fileutils'
3 |
4 | required_plugins = %w( vagrant-hostmanager vagrant-vbguest )
5 | required_plugins.each do |plugin|
6 | exec "vagrant plugin install #{plugin}" unless Vagrant.has_plugin? plugin
7 | end
8 |
9 | domains = {
10 | app: 'yii2basic.test'
11 | }
12 |
13 | vagrantfile_dir_path = File.dirname(__FILE__)
14 |
15 | config = {
16 | local: vagrantfile_dir_path + '/vagrant/config/vagrant-local.yml',
17 | example: vagrantfile_dir_path + '/vagrant/config/vagrant-local.example.yml'
18 | }
19 |
20 | # copy config from example if local config not exists
21 | FileUtils.cp config[:example], config[:local] unless File.exist?(config[:local])
22 | # read config
23 | options = YAML.load_file config[:local]
24 |
25 | # check github token
26 | if options['github_token'].nil? || options['github_token'].to_s.length != 40
27 | puts "You must place REAL GitHub token into configuration:\n/yii2-app-basic/vagrant/config/vagrant-local.yml"
28 | exit
29 | end
30 |
31 | # vagrant configurate
32 | Vagrant.configure(2) do |config|
33 | # select the box
34 | config.vm.box = 'bento/ubuntu-16.04'
35 |
36 | # should we ask about box updates?
37 | config.vm.box_check_update = options['box_check_update']
38 |
39 | config.vm.provider 'virtualbox' do |vb|
40 | # machine cpus count
41 | vb.cpus = options['cpus']
42 | # machine memory size
43 | vb.memory = options['memory']
44 | # machine name (for VirtualBox UI)
45 | vb.name = options['machine_name']
46 | end
47 |
48 | # machine name (for vagrant console)
49 | config.vm.define options['machine_name']
50 |
51 | # machine name (for guest machine console)
52 | config.vm.hostname = options['machine_name']
53 |
54 | # network settings
55 | config.vm.network 'private_network', ip: options['ip']
56 |
57 | # sync: folder 'yii2-app-advanced' (host machine) -> folder '/app' (guest machine)
58 | config.vm.synced_folder './', '/app', owner: 'vagrant', group: 'vagrant'
59 |
60 | # disable folder '/vagrant' (guest machine)
61 | config.vm.synced_folder '.', '/vagrant', disabled: true
62 |
63 | # hosts settings (host machine)
64 | config.vm.provision :hostmanager
65 | config.hostmanager.enabled = true
66 | config.hostmanager.manage_host = true
67 | config.hostmanager.ignore_private_ip = false
68 | config.hostmanager.include_offline = true
69 | config.hostmanager.aliases = domains.values
70 |
71 | # quick fix for failed guest additions installations
72 | # config.vbguest.auto_update = false
73 |
74 | # provisioners
75 | config.vm.provision 'shell', path: './vagrant/provision/once-as-root.sh', args: [options['timezone']]
76 | config.vm.provision 'shell', path: './vagrant/provision/once-as-vagrant.sh', args: [options['github_token']], privileged: false
77 | config.vm.provision 'shell', path: './vagrant/provision/always-as-root.sh', run: 'always'
78 |
79 | # post-install message (vagrant console)
80 | config.vm.post_up_message = "App URL: http://#{domains[:app]}"
81 | end
82 |
--------------------------------------------------------------------------------
/web/js/checkout/main.js:
--------------------------------------------------------------------------------
1 | const app = new Vue({
2 | el: '.checkout-index',
3 | data: {
4 | items: [],
5 | totalPaid: 0,
6 | saleId: '',
7 | products: [],
8 | totalOrder: 0
9 | },
10 | mounted() {
11 | this.saleId = document.querySelector('#pay-sale_id').value;
12 | this.load();
13 | },
14 | methods: {
15 | pushPay() {
16 | $('form').yiiActiveForm('validate', true);
17 |
18 | const form = document.querySelector('form');
19 | const formData = new FormData(form);
20 |
21 | axios.post('/api/pay/create', formData)
22 | .then(({ data }) => {
23 | this.items.push(data.pay);
24 | this.totalPaid = data.total;
25 | const $form = $('form');
26 | $form.get(0).reset();
27 | })
28 | .catch(({ response }) => {
29 | const { name, message } = response.data;
30 | showToast(name, message);
31 | });
32 | },
33 | popItem(index) {
34 | const id = this.items[index].id;
35 | axios.delete('/api/pay/delete', { params: { id } })
36 | .then(({ data }) => {
37 | this.totalPaid = data.total;
38 | this.items.splice(index, 1);
39 | })
40 | .catch(({ response }) => {
41 | const { name, message } = response.data;
42 | showToast(name, message);
43 | });
44 | },
45 | load() {
46 | axios.get('/api/sale/', { params: { id: this.saleId } })
47 | .then(({ data }) => {
48 | this.items = data.pays;
49 | this.totalPaid = data.total;
50 | this.products = data.order.orderItems;
51 | this.totalOrder = data.order.total_value;
52 | })
53 | .catch(({ response }) => {
54 | const { name, message } = response.data;
55 | showToast(name, message);
56 | });
57 | },
58 | }
59 | });
60 |
61 | Vue.component('payment_items', {
62 | props: ['id', 'index', 'name', 'installments', 'value'],
63 | template: '\
64 | \
65 | | {{ index+1 }} | \
66 | {{ name }} | \
67 | {{ installments }} | \
68 | {{ value }} | \
69 | \
70 | \
71 | \
72 | \
73 | | \
74 |
\
75 | ',
76 | })
77 |
78 | function showToast(title, message = '') {
79 | $(document).Toasts('create', {
80 | title: title,
81 | body: message,
82 | autohide: true,
83 | delay: 5000,
84 | class: ['bg-warning', 'fix-toast']
85 | });
86 | }
87 |
88 | $('form').on('submit', e => false);
--------------------------------------------------------------------------------
/models/Category.php:
--------------------------------------------------------------------------------
1 | 'company',
31 | 'on' => 'category.company_id = company.id'
32 | ]
33 | ];
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public static function tableName()
38 | {
39 | return 'category';
40 | }
41 |
42 | public function behaviors()
43 | {
44 | return [
45 | [
46 | 'class' => TimestampBehavior::class,
47 | 'value' => new Expression('NOW()'),
48 | ],
49 | ];
50 | }
51 |
52 | /**
53 | * {@inheritdoc}
54 | */
55 | public function rules()
56 | {
57 | return [
58 | [['name', 'company_id'], 'required'],
59 | [['created_at', 'updated_at'], 'safe'],
60 | [['company_id'], 'integer'],
61 | [['name'], 'unique'],
62 | [['name'], 'string', 'max' => 64],
63 | [['company_id'], 'exist', 'skipOnError' => true, 'targetClass' => Company::class, 'targetAttribute' => ['company_id' => 'id']],
64 | ];
65 | }
66 |
67 | /**
68 | * {@inheritdoc}
69 | */
70 | public function attributeLabels()
71 | {
72 | return [
73 | 'id' => Yii::t('app', 'ID'),
74 | 'name' => Yii::t('app', 'Name'),
75 | 'created_at' => Yii::t('app', 'Created At'),
76 | 'updated_at' => Yii::t('app', 'Updated At'),
77 | 'company_id' => Yii::t('app', 'Company'),
78 | ];
79 | }
80 |
81 | /**
82 | * Gets query for [[Company]].
83 | *
84 | * @return \yii\db\ActiveQuery
85 | */
86 | public function getCompany()
87 | {
88 | return $this->hasOne(Company::class, ['id' => 'company_id']);
89 | }
90 |
91 | /**
92 | * Gets query for [[Products]].
93 | *
94 | * @return \yii\db\ActiveQuery
95 | */
96 | public function getProducts()
97 | {
98 | return $this->hasMany(Product::class, ['category_id' => 'id']);
99 | }
100 |
101 | /**
102 | * Gets query for [[Variations]].
103 | *
104 | * @return \yii\db\ActiveQuery
105 | */
106 | public function getVariations()
107 | {
108 | return $this->hasMany(Variation::class, ['category_id' => 'id']);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/views/product/_form.php:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | = $form->field($model, 'code')->textInput(['maxlength' => true]) ?>
22 |
23 | = $form->field($model, 'name')->widget(Select2::class, [
24 | 'data' => ArrayHelper::map($model::find()->all(), 'name', 'name'),
25 | 'options' => [
26 | 'placeholder' => '',
27 | 'value' => $model->name,
28 | ],
29 | 'pluginOptions' => [
30 | 'tags' => true,
31 | 'allowClear' => true,
32 | ],
33 | ]) ?>
34 |
35 | category->id);
37 | foreach ($variation_sets as $variation_set) {
38 | echo $form->field($model, "variations_form[$variation_set->id]")->widget(Select2::class, [
39 | 'data' => ArrayHelper::map($variation_set->productVariations, 'name', 'name'),
40 | 'options' => [
41 | 'placeholder' => Yii::t('app', 'Select'),
42 | ],
43 | 'pluginOptions' => [
44 | 'tags' => true,
45 | 'allowClear' => true,
46 | ],
47 | ])->label($variation_set->name);
48 | }
49 | ?>
50 |
51 | = $form->field($model, 'unit_price', [
52 | 'addon' => [
53 | 'prepend' => [
54 | 'content' => Yii::$app->formatter->getCurrencySymbol(),
55 | 'options' => ['class' => 'alert-secondary'],
56 | ]
57 | ]
58 | ])->widget(NumberControl::class, [
59 | 'maskedInputOptions' => [
60 | 'allowMinus' => false,
61 | 'rightAlign' => false,
62 | ],
63 | 'displayOptions' => [
64 | 'class' => 'form-control rounded-right'
65 | ]
66 | ]) ?>
67 |
68 | = $form->field($model, 'max_amount')->widget(NumberControl::class, [
69 | 'maskedInputOptions' => [
70 | 'allowMinus' => false,
71 | 'rightAlign' => false,
72 | ],
73 | ]) ?>
74 |
75 | = $form->field($model, 'min_amount')->widget(NumberControl::class, [
76 | 'maskedInputOptions' => [
77 | 'allowMinus' => false,
78 | 'rightAlign' => false,
79 | ],
80 | ]) ?>
81 |
82 |
83 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/models/ProductSearch.php:
--------------------------------------------------------------------------------
1 | $query,
52 | ]);
53 |
54 | $this->load($params);
55 |
56 | $dataProvider->sort->attributes = array_merge($dataProvider->sort->attributes, [
57 | 'category_id' => [
58 | 'asc' => ['category.name' => SORT_ASC],
59 | 'desc' => ['category.name' => SORT_DESC],
60 | ],
61 | ]);
62 |
63 | if (!$this->validate()) {
64 | // uncomment the following line if you do not want to return any records when validation fails
65 | // $query->where('0=1');
66 | return $dataProvider;
67 | }
68 |
69 | // grid filtering conditions
70 | $this->makeFiltersWhere($query);
71 | $this->filterName($query, $this->name);
72 |
73 | return $dataProvider;
74 | }
75 |
76 | public static function filterName(ActiveQuery &$query, $name)
77 | {
78 | $terms = explode(' ', $name);
79 |
80 | $query->leftJoin('product_variation', 'product.id = product_variation.product_id');
81 | $query->groupBy('product.id');
82 |
83 | $query->select(new Expression("product.*, concat_ws(' ', product.name, NULL, group_concat(product_variation.name SEPARATOR ' ')) full_name"));
84 |
85 | foreach ($terms as $term) {
86 | $query->andFilterHaving(['like', 'full_name', $term]);
87 | }
88 | }
89 |
90 | protected function makeFiltersWhere(ActiveQuery &$query)
91 | {
92 | $query->andFilterWhere([
93 | 'unit_price' => $this->unit_price,
94 | 'amount' => $this->amount,
95 | 'is_deleted' => $this->is_deleted,
96 | ]);
97 |
98 | $query->andFilterWhere(['like', 'category.name', $this->category_id]);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/views/pos/index.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Point of Sale');
10 | $this->params['breadcrumbs'][] = $this->title;
11 |
12 | PosAsset::register($this);
13 | Dialog::widget();
14 |
15 | $this->registerCSS(
16 | <<< CSS
17 | a {
18 | cursor: pointer;
19 | }
20 | div .col-sm {
21 | margin-top: 1.25rem;
22 | }
23 | th:last-child, td:last-child {
24 | text-align: center;
25 | }
26 | CSS
27 | );
28 |
29 | ?>
30 |
31 |
32 | = $this->render('_form', [
33 | 'model' => $item
34 | ]); ?>
35 |
36 |
39 |
40 |
41 |
42 |
43 | | # |
44 | = Yii::t('app', 'Product') ?> |
45 | = Yii::t('app', 'Un. Price') ?> |
46 | = Yii::t('app', 'Amount') ?> |
47 | = Yii::t('app', 'Total') ?> |
48 | = Yii::t('app', 'Actions') ?> |
49 |
50 |
51 |
52 |
53 |
54 | | = Yii::t('app', 'Total:') ?> {{total}} |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | = Html::a(Yii::t('app', 'Cancel Sale'), ['/order/delete', 'id' => $item->order_id], [
63 | 'class' => 'btn btn-outline-danger',
64 | 'data' => [
65 | 'confirm' => Yii::t('app', 'Are you sure you want to delete this order?'),
66 | 'method' => 'post',
67 | ],
68 | ]) ?>
69 |
70 |
71 |
$code]) ?>>
72 |
73 |
= Yii::t('app', 'Checkout') ?>
74 |
= Yii::t('app', 'Choose payment methods') ?>
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/views/address/_form.php:
--------------------------------------------------------------------------------
1 | registerJs(
14 | <<< 'JS'
15 | function getZipcode(code) {
16 | axios.get('/api/address',{
17 | params: { code: code }
18 | }).then(function (response) {
19 | const data = response.data;
20 | setForm(data);
21 | delete data['complement'];
22 | delete data['zip_code'];
23 | toggleReadOnly(data);
24 | }).catch(function (error) {
25 | const data = error.response.data;
26 | $(document).Toasts('create', {
27 | title: data.name,
28 | body: data.message,
29 | autohide: true,
30 | delay: 5000,
31 | class: 'bg-warning fix-toast'
32 | })
33 | });
34 | }
35 |
36 | function setForm(data) {
37 | $('#address-federated_unit').val(data.federated_unit);
38 | $('#address-city').val(data.city);
39 | $('#address-neighborhood').val(data.neighborhood);
40 | $('#address-street').val(data.street);
41 | }
42 |
43 | function toggleReadOnly(data) {
44 | Object.keys(data).forEach(function(element) {
45 | const target = $('#address-'+element);
46 | if (target.val() == '')
47 | target.prop('readonly', false);
48 | else if (!target.is('[readonly]'))
49 | target.prop('readonly', true);
50 | })
51 | }
52 |
53 | $("#address-zip_code").keyup(function(event) {
54 | const zipcode = event.target.value;
55 | if (zipcode.length == 8)
56 | getZipcode(zipcode);
57 | else
58 | setForm({ federated_unit: '', city: '', neighborhood: '', street: ''});
59 | });
60 | JS,
61 | $this::POS_END
62 | )
63 |
64 | ?>
65 |
66 |
93 |
--------------------------------------------------------------------------------
/modules/api/controllers/SaleController.php:
--------------------------------------------------------------------------------
1 | ContentNegotiator::class,
21 | 'formats' => [
22 | 'application/json' => Response::FORMAT_JSON,
23 | ],
24 | ],
25 | 'access' => [
26 | 'class' => AccessControl::class,
27 | 'rules' => [
28 | [
29 | 'actions' => ['week'],
30 | 'allow' => true,
31 | 'roles' => ['admin']
32 | ],
33 | [
34 | 'actions' => ['index'],
35 | 'allow' => true,
36 | 'roles' => ['cashier']
37 | ],
38 | ],
39 | ],
40 | 'verbs' => [
41 | 'class' => VerbFilter::class,
42 | 'actions' => [
43 | 'index' => ['GET'],
44 | ],
45 | ],
46 | ];
47 | }
48 |
49 | public function actionIndex($id)
50 | {
51 | $sale = $this->findModel($id);
52 | return $sale;
53 | }
54 |
55 | public function actionWeek()
56 | {
57 | $data = [
58 | 'dates' => [],
59 | 'amount_paid' => [
60 | 'label' => Yii::t('app', 'Sales value'),
61 | 'values' => []
62 | ],
63 | 'total_sale' => [
64 | 'label' => Yii::t('app', 'Total sales'),
65 | 'values' => []
66 | ]
67 | ];
68 |
69 | for ($i = 7; $i > 0; $i--) {
70 | $date = date('Y-m-d', strtotime("-$i day"));
71 | $start = $date.' 00:00:00';
72 | $end = $date.' 23:59:59';
73 | $amount_paid = Sale::find()
74 | ->andWhere(['between', 'sale_at', $start, $end])
75 | ->andWhere(['is_canceled' => 0])->sum('amount_paid');
76 |
77 | $total_sale = Sale::find()
78 | ->andWhere(['between', 'sale_at', $start, $end])
79 | ->andWhere(['is_canceled' => 0])->count();
80 |
81 | array_push($data['dates'], Yii::$app->formatter->asDate($date));
82 | array_push($data['amount_paid']['values'], (float) $amount_paid);
83 | array_push($data['total_sale']['values'], $total_sale);
84 | }
85 |
86 | return $data;
87 | }
88 |
89 | protected function findModel($id)
90 | {
91 | $model = Sale::find()
92 | ->andWhere(['sale.id' => $id])
93 | ->one();
94 |
95 | if ($model !== null)
96 | return $model;
97 |
98 | throw new NotFoundHttpException(Yii::t('app', 'Sale not exist.'));
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/modules/api/controllers/OrderItemController.php:
--------------------------------------------------------------------------------
1 | ContentNegotiator::class,
24 | 'formats' => [
25 | 'application/json' => Response::FORMAT_JSON,
26 | ],
27 | ],
28 | 'access' => [
29 | 'class' => AccessControl::class,
30 | 'rules' => [
31 | [
32 | 'actions' => ['create', 'delete', 'validation'],
33 | 'allow' => true,
34 | 'roles' => ['cashier']
35 | ],
36 | ],
37 | ],
38 | 'verbs' => [
39 | 'class' => VerbFilter::class,
40 | 'actions' => [
41 | 'create' => ['POST'],
42 | 'validation' => ['POST'],
43 | 'delete' => ['DELETE'],
44 | ],
45 | ],
46 | ];
47 | }
48 |
49 | public function beforeAction($action)
50 | {
51 | if (in_array($action->id, ['delete'])) {
52 | $this->enableCsrfValidation = false;
53 | }
54 |
55 | return parent::beforeAction($action);
56 | }
57 |
58 | public function actionCreate()
59 | {
60 | $model = new OrderItem();
61 |
62 | if ($model->load(Yii::$app->request->post()) && $model->save()) {
63 | return [
64 | 'orderItem' => $model,
65 | 'total' => $model->order->toArray()['total_value']
66 | ];
67 | }
68 |
69 | foreach($model->firstErrors as $erro) break;
70 | throw new BadRequestHttpException($erro);
71 | }
72 |
73 | public function actionDelete($id)
74 | {
75 | $model = $this->findModel($id);
76 | $orderId = $model->order_id;
77 |
78 | $model->delete($id);
79 |
80 | return [
81 | 'total' => Order::findOne($orderId)->total_value
82 | ];
83 | }
84 |
85 | public function actionValidation()
86 | {
87 | $model = new OrderItem();
88 |
89 | if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
90 | Yii::$app->response->format = Response::FORMAT_JSON;
91 | return ActiveForm::validate($model);
92 | }
93 | }
94 |
95 | protected function findModel($id)
96 | {
97 | $model = OrderItem::find()
98 | ->andWhere(['order_item.id' => $id])
99 | ->one();
100 |
101 | if ($model !== null)
102 | return $model;
103 |
104 | throw new NotFoundHttpException(Yii::t('app', 'Order not exist.'));
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yiisoft/yii2-app-basic",
3 | "description": "Yii 2 Basic Project Template",
4 | "keywords": ["yii2", "framework", "basic", "project template"],
5 | "homepage": "http://www.yiiframework.com/",
6 | "type": "project",
7 | "license": "BSD-3-Clause",
8 | "support": {
9 | "issues": "https://github.com/yiisoft/yii2/issues?state=open",
10 | "forum": "http://www.yiiframework.com/forum/",
11 | "wiki": "http://www.yiiframework.com/wiki/",
12 | "irc": "irc://irc.freenode.net/yii",
13 | "source": "https://github.com/yiisoft/yii2"
14 | },
15 | "minimum-stability": "stable",
16 | "require": {
17 | "php": ">=5.6.0",
18 | "yiisoft/yii2": "~2.0.14",
19 | "yiisoft/yii2-bootstrap4": "^2.0",
20 | "yiisoft/yii2-swiftmailer": "~2.0.0 || ~2.1.0",
21 | "rmrevin/yii2-fontawesome": "~3.5",
22 | "npm-asset/axios": "~0.19.0",
23 | "almasaeed2010/adminlte": "~3.0",
24 | "npm-asset/source-sans-pro" : "^3.012",
25 | "kartik-v/yii2-widget-datepicker": "^1.4",
26 | "kartik-v/yii2-icons": "^1.4",
27 | "kartik-v/yii2-number": "@dev",
28 | "kartik-v/yii2-widget-activeform": "^1.5",
29 | "kartik-v/yii2-widget-select2": "2.1.7",
30 | "kartik-v/yii2-widget-datetimepicker": "^1.4",
31 | "kartik-v/yii2-date-range": "^1.7",
32 | "yii2tech/ar-softdelete": "^1.0",
33 | "npm-asset/vue": "^2.6",
34 | "kartik-v/yii2-grid": "@dev",
35 | "npm-asset/chart.js": "^2.9",
36 | "kartik-v/yii2-export": "^1.4"
37 | },
38 | "require-dev": {
39 | "yiisoft/yii2-debug": "~2.1.0",
40 | "yiisoft/yii2-gii": "~2.1.0",
41 | "yiisoft/yii2-faker": "~2.0.0",
42 | "codeception/codeception": "^4.0",
43 | "codeception/verify": "~0.5.0 || ~1.1.0",
44 | "codeception/specify": "~0.4.6",
45 | "symfony/browser-kit": ">=2.7 <=4.2.4",
46 | "codeception/module-filesystem": "^1.0.0",
47 | "codeception/module-yii2": "^1.0.0",
48 | "codeception/module-asserts": "^1.0.0"
49 | },
50 | "config": {
51 | "process-timeout": 1800,
52 | "fxp-asset": {
53 | "enabled": false
54 | }
55 | },
56 | "scripts": {
57 | "post-install-cmd": [
58 | "yii\\composer\\Installer::postInstall"
59 | ],
60 | "post-create-project-cmd": [
61 | "yii\\composer\\Installer::postCreateProject",
62 | "yii\\composer\\Installer::postInstall"
63 | ]
64 | },
65 | "extra": {
66 | "yii\\composer\\Installer::postCreateProject": {
67 | "setPermission": [
68 | {
69 | "runtime": "0777",
70 | "web/assets": "0777",
71 | "yii": "0755"
72 | }
73 | ]
74 | },
75 | "yii\\composer\\Installer::postInstall": {
76 | "generateCookieValidationKey": [
77 | "config/web.php"
78 | ]
79 | },
80 | "asset-installer-paths": {
81 | "npm-asset-library": "vendor/npm",
82 | "bower-asset-library": "vendor/bower"
83 | }
84 | },
85 | "repositories": [
86 | {
87 | "type": "composer",
88 | "url": "https://asset-packagist.org"
89 | }
90 | ]
91 | }
92 |
--------------------------------------------------------------------------------
/models/Expense.php:
--------------------------------------------------------------------------------
1 | 'company',
35 | 'on' => 'expense.company_id = company.id'
36 | ]
37 | ];
38 | const SCENARIO_PAID = 'paid';
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | public static function tableName()
44 | {
45 | return 'expense';
46 | }
47 |
48 | public function behaviors()
49 | {
50 | return [
51 | [
52 | 'class' => TimestampBehavior::class,
53 | 'value' => new Expression('NOW()'),
54 | ],
55 | ];
56 | }
57 |
58 | /**
59 | * {@inheritdoc}
60 | */
61 | public function rules()
62 | {
63 | return [
64 | [['name', 'value', 'payday', 'company_id'], 'required'],
65 | [['paid_at', 'is_paid'], 'required', 'on' => self::SCENARIO_PAID],
66 | [['description'], 'string'],
67 | [['value'], DecimalValidator::class],
68 | [['payday', 'paid_at', 'created_at', 'updated_at'], 'safe'],
69 | [['payday'], 'default', 'value' => null],
70 | [['company_id'], 'integer'],
71 | [['is_paid'], 'boolean'],
72 | [['is_paid'], 'default', 'value' => 0],
73 | [['name'], 'string', 'max' => 64],
74 | [['company_id'], 'exist', 'skipOnError' => true, 'targetClass' => Company::class, 'targetAttribute' => ['company_id' => 'id']],
75 | ];
76 | }
77 |
78 | public function scenarios()
79 | {
80 | $scenarios = parent::scenarios();
81 | $scenarios[self::SCENARIO_PAID] = ['paid_at', 'is_paid'];
82 |
83 | return $scenarios;
84 | }
85 |
86 | /**
87 | * {@inheritdoc}
88 | */
89 | public function attributeLabels()
90 | {
91 | return [
92 | 'id' => Yii::t('app', 'ID'),
93 | 'name' => Yii::t('app', 'Name'),
94 | 'description' => Yii::t('app', 'Description'),
95 | 'value' => Yii::t('app', 'Value'),
96 | 'payday' => Yii::t('app', 'Payday'),
97 | 'is_paid' => Yii::t('app', 'Is Paid'),
98 | 'paid_at' => Yii::t('app', 'Paid At'),
99 | 'created_at' => Yii::t('app', 'Created At'),
100 | 'updated_at' => Yii::t('app', 'Updated At'),
101 | 'company_id' => Yii::t('app', 'Company'),
102 | ];
103 | }
104 |
105 | /**
106 | * Gets query for [[Company]].
107 | *
108 | * @return \yii\db\ActiveQuery
109 | */
110 | public function getCompany()
111 | {
112 | return $this->hasOne(Company::class, ['id' => 'company_id']);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/views/category/view.php:
--------------------------------------------------------------------------------
1 | title = $model->name;
12 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Categories'), 'url' => ['index']];
13 | $this->params['breadcrumbs'][] = $this->title;
14 |
15 | $this->registerCssFile('@web/css/detailView.css');
16 | $this->registerJsFile('@web/js/modal.js', ['depends' => [yii\web\JqueryAsset::class]]);
17 |
18 | Dialog::widget();
19 |
20 | ?>
21 |
22 | = $this->render('@app/views/layouts/modal.php', ['options' => ['title' => Yii::t('app', 'Category')]]) ?>
23 |
24 |
25 |
26 |
27 |
35 |
36 | = DetailView::widget([
37 | 'model' => $model,
38 | 'attributes' => [
39 | 'name'
40 | ],
41 | ]) ?>
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
58 |
59 | = DetailView::widget([
60 | 'model' => $model,
61 | 'attributes' => [
62 | 'created_at:datetime',
63 | 'updated_at:datetime',
64 | ],
65 | ]) ?>
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->id], ['value' => Url::to(['update', 'id' => $model->id]), 'class' => 'btn btn-primary btn-modal', 'data-toggle' => 'modal']) ?>
75 | = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->id], [
76 | 'class' => 'btn btn-danger',
77 | 'data' => [
78 | 'confirm' => Yii::t('app', 'Are you sure you want to delete this category?'),
79 | 'method' => 'post',
80 | ],
81 | ]) ?>
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/views/sale/_search.php:
--------------------------------------------------------------------------------
1 |
15 |
16 | ['index'],
18 | 'method' => 'get',
19 | ]); ?>
20 |
21 |
22 |
31 |
32 |
33 | = $form->field($model, 'order_id')->textInput(['maxlength' => 64]) ?>
34 |
35 | = $form->field($model, 'employee_id')->widget(Select2::class, [
36 | 'data' => ArrayHelper::map(Employee::find()->orderBy('full_name')->asArray()->all(), 'id', 'full_name'),
37 | 'options' => [
38 | 'placeholder' => Yii::t('app', 'All'),
39 | ],
40 | 'pluginOptions' => [
41 | 'allowClear' => true,
42 | ],
43 | ]) ?>
44 |
45 | = $form->field($model, 'sale_at')->widget(DateRangePicker::class, [
46 | 'presetDropdown' => true,
47 | 'convertFormat' => true,
48 | 'pluginOptions' => [
49 | 'timePicker' => true,
50 | 'timePickerIncrement' => 5,
51 | 'locale' => ['format' => 'd/m/Y h:i:s A']
52 | ],
53 | 'options' => ['placeholder' => Yii::t('app', 'Select range dates')]
54 | ]) ?>
55 |
56 | = $form->field($model, 'order_total_value', [
57 | 'addon' => [
58 | 'prepend' => [
59 | 'content' => Yii::$app->formatter->getCurrencySymbol(),
60 | 'options' => ['class' => 'alert-secondary'],
61 | ]
62 | ]
63 | ])->widget(NumberControl::class, [
64 | 'maskedInputOptions' => [
65 | 'allowMinus' => false,
66 | 'rightAlign' => false,
67 | ],
68 | 'displayOptions' => [
69 | 'class' => 'form-control rounded-right'
70 | ]
71 | ]) ?>
72 |
73 | = $form->field($model, 'setting_search_order_total_value')->radioList([
74 | 0 => Yii::t('app', 'Specific amount'),
75 | 1 => Yii::t('app', 'From amount'),
76 | 2 => Yii::t('app', 'Up to')
77 | ]) ?>
78 |
79 | = $form->field($model, 'status')->checkboxList([
80 | 'sold' => Yii::t('app', 'Sold'),
81 | 'canceled' => Yii::t('app', 'Canceled'),
82 | ])->label(Yii::t('app', 'Sale status')) ?>
83 |
84 |
85 |
86 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/views/variation/view.php:
--------------------------------------------------------------------------------
1 | title = $model->name;
11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Variations'), 'url' => ['index']];
12 | $this->params['breadcrumbs'][] = $this->title;
13 |
14 | $this->registerCssFile('@web/css/detailView.css');
15 | $this->registerJsFile('@web/js/modal.js', ['depends' => [yii\web\JqueryAsset::class]]);
16 |
17 | ?>
18 |
19 | = $this->render('@app/views/layouts/modal.php', ['options' => ['title' => Yii::t('app', 'Variation')]]) ?>
20 |
21 |
22 |
23 |
24 |
32 |
33 | = DetailView::widget([
34 | 'model' => $model,
35 | 'attributes' => [
36 | 'name',
37 | [
38 | 'attribute' => 'category.name',
39 | 'label' => Yii::t('app', 'Category')
40 | ]
41 | ],
42 | ]) ?>
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
59 |
60 | = DetailView::widget([
61 | 'model' => $model,
62 | 'attributes' => [
63 | 'created_at:datetime',
64 | 'updated_at:datetime',
65 | ],
66 | ]) ?>
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->id], ['value' => Url::to(['update', 'id' => $model->id]), 'class' => 'btn btn-primary btn-modal', 'data-toggle' => 'modal']) ?>
76 | = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->id], [
77 | 'class' => 'btn btn-danger',
78 | 'data' => [
79 | 'confirm' => Yii::t('app', 'Are you sure you want to delete this employee?'),
80 | 'method' => 'post',
81 | ],
82 | ]) ?>
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------